lunibee 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +3 -3
  2. package/dist/builders/attachment.d.ts +8 -4
  3. package/dist/builders/commands.d.ts +122 -149
  4. package/dist/builders/components.d.ts +10 -150
  5. package/dist/builders/embed.d.ts +66 -37
  6. package/dist/builders/index.d.ts +0 -4
  7. package/dist/builders/index.js +112 -221
  8. package/dist/builders/index.js.map +10 -9
  9. package/dist/collection/cache.d.ts +15 -6
  10. package/dist/collection/index.d.ts +92 -17
  11. package/dist/collection/index.js +471 -279
  12. package/dist/collection/index.js.map +4 -4
  13. package/dist/core/api.d.ts +130 -0
  14. package/dist/core/channel-create.d.ts +72 -0
  15. package/dist/core/commands.d.ts +232 -0
  16. package/dist/core/events.d.ts +24 -7
  17. package/dist/core/handles.d.ts +157 -0
  18. package/dist/core/index.d.ts +68 -4
  19. package/dist/core/index.js +2639 -493
  20. package/dist/core/index.js.map +47 -37
  21. package/dist/core/permissions.d.ts +7 -7
  22. package/dist/formatters/index.js +9 -5
  23. package/dist/formatters/index.js.map +3 -3
  24. package/dist/handlers/index.js +5 -10
  25. package/dist/handlers/index.js.map +3 -3
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.js +9527 -7809
  28. package/dist/index.js.map +63 -53
  29. package/dist/managers/advanced.d.ts +29 -1
  30. package/dist/managers/base.d.ts +22 -14
  31. package/dist/managers/emoji.d.ts +0 -2
  32. package/dist/managers/guild-resources.d.ts +13 -5
  33. package/dist/managers/guild.d.ts +40 -4
  34. package/dist/managers/index.d.ts +21 -13
  35. package/dist/managers/index.js +969 -386
  36. package/dist/managers/index.js.map +30 -25
  37. package/dist/managers/member.d.ts +9 -2
  38. package/dist/managers/message.d.ts +10 -15
  39. package/dist/managers/role.d.ts +18 -10
  40. package/dist/managers/thread.d.ts +68 -0
  41. package/dist/managers/user.d.ts +18 -0
  42. package/dist/rest/errors.d.ts +14 -7
  43. package/dist/rest/index.d.ts +24 -12
  44. package/dist/rest/index.js +70 -20
  45. package/dist/rest/index.js.map +9 -9
  46. package/dist/rest/redis.d.ts +1 -1
  47. package/dist/rest/route.d.ts +8 -4
  48. package/dist/rest/routes.d.ts +122 -48
  49. package/dist/rest/transport.d.ts +10 -5
  50. package/dist/rest/webhook.d.ts +9 -5
  51. package/dist/sharding/bus.d.ts +47 -19
  52. package/dist/sharding/cluster.d.ts +11 -0
  53. package/dist/sharding/gateway-bot.d.ts +18 -0
  54. package/dist/sharding/index.d.ts +64 -25
  55. package/dist/sharding/index.js +439 -124
  56. package/dist/sharding/index.js.map +19 -14
  57. package/dist/sharding/supervisor.d.ts +32 -0
  58. package/dist/sharding/transport.d.ts +45 -0
  59. package/dist/structures/audit-log.d.ts +99 -2
  60. package/dist/structures/base.d.ts +40 -23
  61. package/dist/structures/channels.d.ts +45 -14
  62. package/dist/structures/index.d.ts +8 -12
  63. package/dist/structures/index.js +280 -132
  64. package/dist/structures/index.js.map +21 -15
  65. package/dist/structures/interactions.d.ts +91 -63
  66. package/dist/structures/resources.d.ts +99 -52
  67. package/dist/structures/sticker.d.ts +33 -0
  68. package/dist/types/channel-kind.d.ts +26 -0
  69. package/dist/types/gateway.d.ts +24 -12
  70. package/dist/types/index.d.ts +115 -113
  71. package/dist/types/index.js +53 -48
  72. package/dist/types/index.js.map +6 -5
  73. package/dist/voice/index.d.ts +12 -4
  74. package/dist/voice/index.js +14 -18
  75. package/dist/voice/index.js.map +3 -3
  76. package/dist/ws/decoder.d.ts +6 -2
  77. package/dist/ws/dispatcher.d.ts +19 -0
  78. package/dist/ws/index.d.ts +61 -15
  79. package/dist/ws/index.js +194 -77
  80. package/dist/ws/index.js.map +13 -11
  81. package/dist/ws/protocol.d.ts +10 -2
  82. package/dist/ws/reconnect.d.ts +10 -0
  83. package/dist/ws/send-budget.d.ts +6 -2
  84. package/dist/ws/session.d.ts +14 -4
  85. package/dist/ws/state.d.ts +22 -11
  86. package/package.json +20 -16
@@ -5,7 +5,6 @@ var PermissionOverwriteEnum;
5
5
  PermissionOverwriteEnum[PermissionOverwriteEnum["Role"] = 0] = "Role";
6
6
  PermissionOverwriteEnum[PermissionOverwriteEnum["Member"] = 1] = "Member";
7
7
  })(PermissionOverwriteEnum ||= {});
8
- var PermissionOverwriteType = PermissionOverwriteEnum;
9
8
  var Permission = {
10
9
  createInstantInvite: 1n << 0n,
11
10
  kickMembers: 1n << 1n,
@@ -114,60 +113,7 @@ var Permissions = {
114
113
  PinMessages: 1n << 51n,
115
114
  BypassSlowmode: 1n << 52n
116
115
  };
117
- var PermissionFlagsBits = {
118
- CreateInstantInvite: 1n << 0n,
119
- KickMembers: 1n << 1n,
120
- BanMembers: 1n << 2n,
121
- Administrator: 1n << 3n,
122
- ManageChannels: 1n << 4n,
123
- ManageGuild: 1n << 5n,
124
- AddReactions: 1n << 6n,
125
- ViewAuditLog: 1n << 7n,
126
- PrioritySpeaker: 1n << 8n,
127
- Stream: 1n << 9n,
128
- ViewChannel: 1n << 10n,
129
- SendMessages: 1n << 11n,
130
- SendTTSMessages: 1n << 12n,
131
- ManageMessages: 1n << 13n,
132
- EmbedLinks: 1n << 14n,
133
- AttachFiles: 1n << 15n,
134
- ReadMessageHistory: 1n << 16n,
135
- MentionEveryone: 1n << 17n,
136
- UseExternalEmojis: 1n << 18n,
137
- ViewGuildInsights: 1n << 19n,
138
- Connect: 1n << 20n,
139
- Speak: 1n << 21n,
140
- MuteMembers: 1n << 22n,
141
- DeafenMembers: 1n << 23n,
142
- MoveMembers: 1n << 24n,
143
- UseVAD: 1n << 25n,
144
- ChangeNickname: 1n << 26n,
145
- ManageNicknames: 1n << 27n,
146
- ManageRoles: 1n << 28n,
147
- ManageWebhooks: 1n << 29n,
148
- ManageGuildExpressions: 1n << 30n,
149
- UseApplicationCommands: 1n << 31n,
150
- RequestToSpeak: 1n << 32n,
151
- ManageEvents: 1n << 33n,
152
- ManageThreads: 1n << 34n,
153
- CreatePublicThreads: 1n << 35n,
154
- CreatePrivateThreads: 1n << 36n,
155
- UseExternalStickers: 1n << 37n,
156
- SendMessagesInThreads: 1n << 38n,
157
- UseEmbeddedActivities: 1n << 39n,
158
- ModerateMembers: 1n << 40n,
159
- ViewCreatorMonetizationAnalytics: 1n << 41n,
160
- UseSoundboard: 1n << 42n,
161
- CreateGuildExpressions: 1n << 43n,
162
- CreateEvents: 1n << 44n,
163
- UseExternalSounds: 1n << 45n,
164
- SendVoiceMessages: 1n << 46n,
165
- SetVoiceChannelStatus: 1n << 48n,
166
- SendPolls: 1n << 49n,
167
- UseExternalApps: 1n << 50n,
168
- PinMessages: 1n << 51n,
169
- BypassSlowmode: 1n << 52n
170
- };
116
+ var PermissionFlagsBits = Permissions;
171
117
 
172
118
  class PermissionSet {
173
119
  bitfield;
@@ -468,6 +414,9 @@ function computePermissions(context) {
468
414
  const member = overwrites.find((o) => o.type === 1 /* Member */ && o.id === context.memberId);
469
415
  if (member)
470
416
  base = base & ~bits(member.deny) | bits(member.allow);
417
+ const until = context.timedOutUntil instanceof Date ? context.timedOutUntil.getTime() : context.timedOutUntil ?? 0;
418
+ if (until > Date.now())
419
+ base &= Permission.viewChannel | Permission.readMessageHistory;
471
420
  return new PermissionSet(base);
472
421
  }
473
422
  // packages/core/src/events.ts
@@ -688,6 +637,152 @@ class Collector extends EventEmitter {
688
637
  });
689
638
  }
690
639
  }
640
+ // packages/core/src/api.ts
641
+ var SAFE_WORD = /^[a-z][a-zA-Z0-9]*$/;
642
+ var SAFE_SEGMENT = /^[A-Za-z0-9_@.%-]+$/;
643
+ var PAGING = {
644
+ members: {
645
+ limit: 1000,
646
+ cursor: "after",
647
+ idOf: (member) => member.user.id
648
+ },
649
+ bans: {
650
+ limit: 1000,
651
+ cursor: "after",
652
+ idOf: (ban) => ban.user.id
653
+ },
654
+ messages: { limit: 100, cursor: "before" },
655
+ guilds: { limit: 200, cursor: "after" }
656
+ };
657
+ var DEFAULT_PAGING = { limit: 100, cursor: "after" };
658
+ var DEFAULT_MAX = 1e4;
659
+ function kebab(word) {
660
+ return word.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
661
+ }
662
+ function idSegment(id) {
663
+ if (typeof id !== "string" && typeof id !== "number" && typeof id !== "bigint")
664
+ throw new TypeError("A route id must be a string, number or bigint.");
665
+ const text = String(id);
666
+ if (text === "")
667
+ throw new TypeError("A route id cannot be empty.");
668
+ return encodeURIComponent(text).replace(/%40/g, "@");
669
+ }
670
+ function defaultId(item) {
671
+ const record = item;
672
+ const id = record?.id ?? record?.user?.id;
673
+ if (typeof id !== "string")
674
+ throw new TypeError("Cannot find an id on a page item; pass idOf to pages().");
675
+ return id;
676
+ }
677
+ function node(rest, segments) {
678
+ const path = `/${segments.join("/")}`;
679
+ const send = (method, hasBody) => (first, second) => rest.request(method, path, hasBody ? first : undefined, (hasBody ? second : first) ?? {});
680
+ const last = segments[segments.length - 1] ?? "";
681
+ const pages = async function* (options = {}) {
682
+ const paging = { ...DEFAULT_PAGING, ...PAGING[last] };
683
+ const cursorName = options.cursor ?? paging.cursor;
684
+ const limit = options.limit ?? paging.limit;
685
+ const idOf = options.idOf ?? paging.idOf ?? defaultId;
686
+ let cursor = options.start;
687
+ for (;; ) {
688
+ const batch = await rest.request("GET", path, undefined, {
689
+ signal: options.signal,
690
+ query: {
691
+ ...options.query,
692
+ limit,
693
+ ...cursor === undefined ? {} : { [cursorName]: cursor }
694
+ }
695
+ });
696
+ if (!Array.isArray(batch) || batch.length === 0)
697
+ return;
698
+ yield batch;
699
+ if (batch.length < limit)
700
+ return;
701
+ cursor = idOf(batch[batch.length - 1]);
702
+ }
703
+ };
704
+ const all = async (options = {}) => {
705
+ const max = options.max ?? DEFAULT_MAX;
706
+ const paging = { ...DEFAULT_PAGING, ...PAGING[last] };
707
+ const items = [];
708
+ if (max <= 0)
709
+ return items;
710
+ for await (const page of pages({
711
+ ...options,
712
+ limit: Math.min(options.limit ?? paging.limit, max)
713
+ })) {
714
+ items.push(...page);
715
+ if (items.length >= max)
716
+ break;
717
+ }
718
+ return items.slice(0, max);
719
+ };
720
+ const members = {
721
+ path,
722
+ get: send("GET", false),
723
+ post: send("POST", true),
724
+ put: send("PUT", true),
725
+ patch: send("PATCH", true),
726
+ delete: send("DELETE", false),
727
+ pages,
728
+ all,
729
+ toString: () => path
730
+ };
731
+ return new Proxy(function route() {}, {
732
+ get(_target, property) {
733
+ if (typeof property !== "string")
734
+ return;
735
+ if (property in members)
736
+ return members[property];
737
+ if (property === "then" || !SAFE_WORD.test(property))
738
+ return;
739
+ return node(rest, [...segments, kebab(property)]);
740
+ },
741
+ apply(_target, _this, args) {
742
+ if (args.length !== 1)
743
+ throw new TypeError(`Call a route with exactly one id, e.g. ${path}(id).`);
744
+ return node(rest, [...segments, idSegment(args[0])]);
745
+ }
746
+ });
747
+ }
748
+ function createApi(rest) {
749
+ const root = node(rest, []);
750
+ return new Proxy(root, {
751
+ get(target, property, receiver) {
752
+ if (property === "to")
753
+ return (...segments) => {
754
+ if (segments.length === 0)
755
+ throw new TypeError("to() needs at least one segment.");
756
+ const cleaned = segments.map((segment) => {
757
+ const text = String(segment);
758
+ if (!SAFE_SEGMENT.test(text))
759
+ throw new TypeError(`Invalid route segment: ${JSON.stringify(text)}.`);
760
+ return text;
761
+ });
762
+ return node(rest, cleaned);
763
+ };
764
+ return Reflect.get(target, property, receiver);
765
+ }
766
+ });
767
+ }
768
+ // packages/types/src/channel-kind.ts
769
+ var ChannelKinds = {
770
+ text: [0],
771
+ dm: [1],
772
+ voice: [2],
773
+ "group-dm": [3],
774
+ category: [4],
775
+ announcement: [5],
776
+ thread: [10, 11, 12],
777
+ stage: [13],
778
+ directory: [14],
779
+ forum: [15],
780
+ media: [16]
781
+ };
782
+ var KIND_OF_TYPE = new Map(Object.keys(ChannelKinds).flatMap((kind) => ChannelKinds[kind].map((type) => [type, kind])));
783
+ function channelKindOf(type) {
784
+ return KIND_OF_TYPE.get(type) ?? "unknown";
785
+ }
691
786
  // packages/types/src/gateway.ts
692
787
  var GatewayIntentBits = {
693
788
  Guilds: 1 << 0,
@@ -742,220 +837,1427 @@ var IntentBits = {
742
837
  };
743
838
  var Intents = IntentBits;
744
839
  function resolveGatewayIntents(intents) {
745
- if (typeof intents === "number")
746
- return intents;
747
- if (intents && typeof intents === "object" && !Array.isArray(intents))
748
- return intents.bitfield;
749
- if (Array.isArray(intents)) {
750
- return intents.reduce((acc, intent) => {
751
- return acc | resolveGatewayIntents(intent);
752
- }, 0);
753
- }
754
- if (typeof intents === "string") {
755
- if (intents in GatewayIntentBits)
756
- return GatewayIntentBits[intents];
757
- if (intents in IntentBits)
758
- return IntentBits[intents];
759
- const lower = intents.charAt(0).toLowerCase() + intents.slice(1);
760
- if (lower in IntentBits)
761
- return IntentBits[lower];
762
- const num = Number(intents);
763
- if (!Number.isNaN(num))
764
- return num;
765
- }
766
- return 0;
840
+ if (!Array.isArray(intents))
841
+ throw new TypeError("Gateway intents must be an array, e.g. [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages].");
842
+ return intents.reduce((acc, intent) => acc | resolveIntent(intent), 0);
843
+ }
844
+ function resolveIntent(intent) {
845
+ if (typeof intent === "number")
846
+ return intent;
847
+ const bits = {
848
+ ...IntentBits,
849
+ ...GatewayIntentBits
850
+ };
851
+ const lower = intent.charAt(0).toLowerCase() + intent.slice(1);
852
+ if (Object.hasOwn(bits, intent))
853
+ return bits[intent];
854
+ if (Object.hasOwn(bits, lower))
855
+ return bits[lower];
856
+ const num = Number(intent);
857
+ return Number.isNaN(num) ? 0 : num;
858
+ }
859
+ function resolveInput(bits) {
860
+ return resolveGatewayIntents(Array.isArray(bits) ? bits : [bits]);
767
861
  }
768
862
 
769
863
  class IntentsBitField {
770
864
  static Flags = GatewayIntentBits;
771
865
  bitfield;
772
- constructor(bits = 0) {
773
- this.bitfield = resolveGatewayIntents(bits);
866
+ constructor(bits = []) {
867
+ this.bitfield = resolveInput(bits);
774
868
  }
775
869
  has(bits) {
776
- const resolved = resolveGatewayIntents(bits);
870
+ const resolved = resolveInput(bits);
777
871
  return (this.bitfield & resolved) === resolved;
778
872
  }
779
873
  any(bits) {
780
- return (this.bitfield & resolveGatewayIntents(bits)) !== 0;
874
+ return (this.bitfield & resolveInput(bits)) !== 0;
781
875
  }
782
876
  add(...bits) {
783
877
  for (const bit of bits)
784
- this.bitfield |= resolveGatewayIntents(bit);
878
+ this.bitfield |= resolveInput(bit);
785
879
  return this;
786
880
  }
787
881
  remove(...bits) {
788
882
  for (const bit of bits)
789
- this.bitfield &= ~resolveGatewayIntents(bit);
883
+ this.bitfield &= ~resolveInput(bit);
884
+ return this;
885
+ }
886
+ toArray() {
887
+ return Object.keys(GatewayIntentBits).filter((name) => this.has(GatewayIntentBits[name]));
888
+ }
889
+ equals(bits) {
890
+ return this.bitfield === resolveInput(bits);
891
+ }
892
+ valueOf() {
893
+ return this.bitfield;
894
+ }
895
+ toJSON() {
896
+ return this.bitfield;
897
+ }
898
+ }
899
+ // packages/types/src/index.ts
900
+ var InteractionResponseEnum = {
901
+ Pong: 1,
902
+ ChannelMessage: 4,
903
+ DeferredChannelMessage: 5,
904
+ DeferredMessageUpdate: 6,
905
+ MessageUpdate: 7,
906
+ Autocomplete: 8,
907
+ Modal: 9
908
+ };
909
+ var ActivityEnum = {
910
+ Playing: 0,
911
+ Streaming: 1,
912
+ Listening: 2,
913
+ Watching: 3,
914
+ Custom: 4,
915
+ Competing: 5
916
+ };
917
+
918
+ // packages/builders/src/components.ts
919
+ var IS_COMPONENTS_V2 = 1 << 15;
920
+ // packages/builders/src/commands.ts
921
+ var ApplicationCommandOptionEnum = {
922
+ Subcommand: 1,
923
+ SubcommandGroup: 2,
924
+ String: 3,
925
+ Integer: 4,
926
+ Boolean: 5,
927
+ User: 6,
928
+ Channel: 7,
929
+ Role: 8,
930
+ Mentionable: 9,
931
+ Number: 10,
932
+ Attachment: 11
933
+ };
934
+ function validateContexts(contexts) {
935
+ if (contexts.some((context) => context !== 0 && context !== 1 && context !== 2))
936
+ throw new RangeError("Command contexts must be 0, 1 or 2.");
937
+ }
938
+
939
+ class CreateSlashCommand {
940
+ #data = { type: 1 };
941
+ setName(name) {
942
+ validateName(name, "Command name");
943
+ this.#data.name = name;
944
+ return this;
945
+ }
946
+ setDescription(description) {
947
+ validateText(description, "Command description", 100);
948
+ this.#data.description = description;
949
+ return this;
950
+ }
951
+ setContexts(...contexts) {
952
+ validateContexts(contexts);
953
+ this.#data.contexts = [...contexts];
954
+ return this;
955
+ }
956
+ setNSFW(nsfw = true) {
957
+ this.#data.nsfw = nsfw;
958
+ return this;
959
+ }
960
+ setNsfw(nsfw = true) {
961
+ return this.setNSFW(nsfw);
962
+ }
963
+ setDefaultMemberPermissions(permissions) {
964
+ this.#data.default_member_permissions = permissions === null ? null : BigInt(permissions).toString();
965
+ return this;
966
+ }
967
+ setIntegrationTypes(...types) {
968
+ this.#data.integration_types = [...types];
969
+ return this;
970
+ }
971
+ addStringOption(configure) {
972
+ return this.addOption(configure(new CreateStringOption));
973
+ }
974
+ addIntegerOption(configure) {
975
+ return this.addOption(configure(new CreateIntegerOption));
976
+ }
977
+ addNumberOption(configure) {
978
+ return this.addOption(configure(new CreateNumberOption));
979
+ }
980
+ addBooleanOption(configure) {
981
+ return this.addOption(configure(new CreateBooleanOption));
982
+ }
983
+ addUserOption(configure) {
984
+ return this.addOption(configure(new CreateUserOption));
985
+ }
986
+ addChannelOption(configure) {
987
+ return this.addOption(configure(new CreateChannelOption));
988
+ }
989
+ addRoleOption(configure) {
990
+ return this.addOption(configure(new CreateRoleOption));
991
+ }
992
+ addMentionableOption(configure) {
993
+ return this.addOption(configure(new CreateMentionableOption));
994
+ }
995
+ addAttachmentOption(configure) {
996
+ return this.addOption(configure(new CreateAttachmentOption));
997
+ }
998
+ addSubcommand(configure) {
999
+ return this.addOption(configure(new CreateSubcommand));
1000
+ }
1001
+ addSubcommandGroup(configure) {
1002
+ return this.addOption(configure(new CreateSubcommandGroup));
1003
+ }
1004
+ toJSON() {
1005
+ return structuredClone(this.#data);
1006
+ }
1007
+ addOption(option) {
1008
+ this.#data.options = appendOption(this.#data.options, option.toJSON(), "application command");
1009
+ return this;
1010
+ }
1011
+ }
1012
+
1013
+ class CreateCommandOption {
1014
+ data;
1015
+ constructor(type) {
1016
+ this.data = { type };
1017
+ }
1018
+ setName(name) {
1019
+ validateName(name, "Option name");
1020
+ this.data.name = name;
1021
+ return this;
1022
+ }
1023
+ setDescription(description) {
1024
+ validateText(description, "Option description", 100);
1025
+ this.data.description = description;
1026
+ return this;
1027
+ }
1028
+ setRequired(required = true) {
1029
+ this.data.required = required;
1030
+ return this;
1031
+ }
1032
+ setAutocomplete(enabled = true) {
1033
+ this.data.autocomplete = enabled;
1034
+ return this;
1035
+ }
1036
+ toJSON() {
1037
+ return structuredClone(this.data);
1038
+ }
1039
+ }
1040
+
1041
+ class CreateStringOption extends CreateCommandOption {
1042
+ constructor() {
1043
+ super(ApplicationCommandOptionEnum.String);
1044
+ }
1045
+ addChoices(...choices) {
1046
+ for (const choice of choices)
1047
+ validateText(choice.value, "Choice value", 100);
1048
+ appendChoices(this.data, choices);
1049
+ return this;
1050
+ }
1051
+ setMinLength(value) {
1052
+ validateIntegerRange(value, 0, 6000, "min_length");
1053
+ this.data.min_length = value;
1054
+ return this;
1055
+ }
1056
+ setMaxLength(value) {
1057
+ validateIntegerRange(value, 1, 6000, "max_length");
1058
+ if (this.data.min_length !== undefined && this.data.min_length > value)
1059
+ throw new RangeError("min_length cannot exceed max_length.");
1060
+ this.data.max_length = value;
1061
+ return this;
1062
+ }
1063
+ }
1064
+
1065
+ class CreateNumericOption extends CreateCommandOption {
1066
+ #integer;
1067
+ constructor(type, integer) {
1068
+ super(type);
1069
+ this.#integer = integer;
1070
+ }
1071
+ setMinValue(value) {
1072
+ validateNumberRange(value, -MAX_SAFE, MAX_SAFE, "min_value", this.#integer);
1073
+ this.data.min_value = value;
1074
+ return this;
1075
+ }
1076
+ setMaxValue(value) {
1077
+ validateNumberRange(value, -MAX_SAFE, MAX_SAFE, "max_value", this.#integer);
1078
+ const min = this.data.min_value;
1079
+ if (min !== undefined && min > value)
1080
+ throw new RangeError("min_value cannot exceed max_value.");
1081
+ this.data.max_value = value;
1082
+ return this;
1083
+ }
1084
+ setAutocomplete(enabled = true) {
1085
+ if (enabled && Array.isArray(this.data.choices) && this.data.choices.length)
1086
+ throw new RangeError("Autocomplete options cannot define choices.");
1087
+ this.data.autocomplete = enabled;
1088
+ return this;
1089
+ }
1090
+ addChoices(...choices) {
1091
+ appendChoices(this.data, choices);
1092
+ return this;
1093
+ }
1094
+ }
1095
+
1096
+ class CreateIntegerOption extends CreateNumericOption {
1097
+ constructor() {
1098
+ super(ApplicationCommandOptionEnum.Integer, true);
1099
+ }
1100
+ }
1101
+
1102
+ class CreateNumberOption extends CreateNumericOption {
1103
+ constructor() {
1104
+ super(ApplicationCommandOptionEnum.Number, false);
1105
+ }
1106
+ }
1107
+
1108
+ class CreateBooleanOption extends CreateCommandOption {
1109
+ constructor() {
1110
+ super(ApplicationCommandOptionEnum.Boolean);
1111
+ }
1112
+ }
1113
+
1114
+ class CreateUserOption extends CreateCommandOption {
1115
+ constructor() {
1116
+ super(ApplicationCommandOptionEnum.User);
1117
+ }
1118
+ }
1119
+
1120
+ class CreateRoleOption extends CreateCommandOption {
1121
+ constructor() {
1122
+ super(ApplicationCommandOptionEnum.Role);
1123
+ }
1124
+ }
1125
+
1126
+ class CreateMentionableOption extends CreateCommandOption {
1127
+ constructor() {
1128
+ super(ApplicationCommandOptionEnum.Mentionable);
1129
+ }
1130
+ }
1131
+
1132
+ class CreateAttachmentOption extends CreateCommandOption {
1133
+ constructor() {
1134
+ super(ApplicationCommandOptionEnum.Attachment);
1135
+ }
1136
+ }
1137
+
1138
+ class CreateChannelOption extends CreateCommandOption {
1139
+ constructor() {
1140
+ super(ApplicationCommandOptionEnum.Channel);
1141
+ }
1142
+ addChannelTypes(...types) {
1143
+ if (types.some((type) => !Number.isInteger(type) || type < 0))
1144
+ throw new RangeError("Channel types must be non-negative integers.");
1145
+ this.data.channel_types = [...new Set(types)];
1146
+ return this;
1147
+ }
1148
+ }
1149
+
1150
+ class CreateSubcommand extends CreateCommandOption {
1151
+ constructor() {
1152
+ super(ApplicationCommandOptionEnum.Subcommand);
1153
+ }
1154
+ addStringOption(configure) {
1155
+ return this.addChildOption(configure(new CreateStringOption));
1156
+ }
1157
+ addIntegerOption(configure) {
1158
+ return this.addChildOption(configure(new CreateIntegerOption));
1159
+ }
1160
+ addNumberOption(configure) {
1161
+ return this.addChildOption(configure(new CreateNumberOption));
1162
+ }
1163
+ addBooleanOption(configure) {
1164
+ return this.addChildOption(configure(new CreateBooleanOption));
1165
+ }
1166
+ addUserOption(configure) {
1167
+ return this.addChildOption(configure(new CreateUserOption));
1168
+ }
1169
+ addChannelOption(configure) {
1170
+ return this.addChildOption(configure(new CreateChannelOption));
1171
+ }
1172
+ addRoleOption(configure) {
1173
+ return this.addChildOption(configure(new CreateRoleOption));
1174
+ }
1175
+ addMentionableOption(configure) {
1176
+ return this.addChildOption(configure(new CreateMentionableOption));
1177
+ }
1178
+ addAttachmentOption(configure) {
1179
+ return this.addChildOption(configure(new CreateAttachmentOption));
1180
+ }
1181
+ addChildOption(option) {
1182
+ const options = this.data.options ?? [];
1183
+ if (options.some((existing) => existing.type === ApplicationCommandOptionEnum.Subcommand))
1184
+ throw new RangeError("Subcommands cannot contain nested subcommands.");
1185
+ this.data.options = appendOption(options, option.toJSON(), "subcommand");
1186
+ return this;
1187
+ }
1188
+ }
1189
+
1190
+ class CreateSubcommandGroup extends CreateCommandOption {
1191
+ constructor() {
1192
+ super(ApplicationCommandOptionEnum.SubcommandGroup);
1193
+ }
1194
+ addSubcommand(configure) {
1195
+ const options = this.data.options ?? [];
1196
+ if (options.length >= 25)
1197
+ throw new RangeError("A subcommand group cannot contain more than 25 subcommands.");
1198
+ const payload = configure(new CreateSubcommand).toJSON();
1199
+ if (options.some((existing) => existing.name === payload.name))
1200
+ throw new RangeError(`Duplicate subcommand name: ${String(payload.name)}.`);
1201
+ if (payload.type !== ApplicationCommandOptionEnum.Subcommand)
1202
+ throw new TypeError("Subcommand group children must be subcommands.");
1203
+ options.push(payload);
1204
+ this.data.options = options;
790
1205
  return this;
791
1206
  }
792
- toArray() {
793
- return Object.keys(GatewayIntentBits).filter((name) => this.has(GatewayIntentBits[name]));
1207
+ }
1208
+ var MAX_SAFE = Number.MAX_SAFE_INTEGER;
1209
+ function appendOption(options, payload, parent) {
1210
+ const list = options ?? [];
1211
+ if (list.length >= 25)
1212
+ throw new RangeError(`${parent === "subcommand" ? "A subcommand" : "An application command"} cannot contain more than 25 options.`);
1213
+ if (list.some((existing) => existing.name === payload.name))
1214
+ throw new RangeError(`Duplicate option name: ${String(payload.name)}.`);
1215
+ if (payload.required === true && list.some((existing) => existing.required !== true))
1216
+ throw new RangeError(`Required ${parent} options must be placed before optional options.`);
1217
+ return [...list, payload];
1218
+ }
1219
+ function appendChoices(data, choices) {
1220
+ const current = data.choices ?? [];
1221
+ if (!choices.length)
1222
+ throw new TypeError("At least one choice is required.");
1223
+ if (data.autocomplete === true)
1224
+ throw new RangeError("Autocomplete options cannot define choices.");
1225
+ if (current.length + choices.length > 25)
1226
+ throw new RangeError("An option cannot contain more than 25 choices.");
1227
+ for (const choice of choices)
1228
+ validateText(choice.name, "Choice name", 100);
1229
+ data.choices = [...current, ...choices.map((choice) => ({ ...choice }))];
1230
+ }
1231
+ function validateName(value, field) {
1232
+ if (!/^[\p{L}\p{N}_-]{1,32}$/u.test(value) || value !== value.toLowerCase())
1233
+ throw new RangeError(`${field} must contain 1-32 lowercase letters, numbers, underscores, or hyphens.`);
1234
+ }
1235
+ function validateText(value, field, max) {
1236
+ if (typeof value !== "string" || !value.trim() || value.length > max)
1237
+ throw new RangeError(`${field} must contain 1-${max} characters.`);
1238
+ }
1239
+ function validateIntegerRange(value, minimum, maximum, field) {
1240
+ if (!Number.isInteger(value) || value < minimum || value > maximum)
1241
+ throw new RangeError(`${field} must be an integer between ${minimum} and ${maximum}.`);
1242
+ }
1243
+ function validateNumberRange(value, minimum, maximum, field, integerOnly = true) {
1244
+ if (!Number.isFinite(value) || integerOnly && !Number.isInteger(value) || value < minimum || value > maximum)
1245
+ throw new RangeError(`${field} must be ${integerOnly ? "an integer" : "a number"} between ${minimum} and ${maximum}.`);
1246
+ }
1247
+ // packages/utils/src/args.ts
1248
+ var SNOWFLAKE = /^\d{16,22}$/;
1249
+ var TRUE_WORDS = new Set([
1250
+ "true",
1251
+ "yes",
1252
+ "y",
1253
+ "on",
1254
+ "1",
1255
+ "enable",
1256
+ "enabled"
1257
+ ]);
1258
+ var FALSE_WORDS = new Set([
1259
+ "false",
1260
+ "no",
1261
+ "n",
1262
+ "off",
1263
+ "0",
1264
+ "disable",
1265
+ "disabled"
1266
+ ]);
1267
+ function tokenizeArgs(input) {
1268
+ const args = [];
1269
+ let current = "";
1270
+ let quote = null;
1271
+ let started = false;
1272
+ for (let index = 0;index < input.length; index++) {
1273
+ const character = input[index];
1274
+ if (character === "\\" && index + 1 < input.length) {
1275
+ current += input[++index];
1276
+ started = true;
1277
+ } else if (quote) {
1278
+ if (character === quote)
1279
+ quote = null;
1280
+ else
1281
+ current += character;
1282
+ } else if (character === '"' || character === "'") {
1283
+ if (started)
1284
+ current += character;
1285
+ else {
1286
+ quote = character;
1287
+ started = true;
1288
+ }
1289
+ } else if (/\s/.test(character)) {
1290
+ if (started)
1291
+ args.push(current);
1292
+ current = "";
1293
+ started = false;
1294
+ } else {
1295
+ current += character;
1296
+ started = true;
1297
+ }
1298
+ }
1299
+ if (started)
1300
+ args.push(current);
1301
+ return args;
1302
+ }
1303
+ function parseUserMention(value) {
1304
+ const id = /^<@!?(\d{16,22})>$/.exec(value)?.[1] ?? value;
1305
+ return SNOWFLAKE.test(id) ? id : null;
1306
+ }
1307
+ function parseRoleMention(value) {
1308
+ const id = /^<@&(\d{16,22})>$/.exec(value)?.[1] ?? value;
1309
+ return SNOWFLAKE.test(id) ? id : null;
1310
+ }
1311
+ function parseChannelMention(value) {
1312
+ const id = /^<#(\d{16,22})>$/.exec(value)?.[1] ?? value;
1313
+ return SNOWFLAKE.test(id) ? id : null;
1314
+ }
1315
+ function parseMentionable(value) {
1316
+ const role = /^<@&(\d{16,22})>$/.exec(value)?.[1];
1317
+ if (role)
1318
+ return { id: role, type: "role" };
1319
+ const user = parseUserMention(value);
1320
+ return user ? { id: user, type: "user" } : null;
1321
+ }
1322
+ function readOne(spec, raw) {
1323
+ switch (spec.type) {
1324
+ case "string":
1325
+ case "rest":
1326
+ if (spec.maxLength !== undefined && raw.length > spec.maxLength)
1327
+ return {
1328
+ error: `must be at most ${spec.maxLength} characters`
1329
+ };
1330
+ if (spec.choices && !spec.choices.some((choice) => String(choice).toLowerCase() === raw.toLowerCase()))
1331
+ return { error: `must be one of: ${spec.choices.join(", ")}` };
1332
+ return { value: raw };
1333
+ case "integer":
1334
+ case "number": {
1335
+ const value = Number(raw);
1336
+ if (!Number.isFinite(value) || raw.trim() === "")
1337
+ return { error: "must be a number" };
1338
+ if (spec.type === "integer" && !Number.isSafeInteger(value))
1339
+ return { error: "must be a whole number" };
1340
+ if (spec.min !== undefined && value < spec.min)
1341
+ return { error: `must be at least ${spec.min}` };
1342
+ if (spec.max !== undefined && value > spec.max)
1343
+ return { error: `must be at most ${spec.max}` };
1344
+ if (spec.choices && !spec.choices.includes(value))
1345
+ return { error: `must be one of: ${spec.choices.join(", ")}` };
1346
+ return { value };
1347
+ }
1348
+ case "boolean": {
1349
+ const word = raw.toLowerCase();
1350
+ if (TRUE_WORDS.has(word))
1351
+ return { value: true };
1352
+ if (FALSE_WORDS.has(word))
1353
+ return { value: false };
1354
+ return { error: "must be yes or no" };
1355
+ }
1356
+ case "user": {
1357
+ const id = parseUserMention(raw);
1358
+ return id ? { value: id } : { error: "must be a user mention or ID" };
1359
+ }
1360
+ case "role": {
1361
+ const id = parseRoleMention(raw);
1362
+ return id ? { value: id } : { error: "must be a role mention or ID" };
1363
+ }
1364
+ case "channel": {
1365
+ const id = parseChannelMention(raw);
1366
+ return id ? { value: id } : { error: "must be a channel mention or ID" };
1367
+ }
1368
+ case "mentionable": {
1369
+ const value = parseMentionable(raw);
1370
+ return value ? { value } : { error: "must be a user or role mention" };
1371
+ }
1372
+ }
1373
+ }
1374
+ function parsePrefixArgs(input, specs) {
1375
+ const args = typeof input === "string" ? tokenizeArgs(input) : [...input];
1376
+ const values = {};
1377
+ let position = 0;
1378
+ for (const [index, spec] of specs.entries()) {
1379
+ if (spec.type === "rest" && index !== specs.length - 1)
1380
+ throw new TypeError(`The rest argument "${spec.name}" must be the last one.`);
1381
+ const raw = spec.type === "rest" ? args.slice(position).join(" ") : args[position];
1382
+ if (raw === undefined || raw === "") {
1383
+ if (spec.required)
1384
+ return { ok: false, arg: spec.name, error: "is required" };
1385
+ values[spec.name] = undefined;
1386
+ continue;
1387
+ }
1388
+ const read = readOne(spec, raw);
1389
+ if ("error" in read)
1390
+ return { ok: false, arg: spec.name, error: read.error };
1391
+ values[spec.name] = read.value;
1392
+ position += spec.type === "rest" ? args.length : 1;
1393
+ }
1394
+ return { ok: true, values };
1395
+ }
1396
+ // packages/core/src/commands.ts
1397
+ function settings(def) {
1398
+ return def.config;
1399
+ }
1400
+ function define(kind, config, words) {
1401
+ return { kind, config: config ?? {}, words };
1402
+ }
1403
+ var option = {
1404
+ string: (config) => define("string", config),
1405
+ integer: (config) => define("integer", config),
1406
+ number: (config) => define("number", config),
1407
+ boolean: (config) => define("boolean", config),
1408
+ user: (config) => define("user", config),
1409
+ role: (config) => define("role", config),
1410
+ channel: (config) => define("channel", config),
1411
+ mentionable: (config) => define("mentionable", config),
1412
+ attachment: (config) => define("attachment", config),
1413
+ choice: (words, config) => define("string", config, words)
1414
+ };
1415
+ var COMMAND_NAME = /^[-_\p{Ll}\p{Lo}\p{N}\p{sc=Deva}\p{sc=Thai}]{1,32}$/u;
1416
+ var channelTypes = (kinds) => [
1417
+ ...new Set(kinds.flatMap((kind) => ChannelKinds[kind]))
1418
+ ];
1419
+ function named(builder, name, def) {
1420
+ const config = settings(def);
1421
+ return builder.setName(name).setDescription(config.description ?? name).setRequired(config.required === true);
1422
+ }
1423
+ function addOptionTo(slash, name, def) {
1424
+ switch (def.kind) {
1425
+ case "string": {
1426
+ const config = def.config;
1427
+ const text = named(new CreateStringOption, name, def);
1428
+ if (config.min !== undefined)
1429
+ text.setMinLength(config.min);
1430
+ if (config.max !== undefined)
1431
+ text.setMaxLength(config.max);
1432
+ const choices = def.words ?? config.choices;
1433
+ if (choices?.length)
1434
+ text.addChoices(...choices.map((choice) => typeof choice === "string" ? { name: choice, value: choice } : choice));
1435
+ slash.addStringOption(() => text);
1436
+ break;
1437
+ }
1438
+ case "integer":
1439
+ case "number": {
1440
+ const config = def.config;
1441
+ const numeric = named(def.kind === "integer" ? new CreateIntegerOption : new CreateNumberOption, name, def);
1442
+ if (config.min !== undefined)
1443
+ numeric.setMinValue(config.min);
1444
+ if (config.max !== undefined)
1445
+ numeric.setMaxValue(config.max);
1446
+ if (config.choices?.length)
1447
+ numeric.addChoices(...config.choices.map((choice) => typeof choice === "number" ? { name: String(choice), value: choice } : choice));
1448
+ if (def.kind === "integer")
1449
+ slash.addIntegerOption(() => numeric);
1450
+ else
1451
+ slash.addNumberOption(() => numeric);
1452
+ break;
1453
+ }
1454
+ case "boolean":
1455
+ slash.addBooleanOption(() => named(new CreateBooleanOption, name, def));
1456
+ break;
1457
+ case "user":
1458
+ slash.addUserOption(() => named(new CreateUserOption, name, def));
1459
+ break;
1460
+ case "role":
1461
+ slash.addRoleOption(() => named(new CreateRoleOption, name, def));
1462
+ break;
1463
+ case "mentionable":
1464
+ slash.addMentionableOption(() => named(new CreateMentionableOption, name, def));
1465
+ break;
1466
+ case "attachment":
1467
+ slash.addAttachmentOption(() => named(new CreateAttachmentOption, name, def));
1468
+ break;
1469
+ case "channel": {
1470
+ const config = def.config;
1471
+ const channel = named(new CreateChannelOption, name, def);
1472
+ if (config.kinds?.length)
1473
+ channel.addChannelTypes(...channelTypes(config.kinds));
1474
+ slash.addChannelOption(() => channel);
1475
+ break;
1476
+ }
1477
+ }
1478
+ }
1479
+ var PREFIX_TYPES = {
1480
+ string: "string",
1481
+ integer: "integer",
1482
+ number: "number",
1483
+ boolean: "boolean",
1484
+ user: "user",
1485
+ role: "role",
1486
+ channel: "channel",
1487
+ mentionable: "mentionable",
1488
+ attachment: null
1489
+ };
1490
+ function prefixSpecs(options) {
1491
+ const entries = Object.entries(options);
1492
+ const specs = [];
1493
+ for (const [index, [name, def]] of entries.entries()) {
1494
+ const type = PREFIX_TYPES[def.kind];
1495
+ if (type === null)
1496
+ continue;
1497
+ const config = def.config;
1498
+ const choices = (def.words ?? config.choices)?.map((choice) => typeof choice === "object" ? choice.value : choice);
1499
+ specs.push({
1500
+ name,
1501
+ type: type === "string" && index === entries.length - 1 ? "rest" : type,
1502
+ required: settings(def).required === true && config.default === undefined,
1503
+ min: config.min,
1504
+ max: config.max,
1505
+ maxLength: def.kind === "string" ? config.max : undefined,
1506
+ choices
1507
+ });
1508
+ }
1509
+ return specs;
1510
+ }
1511
+ function command(definition) {
1512
+ const slash = definition.slash ?? true;
1513
+ const prefix = definition.prefix ? {
1514
+ aliases: typeof definition.prefix === "object" ? [...definition.prefix.aliases ?? []] : []
1515
+ } : false;
1516
+ if (!COMMAND_NAME.test(definition.name))
1517
+ throw new RangeError(`Invalid command name: ${definition.name}. Use 1-32 lowercase letters, digits, - or _.`);
1518
+ if (definition.description.length < 1 || definition.description.length > 100)
1519
+ throw new RangeError("A command description must be 1-100 characters.");
1520
+ if (!slash && !prefix)
1521
+ throw new TypeError(`Command ${definition.name} is neither a slash nor a prefix command.`);
1522
+ for (const alias of prefix ? prefix.aliases : [])
1523
+ if (!COMMAND_NAME.test(alias))
1524
+ throw new RangeError(`Invalid alias for ${definition.name}: ${alias}.`);
1525
+ const options = definition.options ?? {};
1526
+ let optionalSeen;
1527
+ for (const [name, def] of Object.entries(options)) {
1528
+ const needed = settings(def).required === true;
1529
+ if (needed && optionalSeen !== undefined)
1530
+ throw new TypeError(`Option ${name} is required but follows the optional option ${optionalSeen}; put required options first.`);
1531
+ if (!needed)
1532
+ optionalSeen ??= name;
1533
+ }
1534
+ const permissions = definition.permissions ?? [];
1535
+ const guildOnly = definition.guildOnly ?? permissions.length > 0;
1536
+ const toJSON = () => {
1537
+ const builder = new CreateSlashCommand().setName(definition.name).setDescription(definition.description);
1538
+ for (const [name, def] of Object.entries(options))
1539
+ addOptionTo(builder, name, def);
1540
+ if (permissions.length)
1541
+ builder.setDefaultMemberPermissions(new PermissionSet([...permissions]).bitfield);
1542
+ if (guildOnly)
1543
+ builder.setContexts(0);
1544
+ if (definition.nsfw)
1545
+ builder.setNSFW(true);
1546
+ return builder.toJSON();
1547
+ };
1548
+ if (slash)
1549
+ toJSON();
1550
+ return Object.freeze({
1551
+ name: definition.name,
1552
+ description: definition.description,
1553
+ options,
1554
+ permissions,
1555
+ guildOnly,
1556
+ nsfw: definition.nsfw ?? false,
1557
+ slash,
1558
+ prefix,
1559
+ run: definition.run,
1560
+ toJSON,
1561
+ usage(prefixText) {
1562
+ const parts = Object.entries(options).map(([name, def]) => {
1563
+ const required = settings(def).required === true && settings(def).default === undefined;
1564
+ const label = def.kind === "attachment" ? `${name}\uD83D\uDCCE` : name;
1565
+ return required ? `<${label}>` : `[${label}]`;
1566
+ });
1567
+ return [`${prefixText}${definition.name}`, ...parts].join(" ");
1568
+ }
1569
+ });
1570
+ }
1571
+ function applyDefault(def, value) {
1572
+ if (value !== null && value !== undefined)
1573
+ return value;
1574
+ return settings(def).default;
1575
+ }
1576
+ function readSlashOptions(options, interaction) {
1577
+ const values = {};
1578
+ for (const [name, def] of Object.entries(options)) {
1579
+ const source = interaction.options;
1580
+ let value;
1581
+ switch (def.kind) {
1582
+ case "string":
1583
+ value = source.getString(name);
1584
+ break;
1585
+ case "integer":
1586
+ value = source.getInteger(name);
1587
+ break;
1588
+ case "number":
1589
+ value = source.getNumber(name);
1590
+ break;
1591
+ case "boolean":
1592
+ value = source.getBoolean(name);
1593
+ break;
1594
+ case "user":
1595
+ value = idOf(source.getUser(name));
1596
+ break;
1597
+ case "role":
1598
+ value = idOf(source.getRole(name));
1599
+ break;
1600
+ case "channel":
1601
+ value = idOf(source.getChannel(name));
1602
+ break;
1603
+ case "mentionable": {
1604
+ const id = idOf(source.getMentionable(name));
1605
+ value = id ? { id, type: source.getMentionableType(name) ?? "user" } : null;
1606
+ break;
1607
+ }
1608
+ case "attachment":
1609
+ value = source.getAttachment(name);
1610
+ break;
1611
+ }
1612
+ values[name] = applyDefault(def, value);
1613
+ }
1614
+ return values;
1615
+ }
1616
+ function idOf(record) {
1617
+ return record === null ? null : String(record.id);
1618
+ }
1619
+ function words(name) {
1620
+ return name.replace(/([a-z])([A-Z])/g, "$1 $2");
1621
+ }
1622
+
1623
+ class CommandRegistry {
1624
+ #bot;
1625
+ #commands = new Map;
1626
+ #names = new Map;
1627
+ #listening = false;
1628
+ constructor(bot) {
1629
+ this.#bot = bot;
1630
+ }
1631
+ add(...commands) {
1632
+ for (const added of commands) {
1633
+ const taken = [
1634
+ added.name,
1635
+ ...added.prefix ? added.prefix.aliases : []
1636
+ ];
1637
+ for (const name of taken) {
1638
+ const owner = this.#names.get(name);
1639
+ if (owner && owner.name !== added.name)
1640
+ throw new TypeError(`The name "${name}" is already used by the ${owner.name} command.`);
1641
+ }
1642
+ const previous = this.#commands.get(added.name);
1643
+ if (previous)
1644
+ for (const name of [
1645
+ previous.name,
1646
+ ...previous.prefix ? previous.prefix.aliases : []
1647
+ ])
1648
+ this.#names.delete(name);
1649
+ this.#commands.set(added.name, added);
1650
+ if (added.slash || added.prefix)
1651
+ for (const name of taken)
1652
+ this.#names.set(name, added);
1653
+ }
1654
+ return this;
1655
+ }
1656
+ get(name) {
1657
+ return this.#names.get(name.toLowerCase());
1658
+ }
1659
+ list() {
1660
+ return [...this.#commands.values()];
1661
+ }
1662
+ async deploy(options = {}) {
1663
+ if (!this.#bot.isReady())
1664
+ throw new Error('Deploy commands after the client is ready: bot.once("ready", () => bot.commands.deploy()).');
1665
+ const payloads = this.list().filter((entry) => entry.slash).map((entry) => entry.toJSON());
1666
+ await (options.guildId === undefined ? this.#bot.application.commands.set(payloads) : this.#bot.application.commands.setGuild(options.guildId, payloads));
1667
+ return payloads.length;
1668
+ }
1669
+ listen(options = {}) {
1670
+ if (this.#listening)
1671
+ throw new TypeError("bot.commands.listen() was already called.");
1672
+ this.#listening = true;
1673
+ const prefixes = options.prefix === undefined ? [] : typeof options.prefix === "string" ? [options.prefix] : [...options.prefix];
1674
+ if (prefixes.some((prefix) => prefix === ""))
1675
+ throw new TypeError("A command prefix cannot be empty.");
1676
+ this.#bot.on("interactionCreate", async (interaction) => {
1677
+ if (!interaction.isChatInputCommand())
1678
+ return;
1679
+ const found = this.#commands.get(interaction.commandName);
1680
+ if (found?.slash)
1681
+ await this.#runSlash(found, interaction);
1682
+ });
1683
+ if (prefixes.length) {
1684
+ this.#warnAboutIntents();
1685
+ this.#bot.on("messageCreate", async (message) => {
1686
+ if (message.author.bot)
1687
+ return;
1688
+ const prefix = prefixes.find((p) => message.content.startsWith(p));
1689
+ if (prefix !== undefined)
1690
+ await this.#runPrefix(message, prefix);
1691
+ });
1692
+ }
1693
+ return this;
1694
+ }
1695
+ #warnAboutIntents() {
1696
+ const intents = resolveGatewayIntents(this.#bot.options.intents);
1697
+ const missing = [];
1698
+ if ((intents & 1 << 15) === 0)
1699
+ missing.push("MessageContent");
1700
+ if ((intents & (1 << 9 | 1 << 12)) === 0)
1701
+ missing.push("GuildMessages or DirectMessages");
1702
+ if (missing.length === 0)
1703
+ return;
1704
+ process.emitWarning(`Prefix commands need the ${missing.join(" and ")} intent${missing.length > 1 ? "s" : ""}; without ${missing.length > 1 ? "them" : "it"} Discord sends empty message content and no prefix command will match.`, { type: "LunibeeWarning", code: "LUNIBEE_MESSAGE_CONTENT_INTENT" });
1705
+ }
1706
+ async#runSlash(found, interaction) {
1707
+ let answered = false;
1708
+ const reply = async (content) => {
1709
+ const body = typeof content === "string" ? { content } : content;
1710
+ if (answered)
1711
+ await interaction.followUp(body);
1712
+ else if (interaction.deferred)
1713
+ await interaction.editReply(body);
1714
+ else
1715
+ await interaction.reply(body);
1716
+ answered = true;
1717
+ };
1718
+ await found.run({
1719
+ bot: this.#bot,
1720
+ options: readSlashOptions(found.options, interaction),
1721
+ source: "slash",
1722
+ guildId: interaction.guildId,
1723
+ channelId: interaction.channelId,
1724
+ userId: interaction.user?.id ?? "",
1725
+ member: interaction.member,
1726
+ interaction,
1727
+ message: undefined,
1728
+ reply,
1729
+ defer: async (deferOptions) => {
1730
+ await interaction.deferReply({
1731
+ ephemeral: deferOptions?.ephemeral ?? false
1732
+ });
1733
+ }
1734
+ });
1735
+ }
1736
+ async#runPrefix(message, prefix) {
1737
+ const text = message.content.slice(prefix.length).trim();
1738
+ const [name = "", ...rest] = text.split(/\s+/);
1739
+ const found = this.get(name);
1740
+ if (!found?.prefix)
1741
+ return;
1742
+ const reply = async (content) => {
1743
+ const { ephemeral: _ephemeral, ...body } = typeof content === "string" ? { content } : content;
1744
+ await message.reply(body);
1745
+ };
1746
+ if (found.guildOnly && !message.guildId) {
1747
+ await reply("This command only works in servers.");
1748
+ return;
1749
+ }
1750
+ if (found.permissions.length) {
1751
+ const lacking = found.permissions.filter((permission) => !message.member?.permissions.has(permission));
1752
+ if (lacking.length) {
1753
+ await reply(`You need ${lacking.map((p) => `**${words(p)}**`).join(", ")}.`);
1754
+ return;
1755
+ }
1756
+ }
1757
+ const parsed = parsePrefixArgs(rest.join(" "), prefixSpecs(found.options));
1758
+ const attachments = [...message.attachments];
1759
+ const values = {};
1760
+ if (parsed.ok)
1761
+ for (const [key, def] of Object.entries(found.options)) {
1762
+ if (def.kind === "attachment") {
1763
+ const attachment = attachments.shift();
1764
+ if (!attachment && settings(def).required === true) {
1765
+ await reply(`\`${key}\` is required. Usage: \`${found.usage(prefix)}\``);
1766
+ return;
1767
+ }
1768
+ values[key] = applyDefault(def, attachment);
1769
+ } else
1770
+ values[key] = applyDefault(def, parsed.values[key]);
1771
+ }
1772
+ else {
1773
+ await reply(`\`${parsed.arg}\` ${parsed.error}. Usage: \`${found.usage(prefix)}\``);
1774
+ return;
1775
+ }
1776
+ await found.run({
1777
+ bot: this.#bot,
1778
+ options: values,
1779
+ source: "prefix",
1780
+ guildId: message.guildId,
1781
+ channelId: message.channelId,
1782
+ userId: message.author.id,
1783
+ member: message.member ?? null,
1784
+ interaction: undefined,
1785
+ message,
1786
+ reply,
1787
+ defer: async () => {}
1788
+ });
1789
+ }
1790
+ }
1791
+ // packages/core/src/channel-create.ts
1792
+ var CREATABLE = [
1793
+ "text",
1794
+ "announcement",
1795
+ "voice",
1796
+ "stage",
1797
+ "category",
1798
+ "forum",
1799
+ "media"
1800
+ ];
1801
+ function emoji(value) {
1802
+ return /^\d+$/.test(value) ? { emoji_id: value, emoji_name: null } : { emoji_id: null, emoji_name: value };
1803
+ }
1804
+ function channelCreatePayload(options) {
1805
+ if (!CREATABLE.includes(options.kind))
1806
+ throw new TypeError(`Cannot create a ${String(options.kind)} channel here. Use one of: ${CREATABLE.join(", ")}.`);
1807
+ if (options.name.length < 1 || options.name.length > 100)
1808
+ throw new RangeError("A channel name must be 1-100 characters.");
1809
+ const body = {
1810
+ name: options.name,
1811
+ type: ChannelKinds[options.kind][0],
1812
+ parent_id: options.parent,
1813
+ position: options.position,
1814
+ permission_overwrites: options.permissionOverwrites,
1815
+ reason: options.reason
1816
+ };
1817
+ switch (options.kind) {
1818
+ case "text":
1819
+ case "announcement":
1820
+ case "forum":
1821
+ case "media": {
1822
+ if (options.slowmode !== undefined && (!Number.isInteger(options.slowmode) || options.slowmode < 0 || options.slowmode > 21600))
1823
+ throw new RangeError("slowmode must be 0-21600 seconds.");
1824
+ body.topic = options.topic;
1825
+ body.nsfw = options.nsfw;
1826
+ body.rate_limit_per_user = options.slowmode;
1827
+ body.default_auto_archive_duration = options.autoArchive;
1828
+ if (options.kind === "forum" || options.kind === "media") {
1829
+ body.default_thread_rate_limit_per_user = options.threadSlowmode;
1830
+ body.available_tags = options.tags?.map((tag) => ({
1831
+ name: tag.name,
1832
+ moderated: tag.moderated ?? false,
1833
+ ...tag.emoji ? emoji(tag.emoji) : {}
1834
+ }));
1835
+ if (options.defaultReaction)
1836
+ body.default_reaction_emoji = emoji(options.defaultReaction);
1837
+ if (options.sortOrder)
1838
+ body.default_sort_order = options.sortOrder === "latest-activity" ? 0 : 1;
1839
+ if (options.layout)
1840
+ body.default_forum_layout = options.layout === "list" ? 1 : 2;
1841
+ }
1842
+ break;
1843
+ }
1844
+ case "voice":
1845
+ case "stage":
1846
+ if (options.userLimit !== undefined && (!Number.isInteger(options.userLimit) || options.userLimit < 0 || options.userLimit > 99))
1847
+ throw new RangeError("userLimit must be 0-99.");
1848
+ body.bitrate = options.bitrate;
1849
+ body.user_limit = options.userLimit;
1850
+ body.rtc_region = options.region;
1851
+ body.nsfw = options.nsfw;
1852
+ if (options.videoQuality)
1853
+ body.video_quality_mode = options.videoQuality === "auto" ? 1 : 2;
1854
+ break;
1855
+ case "category":
1856
+ break;
1857
+ }
1858
+ return body;
1859
+ }
1860
+ // packages/core/src/handles.ts
1861
+ var UNIT_MS = {
1862
+ ms: 1,
1863
+ s: 1000,
1864
+ m: 60000,
1865
+ h: 3600000,
1866
+ d: 86400000,
1867
+ w: 604800000
1868
+ };
1869
+ function parseDuration(value) {
1870
+ if (typeof value === "number") {
1871
+ if (!Number.isFinite(value) || value < 0)
1872
+ throw new RangeError("A duration must be a non-negative, finite number of milliseconds.");
1873
+ return value;
1874
+ }
1875
+ const text = value.replace(/\s+/g, "").toLowerCase();
1876
+ if (!/^(?:\d+(?:\.\d+)?(?:ms|s|m|h|d|w))+$/.test(text))
1877
+ throw new RangeError(`Invalid duration: ${JSON.stringify(value)}. Use a number of milliseconds or text such as "90s", "10m" or "1h30m".`);
1878
+ let total = 0;
1879
+ for (const [, amount, unit] of text.matchAll(/(\d+(?:\.\d+)?)(ms|s|m|h|d|w)/g))
1880
+ total += Number(amount) * UNIT_MS[unit];
1881
+ return Math.round(total);
1882
+ }
1883
+ function payload(input) {
1884
+ return typeof input === "string" ? { content: input } : input;
1885
+ }
1886
+
1887
+ class UserHandle {
1888
+ bot;
1889
+ id;
1890
+ constructor(bot, id) {
1891
+ this.bot = bot;
1892
+ this.id = id;
1893
+ }
1894
+ peek() {
1895
+ return this.bot.users.get(this.id);
1896
+ }
1897
+ get() {
1898
+ return this.bot.users.resolve(this.id);
1899
+ }
1900
+ fetch() {
1901
+ return this.bot.users.fetch(this.id);
1902
+ }
1903
+ async dm() {
1904
+ const data = await this.bot.users.createDM(this.id);
1905
+ this.bot.channels.upsert(data);
1906
+ return new ChannelHandle(this.bot, data.id);
1907
+ }
1908
+ async send(message) {
1909
+ return (await this.dm()).send(message);
1910
+ }
1911
+ }
1912
+
1913
+ class GuildHandle {
1914
+ bot;
1915
+ id;
1916
+ constructor(bot, id) {
1917
+ this.bot = bot;
1918
+ this.id = id;
1919
+ }
1920
+ peek() {
1921
+ return this.bot.guilds.get(this.id);
1922
+ }
1923
+ get() {
1924
+ return this.bot.guilds.resolve(this.id);
1925
+ }
1926
+ fetch() {
1927
+ return this.bot.guilds.fetch(this.id);
1928
+ }
1929
+ get members() {
1930
+ return this.bot.guilds.members(this.id).cache;
1931
+ }
1932
+ get roles() {
1933
+ return this.bot.guilds.roles(this.id).cache;
1934
+ }
1935
+ get channels() {
1936
+ return this.bot.channels.cache.filter((channel) => channel.guildId === this.id);
1937
+ }
1938
+ member(userId) {
1939
+ return new MemberHandle(this.bot, this.id, userId);
1940
+ }
1941
+ role(roleId) {
1942
+ return new RoleHandle(this.bot, this.id, roleId);
1943
+ }
1944
+ channel(channelId) {
1945
+ return new ChannelHandle(this.bot, channelId);
1946
+ }
1947
+ async createChannel(options) {
1948
+ const channel = await this.bot.channels.create(this.id, channelCreatePayload(options));
1949
+ return channel.as(options.kind);
1950
+ }
1951
+ ban(userId, options) {
1952
+ return this.bot.guilds.members(this.id).ban(userId, options);
1953
+ }
1954
+ unban(userId, reason) {
1955
+ return this.bot.guilds.members(this.id).unban(userId, reason);
1956
+ }
1957
+ edit(options) {
1958
+ return this.bot.guilds.edit(this.id, options);
1959
+ }
1960
+ leave() {
1961
+ return this.bot.users.leaveGuild(this.id);
1962
+ }
1963
+ }
1964
+
1965
+ class MemberHandle {
1966
+ bot;
1967
+ guildId;
1968
+ userId;
1969
+ constructor(bot, guildId, userId) {
1970
+ this.bot = bot;
1971
+ this.guildId = guildId;
1972
+ this.userId = userId;
1973
+ }
1974
+ get members() {
1975
+ return this.bot.guilds.members(this.guildId);
1976
+ }
1977
+ peek() {
1978
+ return this.members.get(this.userId);
1979
+ }
1980
+ get() {
1981
+ return this.members.resolve(this.userId);
1982
+ }
1983
+ fetch() {
1984
+ return this.members.fetch(this.userId);
1985
+ }
1986
+ guild() {
1987
+ return new GuildHandle(this.bot, this.guildId);
1988
+ }
1989
+ user() {
1990
+ return new UserHandle(this.bot, this.userId);
1991
+ }
1992
+ kick(reason) {
1993
+ return this.members.kick(this.userId, reason);
1994
+ }
1995
+ ban(options) {
1996
+ return this.members.ban(this.userId, options);
1997
+ }
1998
+ unban(reason) {
1999
+ return this.members.unban(this.userId, reason);
2000
+ }
2001
+ edit(options) {
2002
+ return this.members.edit(this.userId, options);
2003
+ }
2004
+ addRole(roleId, reason) {
2005
+ return this.members.addRole(this.userId, roleId, reason);
2006
+ }
2007
+ removeRole(roleId, reason) {
2008
+ return this.members.removeRole(this.userId, roleId, reason);
2009
+ }
2010
+ async timeout(duration, reason) {
2011
+ return this.members.timeout(this.userId, duration === null ? null : parseDuration(duration), reason);
2012
+ }
2013
+ }
2014
+
2015
+ class RoleHandle {
2016
+ bot;
2017
+ guildId;
2018
+ roleId;
2019
+ constructor(bot, guildId, roleId) {
2020
+ this.bot = bot;
2021
+ this.guildId = guildId;
2022
+ this.roleId = roleId;
2023
+ }
2024
+ get roles() {
2025
+ return this.bot.guilds.roles(this.guildId);
2026
+ }
2027
+ peek() {
2028
+ return this.roles.get(this.roleId);
2029
+ }
2030
+ get() {
2031
+ return this.roles.resolve(this.roleId);
2032
+ }
2033
+ fetch() {
2034
+ return this.roles.fetch(this.roleId);
2035
+ }
2036
+ edit(options) {
2037
+ return this.roles.edit(this.roleId, options);
2038
+ }
2039
+ delete(reason) {
2040
+ return this.roles.remove(this.roleId, reason);
2041
+ }
2042
+ }
2043
+
2044
+ class ChannelHandle {
2045
+ bot;
2046
+ id;
2047
+ constructor(bot, id) {
2048
+ this.bot = bot;
2049
+ this.id = id;
2050
+ }
2051
+ peek() {
2052
+ return this.bot.channels.get(this.id);
2053
+ }
2054
+ get() {
2055
+ return this.bot.channels.resolve(this.id);
2056
+ }
2057
+ fetch() {
2058
+ return this.bot.channels.fetch(this.id);
2059
+ }
2060
+ message(messageId) {
2061
+ return new MessageHandle(this.bot, this.id, messageId);
2062
+ }
2063
+ async as(...kinds) {
2064
+ return (await this.get()).as(...kinds);
2065
+ }
2066
+ send(message) {
2067
+ return this.bot.channels.send(this.id, payload(message));
2068
+ }
2069
+ messages(query = {}) {
2070
+ return this.bot.channels.fetchMessages(this.id, query);
2071
+ }
2072
+ edit(options) {
2073
+ return this.bot.channels.edit(this.id, options);
2074
+ }
2075
+ delete(reason) {
2076
+ return this.bot.channels.remove(this.id, reason);
2077
+ }
2078
+ bulkDelete(messageIds, reason) {
2079
+ return this.bot.channels.bulkDeleteMessages(this.id, messageIds, reason);
2080
+ }
2081
+ }
2082
+
2083
+ class MessageHandle {
2084
+ bot;
2085
+ channelId;
2086
+ messageId;
2087
+ constructor(bot, channelId, messageId) {
2088
+ this.bot = bot;
2089
+ this.channelId = channelId;
2090
+ this.messageId = messageId;
2091
+ }
2092
+ peek() {
2093
+ return this.bot.channels.cachedMessages(this.channelId)?.cache.peek(this.messageId);
794
2094
  }
795
- equals(bits) {
796
- return this.bitfield === resolveGatewayIntents(bits);
2095
+ async get() {
2096
+ return this.peek() ?? this.fetch();
797
2097
  }
798
- valueOf() {
799
- return this.bitfield;
2098
+ fetch() {
2099
+ return this.bot.channels.fetchMessage(this.channelId, this.messageId);
800
2100
  }
801
- toJSON() {
802
- return this.bitfield;
2101
+ channel() {
2102
+ return new ChannelHandle(this.bot, this.channelId);
803
2103
  }
804
- }
805
- // packages/types/src/index.ts
806
- var InteractionResponseEnum = {
807
- Pong: 1,
808
- ChannelMessage: 4,
809
- DeferredChannelMessage: 5,
810
- DeferredMessageUpdate: 6,
811
- MessageUpdate: 7,
812
- Autocomplete: 8,
813
- Modal: 9
814
- };
815
- // packages/collection/src/index.ts
816
- class RecencyList {
817
- #nodes = new Map;
818
- #oldest;
819
- #newest;
820
- get size() {
821
- return this.#nodes.size;
822
- }
823
- oldest() {
824
- return this.#oldest?.key;
825
- }
826
- touch(key) {
827
- const node = this.#nodes.get(key);
828
- if (node)
829
- return this.#moveNewest(node);
830
- const created = { key, older: this.#newest };
831
- if (this.#newest)
832
- this.#newest.newer = created;
833
- else
834
- this.#oldest = created;
835
- this.#newest = created;
836
- this.#nodes.set(key, created);
2104
+ edit(message) {
2105
+ return this.bot.channels.editMessage(this.channelId, this.messageId, typeof message === "string" ? { content: message } : message);
837
2106
  }
838
- promote(key) {
839
- const node = this.#nodes.get(key);
840
- if (node)
841
- this.#moveNewest(node);
2107
+ delete(reason) {
2108
+ return this.bot.channels.deleteMessage(this.channelId, this.messageId, reason);
2109
+ }
2110
+ reply(message) {
2111
+ return this.bot.channels.send(this.channelId, {
2112
+ ...payload(message),
2113
+ message_reference: {
2114
+ message_id: this.messageId,
2115
+ channel_id: this.channelId
2116
+ }
2117
+ });
842
2118
  }
843
- delete(key) {
844
- const node = this.#nodes.get(key);
845
- if (!node)
846
- return false;
847
- this.#unlink(node);
848
- this.#nodes.delete(key);
849
- return true;
2119
+ react(emoji) {
2120
+ return this.bot.channels.addReaction(this.channelId, this.messageId, emoji);
850
2121
  }
851
- clear() {
852
- this.#nodes.clear();
853
- this.#oldest = this.#newest = undefined;
2122
+ pin(reason) {
2123
+ return this.bot.channels.pinMessage(this.channelId, this.messageId, reason);
854
2124
  }
855
- #unlink(node) {
856
- if (node.older)
857
- node.older.newer = node.newer;
858
- else
859
- this.#oldest = node.newer;
860
- if (node.newer)
861
- node.newer.older = node.older;
862
- else
863
- this.#newest = node.older;
864
- node.older = node.newer = undefined;
2125
+ unpin(reason) {
2126
+ return this.bot.channels.unpinMessage(this.channelId, this.messageId, reason);
865
2127
  }
866
- #moveNewest(node) {
867
- if (node === this.#newest)
868
- return;
869
- this.#unlink(node);
870
- node.older = this.#newest;
871
- if (this.#newest)
872
- this.#newest.newer = node;
873
- else
874
- this.#oldest = node;
875
- this.#newest = node;
2128
+ crosspost() {
2129
+ return this.bot.channels.crosspostMessage(this.channelId, this.messageId);
876
2130
  }
877
2131
  }
2132
+ // packages/collection/src/index.ts
2133
+ function newPolicy(options = {}) {
2134
+ return {
2135
+ ttl: options.ttl,
2136
+ jitter: options.jitter ?? 0,
2137
+ maxSize: options.maxSize,
2138
+ slide: options.slide ?? true,
2139
+ onEvict: options.onEvict,
2140
+ expired: 0,
2141
+ evicted: 0,
2142
+ entries: new Map,
2143
+ oldest: undefined,
2144
+ newest: undefined,
2145
+ lruSize: 0,
2146
+ heap: [],
2147
+ armedFor: Infinity
2148
+ };
2149
+ }
2150
+ function peekOf(collection, key) {
2151
+ return collection instanceof Map ? Map.prototype.get.call(collection, key) : collection.get(key);
2152
+ }
2153
+ var mapGet = Map.prototype.get;
2154
+ var mapHas = Map.prototype.has;
2155
+ function sample(items, amount) {
2156
+ if (!Number.isInteger(amount) || amount < 0)
2157
+ throw new RangeError("amount must be a non-negative integer.");
2158
+ const count = Math.min(amount, items.length);
2159
+ for (let i = 0;i < count; i++) {
2160
+ const j = i + Math.floor(Math.random() * (items.length - i));
2161
+ [items[i], items[j]] = [items[j], items[i]];
2162
+ }
2163
+ items.length = count;
2164
+ return items;
2165
+ }
878
2166
  function assertTTL(ttl) {
879
2167
  if (ttl !== undefined && (!Number.isFinite(ttl) || ttl <= 0))
880
2168
  throw new RangeError("TTL must be a positive number, or omitted.");
881
2169
  }
882
2170
 
883
2171
  class Collection extends Map {
884
- #ttl;
885
- #maxSize;
886
- #onEvict;
887
- #expiry = new Map;
888
- #lru = new RecencyList;
889
- #stats = {
890
- hits: 0,
891
- misses: 0,
892
- expired: 0,
893
- evicted: 0
894
- };
895
- #heap = [];
896
- #timer;
897
- #armedFor = Infinity;
2172
+ #policy;
2173
+ #hits = 0;
2174
+ #misses = 0;
898
2175
  constructor(entries, options = {}) {
899
2176
  super();
900
2177
  assertTTL(options.ttl);
2178
+ if (options.jitter !== undefined && !(options.jitter >= 0 && options.jitter <= 1))
2179
+ throw new RangeError("jitter must be between 0 and 1.");
901
2180
  if (options.maxSize !== undefined && (!Number.isInteger(options.maxSize) || options.maxSize <= 0))
902
2181
  throw new RangeError("maxSize must be a positive integer.");
903
- this.#ttl = options.ttl;
904
- this.#maxSize = options.maxSize;
905
- this.#onEvict = options.onEvict;
2182
+ if (options.ttl !== undefined || options.jitter !== undefined || options.maxSize !== undefined || options.slide !== undefined || options.onEvict !== undefined)
2183
+ this.#policy = newPolicy(options);
906
2184
  if (entries)
907
2185
  for (const [key, value] of entries)
908
2186
  this.set(key, value);
909
2187
  }
910
2188
  set(key, value, ttl) {
2189
+ if (ttl === undefined && this.#policy === undefined)
2190
+ return super.set(key, value);
911
2191
  assertTTL(ttl);
912
- const window = ttl ?? this.#ttl;
913
- if (this.#maxSize !== undefined) {
914
- this.#lru.touch(key);
915
- }
916
- super.set(key, value);
2192
+ let window = ttl ?? this.#policy?.ttl;
2193
+ const jitter = this.#policy?.jitter ?? 0;
2194
+ if (window !== undefined && jitter > 0)
2195
+ window = Math.max(1, window * (1 - jitter * Math.random()));
917
2196
  if (window !== undefined)
918
- this.#schedule(key, window);
919
- else if (this.#expiry.size > 0)
920
- this.#expiry.delete(key);
2197
+ this.#policy ??= newPolicy();
2198
+ if (!this.#policy)
2199
+ return super.set(key, value);
2200
+ const before = super.size;
2201
+ super.set(key, value);
2202
+ this.#track(key, value, window, super.size !== before);
921
2203
  this.#enforceCap();
922
2204
  return this;
923
2205
  }
924
2206
  setWithoutTTL(key, value) {
925
- if (this.#expiry.size > 0)
926
- this.#expiry.delete(key);
927
- if (this.#lru.size > 0)
928
- this.#lru.delete(key);
2207
+ if (this.#policy)
2208
+ this.#forget(key);
929
2209
  super.set(key, value);
930
2210
  return this;
931
2211
  }
932
2212
  get(key) {
933
- if (this.#expireIfDue(key)) {
934
- this.#stats.misses++;
2213
+ const policy = this.#policy;
2214
+ const entry = policy?.entries.get(key);
2215
+ if (entry === undefined) {
2216
+ const value = mapGet.call(this, key);
2217
+ if (value !== undefined || mapHas.call(this, key)) {
2218
+ this.#hits++;
2219
+ return value;
2220
+ }
2221
+ this.#misses++;
935
2222
  return;
936
2223
  }
937
- const value = super.get(key);
938
- if (value === undefined && !super.has(key)) {
939
- this.#stats.misses++;
2224
+ const timed = entry.window !== 0;
2225
+ const now = timed ? Date.now() : 0;
2226
+ if (timed && entry.deadline <= now) {
2227
+ this.#expire(entry);
2228
+ this.#misses++;
940
2229
  return;
941
2230
  }
942
- this.#stats.hits++;
943
- const expiry = this.#expiry.get(key);
944
- if (expiry)
945
- expiry.deadline = Date.now() + expiry.window;
946
- this.#lru.promote(key);
947
- return value;
2231
+ this.#hits++;
2232
+ if (timed && policy.slide)
2233
+ entry.deadline = now + entry.window;
2234
+ if (entry.inLru)
2235
+ this.#promote(policy, entry);
2236
+ return entry.value;
2237
+ }
2238
+ ensure(key, factory, ttl) {
2239
+ const existing = this.get(key);
2240
+ if (existing !== undefined || super.has(key))
2241
+ return existing;
2242
+ const created = factory(key, this);
2243
+ this.set(key, created, ttl);
2244
+ return created;
948
2245
  }
949
2246
  peek(key) {
950
2247
  if (!this.#expireIfDue(key) && super.has(key)) {
951
- this.#stats.hits++;
2248
+ this.#hits++;
952
2249
  return super.get(key);
953
2250
  }
954
- this.#stats.misses++;
2251
+ this.#misses++;
955
2252
  return;
956
2253
  }
957
2254
  get stats() {
958
- return { ...this.#stats };
2255
+ return {
2256
+ hits: this.#hits,
2257
+ misses: this.#misses,
2258
+ expired: this.#policy?.expired ?? 0,
2259
+ evicted: this.#policy?.evicted ?? 0
2260
+ };
959
2261
  }
960
2262
  has(key) {
961
2263
  return !this.#expireIfDue(key) && super.has(key);
@@ -963,19 +2265,23 @@ class Collection extends Map {
963
2265
  delete(key) {
964
2266
  if (this.#expireIfDue(key))
965
2267
  return false;
966
- this.#expiry.delete(key);
967
- this.#lru.delete(key);
2268
+ if (this.#policy)
2269
+ this.#forget(key);
968
2270
  return super.delete(key);
969
2271
  }
970
2272
  clear() {
971
2273
  super.clear();
972
- this.#expiry.clear();
973
- this.#lru.clear();
974
- this.#heap.length = 0;
975
- if (this.#timer)
976
- clearTimeout(this.#timer);
977
- this.#timer = undefined;
978
- this.#armedFor = Infinity;
2274
+ const policy = this.#policy;
2275
+ if (!policy)
2276
+ return;
2277
+ policy.entries.clear();
2278
+ policy.oldest = policy.newest = undefined;
2279
+ policy.lruSize = 0;
2280
+ policy.heap.length = 0;
2281
+ if (policy.timer)
2282
+ clearTimeout(policy.timer);
2283
+ policy.timer = undefined;
2284
+ policy.armedFor = Infinity;
979
2285
  }
980
2286
  get size() {
981
2287
  this.purge();
@@ -1003,11 +2309,11 @@ class Collection extends Map {
1003
2309
  ttlRemaining(key) {
1004
2310
  if (this.#expireIfDue(key))
1005
2311
  return;
1006
- const expiry = this.#expiry.get(key);
1007
- return expiry && Math.max(0, expiry.deadline - Date.now());
2312
+ const entry = this.#policy?.entries.get(key);
2313
+ return entry && entry.window !== 0 ? Math.max(0, entry.deadline - Date.now()) : undefined;
1008
2314
  }
1009
2315
  purge() {
1010
- if (this.#expiry.size === 0)
2316
+ if (!this.#policy || this.#policy.heap.length === 0)
1011
2317
  return 0;
1012
2318
  const now = Date.now();
1013
2319
  let removed = 0;
@@ -1018,133 +2324,255 @@ class Collection extends Map {
1018
2324
  this.#remove(due.key);
1019
2325
  removed++;
1020
2326
  }
1021
- this.#stats.expired += removed;
2327
+ this.#policy.expired += removed;
1022
2328
  this.#rearm();
1023
2329
  for (const [key, value] of dropped)
1024
2330
  this.#notify(key, value, "expired");
1025
2331
  return removed;
1026
2332
  }
1027
2333
  #expireIfDue(key) {
1028
- const expiry = this.#expiry.get(key);
1029
- if (!expiry || expiry.deadline > Date.now())
2334
+ const entry = this.#policy?.entries.get(key);
2335
+ if (!entry || entry.window === 0 || entry.deadline > Date.now())
1030
2336
  return false;
1031
- const value = super.get(key);
1032
- this.#remove(key);
1033
- this.#stats.expired++;
1034
- this.#notify(key, value, "expired");
2337
+ this.#expire(entry);
1035
2338
  return true;
1036
2339
  }
2340
+ #expire(entry) {
2341
+ const value = super.get(entry.key);
2342
+ this.#remove(entry.key);
2343
+ this.#policy.expired++;
2344
+ this.#notify(entry.key, value, "expired");
2345
+ }
1037
2346
  #remove(key) {
1038
- this.#expiry.delete(key);
1039
- this.#lru.delete(key);
2347
+ this.#forget(key);
1040
2348
  super.delete(key);
1041
2349
  }
2350
+ #forget(key) {
2351
+ const policy = this.#policy;
2352
+ const entry = policy.entries.get(key);
2353
+ if (!entry)
2354
+ return;
2355
+ if (entry.inLru)
2356
+ this.#unlink(policy, entry);
2357
+ policy.entries.delete(key);
2358
+ }
2359
+ #track(key, value, window, isNew) {
2360
+ const policy = this.#policy;
2361
+ const lru = policy.maxSize !== undefined;
2362
+ let entry = isNew ? undefined : policy.entries.get(key);
2363
+ if (entry)
2364
+ entry.value = value;
2365
+ if (window === undefined) {
2366
+ if (entry && entry.window !== 0) {
2367
+ if (lru)
2368
+ entry = this.#replace(policy, entry, 0, 0);
2369
+ else {
2370
+ this.#forget(key);
2371
+ return;
2372
+ }
2373
+ }
2374
+ if (!entry && lru) {
2375
+ entry = this.#create(key, value, 0, 0);
2376
+ policy.entries.set(key, entry);
2377
+ }
2378
+ if (entry)
2379
+ this.#promote(policy, entry);
2380
+ return;
2381
+ }
2382
+ const deadline = Date.now() + window;
2383
+ if (entry && entry.window !== 0) {
2384
+ entry.deadline = deadline;
2385
+ entry.window = window;
2386
+ if (deadline < entry.filed)
2387
+ entry = this.#replace(policy, entry, window, deadline);
2388
+ else {
2389
+ if (entry.inLru)
2390
+ this.#promote(policy, entry);
2391
+ return;
2392
+ }
2393
+ } else if (entry) {
2394
+ entry.window = window;
2395
+ entry.deadline = entry.filed = deadline;
2396
+ } else {
2397
+ entry = this.#create(key, value, window, deadline);
2398
+ policy.entries.set(key, entry);
2399
+ }
2400
+ if (lru)
2401
+ this.#promote(policy, entry);
2402
+ this.#heapPush(entry);
2403
+ if (policy.heap.length > 2 * policy.entries.size + 8)
2404
+ this.#compact();
2405
+ if (policy.timer === undefined || deadline < policy.armedFor)
2406
+ this.#rearm();
2407
+ }
2408
+ #create(key, value, window, deadline) {
2409
+ return {
2410
+ key,
2411
+ value,
2412
+ older: undefined,
2413
+ newer: undefined,
2414
+ inLru: false,
2415
+ window,
2416
+ deadline,
2417
+ filed: deadline
2418
+ };
2419
+ }
2420
+ #replace(policy, old, window, deadline) {
2421
+ const entry = {
2422
+ ...old,
2423
+ window,
2424
+ deadline,
2425
+ filed: deadline
2426
+ };
2427
+ if (old.inLru) {
2428
+ if (old.older)
2429
+ old.older.newer = entry;
2430
+ else
2431
+ policy.oldest = entry;
2432
+ if (old.newer)
2433
+ old.newer.older = entry;
2434
+ else
2435
+ policy.newest = entry;
2436
+ }
2437
+ policy.entries.set(old.key, entry);
2438
+ return entry;
2439
+ }
2440
+ #promote(policy, entry) {
2441
+ if (entry === policy.newest)
2442
+ return;
2443
+ if (entry.inLru)
2444
+ this.#unlink(policy, entry, true);
2445
+ else {
2446
+ entry.inLru = true;
2447
+ policy.lruSize++;
2448
+ }
2449
+ entry.older = policy.newest;
2450
+ entry.newer = undefined;
2451
+ if (policy.newest)
2452
+ policy.newest.newer = entry;
2453
+ else
2454
+ policy.oldest = entry;
2455
+ policy.newest = entry;
2456
+ }
2457
+ #unlink(policy, entry, keepCount = false) {
2458
+ if (entry.older)
2459
+ entry.older.newer = entry.newer;
2460
+ else
2461
+ policy.oldest = entry.newer;
2462
+ if (entry.newer)
2463
+ entry.newer.older = entry.older;
2464
+ else
2465
+ policy.newest = entry.older;
2466
+ entry.older = entry.newer = undefined;
2467
+ if (!keepCount) {
2468
+ entry.inLru = false;
2469
+ policy.lruSize--;
2470
+ }
2471
+ }
2472
+ #derived() {
2473
+ const Species = this.constructor[Symbol.species] ?? Collection;
2474
+ return new Species;
2475
+ }
1042
2476
  #notify(key, value, reason) {
1043
- if (!this.#onEvict)
2477
+ const onEvict = this.#policy?.onEvict;
2478
+ if (!onEvict)
1044
2479
  return;
1045
2480
  try {
1046
- this.#onEvict(key, value, reason);
2481
+ onEvict(key, value, reason);
1047
2482
  } catch {}
1048
2483
  }
1049
2484
  #enforceCap() {
1050
- if (this.#maxSize === undefined)
2485
+ const policy = this.#policy;
2486
+ const max = policy.maxSize;
2487
+ if (max === undefined)
1051
2488
  return;
1052
- while (this.#lru.size > this.#maxSize) {
1053
- const oldest = this.#lru.oldest();
1054
- const value = super.get(oldest);
1055
- this.#remove(oldest);
1056
- this.#stats.evicted++;
1057
- this.#notify(oldest, value, "evicted");
2489
+ if (policy.lruSize > max && policy.heap.length > 0 && policy.heap[0].filed <= Date.now())
2490
+ this.purge();
2491
+ while (policy.lruSize > max) {
2492
+ const oldest = policy.oldest;
2493
+ const value = policy.onEvict ? super.get(oldest.key) : undefined;
2494
+ this.#unlink(policy, oldest);
2495
+ policy.entries.delete(oldest.key);
2496
+ super.delete(oldest.key);
2497
+ policy.evicted++;
2498
+ this.#notify(oldest.key, value, "evicted");
1058
2499
  }
1059
2500
  }
1060
- #schedule(key, window) {
1061
- const deadline = Date.now() + window;
1062
- const expiry = this.#expiry.get(key);
1063
- if (expiry) {
1064
- expiry.deadline = deadline;
1065
- expiry.window = window;
1066
- if (deadline >= expiry.filed)
1067
- return;
1068
- expiry.filed = deadline;
1069
- } else
1070
- this.#expiry.set(key, { deadline, window, filed: deadline });
1071
- this.#heapPush({ key, deadline });
1072
- if (this.#heap.length > 2 * this.#expiry.size + 8)
1073
- this.#compact();
1074
- this.#rearm();
1075
- }
1076
2501
  #nextDeadline() {
2502
+ const policy = this.#policy;
1077
2503
  for (;; ) {
1078
- const top = this.#heap[0];
2504
+ const top = policy.heap[0];
1079
2505
  if (top === undefined)
1080
2506
  return Infinity;
1081
- const expiry = this.#expiry.get(top.key);
1082
- if (!expiry || expiry.filed !== top.deadline) {
2507
+ if (policy.entries.get(top.key) !== top) {
1083
2508
  this.#heapPop();
1084
2509
  continue;
1085
2510
  }
1086
- if (expiry.deadline === top.deadline)
1087
- return top.deadline;
2511
+ if (top.deadline === top.filed)
2512
+ return top.filed;
1088
2513
  this.#heapPop();
1089
- expiry.filed = expiry.deadline;
1090
- this.#heapPush({ key: top.key, deadline: expiry.deadline });
2514
+ top.filed = top.deadline;
2515
+ this.#heapPush(top);
1091
2516
  }
1092
2517
  }
1093
2518
  #compact() {
1094
- const live = this.#heap.filter((record) => this.#expiry.get(record.key)?.filed === record.deadline);
1095
- this.#heap.length = 0;
1096
- for (const record of live)
1097
- this.#heapPush(record);
2519
+ const policy = this.#policy;
2520
+ const live = policy.heap.filter((entry) => policy.entries.get(entry.key) === entry);
2521
+ policy.heap.length = 0;
2522
+ for (const entry of live)
2523
+ this.#heapPush(entry);
1098
2524
  }
1099
2525
  #rearm() {
2526
+ const p = this.#policy;
1100
2527
  const deadline = this.#nextDeadline();
1101
- if (this.#timer !== undefined && deadline >= this.#armedFor)
2528
+ if (p.timer !== undefined && deadline >= p.armedFor)
1102
2529
  return;
1103
- if (this.#timer)
1104
- clearTimeout(this.#timer);
1105
- this.#timer = undefined;
1106
- this.#armedFor = deadline;
2530
+ if (p.timer)
2531
+ clearTimeout(p.timer);
2532
+ p.timer = undefined;
2533
+ p.armedFor = deadline;
1107
2534
  if (deadline === Infinity)
1108
2535
  return;
1109
- this.#timer = setTimeout(() => {
1110
- this.#timer = undefined;
1111
- this.#armedFor = Infinity;
2536
+ p.timer = setTimeout(() => {
2537
+ p.timer = undefined;
2538
+ p.armedFor = Infinity;
1112
2539
  this.purge();
1113
2540
  }, Math.max(0, deadline - Date.now()));
1114
- this.#timer.unref?.();
2541
+ p.timer.unref?.();
1115
2542
  }
1116
- #heapPush(record) {
1117
- const heap = this.#heap;
1118
- heap.push(record);
1119
- let i = heap.length - 1;
2543
+ #heapPush(entry) {
2544
+ const heap = this.#policy.heap;
2545
+ let i = heap.length;
2546
+ heap.push(entry);
1120
2547
  while (i > 0) {
1121
2548
  const parent = i - 1 >> 1;
1122
- if (heap[parent].deadline <= heap[i].deadline)
2549
+ const above = heap[parent];
2550
+ if (above.filed <= entry.filed)
1123
2551
  break;
1124
- [heap[parent], heap[i]] = [heap[i], heap[parent]];
2552
+ heap[i] = above;
1125
2553
  i = parent;
1126
2554
  }
2555
+ heap[i] = entry;
1127
2556
  }
1128
2557
  #heapPop() {
1129
- const heap = this.#heap;
2558
+ const heap = this.#policy.heap;
1130
2559
  const top = heap[0];
1131
2560
  const last = heap.pop();
1132
2561
  if (top === undefined || heap.length === 0)
1133
2562
  return top;
1134
- heap[0] = last;
1135
- for (let i = 0;; ) {
1136
- const left = 2 * i + 1;
1137
- const right = left + 1;
1138
- let smallest = i;
1139
- if (left < heap.length && heap[left].deadline < heap[smallest].deadline)
1140
- smallest = left;
1141
- if (right < heap.length && heap[right].deadline < heap[smallest].deadline)
1142
- smallest = right;
1143
- if (smallest === i)
2563
+ let i = 0;
2564
+ for (;; ) {
2565
+ let child = 2 * i + 1;
2566
+ if (child >= heap.length)
2567
+ break;
2568
+ if (child + 1 < heap.length && heap[child + 1].filed < heap[child].filed)
2569
+ child++;
2570
+ if (heap[child].filed >= last.filed)
1144
2571
  break;
1145
- [heap[smallest], heap[i]] = [heap[i], heap[smallest]];
1146
- i = smallest;
2572
+ heap[i] = heap[child];
2573
+ i = child;
1147
2574
  }
2575
+ heap[i] = last;
1148
2576
  return top;
1149
2577
  }
1150
2578
  first() {
@@ -1176,19 +2604,35 @@ class Collection extends Map {
1176
2604
  }
1177
2605
  return;
1178
2606
  }
2607
+ findLast(predicate) {
2608
+ const keys = this.keyArray();
2609
+ const values = this.array();
2610
+ for (let i = values.length - 1;i >= 0; i--)
2611
+ if (predicate(values[i], keys[i], this))
2612
+ return values[i];
2613
+ return;
2614
+ }
2615
+ findLastKey(predicate) {
2616
+ const keys = this.keyArray();
2617
+ const values = this.array();
2618
+ for (let i = values.length - 1;i >= 0; i--)
2619
+ if (predicate(values[i], keys[i], this))
2620
+ return keys[i];
2621
+ return;
2622
+ }
1179
2623
  filter(predicate) {
1180
- const result = new Collection;
1181
- for (const [key, value] of this) {
2624
+ const result = this.#derived();
2625
+ this.forEach((value, key) => {
1182
2626
  if (predicate(value, key, this))
1183
2627
  result.set(key, value);
1184
- }
2628
+ });
1185
2629
  return result;
1186
2630
  }
1187
2631
  map(transform) {
1188
2632
  const result = [];
1189
- for (const [key, value] of this) {
2633
+ this.forEach((value, key) => {
1190
2634
  result.push(transform(value, key, this));
1191
- }
2635
+ });
1192
2636
  return result;
1193
2637
  }
1194
2638
  some(predicate) {
@@ -1206,9 +2650,9 @@ class Collection extends Map {
1206
2650
  return true;
1207
2651
  }
1208
2652
  each(callback) {
1209
- for (const [key, value] of this) {
2653
+ this.forEach((value, key) => {
1210
2654
  callback(value, key, this);
1211
- }
2655
+ });
1212
2656
  return this;
1213
2657
  }
1214
2658
  array() {
@@ -1221,9 +2665,10 @@ class Collection extends Map {
1221
2665
  return [...this.entries()];
1222
2666
  }
1223
2667
  clone() {
1224
- const copy = new Collection;
1225
- for (const [key, value] of this)
2668
+ const copy = this.#derived();
2669
+ this.forEach((value, key) => {
1226
2670
  copy.set(key, value);
2671
+ });
1227
2672
  return copy;
1228
2673
  }
1229
2674
  hasAll(...keys) {
@@ -1276,7 +2721,7 @@ class Collection extends Map {
1276
2721
  return result;
1277
2722
  }
1278
2723
  intersection(other) {
1279
- const result = new Collection;
2724
+ const result = this.#derived();
1280
2725
  for (const [key, value] of this) {
1281
2726
  if (other.has(key))
1282
2727
  result.set(key, value);
@@ -1284,7 +2729,7 @@ class Collection extends Map {
1284
2729
  return result;
1285
2730
  }
1286
2731
  difference(other) {
1287
- const result = new Collection;
2732
+ const result = this.#derived();
1288
2733
  for (const [key, value] of this) {
1289
2734
  if (!other.has(key))
1290
2735
  result.set(key, value);
@@ -1292,8 +2737,8 @@ class Collection extends Map {
1292
2737
  return result;
1293
2738
  }
1294
2739
  partition(predicate) {
1295
- const pass = new Collection;
1296
- const fail = new Collection;
2740
+ const pass = this.#derived();
2741
+ const fail = this.#derived();
1297
2742
  for (const [key, value] of this) {
1298
2743
  if (predicate(value, key, this))
1299
2744
  pass.set(key, value);
@@ -1302,6 +2747,81 @@ class Collection extends Map {
1302
2747
  }
1303
2748
  return [pass, fail];
1304
2749
  }
2750
+ symmetricDifference(other) {
2751
+ const result = this.#derived();
2752
+ for (const [key, value] of this)
2753
+ if (!other.has(key))
2754
+ result.set(key, value);
2755
+ for (const [key, value] of other)
2756
+ if (!this.has(key))
2757
+ result.set(key, value);
2758
+ return result;
2759
+ }
2760
+ concat(...collections) {
2761
+ const result = this.clone();
2762
+ for (const collection of collections)
2763
+ for (const [key, value] of collection)
2764
+ result.set(key, value);
2765
+ return result;
2766
+ }
2767
+ merge(other, whenInSelf, whenInOther, whenInBoth) {
2768
+ const result = this.#derived();
2769
+ const decide = (key, kept) => {
2770
+ if (kept.keep)
2771
+ result.set(key, kept.value);
2772
+ };
2773
+ for (const [key, value] of this) {
2774
+ decide(key, other.has(key) ? whenInBoth(value, peekOf(other, key), key) : whenInSelf(value, key));
2775
+ }
2776
+ for (const [key, value] of other)
2777
+ if (!this.has(key))
2778
+ decide(key, whenInOther(value, key));
2779
+ return result;
2780
+ }
2781
+ equals(other) {
2782
+ if (!other)
2783
+ return false;
2784
+ if (other === this)
2785
+ return true;
2786
+ if (this.size !== other.size)
2787
+ return false;
2788
+ for (const [key, value] of this) {
2789
+ if (!other.has(key))
2790
+ return false;
2791
+ if (value !== peekOf(other, key))
2792
+ return false;
2793
+ }
2794
+ return true;
2795
+ }
2796
+ mapValues(transform) {
2797
+ const result = this.#derived();
2798
+ for (const [key, value] of this)
2799
+ result.set(key, transform(value, key, this));
2800
+ return result;
2801
+ }
2802
+ toReversed() {
2803
+ const result = this.#derived();
2804
+ const entries = [...this];
2805
+ for (let i = entries.length - 1;i >= 0; i--)
2806
+ result.set(entries[i][0], entries[i][1]);
2807
+ return result;
2808
+ }
2809
+ toSorted(comparator) {
2810
+ return this.sorted(comparator);
2811
+ }
2812
+ static groupBy(items, keySelector) {
2813
+ const groups = new Collection;
2814
+ let index = 0;
2815
+ for (const item of items)
2816
+ groups.ensure(keySelector(item, index++), () => []).push(item);
2817
+ return groups;
2818
+ }
2819
+ static combineEntries(entries, combine) {
2820
+ const result = new Collection;
2821
+ for (const [key, value] of entries)
2822
+ result.set(key, result.has(key) ? combine(result.get(key), value, key) : value);
2823
+ return result;
2824
+ }
1305
2825
  tap(fn) {
1306
2826
  fn(this);
1307
2827
  return this;
@@ -1320,6 +2840,13 @@ class Collection extends Map {
1320
2840
  acc = fn(acc, value, key, this);
1321
2841
  return acc;
1322
2842
  }
2843
+ reduceRight(fn, initialValue) {
2844
+ const entries = [...this];
2845
+ let acc = initialValue;
2846
+ for (let i = entries.length - 1;i >= 0; i--)
2847
+ acc = fn(acc, entries[i][1], entries[i][0], this);
2848
+ return acc;
2849
+ }
1323
2850
  sorted(comparator) {
1324
2851
  const decorated = [...this.entries()].map((entry, index) => ({
1325
2852
  entry,
@@ -1330,32 +2857,52 @@ class Collection extends Map {
1330
2857
  const comparison = comparator(x.entry[1], y.entry[1], x.entry[0], y.entry[0]);
1331
2858
  return comparison !== 0 ? comparison : x.index - y.index;
1332
2859
  });
1333
- const result = new Collection;
2860
+ const result = this.#derived();
1334
2861
  for (const { entry } of decorated)
1335
2862
  result.set(entry[0], entry[1]);
1336
2863
  return result;
1337
2864
  }
1338
- random() {
1339
- const arr = [...this.values()];
1340
- if (!arr.length)
2865
+ random(amount) {
2866
+ if (amount !== undefined)
2867
+ return sample([...this.values()], amount);
2868
+ const size = this.size;
2869
+ if (size === 0)
1341
2870
  return;
1342
- return arr[Math.floor(Math.random() * arr.length)];
2871
+ let index = Math.floor(Math.random() * size);
2872
+ for (const value of this.values())
2873
+ if (index-- === 0)
2874
+ return value;
2875
+ return;
1343
2876
  }
1344
- randomKey() {
1345
- const arr = [...this.keys()];
1346
- if (!arr.length)
2877
+ randomKey(amount) {
2878
+ if (amount !== undefined)
2879
+ return sample([...this.keys()], amount);
2880
+ const size = this.size;
2881
+ if (size === 0)
1347
2882
  return;
1348
- return arr[Math.floor(Math.random() * arr.length)];
2883
+ let index = Math.floor(Math.random() * size);
2884
+ for (const key of this.keys())
2885
+ if (index-- === 0)
2886
+ return key;
2887
+ return;
1349
2888
  }
1350
2889
  at(index) {
1351
- const arr = [...this.values()];
1352
- const i = index < 0 ? arr.length + index : index;
1353
- return arr[i];
2890
+ let remaining = index < 0 ? this.size + index : index;
2891
+ if (remaining < 0)
2892
+ return;
2893
+ for (const value of this.values())
2894
+ if (remaining-- === 0)
2895
+ return value;
2896
+ return;
1354
2897
  }
1355
2898
  keyAt(index) {
1356
- const arr = [...this.keys()];
1357
- const i = index < 0 ? arr.length + index : index;
1358
- return arr[i];
2899
+ let remaining = index < 0 ? this.size + index : index;
2900
+ if (remaining < 0)
2901
+ return;
2902
+ for (const key of this.keys())
2903
+ if (remaining-- === 0)
2904
+ return key;
2905
+ return;
1359
2906
  }
1360
2907
  toJSON() {
1361
2908
  return [...this.entries()];
@@ -1469,7 +3016,7 @@ class ResourceManager extends Manager {
1469
3016
  // packages/rest/package.json
1470
3017
  var package_default = {
1471
3018
  name: "@lunibee/rest",
1472
- version: "0.2.3",
3019
+ version: "0.3.0",
1473
3020
  type: "module",
1474
3021
  module: "./src/index.ts",
1475
3022
  types: "./src/index.ts",
@@ -1550,7 +3097,7 @@ function kindFromStatus(status) {
1550
3097
  return "client";
1551
3098
  }
1552
3099
  function redactPath(path) {
1553
- return path.replace(/^(\/webhooks\/[^/?]+\/)[^/?]+/, "$1:token").replace(/^(\/interactions\/[^/?]+\/)[^/?]+/, "$1:token");
3100
+ return path.replace(/^(\/webhooks\/[^/?]+\/)[^/?]+/i, "$1:token").replace(/^(\/interactions\/[^/?]+\/)[^/?]+/i, "$1:token");
1554
3101
  }
1555
3102
  var HINTS = {
1556
3103
  50001: "The bot cannot see this resource: check that it is in the guild and has View Channel there.",
@@ -1609,19 +3156,23 @@ async function abortable(promise, signal, path) {
1609
3156
  }
1610
3157
 
1611
3158
  // packages/rest/src/route.ts
3159
+ import { createHash } from "crypto";
1612
3160
  function normalizeRoutePath(path) {
1613
3161
  const queryIndex = path.indexOf("?");
1614
3162
  const routePath = queryIndex === -1 ? path : path.slice(0, queryIndex);
1615
- return routePath.replace(/\/\d+(?=\/|$)/g, "/:id");
3163
+ return redactPath(routePath).replace(/\/\d+(?=\/|$)/g, "/:id");
1616
3164
  }
1617
3165
  function majorParameter(path) {
1618
- const match = /^\/(channels|guilds|webhooks)\/(\d+)(?:\/([^/?]+))?/.exec(path);
3166
+ const match = /^\/(channels|guilds|webhooks)\/(\d+)(?:\/([^/?]+))?/i.exec(path);
1619
3167
  if (!match)
1620
3168
  return "@none";
1621
- if (match[1] === "webhooks" && match[3] !== undefined)
1622
- return `${match[2]}:${match[3]}`;
3169
+ if (match[1].toLowerCase() === "webhooks" && match[3] !== undefined)
3170
+ return `${match[2]}:${tokenDigest(match[3])}`;
1623
3171
  return match[2];
1624
3172
  }
3173
+ function tokenDigest(token) {
3174
+ return createHash("sha256").update(token).digest("hex").slice(0, 16);
3175
+ }
1625
3176
  function scopeBucket(hashOrRoute, major) {
1626
3177
  return `${hashOrRoute}|${major}`;
1627
3178
  }
@@ -1863,6 +3414,9 @@ var Routes = {
1863
3414
  user: () => "/users/@me",
1864
3415
  userById: (userId) => `/users/${snowflake(userId, "User ID")}`,
1865
3416
  guild: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}`,
3417
+ currentUserGuild: (guildId) => `/users/@me/guilds/${snowflake(guildId, "Guild ID")}`,
3418
+ sticker: (stickerId) => `/stickers/${snowflake(stickerId, "Sticker ID")}`,
3419
+ messageReactionUser: (channelId, messageId, emoji, userId) => `${Routes.messageReactions(channelId, messageId, emoji)}/${snowflake(userId, "User ID")}`,
1866
3420
  guildPreview: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/preview`,
1867
3421
  guildActiveThreads: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/threads/active`,
1868
3422
  guildWebhooks: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/webhooks`,
@@ -1879,11 +3433,10 @@ var Routes = {
1879
3433
  message: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/${snowflake(messageId, "Message ID")}`,
1880
3434
  crosspostMessage: (channelId, messageId) => `${Routes.message(channelId, messageId)}/crosspost`,
1881
3435
  messageReactions: (channelId, messageId, emoji) => `${Routes.message(channelId, messageId)}/reactions/${encodeURIComponent(emoji)}`,
3436
+ messageReactionMe: (channelId, messageId, emoji) => `${Routes.messageReactions(channelId, messageId, emoji)}/@me`,
1882
3437
  messageReactionsAll: (channelId, messageId) => `${Routes.message(channelId, messageId)}/reactions`,
1883
3438
  channelMessagesPins: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/pins`,
1884
3439
  channelMessagesPin: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/pins/${snowflake(messageId, "Message ID")}`,
1885
- channelPins: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/pins`,
1886
- channelPin: (channelId, messageId) => `/channels/${snowflake(channelId, "Channel ID")}/pins/${snowflake(messageId, "Message ID")}`,
1887
3440
  messageThread: (channelId, messageId) => `${Routes.message(channelId, messageId)}/threads`,
1888
3441
  channelBulkDelete: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/messages/bulk-delete`,
1889
3442
  channelWebhooks: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/webhooks`,
@@ -1898,6 +3451,10 @@ var Routes = {
1898
3451
  interactionOriginalResponse: (applicationId, token) => `/webhooks/${snowflake(applicationId, "Application ID")}/${encodeURIComponent(token)}/messages/@original`,
1899
3452
  channelPublicArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/public`,
1900
3453
  channelPrivateArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads/archived/private`,
3454
+ channelJoinedPrivateArchivedThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/users/@me/threads/archived/private`,
3455
+ threadMember: (threadId, userId) => `/channels/${snowflake(threadId, "Thread ID")}/thread-members/${userOrMe(userId)}`,
3456
+ channelTyping: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/typing`,
3457
+ channelThreads: (channelId) => `/channels/${snowflake(channelId, "Channel ID")}/threads`,
1901
3458
  threadMembers: (threadId) => `/channels/${snowflake(threadId, "Thread ID")}/thread-members`,
1902
3459
  guildScheduledEvents: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events`,
1903
3460
  guildScheduledEvent: (guildId, eventId) => `/guilds/${snowflake(guildId, "Guild ID")}/scheduled-events/${snowflake(eventId, "Event ID")}`,
@@ -1910,6 +3467,13 @@ var Routes = {
1910
3467
  gatewayBot: () => "/gateway/bot",
1911
3468
  guildAuditLog: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/audit-logs`,
1912
3469
  guildMembers: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/members`,
3470
+ guildMembersSearch: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/search`,
3471
+ guildMemberMe: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/members/@me`,
3472
+ guildBulkBan: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/bulk-ban`,
3473
+ guildVoiceState: (guildId, userId) => `/guilds/${snowflake(guildId, "Guild ID")}/voice-states/${userOrMe(userId)}`,
3474
+ guildIntegration: (guildId, integrationId) => `/guilds/${snowflake(guildId, "Guild ID")}/integrations/${snowflake(integrationId, "Integration ID")}`,
3475
+ userGuilds: () => "/users/@me/guilds",
3476
+ stickerPacks: (packId) => packId ? `/sticker-packs/${snowflake(packId, "Sticker pack ID")}` : "/sticker-packs",
1913
3477
  guildWelcomeScreen: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/welcome-screen`,
1914
3478
  guildOnboarding: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/onboarding`,
1915
3479
  guildPrune: (guildId) => `/guilds/${snowflake(guildId, "Guild ID")}/prune`,
@@ -1930,6 +3494,9 @@ var Routes = {
1930
3494
  consumeEntitlement: (applicationId, entitlementId) => `/applications/${snowflake(applicationId, "Application ID")}/entitlements/${snowflake(entitlementId, "Entitlement ID")}/consume`,
1931
3495
  skuSubscription: (skuId, subscriptionId) => subscriptionId ? `/skus/${snowflake(skuId, "SKU ID")}/subscriptions/${snowflake(subscriptionId, "Subscription ID")}` : `/skus/${snowflake(skuId, "SKU ID")}/subscriptions`
1932
3496
  };
3497
+ function userOrMe(value) {
3498
+ return value === "@me" ? value : snowflake(value, "User ID");
3499
+ }
1933
3500
  function integer(value, field) {
1934
3501
  if (!Number.isInteger(value) || value < 0)
1935
3502
  throw new TypeError(`${field} must be a non-negative integer.`);
@@ -1945,6 +3512,22 @@ function snowflake(value, field) {
1945
3512
  var LIBRARY_VERSION = package_default.version;
1946
3513
  var LIBRARY_URL = "https://github.com/Ekretos/lunibee";
1947
3514
  var USER_AGENT = `DiscordBot (${LIBRARY_URL}, ${LIBRARY_VERSION})`;
3515
+ function assertSafePath(path) {
3516
+ const end = path.search(/[?#]/);
3517
+ const pathname = end === -1 ? path : path.slice(0, end);
3518
+ if (pathname.includes("\\"))
3519
+ throw new TypeError("REST paths must not contain backslashes.");
3520
+ for (const segment of pathname.split("/")) {
3521
+ let decoded;
3522
+ try {
3523
+ decoded = decodeURIComponent(segment);
3524
+ } catch {
3525
+ throw new TypeError("REST path has malformed percent-encoding.");
3526
+ }
3527
+ if (decoded === "." || decoded === "..")
3528
+ throw new TypeError("REST paths must not contain '.' or '..' segments.");
3529
+ }
3530
+ }
1948
3531
  var REQUEST_DATA_KEYS = new Set([
1949
3532
  "body",
1950
3533
  "files",
@@ -2005,7 +3588,7 @@ class REST {
2005
3588
  timeout: options.timeout
2006
3589
  });
2007
3590
  const store = options.store ?? new MemoryRateLimitStore;
2008
- this.#concurrentBuckets = (options.concurrentBuckets ?? false) && typeof store.reserve === "function";
3591
+ this.#concurrentBuckets = (options.concurrentBuckets ?? true) && typeof store.reserve === "function";
2009
3592
  this.#limiter = new RateLimiter(store, {
2010
3593
  concurrent: this.#concurrentBuckets
2011
3594
  });
@@ -2029,6 +3612,7 @@ class REST {
2029
3612
  throw new TypeError("REST method is required.");
2030
3613
  if (!path.startsWith("/"))
2031
3614
  throw new TypeError("REST paths must start with '/'.");
3615
+ assertSafePath(path);
2032
3616
  const route = createRouteKey(method, path);
2033
3617
  const files = options.files;
2034
3618
  if (files && files.length > 0 && !(body instanceof FormData))
@@ -2116,13 +3700,19 @@ class REST {
2116
3700
  #headers(body, options) {
2117
3701
  const isFormData = typeof FormData !== "undefined" && body instanceof FormData;
2118
3702
  const headers = { ...options.headers };
3703
+ const set = (name, value) => {
3704
+ for (const key of Object.keys(headers))
3705
+ if (key.toLowerCase() === name.toLowerCase())
3706
+ delete headers[key];
3707
+ headers[name] = value;
3708
+ };
2119
3709
  if (options.auth !== false && this.#token)
2120
- headers["Authorization"] = `Bot ${this.#token}`;
2121
- headers["User-Agent"] = USER_AGENT;
3710
+ set("Authorization", `Bot ${this.#token}`);
3711
+ set("User-Agent", USER_AGENT);
2122
3712
  if (options.reason !== undefined)
2123
- headers["X-Audit-Log-Reason"] = encodeURIComponent(options.reason);
3713
+ set("X-Audit-Log-Reason", encodeURIComponent(options.reason));
2124
3714
  if (!isFormData)
2125
- headers["Content-Type"] = "application/json";
3715
+ set("Content-Type", "application/json");
2126
3716
  return headers;
2127
3717
  }
2128
3718
  #encodeBody(body) {
@@ -2189,8 +3779,11 @@ class BaseStructure {
2189
3779
  toString() {
2190
3780
  return this.id;
2191
3781
  }
3782
+ get createdTimestamp() {
3783
+ return Number((BigInt(this.id) >> 22n) + 1420070400000n);
3784
+ }
2192
3785
  get createdAt() {
2193
- return new Date(Number((BigInt(this.id) >> 22n) + 1420070400000n));
3786
+ return new Date(this.createdTimestamp);
2194
3787
  }
2195
3788
  }
2196
3789
  var CDN_BASE = "https://cdn.discordapp.com";
@@ -2198,7 +3791,7 @@ function cdnURL(path, hash, options = {}) {
2198
3791
  const isAnimated = hash.startsWith("a_") && !options.forceStatic;
2199
3792
  const ext = options.extension ?? (isAnimated ? "gif" : "png");
2200
3793
  const size = options.size ? `?size=${options.size}` : "";
2201
- return `${CDN_BASE}${path}/${hash}.${ext}${size}`;
3794
+ return `${CDN_BASE}${path}/${encodeURIComponent(hash)}.${ext}${size}`;
2202
3795
  }
2203
3796
 
2204
3797
  class User extends BaseStructure {
@@ -2272,9 +3865,6 @@ class Channel extends BaseStructure {
2272
3865
  throw new Error("This channel is not attached to a client.");
2273
3866
  return this.#context.sendMessage(this.id, options);
2274
3867
  }
2275
- sendMessage(options) {
2276
- return this.send(options);
2277
- }
2278
3868
  edit(options) {
2279
3869
  if (!this.#context?.editChannel)
2280
3870
  throw new Error("This channel is not attached to a client.");
@@ -2304,6 +3894,17 @@ class Channel extends BaseStructure {
2304
3894
  update(options) {
2305
3895
  return this.edit(options);
2306
3896
  }
3897
+ get kind() {
3898
+ return channelKindOf(this.type);
3899
+ }
3900
+ is(...kinds) {
3901
+ return kinds.includes(this.kind);
3902
+ }
3903
+ as(...kinds) {
3904
+ if (!this.is(...kinds))
3905
+ throw new TypeError(`Channel ${this.id} is a ${this.kind} channel, not ${kinds.join(" or ")}.`);
3906
+ return this;
3907
+ }
2307
3908
  isThread() {
2308
3909
  return this.type === 10 || this.type === 11 || this.type === 12;
2309
3910
  }
@@ -2355,6 +3956,7 @@ class Guild extends BaseStructure {
2355
3956
  safetyAlertsChannelId;
2356
3957
  maxPresences;
2357
3958
  maxVideoChannelUsers;
3959
+ premiumProgressBarEnabled;
2358
3960
  constructor(data) {
2359
3961
  super(data.id);
2360
3962
  if (!data.name)
@@ -2392,6 +3994,7 @@ class Guild extends BaseStructure {
2392
3994
  this.safetyAlertsChannelId = data.safety_alerts_channel_id ?? null;
2393
3995
  this.maxPresences = data.max_presences ?? null;
2394
3996
  this.maxVideoChannelUsers = data.max_video_channel_users;
3997
+ this.premiumProgressBarEnabled = data.premium_progress_bar_enabled ?? false;
2395
3998
  }
2396
3999
  iconURL(options = {}) {
2397
4000
  if (!this.icon)
@@ -2488,10 +4091,10 @@ class GuildMember {
2488
4091
  get permissions() {
2489
4092
  if (this.#suppliedPermissions)
2490
4093
  return this.#suppliedPermissions;
2491
- return new PermissionsBitField(this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds) ?? 0n);
4094
+ return new PermissionsBitField(this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds, undefined, this.timedOutUntil) ?? 0n);
2492
4095
  }
2493
4096
  permissionsIn(channelId) {
2494
- const computed = this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds, channelId);
4097
+ const computed = this.#context?.memberPermissions?.(this.guildId, this.user.id, this.roleIds, channelId, this.timedOutUntil);
2495
4098
  return computed ? new PermissionsBitField(computed) : null;
2496
4099
  }
2497
4100
  #action(name) {
@@ -2554,6 +4157,7 @@ class Role extends BaseStructure {
2554
4157
  position;
2555
4158
  unicodeEmoji;
2556
4159
  iconHash;
4160
+ colors;
2557
4161
  tags;
2558
4162
  constructor(data) {
2559
4163
  super(data.id);
@@ -2567,6 +4171,11 @@ class Role extends BaseStructure {
2567
4171
  this.mentionable = data.mentionable ?? false;
2568
4172
  this.position = data.position ?? 0;
2569
4173
  this.unicodeEmoji = data.unicode_emoji ?? null;
4174
+ this.colors = data.colors ? {
4175
+ primary: data.colors.primary_color,
4176
+ secondary: data.colors.secondary_color,
4177
+ tertiary: data.colors.tertiary_color
4178
+ } : null;
2570
4179
  this.iconHash = data.icon ?? null;
2571
4180
  this.tags = data.tags ?? null;
2572
4181
  }
@@ -3030,6 +4639,14 @@ class Interaction {
3030
4639
  this.type = data.type;
3031
4640
  this.data = data;
3032
4641
  }
4642
+ get entitlements() {
4643
+ const entitlements = this.data.entitlements;
4644
+ return Array.isArray(entitlements) ? entitlements : [];
4645
+ }
4646
+ hasEntitlement(skuId) {
4647
+ const now = Date.now();
4648
+ return this.entitlements.some((entitlement) => entitlement.sku_id === skuId && !entitlement.deleted && (!entitlement.ends_at || Date.parse(entitlement.ends_at) > now));
4649
+ }
3033
4650
  #commandType() {
3034
4651
  if (this.type !== InteractionEnum.ApplicationCommand)
3035
4652
  return 0;
@@ -3324,22 +4941,133 @@ function findFocused(options) {
3324
4941
  return found;
3325
4942
  }
3326
4943
  }
3327
- return;
3328
- }
3329
- function createInteraction(client, data) {
3330
- switch (data.type) {
3331
- case InteractionEnum.ApplicationCommand: {
3332
- const commandType = data.data?.type;
3333
- return commandType === COMMAND_USER || commandType === COMMAND_MESSAGE ? new ContextMenuCommandInteraction(client, data) : new CommandInteraction(client, data);
3334
- }
3335
- case InteractionEnum.ApplicationCommandAutocomplete:
3336
- return new AutocompleteInteraction(client, data);
3337
- case InteractionEnum.MessageComponent:
3338
- return new ComponentInteraction(client, data);
3339
- case InteractionEnum.ModalSubmit:
3340
- return new ModalSubmitInteraction(client, data);
3341
- default:
3342
- return new Interaction(client, data);
4944
+ return;
4945
+ }
4946
+ function createInteraction(client, data) {
4947
+ switch (data.type) {
4948
+ case InteractionEnum.ApplicationCommand: {
4949
+ const commandType = data.data?.type;
4950
+ return commandType === COMMAND_USER || commandType === COMMAND_MESSAGE ? new ContextMenuCommandInteraction(client, data) : new CommandInteraction(client, data);
4951
+ }
4952
+ case InteractionEnum.ApplicationCommandAutocomplete:
4953
+ return new AutocompleteInteraction(client, data);
4954
+ case InteractionEnum.MessageComponent:
4955
+ return new ComponentInteraction(client, data);
4956
+ case InteractionEnum.ModalSubmit:
4957
+ return new ModalSubmitInteraction(client, data);
4958
+ default:
4959
+ return new Interaction(client, data);
4960
+ }
4961
+ }
4962
+ // packages/structures/src/audit-log.ts
4963
+ function int(value) {
4964
+ return Number(value ?? 0);
4965
+ }
4966
+ function parseInfo(type, options) {
4967
+ if (!options)
4968
+ return;
4969
+ switch (type) {
4970
+ case 13:
4971
+ case 14:
4972
+ case 15:
4973
+ return {
4974
+ id: options.id ?? "",
4975
+ type: int(options.type),
4976
+ roleName: options.role_name
4977
+ };
4978
+ case 21:
4979
+ return {
4980
+ deleteMemberDays: int(options.delete_member_days),
4981
+ membersRemoved: int(options.members_removed)
4982
+ };
4983
+ case 26:
4984
+ case 72:
4985
+ return {
4986
+ channelId: options.channel_id ?? "",
4987
+ count: int(options.count)
4988
+ };
4989
+ case 27:
4990
+ case 73:
4991
+ return { count: int(options.count) };
4992
+ case 74:
4993
+ case 75:
4994
+ return {
4995
+ channelId: options.channel_id ?? "",
4996
+ messageId: options.message_id ?? ""
4997
+ };
4998
+ case 83:
4999
+ case 84:
5000
+ case 85:
5001
+ return { channelId: options.channel_id ?? "" };
5002
+ case 121:
5003
+ return { applicationId: options.app_id ?? "" };
5004
+ case 143:
5005
+ case 144:
5006
+ case 145:
5007
+ return {
5008
+ ruleName: options.auto_moderation_rule_name ?? "",
5009
+ ruleTriggerType: int(options.auto_moderation_rule_trigger_type),
5010
+ channelId: options.channel_id
5011
+ };
5012
+ default:
5013
+ return;
5014
+ }
5015
+ }
5016
+
5017
+ class AuditLogEntry extends BaseStructure {
5018
+ targetId;
5019
+ userId;
5020
+ actionType;
5021
+ changes;
5022
+ options;
5023
+ info;
5024
+ reason;
5025
+ constructor(data) {
5026
+ super(data.id);
5027
+ this.targetId = data.target_id;
5028
+ this.userId = data.user_id;
5029
+ this.actionType = data.action_type;
5030
+ this.info = parseInfo(data.action_type, data.options);
5031
+ this.changes = data.changes ?? [];
5032
+ this.options = data.options ?? null;
5033
+ this.reason = data.reason ?? null;
5034
+ }
5035
+ is(type) {
5036
+ return this.actionType === type;
5037
+ }
5038
+ change(key) {
5039
+ const found = this.changes.find((change) => change.key === key);
5040
+ return found && { old: found.old_value, new: found.new_value };
5041
+ }
5042
+ }
5043
+
5044
+ class AuditLog {
5045
+ entries;
5046
+ constructor(data) {
5047
+ this.entries = new Map;
5048
+ for (const entryData of data.audit_log_entries) {
5049
+ this.entries.set(entryData.id, new AuditLogEntry(entryData));
5050
+ }
5051
+ }
5052
+ filter(query) {
5053
+ const types = query.type === undefined ? undefined : Array.isArray(query.type) ? query.type : [query.type];
5054
+ const since = query.since === undefined ? undefined : new Date(query.since).getTime();
5055
+ const matches = [];
5056
+ for (const entry of this.entries.values()) {
5057
+ if (types && !types.includes(entry.actionType))
5058
+ continue;
5059
+ if (query.targetId !== undefined && entry.targetId !== query.targetId)
5060
+ continue;
5061
+ if (query.userId !== undefined && entry.userId !== query.userId)
5062
+ continue;
5063
+ if (since !== undefined && entry.createdAt.getTime() < since)
5064
+ continue;
5065
+ matches.push(entry);
5066
+ }
5067
+ return matches;
5068
+ }
5069
+ find(query) {
5070
+ return this.filter(query)[0];
3343
5071
  }
3344
5072
  }
3345
5073
 
@@ -3530,6 +5258,13 @@ class Message extends BaseStructure {
3530
5258
  }
3531
5259
 
3532
5260
  // packages/managers/src/message.ts
5261
+ function toAttachments(files) {
5262
+ return files.map((file) => ({
5263
+ name: file.name,
5264
+ data: typeof file.data === "string" ? new TextEncoder().encode(file.data) : file.data,
5265
+ contentType: file.contentType
5266
+ }));
5267
+ }
3533
5268
  function toRequest(options, allowedMentions) {
3534
5269
  const { files, ...body } = options;
3535
5270
  if (allowedMentions !== undefined && body.allowed_mentions === undefined)
@@ -3538,11 +5273,7 @@ function toRequest(options, allowedMentions) {
3538
5273
  return body;
3539
5274
  return {
3540
5275
  body,
3541
- files: files.map((file) => ({
3542
- name: file.name,
3543
- data: typeof file.data === "string" ? new TextEncoder().encode(file.data) : file.data,
3544
- contentType: file.contentType
3545
- }))
5276
+ files: toAttachments(files)
3546
5277
  };
3547
5278
  }
3548
5279
 
@@ -3616,6 +5347,70 @@ class ThreadManager {
3616
5347
  });
3617
5348
  return createChannel(data, this.#context);
3618
5349
  }
5350
+ async create(options) {
5351
+ const data = await this.#rest.post(Routes.channelThreads(this.#channelId), {
5352
+ name: options.name,
5353
+ type: options.type ?? 12,
5354
+ auto_archive_duration: options.autoArchiveDuration,
5355
+ rate_limit_per_user: options.rateLimitPerUser,
5356
+ invitable: options.invitable
5357
+ }, { reason: options.reason });
5358
+ return createChannel(data, this.#context);
5359
+ }
5360
+ async createForumPost(options) {
5361
+ const { message, appliedTags, reason, ...thread } = options;
5362
+ const { files, ...content } = message;
5363
+ const data = await this.#rest.post(Routes.channelThreads(this.#channelId), {
5364
+ name: thread.name,
5365
+ auto_archive_duration: thread.autoArchiveDuration,
5366
+ rate_limit_per_user: thread.rateLimitPerUser,
5367
+ applied_tags: appliedTags,
5368
+ message: content
5369
+ }, { files: files?.length ? toAttachments(files) : undefined, reason });
5370
+ return createChannel(data, this.#context);
5371
+ }
5372
+ async fetchArchived(scope, options = {}) {
5373
+ const route = scope === "public" ? Routes.channelPublicArchivedThreads(this.#channelId) : scope === "private" ? Routes.channelPrivateArchivedThreads(this.#channelId) : Routes.channelJoinedPrivateArchivedThreads(this.#channelId);
5374
+ const params = new URLSearchParams;
5375
+ if (options.before)
5376
+ params.set("before", options.before);
5377
+ if (options.limit !== undefined)
5378
+ params.set("limit", String(Math.min(100, Math.max(1, options.limit))));
5379
+ const query = params.toString();
5380
+ const page = await this.#rest.get(`${route}${query ? `?${query}` : ""}`);
5381
+ return {
5382
+ threads: page.threads.map((thread) => createChannel(thread, this.#context)),
5383
+ members: page.members,
5384
+ hasMore: page.has_more
5385
+ };
5386
+ }
5387
+ async join(threadId) {
5388
+ await this.#rest.put(Routes.threadMember(threadId, "@me"));
5389
+ }
5390
+ async leave(threadId) {
5391
+ await this.#rest.delete(Routes.threadMember(threadId, "@me"));
5392
+ }
5393
+ async addMember(threadId, userId) {
5394
+ await this.#rest.put(Routes.threadMember(threadId, userId));
5395
+ }
5396
+ async removeMember(threadId, userId) {
5397
+ await this.#rest.delete(Routes.threadMember(threadId, userId));
5398
+ }
5399
+ async fetchMember(threadId, userId, query = {}) {
5400
+ const suffix = query.withMember ? "?with_member=true" : "";
5401
+ return this.#rest.get(`${Routes.threadMember(threadId, userId)}${suffix}`);
5402
+ }
5403
+ async fetchMembers(threadId, options = {}) {
5404
+ const params = new URLSearchParams;
5405
+ if (options.withMember)
5406
+ params.set("with_member", "true");
5407
+ if (options.after)
5408
+ params.set("after", options.after);
5409
+ if (options.limit !== undefined)
5410
+ params.set("limit", String(Math.min(100, Math.max(1, options.limit))));
5411
+ const query = params.toString();
5412
+ return this.#rest.get(`${Routes.threadMembers(threadId)}${query ? `?${query}` : ""}`);
5413
+ }
3619
5414
  }
3620
5415
 
3621
5416
  // packages/managers/src/user.ts
@@ -3633,8 +5428,21 @@ class UserManager extends ResourceManager {
3633
5428
  const data = await this.#rest.patch("/users/@me", options);
3634
5429
  return this.upsert(new User(data));
3635
5430
  }
5431
+ async fetchGuilds(options = {}) {
5432
+ const params = new URLSearchParams;
5433
+ if (options.limit !== undefined)
5434
+ params.set("limit", String(Math.min(200, Math.max(1, options.limit))));
5435
+ if (options.before)
5436
+ params.set("before", options.before);
5437
+ if (options.after)
5438
+ params.set("after", options.after);
5439
+ if (options.withCounts)
5440
+ params.set("with_counts", "true");
5441
+ const query = params.toString();
5442
+ return this.#rest.get(`${Routes.userGuilds()}${query ? `?${query}` : ""}`);
5443
+ }
3636
5444
  async leaveGuild(guildId) {
3637
- await this.#rest.delete(`/users/@me/guilds/${guildId}`);
5445
+ await this.#rest.delete(Routes.currentUserGuild(guildId));
3638
5446
  }
3639
5447
  async createDM(userId) {
3640
5448
  return this.#rest.post("/users/@me/channels", {
@@ -3672,9 +5480,6 @@ class EmojiManager extends Manager {
3672
5480
  });
3673
5481
  this.delete(emojiId);
3674
5482
  }
3675
- deleteEmoji(emojiId) {
3676
- return this.remove(emojiId);
3677
- }
3678
5483
  upsert(data) {
3679
5484
  const key = data.id != null ? data.id : `unicode:${data.name ?? ""}`;
3680
5485
  return this.#merge(key, new Emoji(data));
@@ -3855,6 +5660,39 @@ class MonetizationManager extends Manager {
3855
5660
  }
3856
5661
  }
3857
5662
 
5663
+ class ApplicationEmojiManager extends Manager {
5664
+ #rest;
5665
+ applicationId;
5666
+ constructor(rest, applicationId) {
5667
+ super();
5668
+ this.#rest = rest;
5669
+ this.applicationId = applicationId;
5670
+ }
5671
+ #store(emoji) {
5672
+ if (emoji.id)
5673
+ this.set(emoji.id, emoji);
5674
+ return emoji;
5675
+ }
5676
+ async fetchAll() {
5677
+ const { items } = await this.#rest.get(Routes.applicationEmoji(this.applicationId));
5678
+ this.clear();
5679
+ return items.map((emoji) => this.#store(emoji));
5680
+ }
5681
+ async fetch(emojiId) {
5682
+ return this.#store(await this.#rest.get(Routes.applicationEmoji(this.applicationId, emojiId)));
5683
+ }
5684
+ async create(options) {
5685
+ return this.#store(await this.#rest.post(Routes.applicationEmoji(this.applicationId), options));
5686
+ }
5687
+ async edit(emojiId, options) {
5688
+ return this.#store(await this.#rest.patch(Routes.applicationEmoji(this.applicationId, emojiId), options));
5689
+ }
5690
+ async remove(emojiId) {
5691
+ await this.#rest.delete(Routes.applicationEmoji(this.applicationId, emojiId));
5692
+ this.delete(emojiId);
5693
+ }
5694
+ }
5695
+
3858
5696
  // packages/managers/src/member.ts
3859
5697
  class GuildMemberManager extends ResourceManager {
3860
5698
  #rest;
@@ -3868,6 +5706,27 @@ class GuildMemberManager extends ResourceManager {
3868
5706
  this.#rest = rest;
3869
5707
  this.#context = context;
3870
5708
  }
5709
+ async search(query, limit = 1) {
5710
+ if (!query.trim())
5711
+ throw new TypeError("A search query is required.");
5712
+ const params = new URLSearchParams({
5713
+ query,
5714
+ limit: String(Math.min(1000, Math.max(1, limit)))
5715
+ });
5716
+ const found = await this.#rest.get(`${Routes.guildMembersSearch(this.guildId)}?${params}`);
5717
+ return found.map((data) => {
5718
+ const member = new GuildMember({ ...data, guild_id: this.guildId }, this.#context());
5719
+ this.set(member.user.id, member);
5720
+ return member;
5721
+ });
5722
+ }
5723
+ async editMe(options) {
5724
+ const [payload, reason] = splitReason(options);
5725
+ const data = await this.#rest.patch(Routes.guildMemberMe(this.guildId), payload, { reason });
5726
+ const member = new GuildMember({ ...data, guild_id: this.guildId }, this.#context());
5727
+ this.set(member.user.id, member);
5728
+ return member;
5729
+ }
3871
5730
  async kick(userId, reason) {
3872
5731
  await this.#rest.delete(Routes.guildMember(this.guildId, userId), {
3873
5732
  reason
@@ -3883,10 +5742,10 @@ class GuildMemberManager extends ResourceManager {
3883
5742
  reason
3884
5743
  });
3885
5744
  }
3886
- async edit(userId, options, reason) {
5745
+ async edit(userId, options) {
3887
5746
  const [payload, optionsReason] = splitReason(options);
3888
5747
  const data = await this.#rest.patch(Routes.guildMember(this.guildId, userId), payload, {
3889
- reason: optionsReason ?? reason
5748
+ reason: optionsReason
3890
5749
  });
3891
5750
  const member = new GuildMember({ ...data, guild_id: this.guildId }, this.#context());
3892
5751
  this.set(member.user.id, member);
@@ -3900,7 +5759,10 @@ class GuildMemberManager extends ResourceManager {
3900
5759
  }
3901
5760
  async timeout(userId, milliseconds, reason) {
3902
5761
  const timeoutDate = milliseconds === null ? null : new Date(Date.now() + milliseconds).toISOString();
3903
- return this.edit(userId, { communication_disabled_until: timeoutDate }, reason);
5762
+ return this.edit(userId, {
5763
+ communication_disabled_until: timeoutDate,
5764
+ reason
5765
+ });
3904
5766
  }
3905
5767
  }
3906
5768
 
@@ -3923,20 +5785,15 @@ class RoleManager extends ResourceManager {
3923
5785
  }
3924
5786
  async create(options) {
3925
5787
  const { reason, ...payload } = options;
3926
- const data = await this.#rest.post(Routes.guildRoles(this.guildId), {
3927
- ...payload,
3928
- permissions: payload.permissions !== undefined ? String(payload.permissions) : undefined
3929
- }, { reason });
3930
- const role = new Role(data);
3931
- this.set(role.id, role);
3932
- return role;
5788
+ const data = await this.#rest.post(Routes.guildRoles(this.guildId), rolePayload(payload), { reason });
5789
+ return this.#store(data);
3933
5790
  }
3934
5791
  async edit(roleId, options) {
3935
5792
  const { reason, ...payload } = options;
3936
- const data = await this.#rest.patch(Routes.guildRole(this.guildId, roleId), {
3937
- ...payload,
3938
- permissions: payload.permissions !== undefined ? String(payload.permissions) : undefined
3939
- }, { reason });
5793
+ const data = await this.#rest.patch(Routes.guildRole(this.guildId, roleId), rolePayload(payload), { reason });
5794
+ return this.#store(data);
5795
+ }
5796
+ #store(data) {
3940
5797
  const role = new Role(data);
3941
5798
  this.set(role.id, role);
3942
5799
  return role;
@@ -3955,9 +5812,19 @@ class RoleManager extends ResourceManager {
3955
5812
  return role;
3956
5813
  });
3957
5814
  }
3958
- deleteRole(roleId) {
3959
- return this.remove(roleId);
3960
- }
5815
+ }
5816
+ function rolePayload(options) {
5817
+ const { permissions, colors, unicodeEmoji, ...rest } = options;
5818
+ return {
5819
+ ...rest,
5820
+ permissions: permissions !== undefined ? String(permissions) : undefined,
5821
+ colors: colors && {
5822
+ primary_color: colors.primary,
5823
+ secondary_color: colors.secondary ?? null,
5824
+ tertiary_color: colors.tertiary ?? null
5825
+ },
5826
+ unicode_emoji: unicodeEmoji
5827
+ };
3961
5828
  }
3962
5829
 
3963
5830
  // packages/managers/src/guild-resources.ts
@@ -3997,6 +5864,18 @@ class GuildBanManager extends Manager {
3997
5864
  async create(userId, options = {}) {
3998
5865
  await this.#rest.put(Routes.guildBan(this.guildId, userId), { delete_message_seconds: options.deleteMessageSeconds }, { reason: options.reason });
3999
5866
  }
5867
+ async bulk(userIds, options = {}) {
5868
+ if (userIds.length < 1 || userIds.length > 200)
5869
+ throw new RangeError("Bulk ban takes 1 to 200 user IDs.");
5870
+ const result = await this.#rest.post(Routes.guildBulkBan(this.guildId), {
5871
+ user_ids: userIds,
5872
+ delete_message_seconds: options.deleteMessageSeconds
5873
+ }, { reason: options.reason });
5874
+ return {
5875
+ banned: result.banned_users ?? [],
5876
+ failed: result.failed_users ?? []
5877
+ };
5878
+ }
4000
5879
  async remove(userId, reason) {
4001
5880
  await this.#rest.delete(Routes.guildBan(this.guildId, userId), {
4002
5881
  reason
@@ -4028,13 +5907,13 @@ class GuildScheduledEventManager extends Manager {
4028
5907
  }));
4029
5908
  return events.map((event) => this.#store(event));
4030
5909
  }
4031
- async create(options, reason) {
5910
+ async create(options) {
4032
5911
  const [payload, optionsReason] = splitReason(options);
4033
- return this.#store(await this.#rest.post(Routes.guildScheduledEvents(this.guildId), payload, { reason: optionsReason ?? reason }));
5912
+ return this.#store(await this.#rest.post(Routes.guildScheduledEvents(this.guildId), payload, { reason: optionsReason }));
4034
5913
  }
4035
- async edit(eventId, options, reason) {
5914
+ async edit(eventId, options) {
4036
5915
  const [payload, optionsReason] = splitReason(options);
4037
- return this.#store(await this.#rest.patch(Routes.guildScheduledEvent(this.guildId, eventId), payload, { reason: optionsReason ?? reason }));
5916
+ return this.#store(await this.#rest.patch(Routes.guildScheduledEvent(this.guildId, eventId), payload, { reason: optionsReason }));
4038
5917
  }
4039
5918
  async remove(eventId, reason) {
4040
5919
  await this.#rest.delete(Routes.guildScheduledEvent(this.guildId, eventId), { reason });
@@ -4061,20 +5940,20 @@ class StageInstanceManager extends Manager {
4061
5940
  this.set(stage.channel_id, stage);
4062
5941
  return stage;
4063
5942
  }
4064
- async create(channelId, options, reason) {
5943
+ async create(channelId, options) {
4065
5944
  return this.#store(await this.#rest.post(Routes.stageInstances(), {
4066
5945
  channel_id: channelId,
4067
5946
  topic: options.topic,
4068
5947
  privacy_level: options.privacyLevel,
4069
5948
  send_start_notification: options.sendStartNotification,
4070
5949
  guild_scheduled_event_id: options.guildScheduledEventId
4071
- }, { reason: options.reason ?? reason }));
5950
+ }, { reason: options.reason }));
4072
5951
  }
4073
5952
  async fetch(channelId) {
4074
5953
  return this.#store(await this.#rest.get(Routes.stageInstanceByChannel(channelId)));
4075
5954
  }
4076
- async edit(channelId, options, reason) {
4077
- return this.#store(await this.#rest.patch(Routes.stageInstanceByChannel(channelId), { topic: options.topic, privacy_level: options.privacyLevel }, { reason: options.reason ?? reason }));
5955
+ async edit(channelId, options) {
5956
+ return this.#store(await this.#rest.patch(Routes.stageInstanceByChannel(channelId), { topic: options.topic, privacy_level: options.privacyLevel }, { reason: options.reason }));
4078
5957
  }
4079
5958
  async remove(channelId, reason) {
4080
5959
  await this.#rest.delete(Routes.stageInstanceByChannel(channelId), {
@@ -4104,14 +5983,19 @@ class PermissionOverwriteManager {
4104
5983
  const touched = (changes.allow ?? 0n) | (changes.deny ?? 0n) | (changes.inherit ?? 0n);
4105
5984
  const allow = BigInt(existing?.allow ?? 0) & ~touched | (changes.allow ?? 0n);
4106
5985
  const deny = BigInt(existing?.deny ?? 0) & ~touched | (changes.deny ?? 0n);
4107
- await this.edit(overwriteId, { type, allow, deny }, options.reason);
5986
+ await this.edit(overwriteId, {
5987
+ type,
5988
+ allow,
5989
+ deny,
5990
+ reason: options.reason
5991
+ });
4108
5992
  }
4109
- async edit(overwriteId, options, reason) {
5993
+ async edit(overwriteId, options) {
4110
5994
  await this.#rest.put(Routes.channelPermission(this.channelId, overwriteId), {
4111
5995
  type: options.type,
4112
5996
  allow: BigInt(options.allow ?? 0).toString(),
4113
5997
  deny: BigInt(options.deny ?? 0).toString()
4114
- }, { reason: options.reason ?? reason });
5998
+ }, { reason: options.reason });
4115
5999
  }
4116
6000
  async remove(overwriteId, reason) {
4117
6001
  await this.#rest.delete(Routes.channelPermission(this.channelId, overwriteId), { reason });
@@ -4135,6 +6019,7 @@ class GuildManager extends ResourceManager {
4135
6019
  #soundboard = new Map;
4136
6020
  #voiceStates = new Map;
4137
6021
  #autoModerationRules = new Map;
6022
+ #presences = new Map;
4138
6023
  #invites = new Map;
4139
6024
  #context;
4140
6025
  attachContext(context) {
@@ -4195,6 +6080,9 @@ class GuildManager extends ResourceManager {
4195
6080
  voiceStates(guildId) {
4196
6081
  return this.#perGuild(this.#voiceStates, guildId, () => new Manager);
4197
6082
  }
6083
+ presences(guildId) {
6084
+ return this.#perGuild(this.#presences, guildId, () => new Manager);
6085
+ }
4198
6086
  autoModerationRules(guildId) {
4199
6087
  return this.#perGuild(this.#autoModerationRules, guildId, () => new Manager);
4200
6088
  }
@@ -4220,6 +6108,7 @@ class GuildManager extends ResourceManager {
4220
6108
  this.#stickers.delete(id);
4221
6109
  this.#soundboard.delete(id);
4222
6110
  this.#voiceStates.delete(id);
6111
+ this.#presences.delete(id);
4223
6112
  this.#autoModerationRules.delete(id);
4224
6113
  this.#invites.delete(id);
4225
6114
  return super.delete(id);
@@ -4236,12 +6125,35 @@ class GuildManager extends ResourceManager {
4236
6125
  async remove(id) {
4237
6126
  await this.#rest.delete(Routes.guild(id));
4238
6127
  }
4239
- deleteGuild(id) {
4240
- return this.remove(id);
4241
- }
4242
6128
  async fetchPreview(id) {
4243
6129
  return this.#rest.get(Routes.guildPreview(id));
4244
6130
  }
6131
+ async fetchWelcomeScreen(id) {
6132
+ return this.#rest.get(Routes.guildWelcomeScreen(id));
6133
+ }
6134
+ async editWelcomeScreen(id, options) {
6135
+ const { reason, welcomeChannels, ...rest } = options;
6136
+ return this.#rest.patch(Routes.guildWelcomeScreen(id), { ...rest, welcome_channels: welcomeChannels }, { reason });
6137
+ }
6138
+ async fetchOnboarding(id) {
6139
+ return this.#rest.get(Routes.guildOnboarding(id));
6140
+ }
6141
+ async editOnboarding(id, options) {
6142
+ const { reason, defaultChannelIds, ...rest } = options;
6143
+ return this.#rest.put(Routes.guildOnboarding(id), { ...rest, default_channel_ids: defaultChannelIds }, { reason });
6144
+ }
6145
+ async editVoiceState(id, userId, options) {
6146
+ await this.#rest.patch(Routes.guildVoiceState(id, userId), {
6147
+ channel_id: options.channelId,
6148
+ suppress: options.suppress,
6149
+ request_to_speak_timestamp: options.requestToSpeakTimestamp
6150
+ });
6151
+ }
6152
+ async removeIntegration(id, integrationId, reason) {
6153
+ await this.#rest.delete(Routes.guildIntegration(id, integrationId), {
6154
+ reason
6155
+ });
6156
+ }
4245
6157
  async fetchActiveThreads(id) {
4246
6158
  return this.#rest.get(Routes.guildActiveThreads(id));
4247
6159
  }
@@ -4265,6 +6177,9 @@ class GuildManager extends ResourceManager {
4265
6177
  params.set("limit", String(Math.min(100, Math.max(1, options.limit))));
4266
6178
  return this.#rest.get(withQuery3(Routes.guildAuditLog(guildId), params));
4267
6179
  }
6180
+ async fetchAuditLogEntries(guildId, options = {}) {
6181
+ return new AuditLog(await this.fetchAuditLog(guildId, options));
6182
+ }
4268
6183
  async fetchMembers(guildId, options = {}) {
4269
6184
  const params = new URLSearchParams;
4270
6185
  if (options.limit !== undefined)
@@ -4445,9 +6360,6 @@ class ChannelManager extends Manager {
4445
6360
  await this.#rest.delete(Routes.channel(channelId), { reason });
4446
6361
  this.delete(channelId);
4447
6362
  }
4448
- deleteChannel(channelId) {
4449
- return this.remove(channelId);
4450
- }
4451
6363
  delete(channelId) {
4452
6364
  this.#messageManagers.delete(channelId);
4453
6365
  return super.delete(channelId);
@@ -4455,9 +6367,6 @@ class ChannelManager extends Manager {
4455
6367
  send(channelId, options) {
4456
6368
  return this.messages(channelId).send(options);
4457
6369
  }
4458
- sendMessage(channelId, options) {
4459
- return this.send(channelId, options);
4460
- }
4461
6370
  async fetchMessage(channelId, messageId, options = {}) {
4462
6371
  return this.messages(channelId).fetch(messageId);
4463
6372
  }
@@ -4526,7 +6435,7 @@ class ChannelManager extends Manager {
4526
6435
  }
4527
6436
  }
4528
6437
  async addReaction(channelId, messageId, emoji) {
4529
- await this.#rest.put(`${Routes.messageReactions(channelId, messageId, emoji)}/@me`);
6438
+ await this.#rest.put(Routes.messageReactionMe(channelId, messageId, emoji));
4530
6439
  }
4531
6440
  async fetchReactions(channelId, messageId, emoji, options = {}) {
4532
6441
  const params = new URLSearchParams;
@@ -4539,10 +6448,10 @@ class ChannelManager extends Manager {
4539
6448
  return data.map((user) => new User(user));
4540
6449
  }
4541
6450
  async removeOwnReaction(channelId, messageId, emoji) {
4542
- await this.#rest.delete(`${Routes.messageReactions(channelId, messageId, emoji)}/@me`);
6451
+ await this.#rest.delete(Routes.messageReactionMe(channelId, messageId, emoji));
4543
6452
  }
4544
6453
  async removeReaction(channelId, messageId, emoji, userId) {
4545
- await this.#rest.delete(`${Routes.messageReactions(channelId, messageId, emoji)}/${userId}`);
6454
+ await this.#rest.delete(Routes.messageReactionUser(channelId, messageId, emoji, userId));
4546
6455
  }
4547
6456
  async removeAllReactions(channelId, messageId) {
4548
6457
  await this.#rest.delete(Routes.messageReactionsAll(channelId, messageId));
@@ -4551,6 +6460,46 @@ class ChannelManager extends Manager {
4551
6460
  const data = await this.#rest.get(Routes.channelMessagesPins(channelId));
4552
6461
  return data.items.map((item) => this.messages(channelId).upsert(item.message));
4553
6462
  }
6463
+ async fetchPins(channelId, options = {}) {
6464
+ const params = new URLSearchParams;
6465
+ if (options.before)
6466
+ params.set("before", options.before);
6467
+ if (options.limit !== undefined)
6468
+ params.set("limit", String(Math.min(50, Math.max(1, options.limit))));
6469
+ const query = params.toString();
6470
+ const data = await this.#rest.get(`${Routes.channelMessagesPins(channelId)}${query ? `?${query}` : ""}`);
6471
+ return {
6472
+ messages: data.items.map((item) => this.messages(channelId).upsert(item.message)),
6473
+ hasMore: data.has_more
6474
+ };
6475
+ }
6476
+ async triggerTyping(channelId) {
6477
+ await this.#rest.post(Routes.channelTyping(channelId));
6478
+ }
6479
+ async createForumTag(channelId, tag, reason) {
6480
+ const tags = await this.#forumTags(channelId);
6481
+ return this.#saveForumTags(channelId, [...tags, tag], reason);
6482
+ }
6483
+ async editForumTag(channelId, tagId, changes, reason) {
6484
+ const tags = await this.#forumTags(channelId);
6485
+ if (!tags.some((tag) => tag.id === tagId))
6486
+ throw new Error(`Channel ${channelId} has no tag ${tagId}.`);
6487
+ return this.#saveForumTags(channelId, tags.map((tag) => tag.id === tagId ? { ...tag, ...changes } : tag), reason);
6488
+ }
6489
+ async removeForumTag(channelId, tagId, reason) {
6490
+ const tags = await this.#forumTags(channelId);
6491
+ if (!tags.some((tag) => tag.id === tagId))
6492
+ throw new Error(`Channel ${channelId} has no tag ${tagId}.`);
6493
+ return this.#saveForumTags(channelId, tags.filter((tag) => tag.id !== tagId), reason);
6494
+ }
6495
+ async#forumTags(channelId) {
6496
+ const channel = await this.#rest.get(Routes.channel(channelId));
6497
+ return channel.available_tags ?? [];
6498
+ }
6499
+ async#saveForumTags(channelId, tags, reason) {
6500
+ const updated = await this.#rest.patch(Routes.channel(channelId), { available_tags: tags }, { reason });
6501
+ return updated.available_tags ?? [];
6502
+ }
4554
6503
  async pinMessage(channelId, messageId, reason) {
4555
6504
  await this.#rest.put(Routes.channelMessagesPin(channelId, messageId), undefined, { reason });
4556
6505
  }
@@ -4579,9 +6528,6 @@ class ChannelManager extends Manager {
4579
6528
  hasMore: messages.length === limit
4580
6529
  };
4581
6530
  }
4582
- bulkDelete(channelId, messageIds) {
4583
- return this.bulkDeleteMessages(channelId, messageIds);
4584
- }
4585
6531
  clear() {
4586
6532
  this.#messageManagers.clear();
4587
6533
  super.clear();
@@ -4665,7 +6611,26 @@ class GatewaySession {
4665
6611
  return resume ? { type: "resume", ...resume } : { type: "identify" };
4666
6612
  }
4667
6613
  connectURL(fallback) {
4668
- return this.resumeInfo()?.resumeURL ?? fallback;
6614
+ const resume = this.resumeInfo()?.resumeURL;
6615
+ return resume !== undefined && isTrustedGatewayURL(resume, fallback) ? resume : fallback;
6616
+ }
6617
+ }
6618
+ function isTrustedGatewayURL(url, fallback) {
6619
+ let parsed;
6620
+ try {
6621
+ parsed = new URL(url);
6622
+ } catch {
6623
+ return false;
6624
+ }
6625
+ if (parsed.protocol !== "wss:" || parsed.username || parsed.password)
6626
+ return false;
6627
+ const host = parsed.hostname.toLowerCase();
6628
+ if (host === "discord.gg" || host.endsWith(".discord.gg"))
6629
+ return true;
6630
+ try {
6631
+ return host === new URL(fallback).hostname.toLowerCase();
6632
+ } catch {
6633
+ return false;
4669
6634
  }
4670
6635
  }
4671
6636
 
@@ -4837,6 +6802,7 @@ class GatewayReconnect {
4837
6802
  #options;
4838
6803
  #random;
4839
6804
  #attempt = 0;
6805
+ #recent = [];
4840
6806
  #timer;
4841
6807
  #promise;
4842
6808
  #resolve;
@@ -4877,14 +6843,29 @@ class GatewayReconnect {
4877
6843
  if (this.exhausted)
4878
6844
  return { scheduled: false, reason: "exhausted" };
4879
6845
  const attempt = this.#attempt;
4880
- const delay = Math.min(this.#options.maxDelay, this.#options.baseDelay * 2 ** this.#attempt++);
6846
+ const now = (this.#options.now ?? Date.now)();
6847
+ const window = this.#options.stormWindow ?? 60000;
6848
+ const limit = this.#options.stormLimit ?? 5;
6849
+ while (this.#recent.length && now - this.#recent[0] >= window)
6850
+ this.#recent.shift();
6851
+ const over = this.#recent.length - limit;
6852
+ this.#recent.push(now);
6853
+ let delay = Math.min(this.#options.maxDelay, this.#options.baseDelay * 2 ** this.#attempt++);
6854
+ const unstable = over >= 0;
6855
+ if (unstable)
6856
+ delay = Math.min(this.#options.maxDelay, Math.max(delay, this.#options.baseDelay * 2 ** (over + 1)));
4881
6857
  const jitter = this.#random() * Math.max(1, delay * 0.25);
4882
6858
  const delayMs = delay + jitter;
4883
6859
  this.#timer = setTimeout(() => {
4884
6860
  this.#timer = undefined;
4885
6861
  run();
4886
6862
  }, delayMs);
4887
- return { scheduled: true, delayMs, attempt };
6863
+ return {
6864
+ scheduled: true,
6865
+ delayMs,
6866
+ attempt,
6867
+ ...unstable ? { unstable: { reconnects: this.#recent.length } } : {}
6868
+ };
4888
6869
  }
4889
6870
  cancel() {
4890
6871
  if (this.#timer)
@@ -4941,12 +6922,15 @@ class PlainTextDecoder {
4941
6922
 
4942
6923
  class ZlibStreamDecoder {
4943
6924
  #create;
6925
+ #maxFrameBytes;
4944
6926
  #inflate;
4945
6927
  #chunks = [];
6928
+ #size = 0;
4946
6929
  #failure;
4947
6930
  #pendingReject;
4948
- constructor(create = () => createInflate()) {
6931
+ constructor(create = () => createInflate(), maxFrameBytes = 64 * 1024 * 1024) {
4949
6932
  this.#create = create;
6933
+ this.#maxFrameBytes = maxFrameBytes;
4950
6934
  }
4951
6935
  push(data) {
4952
6936
  if (typeof data === "string")
@@ -4965,6 +6949,7 @@ class ZlibStreamDecoder {
4965
6949
  await this.#awaiting((settle) => inflate.flush(zlibConstants.Z_SYNC_FLUSH, () => settle()));
4966
6950
  const parts = this.#chunks;
4967
6951
  this.#chunks = [];
6952
+ this.#size = 0;
4968
6953
  if (parts.length === 0)
4969
6954
  return [];
4970
6955
  const total = parts.reduce((size, part) => size + part.length, 0);
@@ -4980,6 +6965,7 @@ class ZlibStreamDecoder {
4980
6965
  const inflate = this.#inflate;
4981
6966
  this.#inflate = undefined;
4982
6967
  this.#chunks = [];
6968
+ this.#size = 0;
4983
6969
  this.#failure = undefined;
4984
6970
  this.#pendingReject = undefined;
4985
6971
  if (!inflate)
@@ -5007,7 +6993,17 @@ class ZlibStreamDecoder {
5007
6993
  }
5008
6994
  #open() {
5009
6995
  const inflate = this.#create();
5010
- inflate.on("data", (chunk) => this.#chunks.push(chunk));
6996
+ inflate.on("data", (chunk) => {
6997
+ if (this.#failure)
6998
+ return;
6999
+ this.#size += chunk.length;
7000
+ if (this.#size > this.#maxFrameBytes) {
7001
+ this.#chunks = [];
7002
+ inflate.emit("error", new Error(`Gateway frame inflated past ${this.#maxFrameBytes} bytes.`));
7003
+ return;
7004
+ }
7005
+ this.#chunks.push(chunk);
7006
+ });
5011
7007
  inflate.on("error", (error) => {
5012
7008
  this.#failure = error;
5013
7009
  const reject = this.#pendingReject;
@@ -5250,9 +7246,9 @@ function classifyPayload(payload) {
5250
7246
  }
5251
7247
  function identifyPayload(options) {
5252
7248
  const props = options.properties ?? {};
5253
- const os = props.os ?? "Android";
5254
- const browser = props.browser ?? "Discord Android";
5255
- const device = props.device ?? "Discord Android";
7249
+ const os = props.os ?? process.platform;
7250
+ const browser = props.browser ?? "Lunibee";
7251
+ const device = props.device ?? "Lunibee";
5256
7252
  return {
5257
7253
  op: GatewayOpcodes.Identify,
5258
7254
  d: {
@@ -5294,17 +7290,40 @@ function resumePayload(token, resume) {
5294
7290
  function heartbeatPayload(sequence) {
5295
7291
  return { op: GatewayOpcodes.Heartbeat, d: sequence, s: null, t: null };
5296
7292
  }
5297
- // packages/ws/src/state.ts
5298
- class GatewayError extends Error {
5299
- code;
5300
- constructor(message, code, options) {
5301
- super(message, options);
5302
- this.name = "GatewayError";
5303
- this.code = code;
7293
+ // packages/ws/src/dispatcher.ts
7294
+ class GatewayDispatcher {
7295
+ #listeners = new Map;
7296
+ #onFailure;
7297
+ constructor(onFailure) {
7298
+ this.#onFailure = onFailure;
7299
+ }
7300
+ on(event, listener) {
7301
+ let listeners = this.#listeners.get(event);
7302
+ if (!listeners)
7303
+ this.#listeners.set(event, listeners = new Set);
7304
+ listeners.add(listener);
7305
+ }
7306
+ off(event, listener) {
7307
+ this.#listeners.get(event)?.delete(listener);
7308
+ }
7309
+ emit(event, data) {
7310
+ for (const listener of this.#listeners.get(event) ?? []) {
7311
+ try {
7312
+ const result = listener(data);
7313
+ if (result && typeof result.then === "function")
7314
+ Promise.resolve(result).catch((error) => {
7315
+ if (event !== "error")
7316
+ this.#onFailure(error);
7317
+ });
7318
+ } catch (error) {
7319
+ if (event !== "error")
7320
+ this.#onFailure(error);
7321
+ }
7322
+ }
5304
7323
  }
5305
7324
  }
5306
7325
  // packages/ws/src/send-budget.ts
5307
- class SendBudget {
7326
+ class GatewaySendLimiter {
5308
7327
  #timestamps = [];
5309
7328
  #limit;
5310
7329
  #windowMs;
@@ -5324,7 +7343,15 @@ class SendBudget {
5324
7343
  this.#timestamps.push(now);
5325
7344
  }
5326
7345
  }
5327
-
7346
+ // packages/ws/src/state.ts
7347
+ class GatewayError extends Error {
7348
+ code;
7349
+ constructor(message, code, options) {
7350
+ super(message, options);
7351
+ this.name = "GatewayError";
7352
+ this.code = code;
7353
+ }
7354
+ }
5328
7355
  // packages/ws/src/index.ts
5329
7356
  var RESUMABLE_CLOSE_CODE = 4900;
5330
7357
  var DEFAULT_GATEWAY_URL = "wss://gateway.discord.gg/?v=10&encoding=json";
@@ -5338,8 +7365,9 @@ class Gateway {
5338
7365
  #heartbeat;
5339
7366
  #closed = false;
5340
7367
  #reconnect;
5341
- #listeners = new Map;
5342
- #sendBudget = new SendBudget;
7368
+ #events = new GatewayDispatcher((error) => this.#emitError(error));
7369
+ #sendBudget = new GatewaySendLimiter;
7370
+ #reconnects = 0;
5343
7371
  ping = -1;
5344
7372
  constructor(options) {
5345
7373
  if (!options.token?.trim())
@@ -5357,9 +7385,9 @@ class Gateway {
5357
7385
  heartbeatAckTimeout: 1e4,
5358
7386
  zombieTimeout: 30000,
5359
7387
  properties: {
5360
- os: "Android",
5361
- browser: "Discord Android",
5362
- device: "Discord Android"
7388
+ os: process.platform,
7389
+ browser: "Lunibee",
7390
+ device: "Lunibee"
5363
7391
  },
5364
7392
  presence: {
5365
7393
  status: "online",
@@ -5390,7 +7418,9 @@ class Gateway {
5390
7418
  enabled: this.#options.reconnect,
5391
7419
  maxAttempts: this.#options.maxReconnectAttempts,
5392
7420
  baseDelay: this.#options.reconnectBaseDelay,
5393
- maxDelay: this.#options.reconnectMaxDelay
7421
+ maxDelay: this.#options.reconnectMaxDelay,
7422
+ stormLimit: options.reconnectStormLimit,
7423
+ stormWindow: options.reconnectStormWindow
5394
7424
  });
5395
7425
  this.#heartbeat = new GatewayHeartbeat({
5396
7426
  ackTimeout: this.#options.heartbeatAckTimeout,
@@ -5441,16 +7471,21 @@ class Gateway {
5441
7471
  on(event, listener) {
5442
7472
  if (!event || typeof listener !== "function")
5443
7473
  throw new TypeError("Gateway event and listener are required.");
5444
- let listeners = this.#listeners.get(event);
5445
- if (!listeners)
5446
- this.#listeners.set(event, listeners = new Set);
5447
- listeners.add(listener);
7474
+ this.#events.on(event, listener);
5448
7475
  return this;
5449
7476
  }
5450
7477
  off(event, listener) {
5451
- this.#listeners.get(event)?.delete(listener);
7478
+ this.#events.off(event, listener);
5452
7479
  return this;
5453
7480
  }
7481
+ health() {
7482
+ return {
7483
+ state: this.state,
7484
+ latency: this.#heartbeat.latency,
7485
+ silentFor: this.#heartbeat.silentFor,
7486
+ reconnects: this.#reconnects
7487
+ };
7488
+ }
5454
7489
  send(payload) {
5455
7490
  if (!payload || !Number.isInteger(payload.op))
5456
7491
  throw new TypeError("Gateway payload must contain an integer opcode.");
@@ -5492,6 +7527,22 @@ class Gateway {
5492
7527
  t: null
5493
7528
  });
5494
7529
  }
7530
+ sendVoiceState(guildId, channelId, options = {}) {
7531
+ if (!this.ownsGuild(guildId))
7532
+ return false;
7533
+ return this.setVoiceState({
7534
+ guild_id: guildId,
7535
+ channel_id: channelId,
7536
+ self_mute: options.selfMute ?? false,
7537
+ self_deaf: options.selfDeaf ?? false
7538
+ });
7539
+ }
7540
+ ownsGuild(guildId) {
7541
+ const count = this.#options.shardCount;
7542
+ if (count <= 1)
7543
+ return true;
7544
+ return Number((BigInt(guildId) >> 22n) % BigInt(count)) === this.#options.shardId;
7545
+ }
5495
7546
  requestGuildMembers(data) {
5496
7547
  return this.send({
5497
7548
  op: GatewayOpcodes.RequestGuildMembers,
@@ -5629,7 +7680,16 @@ class Gateway {
5629
7680
  if (this.#closed)
5630
7681
  return;
5631
7682
  const result = this.#reconnect.schedule(() => this.#open(this.#session.connectURL(DEFAULT_GATEWAY_URL)));
5632
- if (result.scheduled || result.reason !== "exhausted")
7683
+ if (result.scheduled) {
7684
+ this.#reconnects++;
7685
+ if (result.unstable)
7686
+ this.#emit("unstable", {
7687
+ reconnects: result.unstable.reconnects,
7688
+ delay: result.delayMs
7689
+ });
7690
+ return;
7691
+ }
7692
+ if (result.reason !== "exhausted")
5633
7693
  return;
5634
7694
  this.#reconnect.settle(new GatewayError("Gateway reconnect attempts exhausted."));
5635
7695
  this.#setState("CONNECT" /* Connect */);
@@ -5650,19 +7710,7 @@ class Gateway {
5650
7710
  this.#emit("error", error instanceof Error ? error : new GatewayError(String(error)));
5651
7711
  }
5652
7712
  #emit(event, data) {
5653
- for (const listener of this.#listeners.get(event) ?? []) {
5654
- try {
5655
- const result = listener(data);
5656
- if (result && typeof result.then === "function")
5657
- Promise.resolve(result).catch((error) => {
5658
- if (event !== "error")
5659
- this.#emitError(error);
5660
- });
5661
- } catch (error) {
5662
- if (event !== "error")
5663
- this.#emitError(error);
5664
- }
5665
- }
7713
+ this.#events.emit(event, data);
5666
7714
  }
5667
7715
  #setState(next) {
5668
7716
  if (this.state === next)
@@ -5675,14 +7723,12 @@ class Gateway {
5675
7723
 
5676
7724
  // packages/core/src/index.ts
5677
7725
  class EventEmitter2 {
5678
- #listeners = new Map;
7726
+ #listeners = {};
7727
+ redactSecrets = (text) => text;
5679
7728
  on(event, listener) {
5680
7729
  if (typeof listener !== "function")
5681
7730
  throw new TypeError("Event listener must be a function.");
5682
- let listeners = this.#listeners.get(event);
5683
- if (!listeners)
5684
- this.#listeners.set(event, listeners = new Set);
5685
- listeners.add(listener);
7731
+ (this.#listeners[event] ??= new Set).add(listener);
5686
7732
  return this;
5687
7733
  }
5688
7734
  once(event, listener) {
@@ -5693,7 +7739,7 @@ class EventEmitter2 {
5693
7739
  return this.on(event, wrapped);
5694
7740
  }
5695
7741
  off(event, listener) {
5696
- this.#listeners.get(event)?.delete(listener);
7742
+ this.#listeners[event]?.delete(listener);
5697
7743
  return this;
5698
7744
  }
5699
7745
  removeListener(event, listener) {
@@ -5701,13 +7747,13 @@ class EventEmitter2 {
5701
7747
  }
5702
7748
  removeAllListeners(event) {
5703
7749
  if (event === undefined)
5704
- this.#listeners.clear();
7750
+ this.#listeners = {};
5705
7751
  else
5706
- this.#listeners.delete(event);
7752
+ delete this.#listeners[event];
5707
7753
  return this;
5708
7754
  }
5709
7755
  emit(event, ...args) {
5710
- const listeners = this.#listeners.get(event);
7756
+ const listeners = this.#listeners[event];
5711
7757
  if (!listeners?.size)
5712
7758
  return false;
5713
7759
  for (const listener of [...listeners]) {
@@ -5723,28 +7769,27 @@ class EventEmitter2 {
5723
7769
  }
5724
7770
  #handleError(event, error) {
5725
7771
  const normalized = error instanceof Error ? error : new Error(String(error), { cause: error });
5726
- const listeners = event === "error" /* Error */ ? [] : [
5727
- ...this.#listeners.get("error" /* Error */) ?? []
5728
- ];
7772
+ const errorListeners = this.#listeners["error" /* Error */];
7773
+ const listeners = event === "error" /* Error */ ? [] : [...errorListeners ?? []];
5729
7774
  if (listeners.length === 0)
5730
- return warnListenerError(event, normalized);
7775
+ return warnListenerError(event, normalized, this.redactSecrets);
5731
7776
  for (const listener of listeners) {
5732
7777
  try {
5733
7778
  const result = listener(normalized);
5734
7779
  if (result && typeof result.then === "function")
5735
- Promise.resolve(result).catch((failure) => warnListenerError("error" /* Error */, failure));
7780
+ Promise.resolve(result).catch((failure) => warnListenerError("error" /* Error */, failure, this.redactSecrets));
5736
7781
  } catch (failure) {
5737
- warnListenerError("error" /* Error */, failure);
7782
+ warnListenerError("error" /* Error */, failure, this.redactSecrets);
5738
7783
  }
5739
7784
  }
5740
7785
  }
5741
7786
  }
5742
- function warnListenerError(event, error) {
7787
+ function warnListenerError(event, error, redact = (text) => text) {
5743
7788
  const failure = error instanceof Error ? error : new Error(String(error));
5744
- process.emitWarning(`A "${String(event)}" listener threw and no error listener handled it: ${failure.message}`, {
7789
+ process.emitWarning(redact(`A "${String(event)}" listener threw and no error listener handled it: ${failure.message}`), {
5745
7790
  type: "LunibeeWarning",
5746
7791
  code: "LUNIBEE_UNHANDLED_LISTENER_ERROR",
5747
- detail: failure.stack
7792
+ detail: failure.stack === undefined ? undefined : redact(failure.stack)
5748
7793
  });
5749
7794
  }
5750
7795
 
@@ -5756,7 +7801,13 @@ class Client extends EventEmitter2 {
5756
7801
  channels;
5757
7802
  stageInstances;
5758
7803
  application;
7804
+ #api;
7805
+ get api() {
7806
+ return this.#api ??= createApi(this.rest);
7807
+ }
7808
+ commands = new CommandRegistry(this);
5759
7809
  monetization;
7810
+ applicationEmojis;
5760
7811
  get ws() {
5761
7812
  return this.#gateway;
5762
7813
  }
@@ -5766,6 +7817,10 @@ class Client extends EventEmitter2 {
5766
7817
  user;
5767
7818
  readyAt;
5768
7819
  state = "idle";
7820
+ redactSecrets = (text) => {
7821
+ const token = this.options.token;
7822
+ return token && token.length >= 8 ? text.split(token).join("[token]") : text;
7823
+ };
5769
7824
  get token() {
5770
7825
  return this.state === "destroyed" ? null : this.options.token ?? null;
5771
7826
  }
@@ -5781,9 +7836,26 @@ class Client extends EventEmitter2 {
5781
7836
  setPresence(data) {
5782
7837
  return this.#gateway.setPresence(data);
5783
7838
  }
7839
+ setActivity(name, options = {}) {
7840
+ const type = options.type ?? ActivityEnum.Playing;
7841
+ return this.setPresence({
7842
+ status: options.status ?? "online",
7843
+ activities: name === null ? [] : [
7844
+ {
7845
+ name,
7846
+ type,
7847
+ url: options.url,
7848
+ state: options.state ?? (type === ActivityEnum.Custom ? name : undefined)
7849
+ }
7850
+ ]
7851
+ });
7852
+ }
5784
7853
  setVoiceState(data) {
5785
7854
  return this.#gateway.setVoiceState(data);
5786
7855
  }
7856
+ sendVoiceState(guildId, channelId, options = {}) {
7857
+ return this.#gateway.sendVoiceState(guildId, channelId, options);
7858
+ }
5787
7859
  requestGuildMembers(data) {
5788
7860
  return this.#gateway.requestGuildMembers(data);
5789
7861
  }
@@ -5801,6 +7873,7 @@ class Client extends EventEmitter2 {
5801
7873
  members: true,
5802
7874
  roles: true,
5803
7875
  emojis: true,
7876
+ presences: false,
5804
7877
  ...options.cache
5805
7878
  };
5806
7879
  this.users = new UserManager(this.rest);
@@ -5812,7 +7885,23 @@ class Client extends EventEmitter2 {
5812
7885
  this.stageInstances = new StageInstanceManager(this.rest);
5813
7886
  const placeholderAppCommands = new ApplicationCommandManager(this.rest, "0");
5814
7887
  this.application = { commands: placeholderAppCommands };
5815
- this.#resourceContext = {
7888
+ this.#resourceContext = this.#createResourceContext();
7889
+ this.guilds.attachContext(this.#resourceContext);
7890
+ this.#gateway = new Gateway({
7891
+ token: options.token,
7892
+ intents: options.intents,
7893
+ ...options.compress === undefined ? {} : { compress: options.compress },
7894
+ ...options.gateway
7895
+ });
7896
+ this.#wireLifecycle();
7897
+ this.#wireMessages();
7898
+ this.#wireGuilds();
7899
+ this.#wireGuildFeatures();
7900
+ this.#wireChannels();
7901
+ this.#wireMisc();
7902
+ }
7903
+ #createResourceContext() {
7904
+ return {
5816
7905
  sendMessage: (channelId, options) => this.channels.send(channelId, options),
5817
7906
  editMessage: (channelId, messageId, options) => this.channels.editMessage(channelId, messageId, options),
5818
7907
  deleteMessage: (channelId, messageId, reason) => this.channels.deleteMessage(channelId, messageId, reason),
@@ -5826,10 +7915,13 @@ class Client extends EventEmitter2 {
5826
7915
  pinMessage: (channelId, messageId, reason) => this.channels.pinMessage(channelId, messageId, reason),
5827
7916
  unpinMessage: (channelId, messageId, reason) => this.channels.unpinMessage(channelId, messageId, reason),
5828
7917
  collectInteractions: (options) => this.createCollector("interactionCreate" /* InteractionCreate */, options),
5829
- memberPermissions: (guildId, memberId, roleIds, channelId) => this.#memberPermissions(guildId, memberId, roleIds, channelId),
7918
+ memberPermissions: (guildId, memberId, roleIds, channelId, timedOutUntil) => this.#memberPermissions(guildId, memberId, roleIds, channelId, timedOutUntil),
5830
7919
  kickMember: (guildId, userId, reason) => this.guilds.members(guildId).kick(userId, reason),
5831
7920
  banMember: (guildId, userId, options) => this.guilds.members(guildId).ban(userId, options),
5832
- editMember: (guildId, userId, options, reason) => this.guilds.members(guildId).edit(userId, options, reason),
7921
+ editMember: (guildId, userId, options, reason) => this.guilds.members(guildId).edit(userId, {
7922
+ ...options,
7923
+ reason: typeof options.reason === "string" ? options.reason : reason
7924
+ }),
5833
7925
  addMemberRole: (guildId, userId, roleId, reason) => this.guilds.members(guildId).addRole(userId, roleId, reason),
5834
7926
  removeMemberRole: (guildId, userId, roleId, reason) => this.guilds.members(guildId).removeRole(userId, roleId, reason),
5835
7927
  editPermissionOverwrite: async (channelId, targetId, changes, options) => {
@@ -5849,12 +7941,8 @@ class Client extends EventEmitter2 {
5849
7941
  return this.channels.permissionOverwrites(channelId).update(targetId, bits, options);
5850
7942
  }
5851
7943
  };
5852
- this.guilds.attachContext(this.#resourceContext);
5853
- this.#gateway = new Gateway({
5854
- token: options.token,
5855
- intents: options.intents,
5856
- ...options.gateway
5857
- });
7944
+ }
7945
+ #wireLifecycle() {
5858
7946
  this.#gateway.on("READY", (data) => {
5859
7947
  const ready = data;
5860
7948
  this.user = ready.user;
@@ -5865,6 +7953,8 @@ class Client extends EventEmitter2 {
5865
7953
  this.application.commands = new ApplicationCommandManager(this.rest, appId);
5866
7954
  if (this.monetization?.applicationId !== appId)
5867
7955
  this.monetization = new MonetizationManager(this.rest, appId);
7956
+ if (this.applicationEmojis?.applicationId !== appId)
7957
+ this.applicationEmojis = new ApplicationEmojiManager(this.rest, appId);
5868
7958
  this.emit("ready" /* Ready */, this.user);
5869
7959
  });
5870
7960
  this.#gateway.on("RESUMED", () => {
@@ -5880,6 +7970,8 @@ class Client extends EventEmitter2 {
5880
7970
  this.state = "idle";
5881
7971
  this.emit("close" /* Close */, data);
5882
7972
  });
7973
+ }
7974
+ #wireMessages() {
5883
7975
  this.#gateway.on("MESSAGE_CREATE", (data) => {
5884
7976
  const payload = data;
5885
7977
  const message = new Message(payload, this.#resourceContext);
@@ -5899,7 +7991,7 @@ class Client extends EventEmitter2 {
5899
7991
  const previous = messages?.cache.peek(message.id);
5900
7992
  if (previous)
5901
7993
  messages.cache.set(message.id, message);
5902
- this.emit("messageUpdate" /* MessageUpdate */, message, previous);
7994
+ this.emit("messageUpdate" /* MessageUpdate */, message, previous ?? null);
5903
7995
  });
5904
7996
  this.#gateway.on("MESSAGE_DELETE", (data) => {
5905
7997
  const payload = data;
@@ -5927,6 +8019,8 @@ class Client extends EventEmitter2 {
5927
8019
  this.#gateway.on("MESSAGE_REACTION_REMOVE_EMOJI", (data) => this.emit("messageReactionRemoveEmoji" /* MessageReactionRemoveEmoji */, data));
5928
8020
  this.#gateway.on("MESSAGE_POLL_VOTE_ADD", (data) => this.emit("messagePollVoteAdd" /* MessagePollVoteAdd */, data));
5929
8021
  this.#gateway.on("MESSAGE_POLL_VOTE_REMOVE", (data) => this.emit("messagePollVoteRemove" /* MessagePollVoteRemove */, data));
8022
+ }
8023
+ #wireGuilds() {
5930
8024
  this.#gateway.on("GUILD_CREATE", (data) => {
5931
8025
  const payload = data;
5932
8026
  if (payload.unavailable) {
@@ -6027,6 +8121,8 @@ class Client extends EventEmitter2 {
6027
8121
  roles.delete(event.role_id);
6028
8122
  this.emit("guildRoleDelete" /* GuildRoleDelete */, event, removed);
6029
8123
  });
8124
+ }
8125
+ #wireGuildFeatures() {
6030
8126
  this.#gateway.on("GUILD_EMOJIS_UPDATE", (data) => {
6031
8127
  const event = data;
6032
8128
  let previous = null;
@@ -6097,8 +8193,10 @@ class Client extends EventEmitter2 {
6097
8193
  });
6098
8194
  this.#gateway.on("GUILD_SCHEDULED_EVENT_UPDATE", (data) => {
6099
8195
  const event = data;
6100
- this.guilds.scheduledEvents(event.guild_id).set(event.id, event);
6101
- this.emit("guildScheduledEventUpdate" /* GuildScheduledEventUpdate */, event);
8196
+ const events = this.guilds.scheduledEvents(event.guild_id);
8197
+ const previous = events.get(event.id) ?? null;
8198
+ events.set(event.id, event);
8199
+ this.emit("guildScheduledEventUpdate" /* GuildScheduledEventUpdate */, event, previous);
6102
8200
  });
6103
8201
  this.#gateway.on("GUILD_SCHEDULED_EVENT_DELETE", (data) => {
6104
8202
  const event = data;
@@ -6114,8 +8212,10 @@ class Client extends EventEmitter2 {
6114
8212
  });
6115
8213
  this.#gateway.on("AUTO_MODERATION_RULE_UPDATE", (data) => {
6116
8214
  const rule = data;
6117
- this.guilds.autoModerationRules(rule.guild_id).set(rule.id, rule);
6118
- this.emit("autoModerationRuleUpdate" /* AutoModerationRuleUpdate */, rule);
8215
+ const rules = this.guilds.autoModerationRules(rule.guild_id);
8216
+ const previous = rules.get(rule.id) ?? null;
8217
+ rules.set(rule.id, rule);
8218
+ this.emit("autoModerationRuleUpdate" /* AutoModerationRuleUpdate */, rule, previous);
6119
8219
  });
6120
8220
  this.#gateway.on("AUTO_MODERATION_RULE_DELETE", (data) => {
6121
8221
  const rule = data;
@@ -6123,6 +8223,8 @@ class Client extends EventEmitter2 {
6123
8223
  this.emit("autoModerationRuleDelete" /* AutoModerationRuleDelete */, rule);
6124
8224
  });
6125
8225
  this.#gateway.on("AUTO_MODERATION_ACTION_EXECUTION", (data) => this.emit("autoModerationActionExecution" /* AutoModerationActionExecution */, data));
8226
+ }
8227
+ #wireChannels() {
6126
8228
  this.#gateway.on("CHANNEL_CREATE", (data) => {
6127
8229
  const channel = this.channels.upsert(data);
6128
8230
  this.emit("channelCreate" /* ChannelCreate */, channel);
@@ -6169,8 +8271,9 @@ class Client extends EventEmitter2 {
6169
8271
  });
6170
8272
  this.#gateway.on("STAGE_INSTANCE_UPDATE", (data) => {
6171
8273
  const stage = data;
8274
+ const previous = this.stageInstances.get(stage.channel_id) ?? null;
6172
8275
  this.stageInstances.set(stage.channel_id, stage);
6173
- this.emit("stageInstanceUpdate" /* StageInstanceUpdate */, stage);
8276
+ this.emit("stageInstanceUpdate" /* StageInstanceUpdate */, stage, previous);
6174
8277
  });
6175
8278
  this.#gateway.on("STAGE_INSTANCE_DELETE", (data) => {
6176
8279
  const stage = data;
@@ -6190,14 +8293,26 @@ class Client extends EventEmitter2 {
6190
8293
  this.emit("inviteDelete" /* InviteDelete */, invite);
6191
8294
  });
6192
8295
  this.#gateway.on("WEBHOOKS_UPDATE", (data) => this.emit("webhooksUpdate" /* WebhooksUpdate */, data));
8296
+ }
8297
+ #wireMisc() {
6193
8298
  this.#gateway.on("VOICE_STATE_UPDATE", (data) => {
6194
8299
  const state = data;
8300
+ const previous = state.guild_id ? this.guilds.voiceStates(state.guild_id).get(state.user_id) ?? null : null;
6195
8301
  if (state.guild_id)
6196
8302
  this.#storeVoiceState(state.guild_id, state);
6197
- this.emit("voiceStateUpdate" /* VoiceStateUpdate */, state);
8303
+ this.emit("voiceStateUpdate" /* VoiceStateUpdate */, state, previous);
6198
8304
  });
6199
8305
  this.#gateway.on("VOICE_SERVER_UPDATE", (data) => this.emit("voiceServerUpdate" /* VoiceServerUpdate */, data));
6200
- this.#gateway.on("PRESENCE_UPDATE", (data) => this.emit("presenceUpdate" /* PresenceUpdate */, data));
8306
+ this.#gateway.on("PRESENCE_UPDATE", (data) => {
8307
+ const presence = data;
8308
+ let previous = null;
8309
+ if (this.#cache.presences && presence.guild_id) {
8310
+ const presences = this.guilds.presences(presence.guild_id);
8311
+ previous = presences.get(presence.user.id) ?? null;
8312
+ presences.set(presence.user.id, presence);
8313
+ }
8314
+ this.emit("presenceUpdate" /* PresenceUpdate */, presence, previous);
8315
+ });
6201
8316
  this.#gateway.on("TYPING_START", (data) => this.emit("typingStart" /* TypingStart */, data));
6202
8317
  this.#gateway.on("INTERACTION_CREATE", (data) => this.emit("interactionCreate" /* InteractionCreate */, createInteraction(this, data)));
6203
8318
  this.#gateway.on("RAW", (data) => this.emit("raw" /* Raw */, data));
@@ -6262,10 +8377,28 @@ class Client extends EventEmitter2 {
6262
8377
  collector.onDispose(() => this.off(event, listener));
6263
8378
  return collector;
6264
8379
  }
8380
+ guild(id) {
8381
+ return new GuildHandle(this, id);
8382
+ }
8383
+ channel(id) {
8384
+ return new ChannelHandle(this, id);
8385
+ }
8386
+ person(id) {
8387
+ return new UserHandle(this, id);
8388
+ }
8389
+ member(guildId, userId) {
8390
+ return new MemberHandle(this, guildId, userId);
8391
+ }
8392
+ role(guildId, roleId) {
8393
+ return new RoleHandle(this, guildId, roleId);
8394
+ }
8395
+ message(channelId, messageId) {
8396
+ return new MessageHandle(this, channelId, messageId);
8397
+ }
6265
8398
  get resourceContext() {
6266
8399
  return this.#resourceContext;
6267
8400
  }
6268
- #memberPermissions(guildId, memberId, roleIds, channelId) {
8401
+ #memberPermissions(guildId, memberId, roleIds, channelId, timedOutUntil) {
6269
8402
  const guild = this.guilds.get(guildId);
6270
8403
  if (!guild)
6271
8404
  return null;
@@ -6282,7 +8415,8 @@ class Client extends EventEmitter2 {
6282
8415
  memberId,
6283
8416
  memberRoleIds: [...roleIds],
6284
8417
  roles: this.guilds.roles(guildId).values(),
6285
- overwrites: channel?.permissionOverwrites
8418
+ overwrites: channel?.permissionOverwrites,
8419
+ timedOutUntil
6286
8420
  });
6287
8421
  }
6288
8422
  permissionsFor(memberId, guildOrChannelId) {
@@ -6303,7 +8437,8 @@ class Client extends EventEmitter2 {
6303
8437
  memberId,
6304
8438
  memberRoleIds: member.roleIds,
6305
8439
  roles: this.guilds.roles(guildId).values(),
6306
- overwrites: channel?.permissionOverwrites
8440
+ overwrites: channel?.permissionOverwrites,
8441
+ timedOutUntil: member.timedOutUntil
6307
8442
  });
6308
8443
  }
6309
8444
  fetchDefaultSoundboardSounds() {
@@ -6351,10 +8486,10 @@ class Client extends EventEmitter2 {
6351
8486
  return Promise.reject(new Error("Client is unauthenticated."));
6352
8487
  return this.rest.delete(Routes.interactionOriginalResponse(this.user.id, token));
6353
8488
  }
6354
- followUpInteraction(token, data) {
8489
+ async followUpInteraction(token, data) {
6355
8490
  if (!this.user)
6356
8491
  return Promise.reject(new Error("Client is unauthenticated."));
6357
- return this.rest.post(`/webhooks/${this.user.id}/${token}`, toRequest(data, this.options.allowedMentions));
8492
+ return this.rest.post(Routes.webhook(this.user.id, token), toRequest(data, this.options.allowedMentions));
6358
8493
  }
6359
8494
  interactionWebhookMessage(method, applicationId, token, messageId, data) {
6360
8495
  const path = messageId === "@original" ? Routes.interactionOriginalResponse(applicationId, token) : Routes.webhookMessage(applicationId, token, messageId);
@@ -6364,11 +8499,11 @@ class Client extends EventEmitter2 {
6364
8499
  return this.rest.patch(path, data ? toRequest(data, this.options.allowedMentions) : data);
6365
8500
  return this.rest.delete(path);
6366
8501
  }
6367
- fetchWebhook(id, token) {
6368
- return this.rest.get(token ? `/webhooks/${id}/${token}` : `/webhooks/${id}`);
8502
+ async fetchWebhook(id, token) {
8503
+ return this.rest.get(Routes.webhook(id, token));
6369
8504
  }
6370
- fetchGuildPreview(guildId) {
6371
- return this.rest.get(`/guilds/${guildId}/preview`);
8505
+ async fetchGuildPreview(guildId) {
8506
+ return this.rest.get(Routes.guildPreview(guildId));
6372
8507
  }
6373
8508
  fetchVoiceRegions() {
6374
8509
  return this.rest.get("/voice/regions");
@@ -6382,11 +8517,11 @@ class Client extends EventEmitter2 {
6382
8517
  const qs = query.toString();
6383
8518
  return this.rest.get(`/invites/${encodeURIComponent(code)}${qs ? `?${qs}` : ""}`);
6384
8519
  }
6385
- fetchSticker(id) {
6386
- return this.rest.get(`/stickers/${id}`);
8520
+ async fetchSticker(id) {
8521
+ return this.rest.get(Routes.sticker(id));
6387
8522
  }
6388
8523
  fetchPremiumStickerPacks() {
6389
- return this.rest.get(`/sticker-packs`);
8524
+ return this.rest.get(Routes.stickerPacks());
6390
8525
  }
6391
8526
  fetchGuildTemplate(code) {
6392
8527
  return this.rest.get(`/guilds/templates/${encodeURIComponent(code)}`);
@@ -6405,23 +8540,34 @@ class Client extends EventEmitter2 {
6405
8540
  }
6406
8541
  }
6407
8542
  export {
8543
+ ChannelHandle,
6408
8544
  Client,
6409
8545
  ClientEvent,
6410
8546
  Collector,
8547
+ CommandRegistry,
6411
8548
  ClientEvent as Events,
6412
8549
  GatewayIntentBits,
8550
+ GuildHandle,
6413
8551
  IntentBits,
6414
8552
  Intents,
6415
8553
  IntentsBitField,
8554
+ MemberHandle,
8555
+ MessageHandle,
6416
8556
  Permission,
6417
8557
  PermissionFlagsBits,
6418
8558
  PermissionOverwriteEnum,
6419
- PermissionOverwriteType,
6420
8559
  PermissionSet,
6421
8560
  Permissions,
6422
8561
  PermissionsBitField,
8562
+ RoleHandle,
8563
+ UserHandle,
8564
+ channelCreatePayload,
8565
+ command,
6423
8566
  computePermissions,
8567
+ createApi,
8568
+ option,
8569
+ parseDuration,
6424
8570
  resolveGatewayIntents
6425
8571
  };
6426
8572
 
6427
- //# debugId=A8C0111D9C7EBB3664756E2164756E21
8573
+ //# debugId=EEC9EA7D2E19B5EE64756E2164756E21