lunibee 0.3.0 → 0.3.1

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 (38) hide show
  1. package/dist/builders/embed.d.ts +1 -0
  2. package/dist/builders/index.js +6 -2
  3. package/dist/builders/index.js.map +4 -4
  4. package/dist/core/index.js +66 -15
  5. package/dist/core/index.js.map +8 -8
  6. package/dist/index.js +2345 -199
  7. package/dist/index.js.map +22 -14
  8. package/dist/managers/index.js +2 -2
  9. package/dist/managers/index.js.map +3 -3
  10. package/dist/rest/errors.d.ts +8 -0
  11. package/dist/rest/index.d.ts +2 -0
  12. package/dist/rest/index.js +65 -14
  13. package/dist/rest/index.js.map +5 -5
  14. package/dist/rest/transport.d.ts +9 -0
  15. package/dist/sharding/bus.d.ts +8 -0
  16. package/dist/sharding/cluster.d.ts +224 -14
  17. package/dist/sharding/context.d.ts +41 -0
  18. package/dist/sharding/gateway-bot.d.ts +20 -0
  19. package/dist/sharding/health.d.ts +94 -0
  20. package/dist/sharding/host.d.ts +70 -0
  21. package/dist/sharding/identify.d.ts +62 -0
  22. package/dist/sharding/index.d.ts +56 -12
  23. package/dist/sharding/index.js +2255 -164
  24. package/dist/sharding/index.js.map +18 -10
  25. package/dist/sharding/protocol.d.ts +47 -0
  26. package/dist/sharding/remote-protocol.d.ts +100 -0
  27. package/dist/sharding/remote.d.ts +118 -0
  28. package/dist/sharding/requests.d.ts +54 -0
  29. package/dist/sharding/worker.d.ts +45 -0
  30. package/dist/structures/index.js +2 -2
  31. package/dist/structures/index.js.map +3 -3
  32. package/dist/types/gateway.d.ts +3 -0
  33. package/dist/types/index.js.map +1 -1
  34. package/dist/ws/index.d.ts +1 -1
  35. package/dist/ws/index.js +2 -2
  36. package/dist/ws/index.js.map +5 -5
  37. package/dist/ws/send-budget.d.ts +1 -1
  38. package/package.json +18 -16
@@ -3,7 +3,7 @@
3
3
  "sources": ["../../packages/types/src/channel-kind.ts", "../../packages/types/src/gateway.ts", "../../packages/types/src/index.ts", "../../packages/structures/src/base.ts", "../../packages/core/src/permissions.ts", "../../packages/builders/src/components.ts", "../../packages/builders/src/commands.ts", "../../packages/utils/src/args.ts", "../../packages/collection/src/index.ts", "../../packages/rest/src/index.ts", "../../packages/managers/src/index.ts", "../../packages/ws/src/close-codes.ts", "../../packages/ws/src/reconnect.ts", "../../packages/structures/src/resources.ts", "../../packages/structures/src/channels.ts", "../../packages/structures/src/options.ts", "../../packages/structures/src/interactions.ts", "../../packages/structures/src/embed.ts", "../../packages/structures/src/audit-log.ts", "../../packages/structures/src/sticker.ts", "../../packages/structures/src/index.ts"],
4
4
  "sourcesContent": [
5
5
  "/**\n * Channel kinds: a short name for what a channel is, in place of the numeric\n * `type` Discord sends. Threads share one kind; `type` still tells the three\n * apart.\n */\nexport const ChannelKinds = {\n text: [0],\n dm: [1],\n voice: [2],\n \"group-dm\": [3],\n category: [4],\n announcement: [5],\n thread: [10, 11, 12],\n stage: [13],\n directory: [14],\n forum: [15],\n media: [16],\n} as const;\n\n/** The name of a kind of channel, such as `\"text\"` or `\"voice\"`. */\nexport type ChannelKind = keyof typeof ChannelKinds;\n\n/** A channel type Discord has no kind for yet. */\nexport type UnknownChannelKind = \"unknown\";\n\nconst KIND_OF_TYPE = new Map<number, ChannelKind>(\n (Object.keys(ChannelKinds) as ChannelKind[]).flatMap((kind) =>\n ChannelKinds[kind].map((type): [number, ChannelKind] => [type, kind]),\n ),\n);\n\n/** The kind of a Discord channel `type`, or `\"unknown\"` for a type this version does not know. */\nexport function channelKindOf(type: number): ChannelKind | UnknownChannelKind {\n return KIND_OF_TYPE.get(type) ?? \"unknown\";\n}\n\n/** The Discord channel `type` numbers a kind covers (three for `\"thread\"`, one for the rest). */\nexport function channelTypesOf(kind: ChannelKind): readonly number[] {\n return ChannelKinds[kind];\n}\n",
6
- "// ─── Gateway Intents ─────────────────────────────────────────────────────────\n\n/** Gateway intent bit flags supported by Discord (PascalCase). */\nexport const GatewayIntentBits = {\n Guilds: 1 << 0,\n GuildMembers: 1 << 1,\n GuildModeration: 1 << 2,\n GuildExpressions: 1 << 3,\n GuildIntegrations: 1 << 4,\n GuildWebhooks: 1 << 5,\n GuildInvites: 1 << 6,\n GuildVoiceStates: 1 << 7,\n GuildPresences: 1 << 8,\n GuildMessages: 1 << 9,\n GuildMessageReactions: 1 << 10,\n GuildMessageTyping: 1 << 11,\n DirectMessages: 1 << 12,\n DirectMessageReactions: 1 << 13,\n DirectMessageTyping: 1 << 14,\n MessageContent: 1 << 15,\n GuildScheduledEvents: 1 << 16,\n AutoModerationConfiguration: 1 << 20,\n AutoModerationExecution: 1 << 21,\n GuildMessagePolls: 1 << 24,\n DirectMessagePolls: 1 << 25,\n} as const;\n\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\nexport const IntentBits = {\n guild: 1 << 0,\n guilds: 1 << 0,\n guildMembers: 1 << 1,\n guildModeration: 1 << 2,\n guildBans: 1 << 2,\n guildExpressions: 1 << 3,\n guildEmojis: 1 << 3,\n guildEmojisAndStickers: 1 << 3,\n guildIntegrations: 1 << 4,\n guildWebhooks: 1 << 5,\n guildInvites: 1 << 6,\n guildVoiceStates: 1 << 7,\n guildPresences: 1 << 8,\n guildMessages: 1 << 9,\n guildMessage: 1 << 9,\n guildMessageReactions: 1 << 10,\n guildMessageTyping: 1 << 11,\n directMessages: 1 << 12,\n directMessageReactions: 1 << 13,\n directMessageTyping: 1 << 14,\n messageContent: 1 << 15,\n guildScheduledEvents: 1 << 16,\n autoModerationConfiguration: 1 << 20,\n autoModerationExecution: 1 << 21,\n guildMessagePolls: 1 << 24,\n directMessagePolls: 1 << 25,\n} as const;\n\n/** Alias for IntentBits. */\nexport const Intents = IntentBits;\n\n/** Gateway intents: an array of intent bits or names, e.g. `[GatewayIntentBits.Guilds, \"GuildMessages\"]`. A single number, name or OR-ed bitfield is not accepted. */\nexport type GatewayIntentResolvable = readonly (\n number | keyof typeof GatewayIntentBits | keyof typeof IntentBits | string\n)[];\n\n/**\n * Resolves an array of intents into a raw bitfield integer.\n * @throws {TypeError} If `intents` is not an array (an OR-ed number, a single name or a bitfield object).\n */\nexport function resolveGatewayIntents(\n intents: GatewayIntentResolvable,\n): number {\n if (!Array.isArray(intents))\n throw new TypeError(\n \"Gateway intents must be an array, e.g. [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages].\",\n );\n return intents.reduce<number>(\n (acc, intent) => acc | resolveIntent(intent),\n 0,\n );\n}\n\n/** Resolves one intent: a bitfield, an intent name in either casing, or a numeric string. */\nfunction resolveIntent(intent: number | string): number {\n if (typeof intent === \"number\") return intent;\n const bits: Record<string, number> = {\n ...IntentBits,\n ...GatewayIntentBits,\n };\n const lower = intent.charAt(0).toLowerCase() + intent.slice(1);\n if (Object.hasOwn(bits, intent)) return bits[intent]!;\n if (Object.hasOwn(bits, lower)) return bits[lower]!;\n const num = Number(intent);\n return Number.isNaN(num) ? 0 : num;\n}\n\n/** What `IntentsBitField` methods take: one intent or an array of them. Clients and gateways take only arrays. */\ntype IntentsInput = GatewayIntentResolvable[number] | GatewayIntentResolvable;\n\nfunction resolveInput(bits: IntentsInput): number {\n return resolveGatewayIntents(Array.isArray(bits) ? bits : [bits as number]);\n}\n\n/**\n * Discord.js-familiar intent bitfield.\n * @example new IntentsBitField([IntentsBitField.Flags.Guilds, \"GuildMessages\"])\n */\nexport class IntentsBitField {\n /** Intent flag values by name. */\n public static readonly Flags = GatewayIntentBits;\n /** Raw intent bitfield. */\n public bitfield: number;\n\n public constructor(bits: IntentsInput = []) {\n this.bitfield = resolveInput(bits);\n }\n\n /** Whether every given intent is set. */\n public has(bits: IntentsInput): boolean {\n const resolved = resolveInput(bits);\n return (this.bitfield & resolved) === resolved;\n }\n\n /** Whether any given intent is set. */\n public any(bits: IntentsInput): boolean {\n return (this.bitfield & resolveInput(bits)) !== 0;\n }\n\n /** Adds intents in place. @returns This bitfield. */\n public add(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield |= resolveInput(bit);\n return this;\n }\n\n /** Removes intents in place. @returns This bitfield. */\n public remove(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield &= ~resolveInput(bit);\n return this;\n }\n\n /** Names of the set intents. */\n public toArray(): (keyof typeof GatewayIntentBits)[] {\n return (\n Object.keys(GatewayIntentBits) as (keyof typeof GatewayIntentBits)[]\n ).filter((name) => this.has(GatewayIntentBits[name]));\n }\n\n /** Whether this equals another bitfield. */\n public equals(bits: IntentsInput): boolean {\n return this.bitfield === resolveInput(bits);\n }\n\n public valueOf(): number {\n return this.bitfield;\n }\n\n public toJSON(): number {\n return this.bitfield;\n }\n}\n\n// ─── Gateway Config ───────────────────────────────────────────────────────────\n\n/** Gateway connection properties (OS, browser, device). */\nexport interface GatewayProperties {\n os?: string;\n browser?: string;\n device?: string;\n [key: string]: string | undefined;\n}\n/** Gateway presence activity. */\nexport interface GatewayPresenceActivity {\n name: string;\n type?: number;\n url?: string;\n state?: string;\n}\n/** Gateway presence data. */\nexport interface GatewayPresence {\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\n activities?: GatewayPresenceActivity[];\n afk?: boolean;\n since?: number | null;\n}\n/** Client configuration. */\nexport interface ClientOptions {\n token: string;\n intents: GatewayIntentResolvable;\n gateway?: GatewayOptions;\n /**\n * zlib-stream transport compression for the Gateway connection, about a\n * tenth of the inbound bandwidth. Shorthand for `gateway: { compress }`,\n * which wins when both are set. Off by default.\n */\n compress?: boolean;\n rest?: RESTOptions;\n /** Opt-in bounded per-channel message cache. Messages are not cached by default. */\n messageCache?: { maxSize?: number; ttl?: number };\n /**\n * Which resources the client keeps from Gateway events. Everything is\n * cached by default; large bots can turn off what they do not read.\n * Events are still emitted either way.\n */\n cache?: {\n users?: boolean;\n members?: boolean;\n roles?: boolean;\n emojis?: boolean;\n /** Keep each member's latest presence (needs the `GuildPresences` intent). Off by default: it is a lot of data. */\n presences?: boolean;\n };\n /**\n * Default `allowed_mentions` for every message the client sends (channel\n * send/edit, replies, interaction replies, edits and follow-ups), e.g.\n * `{ parse: [] }` so nothing pings unless a message says so. A message's\n * own `allowed_mentions` always wins.\n */\n allowedMentions?: AllowedMentions;\n}\n/** Discord's `allowed_mentions` object. */\nexport interface AllowedMentions {\n parse?: (\"users\" | \"roles\" | \"everyone\")[];\n users?: string[];\n roles?: string[];\n replied_user?: boolean;\n}\n/** Gateway connection configuration. */\nexport interface GatewayOptions {\n intents?: GatewayIntentResolvable;\n reconnect?: boolean;\n maxReconnectAttempts?: number;\n reconnectBaseDelay?: number;\n reconnectMaxDelay?: number;\n shardId?: number;\n shardCount?: number;\n properties?: GatewayProperties;\n presence?: GatewayPresence;\n}\n\n/** REST request configuration. */\nexport interface RESTOptions {\n timeout?: number;\n retries?: number;\n baseURL?: string;\n}\n",
6
+ "// ─── Gateway Intents ─────────────────────────────────────────────────────────\n\n/** Gateway intent bit flags supported by Discord (PascalCase). */\nexport const GatewayIntentBits = {\n Guilds: 1 << 0,\n GuildMembers: 1 << 1,\n GuildModeration: 1 << 2,\n GuildExpressions: 1 << 3,\n GuildIntegrations: 1 << 4,\n GuildWebhooks: 1 << 5,\n GuildInvites: 1 << 6,\n GuildVoiceStates: 1 << 7,\n GuildPresences: 1 << 8,\n GuildMessages: 1 << 9,\n GuildMessageReactions: 1 << 10,\n GuildMessageTyping: 1 << 11,\n DirectMessages: 1 << 12,\n DirectMessageReactions: 1 << 13,\n DirectMessageTyping: 1 << 14,\n MessageContent: 1 << 15,\n GuildScheduledEvents: 1 << 16,\n AutoModerationConfiguration: 1 << 20,\n AutoModerationExecution: 1 << 21,\n GuildMessagePolls: 1 << 24,\n DirectMessagePolls: 1 << 25,\n} as const;\n\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\nexport const IntentBits = {\n guild: 1 << 0,\n guilds: 1 << 0,\n guildMembers: 1 << 1,\n guildModeration: 1 << 2,\n guildBans: 1 << 2,\n guildExpressions: 1 << 3,\n guildEmojis: 1 << 3,\n guildEmojisAndStickers: 1 << 3,\n guildIntegrations: 1 << 4,\n guildWebhooks: 1 << 5,\n guildInvites: 1 << 6,\n guildVoiceStates: 1 << 7,\n guildPresences: 1 << 8,\n guildMessages: 1 << 9,\n guildMessage: 1 << 9,\n guildMessageReactions: 1 << 10,\n guildMessageTyping: 1 << 11,\n directMessages: 1 << 12,\n directMessageReactions: 1 << 13,\n directMessageTyping: 1 << 14,\n messageContent: 1 << 15,\n guildScheduledEvents: 1 << 16,\n autoModerationConfiguration: 1 << 20,\n autoModerationExecution: 1 << 21,\n guildMessagePolls: 1 << 24,\n directMessagePolls: 1 << 25,\n} as const;\n\n/** Alias for IntentBits. */\nexport const Intents = IntentBits;\n\n/** Gateway intents: an array of intent bits or names, e.g. `[GatewayIntentBits.Guilds, \"GuildMessages\"]`. A single number, name or OR-ed bitfield is not accepted. */\nexport type GatewayIntentResolvable = readonly (\n number | keyof typeof GatewayIntentBits | keyof typeof IntentBits | string\n)[];\n\n/**\n * Resolves an array of intents into a raw bitfield integer.\n * @throws {TypeError} If `intents` is not an array (an OR-ed number, a single name or a bitfield object).\n */\nexport function resolveGatewayIntents(\n intents: GatewayIntentResolvable,\n): number {\n if (!Array.isArray(intents))\n throw new TypeError(\n \"Gateway intents must be an array, e.g. [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages].\",\n );\n return intents.reduce<number>(\n (acc, intent) => acc | resolveIntent(intent),\n 0,\n );\n}\n\n/** Resolves one intent: a bitfield, an intent name in either casing, or a numeric string. */\nfunction resolveIntent(intent: number | string): number {\n if (typeof intent === \"number\") return intent;\n const bits: Record<string, number> = {\n ...IntentBits,\n ...GatewayIntentBits,\n };\n const lower = intent.charAt(0).toLowerCase() + intent.slice(1);\n if (Object.hasOwn(bits, intent)) return bits[intent]!;\n if (Object.hasOwn(bits, lower)) return bits[lower]!;\n const num = Number(intent);\n return Number.isNaN(num) ? 0 : num;\n}\n\n/** What `IntentsBitField` methods take: one intent or an array of them. Clients and gateways take only arrays. */\ntype IntentsInput = GatewayIntentResolvable[number] | GatewayIntentResolvable;\n\nfunction resolveInput(bits: IntentsInput): number {\n return resolveGatewayIntents(Array.isArray(bits) ? bits : [bits as number]);\n}\n\n/**\n * Discord.js-familiar intent bitfield.\n * @example new IntentsBitField([IntentsBitField.Flags.Guilds, \"GuildMessages\"])\n */\nexport class IntentsBitField {\n /** Intent flag values by name. */\n public static readonly Flags = GatewayIntentBits;\n /** Raw intent bitfield. */\n public bitfield: number;\n\n public constructor(bits: IntentsInput = []) {\n this.bitfield = resolveInput(bits);\n }\n\n /** Whether every given intent is set. */\n public has(bits: IntentsInput): boolean {\n const resolved = resolveInput(bits);\n return (this.bitfield & resolved) === resolved;\n }\n\n /** Whether any given intent is set. */\n public any(bits: IntentsInput): boolean {\n return (this.bitfield & resolveInput(bits)) !== 0;\n }\n\n /** Adds intents in place. @returns This bitfield. */\n public add(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield |= resolveInput(bit);\n return this;\n }\n\n /** Removes intents in place. @returns This bitfield. */\n public remove(...bits: IntentsInput[]): this {\n for (const bit of bits) this.bitfield &= ~resolveInput(bit);\n return this;\n }\n\n /** Names of the set intents. */\n public toArray(): (keyof typeof GatewayIntentBits)[] {\n return (\n Object.keys(GatewayIntentBits) as (keyof typeof GatewayIntentBits)[]\n ).filter((name) => this.has(GatewayIntentBits[name]));\n }\n\n /** Whether this equals another bitfield. */\n public equals(bits: IntentsInput): boolean {\n return this.bitfield === resolveInput(bits);\n }\n\n public valueOf(): number {\n return this.bitfield;\n }\n\n public toJSON(): number {\n return this.bitfield;\n }\n}\n\n// ─── Gateway Config ───────────────────────────────────────────────────────────\n\n/** Gateway connection properties (OS, browser, device). */\nexport interface GatewayProperties {\n os?: string;\n browser?: string;\n device?: string;\n [key: string]: string | undefined;\n}\n/** Gateway presence activity. */\nexport interface GatewayPresenceActivity {\n name: string;\n type?: number;\n url?: string;\n state?: string;\n}\n/** Gateway presence data. */\nexport interface GatewayPresence {\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\n activities?: GatewayPresenceActivity[];\n afk?: boolean;\n since?: number | null;\n}\n/** Client configuration. */\nexport interface ClientOptions {\n token: string;\n intents: GatewayIntentResolvable;\n gateway?: GatewayOptions;\n /**\n * zlib-stream transport compression for the Gateway connection, about a\n * tenth of the inbound bandwidth. Shorthand for `gateway: { compress }`,\n * which wins when both are set. Off by default.\n */\n compress?: boolean;\n rest?: RESTOptions;\n /** Opt-in bounded per-channel message cache. Messages are not cached by default. */\n messageCache?: { maxSize?: number; ttl?: number };\n /**\n * Which resources the client keeps from Gateway events. Everything is\n * cached by default; large bots can turn off what they do not read.\n * Events are still emitted either way.\n */\n cache?: {\n users?: boolean;\n members?: boolean;\n roles?: boolean;\n emojis?: boolean;\n /** Keep each member's latest presence (needs the `GuildPresences` intent). Off by default: it is a lot of data. */\n presences?: boolean;\n };\n /**\n * Default `allowed_mentions` for every message the client sends (channel\n * send/edit, replies, interaction replies, edits and follow-ups), e.g.\n * `{ parse: [] }` so nothing pings unless a message says so. A message's\n * own `allowed_mentions` always wins.\n */\n allowedMentions?: AllowedMentions;\n}\n/** Discord's `allowed_mentions` object. */\nexport interface AllowedMentions {\n parse?: (\"users\" | \"roles\" | \"everyone\")[];\n users?: string[];\n roles?: string[];\n replied_user?: boolean;\n}\n/** Gateway connection configuration. */\nexport interface GatewayOptions {\n intents?: GatewayIntentResolvable;\n reconnect?: boolean;\n maxReconnectAttempts?: number;\n reconnectBaseDelay?: number;\n reconnectMaxDelay?: number;\n shardId?: number;\n shardCount?: number;\n properties?: GatewayProperties;\n presence?: GatewayPresence;\n}\n\n/** REST request configuration. */\nexport interface RESTOptions {\n timeout?: number;\n retries?: number;\n /** Where requests go. Must be `https:` (or `http:` on this machine) with no credentials, query or fragment. */\n baseURL?: string;\n /** Allow a cleartext `http://` `baseURL` on a network you trust, such as a REST proxy. Default false. */\n allowInsecureBaseURL?: boolean;\n}\n",
7
7
  "/** Discord API snowflake identifier. */\nexport type Snowflake = string;\n\nexport * from \"./channel-kind.js\";\nexport * from \"./gateway.js\";\nexport * from \"./gateway-events.js\";\n\n// ─── Core Discord Objects ─────────────────────────────────────────────────────\n\n/** Discord user object. */\nexport interface UserData {\n id: Snowflake;\n username: string;\n discriminator?: string;\n global_name?: string | null;\n avatar?: string | null;\n bot?: boolean;\n system?: boolean;\n public_flags?: number;\n}\n/** Current authenticated bot user. */\nexport type ClientUser = UserData;\n/** Discord Gateway payload envelope. */\nexport interface GatewayPayload<T = unknown> {\n op: number;\n d: T | null;\n s: number | null;\n t: string | null;\n}\n/** Generic Discord API error payload. */\nexport interface APIError {\n code?: number;\n message?: string;\n errors?: Record<string, unknown>;\n}\n\n// ─── Discord Flags & Enums ────────────────────────────────────────────────────\n\n/** Discord message flags. */\nexport const MessageFlags = {\n Crossposted: 1,\n IsCrosspost: 2,\n SuppressEmbeds: 4,\n SourceMessageDeleted: 8,\n Urgent: 16,\n HasThread: 32,\n Ephemeral: 64,\n Loading: 128,\n IsComponentsV2: 32768,\n} as const;\n/** One {@link MessageFlags} value, usable as a type. */\nexport type MessageFlags = (typeof MessageFlags)[keyof typeof MessageFlags];\n\n/** Discord channel types. */\nexport const ChannelEnum = {\n GuildText: 0,\n DM: 1,\n GuildVoice: 2,\n GroupDM: 3,\n GuildCategory: 4,\n GuildAnnouncement: 5,\n AnnouncementThread: 10,\n PublicThread: 11,\n PrivateThread: 12,\n GuildStageVoice: 13,\n GuildDirectory: 14,\n GuildForum: 15,\n GuildMedia: 16,\n} as const;\n/** One {@link ChannelEnum} value, usable as a type. */\nexport type ChannelEnum = (typeof ChannelEnum)[keyof typeof ChannelEnum];\n\n/** Discord component types. */\nexport const ComponentEnum = {\n ActionRow: 1,\n Button: 2,\n StringSelect: 3,\n TextInput: 4,\n UserSelect: 5,\n RoleSelect: 6,\n MentionableSelect: 7,\n ChannelSelect: 8,\n Section: 9,\n TextDisplay: 10,\n Thumbnail: 11,\n MediaGallery: 12,\n File: 13,\n Separator: 14,\n ContentInventoryEntry: 16,\n Container: 17,\n} as const;\n/** One {@link ComponentEnum} value, usable as a type. */\nexport type ComponentEnum = (typeof ComponentEnum)[keyof typeof ComponentEnum];\n\n/** Discord button style types. */\nexport const ButtonType = {\n Primary: 1,\n Secondary: 2,\n Success: 3,\n Danger: 4,\n Link: 5,\n Premium: 6,\n} as const;\n/** One {@link ButtonType} value, usable as a type: `style: ButtonType`. */\nexport type ButtonType = (typeof ButtonType)[keyof typeof ButtonType];\n\n/** Discord text input style types. */\nexport const TextInputType = {\n Short: 1,\n Paragraph: 2,\n} as const;\n/** One {@link TextInputType} value, usable as a type. */\nexport type TextInputType = (typeof TextInputType)[keyof typeof TextInputType];\n\n/** Discord interaction response types. */\nexport const InteractionResponseEnum = {\n Pong: 1,\n ChannelMessage: 4,\n DeferredChannelMessage: 5,\n DeferredMessageUpdate: 6,\n MessageUpdate: 7,\n Autocomplete: 8,\n Modal: 9,\n} as const;\n/** One {@link InteractionResponseEnum} value, usable as a type. */\nexport type InteractionResponseEnum =\n (typeof InteractionResponseEnum)[keyof typeof InteractionResponseEnum];\n\n/** Discord sticker format types. */\nexport const StickerFormatEnum = {\n PNG: 1,\n APNG: 2,\n Lottie: 3,\n GIF: 4,\n} as const;\n/** One {@link StickerFormatEnum} value, usable as a type. */\nexport type StickerFormatEnum =\n (typeof StickerFormatEnum)[keyof typeof StickerFormatEnum];\n\n/** Discord sticker types. */\nexport const StickerEnum = {\n Standard: 1,\n Guild: 2,\n} as const;\n/** One {@link StickerEnum} value, usable as a type. */\nexport type StickerEnum = (typeof StickerEnum)[keyof typeof StickerEnum];\n\n/** Discord verification levels. */\nexport const VerificationLevel = {\n None: 0,\n Low: 1,\n Medium: 2,\n High: 3,\n VeryHigh: 4,\n} as const;\n\n/** Discord premium tiers. */\nexport const PremiumTier = {\n None: 0,\n Tier1: 1,\n Tier2: 2,\n Tier3: 3,\n} as const;\n\n// ─── Embeds ───────────────────────────────────────────────────────────────────\n\n/** Raw Discord embed footer object. */\nexport interface APIEmbedFooter {\n text: string;\n icon_url?: string;\n proxy_icon_url?: string;\n}\n/** Raw Discord embed image/thumbnail/video object. */\nexport interface APIEmbedMedia {\n url: string;\n proxy_url?: string;\n height?: number;\n width?: number;\n}\n/** Raw Discord embed provider object. */\nexport interface APIEmbedProvider {\n name?: string;\n url?: string;\n}\n/** Raw Discord embed author object. */\nexport interface APIEmbedAuthor {\n name: string;\n url?: string;\n icon_url?: string;\n proxy_icon_url?: string;\n}\n/** Raw Discord embed field object. */\nexport interface APIEmbedField {\n name: string;\n value: string;\n inline?: boolean;\n}\n/** Raw Discord embed object. */\nexport interface APIEmbed {\n title?: string;\n type?: string;\n description?: string;\n url?: string;\n timestamp?: string;\n color?: number;\n footer?: APIEmbedFooter;\n image?: APIEmbedMedia;\n thumbnail?: APIEmbedMedia;\n video?: APIEmbedMedia;\n provider?: APIEmbedProvider;\n author?: APIEmbedAuthor;\n fields?: APIEmbedField[];\n}\n\n// ─── Message Components ───────────────────────────────────────────────────────\n\n/** Raw Discord select menu option. */\nexport interface APISelectMenuOption {\n label: string;\n value: string;\n description?: string;\n emoji?: APIPartialEmoji;\n default?: boolean;\n}\n/** Raw Discord emoji (partial, as used in components). */\nexport interface APIPartialEmoji {\n id?: Snowflake | null;\n name?: string | null;\n animated?: boolean;\n}\n/** Raw Discord button component. */\nexport interface APIButtonComponent {\n type: 2;\n style: number;\n label?: string;\n emoji?: APIPartialEmoji;\n custom_id?: string;\n url?: string;\n sku_id?: string;\n disabled?: boolean;\n}\n/** Raw Discord select menu component. */\nexport interface APISelectMenuComponent {\n type: 3 | 5 | 6 | 7 | 8;\n custom_id: string;\n options?: APISelectMenuOption[];\n channel_types?: number[];\n placeholder?: string;\n min_values?: number;\n max_values?: number;\n disabled?: boolean;\n}\n/** Raw Discord text input component. */\nexport interface APITextInputComponent {\n type: 4;\n custom_id: string;\n style: number;\n label: string;\n min_length?: number;\n max_length?: number;\n required?: boolean;\n value?: string;\n placeholder?: string;\n}\n/** Union of all raw message component types. */\nexport type APIMessageActionRowComponent =\n APIButtonComponent | APISelectMenuComponent | APITextInputComponent;\n/** Raw Discord action row component. */\nexport interface APIActionRowComponent {\n type: 1;\n id?: number;\n components: APIMessageActionRowComponent[];\n}\n/** Union including action rows and V2 components. */\nexport type APIMessageComponent =\n APIActionRowComponent | { type: Exclude<number, 1>; [key: string]: any };\n\n// ─── Message payloads ────────────────────────────────────────────────────────\n\n/** A Discord payload, or a builder that serialises to one (its `toJSON()` runs when the request is sent). */\nexport type Buildable<T> = T | { toJSON(): T };\n\n/**\n * A message Lunibee sends: channel send and edit, message reply and edit,\n * interaction replies, follow-ups and updates. Embeds and components may be\n * builders or raw objects. Other Discord fields pass through unchanged.\n */\nexport interface MessagePayload {\n content?: string;\n embeds?: Buildable<APIEmbed>[];\n components?: Buildable<APIMessageComponent>[];\n allowed_mentions?: import(\"./gateway.js\").AllowedMentions;\n /** Discord message flags, e.g. `MessageFlags.SuppressEmbeds`. */\n flags?: number;\n tts?: boolean;\n /** Attachment metadata for files sent with the message. */\n attachments?: Array<{\n id: number | string;\n filename?: string;\n description?: string;\n }>;\n [key: string]: unknown;\n}\n\n/** A file to upload with a message. */\nexport interface MessageFile {\n name: string;\n data: Blob | Uint8Array | ArrayBuffer | string;\n contentType?: string;\n}\n\n/** A poll to send; Discord fills in `results` and `expiry` when it is read back. */\nexport interface MessagePoll {\n question: { text?: string; emoji?: APIPartialEmoji };\n answers: Array<{ poll_media: { text?: string; emoji?: APIPartialEmoji } }>;\n /** Hours the poll stays open (1-768). */\n duration?: number;\n allow_multiselect?: boolean;\n layout_type?: number;\n}\n\n/** What `send()` takes. */\nexport interface MessageCreateOptions extends MessagePayload {\n /** Files to upload; the message is sent as multipart. */\n files?: MessageFile[];\n /** Reply to or forward another message. */\n message_reference?: APIMessageReference;\n poll?: MessagePoll;\n sticker_ids?: Snowflake[];\n /** Returned on the Gateway event so the client can match its own message. */\n nonce?: string | number;\n enforce_nonce?: boolean;\n}\n\n/** What `edit()` takes: the fields Discord lets you change after sending. */\nexport interface MessageEditOptions extends MessagePayload {\n files?: MessageFile[];\n}\n\n/** What a reply, edit-reply, follow-up or update to an interaction takes. */\nexport interface InteractionReplyOptions extends MessagePayload {\n /** Only the person who ran the command sees it. */\n ephemeral?: boolean;\n files?: MessageFile[];\n poll?: MessagePoll;\n /** On `reply()` / `update()`: return the created message (one request, no `fetchReply()`). */\n withResponse?: boolean;\n}\n\n/** What `update()` on a component interaction takes. */\nexport type InteractionUpdateOptions = InteractionReplyOptions;\n\n/** What `followUp()` takes. */\nexport type FollowUpOptions = InteractionReplyOptions;\n\n// ─── Attachments ─────────────────────────────────────────────────────────────\n\n/** Raw Discord attachment object. */\nexport interface APIAttachment {\n id: Snowflake;\n filename: string;\n description?: string | null;\n content_type?: string;\n size: number;\n url: string;\n proxy_url: string;\n height?: number | null;\n width?: number | null;\n ephemeral?: boolean;\n duration_secs?: number;\n waveform?: string;\n flags?: number;\n}\n\n// ─── Stickers ────────────────────────────────────────────────────────────────\n\n/** Compact sticker item as included in messages. */\nexport interface APIStickerItem {\n id: Snowflake;\n name: string;\n format_type: number;\n}\n/** Full Discord sticker object. */\nexport interface APISticker {\n id: Snowflake;\n pack_id?: Snowflake;\n name: string;\n description?: string | null;\n tags?: string;\n type: number;\n format_type: number;\n available?: boolean;\n guild_id?: Snowflake;\n user?: UserData;\n sort_value?: number;\n}\n\n// ─── Messages ────────────────────────────────────────────────────────────────\n\n/** Raw Discord message reference. */\nexport interface APIMessageReference {\n type?: number;\n message_id?: Snowflake;\n channel_id?: Snowflake;\n guild_id?: Snowflake;\n fail_if_not_exists?: boolean;\n}\n/** Raw Discord message reaction. */\nexport interface APIMessageReaction {\n count: number;\n count_details?: Record<string, number>;\n me?: boolean;\n me_burst?: boolean;\n emoji: APIPartialEmoji;\n burst_colors?: string[];\n}\n/** Raw Discord poll object. */\nexport interface APIPoll {\n question: { text?: string; emoji?: APIPartialEmoji };\n answers: Array<{\n answer_id: number;\n poll_media: { text?: string; emoji?: APIPartialEmoji };\n }>;\n expiry: string | null;\n allow_multiselect: boolean;\n layout_type: number;\n results?: {\n is_finalized: boolean;\n answer_counts: Array<{ id: number; count: number; me_voted: boolean }>;\n };\n}\n/** A forwarded message's snapshot of the original. */\nexport interface APIMessageSnapshot {\n message: Partial<\n Pick<\n APIMessage,\n | \"type\"\n | \"content\"\n | \"embeds\"\n | \"attachments\"\n | \"timestamp\"\n | \"edited_timestamp\"\n | \"flags\"\n | \"mentions\"\n | \"mention_roles\"\n | \"sticker_items\"\n | \"components\"\n >\n >;\n}\n/** Raw Discord message object. */\nexport interface APIMessage {\n id: Snowflake;\n channel_id: Snowflake;\n author: UserData;\n content: string;\n timestamp?: string;\n edited_timestamp?: string | null;\n tts?: boolean;\n mention_everyone?: boolean;\n mentions?: UserData[];\n mention_roles?: Snowflake[];\n mention_channels?: Array<Record<string, unknown>>;\n attachments?: APIAttachment[];\n embeds?: APIEmbed[];\n reactions?: APIMessageReaction[];\n nonce?: string | number | null;\n pinned?: boolean;\n webhook_id?: Snowflake;\n type?: number;\n activity?: Record<string, unknown>;\n application?: Record<string, unknown>;\n application_id?: Snowflake;\n message_reference?: APIMessageReference;\n flags?: number;\n referenced_message?: APIMessage | null;\n interaction_metadata?: Record<string, unknown>;\n thread?: Record<string, unknown>;\n components?: APIMessageComponent[];\n sticker_items?: APIStickerItem[];\n stickers?: APISticker[];\n position?: number;\n role_subscription_data?: Record<string, unknown>;\n purchase_notification?: Record<string, unknown>;\n poll?: APIPoll;\n /** Forwarded messages: a partial copy of the original (content, embeds, attachments, …). */\n message_snapshots?: APIMessageSnapshot[];\n guild_id?: Snowflake;\n /** The author's guild member, on guild messages from the Gateway (without `user`; the author is the user). */\n member?: Omit<APIGuildMember, \"user\"> & { user?: UserData };\n}\n\n// ─── Channels ────────────────────────────────────────────────────────────────\n\n/** Raw Discord channel object. */\nexport interface APIChannel {\n id: Snowflake;\n type: number;\n guild_id?: Snowflake;\n position?: number;\n permission_overwrites?: APIOverwrite[];\n name?: string | null;\n topic?: string | null;\n nsfw?: boolean;\n last_message_id?: Snowflake | null;\n bitrate?: number;\n user_limit?: number;\n rate_limit_per_user?: number;\n recipients?: UserData[];\n icon?: string | null;\n owner_id?: Snowflake;\n application_id?: Snowflake;\n parent_id?: Snowflake | null;\n last_pin_timestamp?: string | null;\n rtc_region?: string | null;\n video_quality_mode?: number;\n message_count?: number;\n member_count?: number;\n thread_metadata?: APIThreadMetadata;\n default_auto_archive_duration?: number;\n flags?: number;\n total_message_sent?: number;\n available_tags?: APIForumTag[];\n default_reaction_emoji?: APIPartialEmoji | null;\n default_thread_rate_limit_per_user?: number;\n default_sort_order?: number | null;\n default_forum_layout?: number;\n}\n\n/** Raw Discord thread metadata. */\nexport interface APIThreadMetadata {\n archived: boolean;\n auto_archive_duration: number;\n archive_timestamp: string;\n locked: boolean;\n invitable?: boolean;\n create_timestamp?: string | null;\n}\n\n/** Fields for a forum or media channel tag. */\nexport interface ForumTagInput {\n name: string;\n /** Only moderators (Manage Threads) can apply it. */\n moderated?: boolean;\n emoji_id?: Snowflake | null;\n emoji_name?: string | null;\n}\n\n/** A role's colours: a solid colour, or a gradient or holographic style. */\nexport interface APIRoleColors {\n primary_color: number;\n secondary_color: number | null;\n tertiary_color: number | null;\n}\n\n/** Raw Discord forum tag. */\nexport interface APIForumTag {\n id: Snowflake;\n name: string;\n moderated: boolean;\n emoji_id?: Snowflake | null;\n emoji_name?: string | null;\n}\n\n/** Raw Discord thread member. */\nexport interface APIThreadMember {\n id?: Snowflake;\n user_id?: Snowflake;\n join_timestamp: string;\n flags: number;\n member?: APIGuildMember;\n}\n\n// ─── Guild welcome screen and onboarding ──────────────────────────────────────\n\n/** A channel shown on the welcome screen. */\nexport interface APIWelcomeScreenChannel {\n channel_id: Snowflake;\n description: string;\n emoji_id: Snowflake | null;\n emoji_name: string | null;\n}\n\n/** Raw Discord welcome screen. */\nexport interface APIWelcomeScreen {\n description: string | null;\n welcome_channels: APIWelcomeScreenChannel[];\n}\n\n/** One answer to an onboarding question. */\nexport interface APIOnboardingOption {\n id: Snowflake;\n channel_ids: Snowflake[];\n role_ids: Snowflake[];\n emoji?: APIPartialEmoji;\n title: string;\n description: string | null;\n}\n\n/** A question in a guild's onboarding. */\nexport interface APIOnboardingPrompt {\n id: Snowflake;\n type: number;\n options: APIOnboardingOption[];\n title: string;\n single_select: boolean;\n required: boolean;\n in_onboarding: boolean;\n}\n\n/** Raw Discord guild onboarding. */\nexport interface APIGuildOnboarding {\n guild_id: Snowflake;\n prompts: APIOnboardingPrompt[];\n default_channel_ids: Snowflake[];\n enabled: boolean;\n mode: number;\n}\n\n// ─── Roles ────────────────────────────────────────────────────────────────────\n\n/** Raw Discord role object. */\nexport interface APIRole {\n id: Snowflake;\n name: string;\n color: number;\n hoist: boolean;\n icon?: string | null;\n unicode_emoji?: string | null;\n /** Gradient and holographic colours; `color` stays the primary colour. */\n colors?: APIRoleColors;\n position: number;\n permissions: string;\n managed: boolean;\n mentionable: boolean;\n tags?: Record<string, unknown>;\n flags?: number;\n}\n\n// ─── Emoji ────────────────────────────────────────────────────────────────────\n\n/** Raw Discord emoji object. */\nexport interface APIEmoji {\n id: Snowflake | null;\n name: string | null;\n roles?: Snowflake[];\n user?: UserData;\n require_colons?: boolean;\n managed?: boolean;\n animated?: boolean;\n available?: boolean;\n}\n\n// ─── Guilds ───────────────────────────────────────────────────────────────────\n\n/** Raw Discord guild object. */\nexport interface APIGuild {\n id: Snowflake;\n name: string;\n icon?: string | null;\n icon_hash?: string | null;\n splash?: string | null;\n discovery_splash?: string | null;\n owner?: boolean;\n owner_id?: Snowflake;\n permissions?: string;\n afk_channel_id?: Snowflake | null;\n afk_timeout?: number;\n widget_enabled?: boolean;\n widget_channel_id?: Snowflake | null;\n verification_level?: number;\n default_message_notifications?: number;\n explicit_content_filter?: number;\n roles?: APIRole[];\n emojis?: APIEmoji[];\n stickers?: APISticker[];\n features?: string[];\n mfa_level?: number;\n application_id?: Snowflake | null;\n system_channel_id?: Snowflake | null;\n system_channel_flags?: number;\n rules_channel_id?: Snowflake | null;\n max_presences?: number | null;\n max_members?: number;\n vanity_url_code?: string | null;\n description?: string | null;\n banner?: string | null;\n premium_tier?: number;\n premium_subscription_count?: number;\n preferred_locale?: string;\n public_updates_channel_id?: Snowflake | null;\n max_video_channel_users?: number;\n premium_progress_bar_enabled?: boolean;\n approximate_member_count?: number;\n approximate_presence_count?: number;\n nsfw_level?: number;\n safety_alerts_channel_id?: Snowflake | null;\n member_count?: number;\n}\n\n// ─── Welcome Screen ───────────────────────────────────────────────────────────\n\nexport interface APIWelcomeScreenChannel {\n channel_id: Snowflake;\n description: string;\n emoji_id: Snowflake | null;\n emoji_name: string | null;\n}\n\nexport interface APIGuildWelcomeScreen {\n description: string | null;\n welcome_channels: APIWelcomeScreenChannel[];\n}\n\n// ─── Guild Onboarding ─────────────────────────────────────────────────────────\n\nexport interface APIOnboardingPromptOption {\n id: Snowflake;\n channel_ids: Snowflake[];\n role_ids: Snowflake[];\n emoji?: APIPartialEmoji;\n title: string;\n description: string | null;\n}\n\nexport interface APIOnboardingPrompt {\n id: Snowflake;\n type: number;\n options: APIOnboardingPromptOption[];\n title: string;\n single_select: boolean;\n required: boolean;\n in_onboarding: boolean;\n}\n\nexport interface APIGuildOnboarding {\n guild_id: Snowflake;\n prompts: APIOnboardingPrompt[];\n default_channel_ids: Snowflake[];\n enabled: boolean;\n mode: number;\n}\n\n/** Raw Discord guild member object. */\nexport interface APIGuildMember {\n user: UserData;\n nick?: string | null;\n avatar?: string | null;\n roles: Snowflake[];\n joined_at: string;\n premium_since?: string | null;\n deaf?: boolean;\n mute?: boolean;\n flags?: number;\n pending?: boolean;\n permissions?: string;\n communication_disabled_until?: string | null;\n guild_id?: Snowflake;\n}\n\n// ─── Voice ────────────────────────────────────────────────────────────────────\n\n/** Raw Discord voice state object. */\nexport interface APIVoiceState {\n guild_id?: Snowflake;\n channel_id?: Snowflake | null;\n user_id: Snowflake;\n member?: APIGuildMember;\n session_id: string;\n deaf: boolean;\n mute: boolean;\n self_deaf: boolean;\n self_mute: boolean;\n self_stream?: boolean;\n self_video: boolean;\n suppress: boolean;\n request_to_speak_timestamp?: string | null;\n}\n\n/** Raw Discord voice server update event. */\nexport interface APIVoiceServerUpdate {\n token: string;\n guild_id: Snowflake;\n endpoint?: string | null;\n}\n\n// ─── Presence ────────────────────────────────────────────────────────────────\n\n/** Raw Discord activity object. */\n/** Discord activity types, for presences (`setPresence({ activities: [{ name, type: ActivityEnum.Watching }] })`). */\nexport const ActivityEnum = {\n Playing: 0,\n Streaming: 1,\n Listening: 2,\n Watching: 3,\n Custom: 4,\n Competing: 5,\n} as const;\n/** One {@link ActivityEnum} value, usable as a type. */\nexport type ActivityEnum = (typeof ActivityEnum)[keyof typeof ActivityEnum];\n\nexport interface APIActivity {\n name: string;\n type: number;\n url?: string | null;\n created_at: number;\n state?: string | null;\n details?: string | null;\n emoji?: APIPartialEmoji | null;\n}\n\n/** Raw Discord presence update event. */\nexport interface APIPresenceUpdate {\n user: { id: Snowflake } & Partial<UserData>;\n guild_id: Snowflake;\n status: string;\n activities: APIActivity[];\n client_status: { desktop?: string; mobile?: string; web?: string };\n}\n\n// ─── Typing ───────────────────────────────────────────────────────────────────\n\n/** Raw Discord typing start event. */\nexport interface APITypingStart {\n channel_id: Snowflake;\n guild_id?: Snowflake;\n user_id: Snowflake;\n timestamp: number;\n member?: APIGuildMember;\n}\n\n// ─── Guilds ──────────────────────────────────────────────────────────────────\n\n/** Raw Discord guild preview. */\nexport interface APIGuildPreview {\n id: Snowflake;\n name: string;\n icon: string | null;\n splash: string | null;\n discovery_splash: string | null;\n emojis: unknown[];\n features: string[];\n approximate_member_count: number;\n approximate_presence_count: number;\n description: string | null;\n stickers: unknown[];\n}\n\n// ─── Invites ─────────────────────────────────────────────────────────────────\n\n/** Raw Discord invite. */\nexport interface APIInvite {\n code: string;\n guild?: Partial<APIGuild>;\n channel: Partial<APIChannel>;\n inviter?: UserData;\n target_type?: number;\n target_user?: UserData;\n approximate_presence_count?: number;\n approximate_member_count?: number;\n}\n\n/** Raw Discord invite create event. */\nexport interface APIInviteCreate {\n channel_id: Snowflake;\n code: string;\n created_at: string;\n guild_id?: Snowflake;\n inviter?: UserData;\n max_age: number;\n max_uses: number;\n target_type?: number;\n target_user?: UserData;\n temporary: boolean;\n uses: number;\n}\n\n/** Raw Discord invite delete event. */\nexport interface APIInviteDelete {\n channel_id: Snowflake;\n guild_id?: Snowflake;\n code: string;\n}\n\n// ─── Webhooks ────────────────────────────────────────────────────────────────\n\n/** Raw Discord webhooks update event. */\nexport interface APIWebhooksUpdate {\n guild_id: Snowflake;\n channel_id: Snowflake;\n}\n\n/** Raw Discord webhook object. */\nexport interface APIWebhook {\n id: Snowflake;\n type: number;\n guild_id?: Snowflake | null;\n channel_id?: Snowflake | null;\n user?: UserData;\n name?: string | null;\n avatar?: string | null;\n token?: string;\n application_id?: Snowflake | null;\n url?: string;\n}\n\n// ─── AutoMod ─────────────────────────────────────────────────────────────────\n\n/** Raw Discord AutoMod action. */\nexport interface APIAutoModerationAction {\n type: number;\n metadata?: {\n channel_id?: Snowflake;\n duration_seconds?: number;\n custom_message?: string;\n };\n}\n\n// ─── Audit Logs ──────────────────────────────────────────────────────────────\n\n/** Raw Discord audit log change object. */\nexport interface APIAuditLogChange {\n new_value?: unknown;\n old_value?: unknown;\n key: string;\n}\n\n/** Raw Discord audit log entry options object. */\nexport interface APIAuditLogOptions {\n app_id?: Snowflake;\n auto_moderation_rule_name?: string;\n auto_moderation_rule_trigger_type?: string;\n channel_id?: Snowflake;\n count?: string;\n delete_member_days?: string;\n id?: Snowflake;\n members_removed?: string;\n message_id?: Snowflake;\n role_name?: string;\n type?: string;\n integration_type?: string;\n}\n\n/** Raw Discord audit log entry object. */\nexport interface APIAuditLogEntry {\n target_id: string | null;\n changes?: APIAuditLogChange[];\n user_id: Snowflake | null;\n id: Snowflake;\n action_type: number;\n options?: APIAuditLogOptions;\n reason?: string;\n}\n\n/** Raw Discord audit log object. */\nexport interface APIAuditLog {\n audit_log_entries: APIAuditLogEntry[];\n guild_scheduled_events: APIGuildScheduledEvent[];\n integrations: Array<Record<string, unknown>>;\n threads: APIChannel[];\n users: UserData[];\n webhooks: APIWebhook[];\n application_commands: APIApplicationCommand[];\n auto_moderation_rules: APIAutoModerationRule[];\n}\n\n/** Raw Discord AutoMod rule object. */\nexport interface APIAutoModerationRule {\n id: Snowflake;\n guild_id: Snowflake;\n name: string;\n creator_id: Snowflake;\n event_type: number;\n trigger_type: number;\n trigger_metadata: Record<string, unknown>;\n actions: APIAutoModerationAction[];\n enabled: boolean;\n exempt_roles: Snowflake[];\n exempt_channels: Snowflake[];\n}\n\n/** Raw Discord AutoMod action execution event. */\nexport interface APIAutoModerationActionExecution {\n guild_id: Snowflake;\n action: APIAutoModerationAction;\n rule_id: Snowflake;\n rule_trigger_type: number;\n user_id: Snowflake;\n channel_id?: Snowflake;\n message_id?: Snowflake;\n alert_system_message_id?: Snowflake;\n content: string;\n matched_keyword?: string | null;\n matched_content?: string | null;\n}\n\n// ─── Scheduled Events ─────────────────────────────────────────────────────────\n\n/** Raw Discord guild scheduled event entity metadata. */\nexport interface APIGuildScheduledEventEntityMetadata {\n location?: string;\n}\n\n/** Raw Discord guild scheduled event object. */\nexport interface APIGuildScheduledEvent {\n id: Snowflake;\n guild_id: Snowflake;\n channel_id?: Snowflake | null;\n creator_id?: Snowflake | null;\n name: string;\n description?: string | null;\n scheduled_start_time: string;\n scheduled_end_time?: string | null;\n privacy_level: number;\n status: number;\n entity_type: number;\n entity_id?: Snowflake | null;\n entity_metadata?: APIGuildScheduledEventEntityMetadata | null;\n creator?: UserData;\n user_count?: number;\n image?: string | null;\n}\n\n// ─── Stage Instances ─────────────────────────────────────────────────────────\n\n/** Raw Discord stage instance object. */\nexport interface APIStageInstance {\n id: Snowflake;\n guild_id: Snowflake;\n channel_id: Snowflake;\n topic: string;\n privacy_level: number;\n discoverable_disabled?: boolean;\n guild_scheduled_event_id?: Snowflake | null;\n}\n\n// ─── Application Commands ─────────────────────────────────────────────────────\n\n/** Discord application command option types. */\nexport const ApplicationCommandOptionEnum = {\n SubCommand: 1,\n SubCommandGroup: 2,\n String: 3,\n Integer: 4,\n Boolean: 5,\n User: 6,\n Channel: 7,\n Role: 8,\n Mentionable: 9,\n Number: 10,\n Attachment: 11,\n} as const;\n/** One {@link ApplicationCommandOptionEnum} value, usable as a type. */\nexport type ApplicationCommandOptionEnum =\n (typeof ApplicationCommandOptionEnum)[keyof typeof ApplicationCommandOptionEnum];\n\n/** Discord application command types. */\nexport const ApplicationCommandEnum = {\n ChatInput: 1,\n User: 2,\n Message: 3,\n} as const;\n/** One {@link ApplicationCommandEnum} value, usable as a type. */\nexport type ApplicationCommandEnum =\n (typeof ApplicationCommandEnum)[keyof typeof ApplicationCommandEnum];\n\n/** Raw Discord application command option. */\nexport interface APIApplicationCommandOption {\n type: number;\n name: string;\n description: string;\n required?: boolean;\n choices?: Array<{ name: string; value: string | number }>;\n options?: APIApplicationCommandOption[];\n min_value?: number;\n max_value?: number;\n min_length?: number;\n max_length?: number;\n autocomplete?: boolean;\n channel_types?: number[];\n}\n\n/** Raw Discord application command object (from API). */\nexport interface APIApplicationCommand {\n id: Snowflake;\n type?: number;\n application_id: Snowflake;\n guild_id?: Snowflake;\n name: string;\n description: string;\n options?: APIApplicationCommandOption[];\n default_member_permissions?: string | null;\n /** Where the command can be used: 0 guilds, 1 the bot's DMs, 2 other DMs. */\n contexts?: number[];\n nsfw?: boolean;\n version: Snowflake;\n}\n\n/** Data for creating/overwriting an application command. */\nexport interface ApplicationCommandData {\n name: string;\n description: string;\n type?: number;\n options?: APIApplicationCommandOption[];\n default_member_permissions?: string | null;\n /** Where the command can be used: 0 guilds, 1 the bot's DMs, 2 other DMs. */\n contexts?: number[];\n nsfw?: boolean;\n}\n\n/** A channel permission overwrite for a role (type 0) or member (type 1). */\nexport interface APIOverwrite {\n id: Snowflake;\n type: 0 | 1;\n /** Allowed permission bits as a decimal string. */\n allow: string;\n /** Denied permission bits as a decimal string. */\n deny: string;\n}\n\n// ─── Soundboard & Monetization ───────────────────────────────────────────────\n/** Raw Discord soundboard sound. */\nexport interface APISoundboardSound {\n sound_id: string;\n name: string;\n volume: number;\n emoji_id: string | null;\n emoji_name: string | null;\n guild_id?: string;\n available: boolean;\n user?: UserData;\n}\n\n/** Raw Discord SKU. */\nexport interface APISKU {\n id: string;\n type: number;\n application_id: string;\n name: string;\n slug: string;\n flags: number;\n}\n\n/** Raw Discord entitlement. */\nexport interface APIEntitlement {\n id: string;\n sku_id: string;\n application_id: string;\n user_id?: string;\n guild_id?: string;\n type: number;\n deleted: boolean;\n starts_at?: string | null;\n ends_at?: string | null;\n consumed?: boolean;\n}\n\n/** Raw Discord subscription. */\nexport interface APISubscription {\n id: string;\n user_id: string;\n sku_ids: string[];\n entitlement_ids: string[];\n current_period_start: string;\n current_period_end: string;\n status: number;\n canceled_at: string | null;\n country?: string;\n}\n",
8
8
  "import {\n channelKindOf,\n type ChannelKind,\n type MessageCreateOptions,\n type MessageEditOptions,\n type UnknownChannelKind,\n} from \"@lunibee/types\";\nimport type { ChannelOfKind } from \"./channels.js\";\n\n/** Common Discord snowflake-backed structure. */\nexport class BaseStructure {\n public readonly id: string;\n public constructor(id: string) {\n if (!/^\\d{1,20}$/.test(id))\n throw new TypeError(\n \"A Discord structure requires a valid snowflake ID.\",\n );\n this.id = id;\n }\n /** Returns the snowflake ID as a string. */\n public toString(): string {\n return this.id;\n }\n /** Milliseconds since the Unix epoch at which this snowflake was created. */\n public get createdTimestamp(): number {\n return Number((BigInt(this.id) >> 22n) + 1420070400000n);\n }\n /** Returns the timestamp this snowflake was created at. */\n public get createdAt(): Date {\n return new Date(this.createdTimestamp);\n }\n}\n\nexport interface ResourceContext {\n sendMessage(\n channelId: string,\n options: MessageCreateOptions,\n ): Promise<import(\"./index.js\").Message>;\n editMessage(\n channelId: string,\n messageId: string,\n options: MessageEditOptions,\n ): Promise<import(\"./index.js\").Message>;\n deleteMessage(\n channelId: string,\n messageId: string,\n reason?: string,\n ): Promise<void>;\n crosspostMessage(\n channelId: string,\n messageId: string,\n ): Promise<import(\"./index.js\").Message>;\n editChannel?(\n channelId: string,\n options: Record<string, unknown>,\n ): Promise<import(\"./base.js\").Channel>;\n deleteChannel?(channelId: string, reason?: string): Promise<void>;\n addReaction?(\n channelId: string,\n messageId: string,\n emoji: string,\n ): Promise<void>;\n removeOwnReaction?(\n channelId: string,\n messageId: string,\n emoji: string,\n ): Promise<void>;\n removeReaction?(\n channelId: string,\n messageId: string,\n emoji: string,\n userId: string,\n ): Promise<void>;\n removeAllReactions?(channelId: string, messageId: string): Promise<void>;\n pinMessage?(\n channelId: string,\n messageId: string,\n reason?: string,\n ): Promise<void>;\n unpinMessage?(\n channelId: string,\n messageId: string,\n reason?: string,\n ): Promise<void>;\n /**\n * A member's permissions from cached state: guild-level, or channel-level\n * (with overwrites) when a channel ID is given. Null when the guild (or\n * channel) is not cached.\n */\n memberPermissions?(\n guildId: string,\n memberId: string,\n roleIds: readonly string[],\n channelId?: string,\n timedOutUntil?: Date | null,\n ): import(\"@lunibee/core\").PermissionSet | null;\n kickMember?(\n guildId: string,\n userId: string,\n reason?: string,\n ): Promise<void>;\n banMember?(\n guildId: string,\n userId: string,\n options?: { reason?: string; deleteMessageSeconds?: number },\n ): Promise<void>;\n editMember?(\n guildId: string,\n userId: string,\n options: Record<string, unknown>,\n reason?: string,\n ): Promise<import(\"./resources.js\").GuildMember>;\n addMemberRole?(\n guildId: string,\n userId: string,\n roleId: string,\n reason?: string,\n ): Promise<void>;\n removeMemberRole?(\n guildId: string,\n userId: string,\n roleId: string,\n reason?: string,\n ): Promise<void>;\n /** Collects the client's interactions (a `Collector` over `interactionCreate`). Used by message and interaction collectors. */\n collectInteractions?(\n options: import(\"@lunibee/core\").CollectorOptions<\n import(\"./interactions.js\").Interaction\n >,\n ): import(\"@lunibee/core\").Collector<\n string,\n import(\"./interactions.js\").Interaction\n >;\n editPermissionOverwrite?(\n channelId: string,\n targetId: string,\n changes: PermissionOverwriteChanges,\n options?: { type?: 0 | 1; reason?: string },\n ): Promise<void>;\n}\n\n/**\n * Permissions to change on one overwrite: `true` allows, `false` denies,\n * `null` goes back to inheriting. Permissions not named keep their state.\n */\nexport type PermissionOverwriteChanges = Partial<\n Record<import(\"@lunibee/core\").PermissionName, boolean | null>\n>;\n\n// ─── CDN helpers ──────────────────────────────────────────────────────────────\n\nexport const CDN_BASE = \"https://cdn.discordapp.com\";\n\n/** Options for CDN image URLs. */\nexport interface ImageURLOptions {\n extension?: \"png\" | \"jpg\" | \"webp\" | \"gif\" | \"jpeg\";\n size?: 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048 | 4096;\n forceStatic?: boolean;\n}\n\n/** Builds a Discord CDN asset URL from a directory path and asset hash, applying\n * animated-hash (`a_`) gif detection, extension and size options. Shared by all\n * hash-backed asset accessors (user/member/role/webhook) to avoid duplicating the logic.\n * @param path CDN directory path, e.g. `/role-icons/{id}`. @param hash Asset hash. @param options Image URL options. */\nexport function cdnURL(\n path: string,\n hash: string,\n options: ImageURLOptions = {},\n): string {\n const isAnimated = hash.startsWith(\"a_\") && !options.forceStatic;\n const ext = options.extension ?? (isAnimated ? \"gif\" : \"png\");\n const size = options.size ? `?size=${options.size}` : \"\";\n // Encoded, so a hash holding `/`, `?` or `#` cannot change the CDN path.\n return `${CDN_BASE}${path}/${encodeURIComponent(hash)}.${ext}${size}`;\n}\n\n// ─── User ─────────────────────────────────────────────────────────────────────\n\nexport class User extends BaseStructure {\n public username: string;\n public globalName: string | null;\n public avatar: string | null;\n public bot: boolean;\n public system: boolean;\n public flags: number;\n public discriminator: string | null;\n public constructor(data: import(\"@lunibee/types\").UserData) {\n super(data.id);\n if (!data.username)\n throw new TypeError(\"A Discord user requires a username.\");\n this.username = data.username;\n this.globalName = data.global_name ?? null;\n this.avatar = data.avatar ?? null;\n this.bot = data.bot ?? false;\n this.system = data.system ?? false;\n this.flags = data.public_flags ?? 0;\n this.discriminator =\n data.discriminator && data.discriminator !== \"0\"\n ? data.discriminator\n : null;\n }\n /** Effective display name — global name, falling back to username. */\n public get displayName(): string {\n return this.globalName ?? this.username;\n }\n /** Returns the user's avatar URL, or null if they have no avatar. */\n public avatarURL(options: ImageURLOptions = {}): string | null {\n if (!this.avatar) return null;\n return cdnURL(`/avatars/${this.id}`, this.avatar, options);\n }\n /** Returns the default avatar URL based on the user's discriminator/snowflake. */\n public defaultAvatarURL(): string {\n const index = this.discriminator\n ? Number(this.discriminator) % 5\n : Number((BigInt(this.id) >> 22n) % 6n);\n return `${CDN_BASE}/embed/avatars/${index}.png`;\n }\n /** Returns the avatar URL if available, falling back to the default avatar URL. */\n public displayAvatarURL(options: ImageURLOptions = {}): string {\n return this.avatarURL(options) ?? this.defaultAvatarURL();\n }\n /** Returns the Discord mention string. Used automatically in template literals. */\n public override toString(): string {\n return `<@${this.id}>`;\n }\n}\n\n// ─── Channel ──────────────────────────────────────────────────────────────────\n\nexport class Channel extends BaseStructure {\n public type: number;\n public name?: string | null;\n public guildId?: string;\n public topic?: string | null;\n public parentId?: string | null;\n public nsfw?: boolean;\n public rateLimitPerUser?: number;\n public position?: number;\n /** Role and member permission overwrites (empty for DMs and when not provided). */\n public permissionOverwrites: import(\"@lunibee/types\").APIOverwrite[];\n readonly #context?: ResourceContext;\n\n /** Creates a channel structure from Discord channel data. */\n public constructor(\n data: import(\"@lunibee/types\").APIChannel,\n context?: ResourceContext,\n ) {\n super(data.id);\n if (!Number.isInteger(data.type) || data.type < 0)\n throw new RangeError(\n \"A Discord channel requires a valid channel type.\",\n );\n this.type = data.type;\n this.name = data.name;\n this.guildId = data.guild_id;\n this.topic = data.topic;\n this.parentId = data.parent_id;\n this.nsfw = data.nsfw;\n this.rateLimitPerUser = data.rate_limit_per_user;\n this.position = data.position;\n this.permissionOverwrites = (data.permission_overwrites ?? []).map(\n (overwrite) => ({ ...overwrite }),\n );\n this.#context = context;\n }\n\n /** Sends a message to this channel. @param options Message payload. @returns The created message. @throws {Error} If the channel is not attached to a client. */\n public send(\n options: MessageCreateOptions,\n ): Promise<import(\"./index.js\").Message> {\n if (!this.#context)\n throw new Error(\"This channel is not attached to a client.\");\n return this.#context.sendMessage(this.id, options);\n }\n\n /** Edits this channel. @param options Channel fields to change. @returns The updated channel. @throws {Error} If the channel is not attached to a client. */\n public edit(options: Record<string, unknown>): Promise<Channel> {\n if (!this.#context?.editChannel)\n throw new Error(\"This channel is not attached to a client.\");\n return this.#context.editChannel(this.id, options);\n }\n\n /** Changes only this channel's name. @param name New channel name. @returns The updated channel. @throws {TypeError} If the name is empty. @throws {Error} If the channel is not attached to a client. */\n public editName(name: string): Promise<Channel> {\n if (!name.trim()) throw new TypeError(\"Channel name cannot be empty.\");\n return this.edit({ name });\n }\n\n /** Changes only this channel's topic. @param topic New topic, or null to clear it. @returns The updated channel. @throws {Error} If the channel is not attached to a client. */\n public editTopic(topic: string | null): Promise<Channel> {\n return this.edit({ topic });\n }\n\n /** Moves this channel to another parent category. @param parentId Parent category ID, or null to remove the parent. @returns The updated channel. @throws {Error} If the channel is not attached to a client. */\n /**\n * Changes some permissions of one overwrite and keeps the rest: `true`\n * allows, `false` denies, `null` resets to inherited.\n * @param targetId Role or member ID. @param changes Permissions to change.\n * @param options `type` (0 role, 1 member) when the overwrite does not exist yet; audit-log `reason`.\n * @example channel.editPermissionOverwrite(userId, { ViewChannel: true, SendMessages: false }, { type: 1 })\n */\n public editPermissionOverwrite(\n targetId: string,\n changes: PermissionOverwriteChanges,\n options?: { type?: 0 | 1; reason?: string },\n ): Promise<void> {\n if (!this.#context?.editPermissionOverwrite)\n throw new Error(\"This channel is not attached to a client.\");\n return this.#context.editPermissionOverwrite(\n this.id,\n targetId,\n changes,\n options,\n );\n }\n\n public editParent(parentId: string | null): Promise<Channel> {\n return this.edit({ parent_id: parentId });\n }\n\n /** Deletes this channel. @param reason Audit-log reason. @returns A promise fulfilled when Discord confirms deletion. @throws {Error} If the channel is not attached to a client. */\n public delete(reason?: string): Promise<void> {\n if (!this.#context?.deleteChannel)\n throw new Error(\"This channel is not attached to a client.\");\n return this.#context.deleteChannel(this.id, reason);\n }\n\n /** Updates this channel using the same resource operation as edit. @param options Channel fields to change. @returns The updated channel. @throws {Error} If the channel is not attached to a client. */\n public update(options: Record<string, unknown>): Promise<Channel> {\n return this.edit(options);\n }\n\n /** What kind of channel this is: `\"text\"`, `\"voice\"`, `\"thread\"`, `\"forum\"`… (`\"unknown\"` for a type this version does not know). */\n public get kind(): ChannelKind | UnknownChannelKind {\n return channelKindOf(this.type);\n }\n\n /**\n * Whether this channel is one of `kinds`, narrowing its type to match.\n * @example if (channel.is(\"text\", \"announcement\")) await channel.send(\"hi\");\n */\n public is<K extends ChannelKind | UnknownChannelKind>(\n ...kinds: K[]\n ): this is ChannelOfKind<K> {\n return (kinds as string[]).includes(this.kind);\n }\n\n /**\n * Returns this channel typed as one of `kinds`.\n * @throws {TypeError} If it is a different kind.\n */\n public as<K extends ChannelKind | UnknownChannelKind>(\n ...kinds: K[]\n ): ChannelOfKind<K> {\n if (!this.is(...kinds))\n throw new TypeError(\n `Channel ${this.id} is a ${this.kind} channel, not ${kinds.join(\" or \")}.`,\n );\n return this;\n }\n\n /** Whether this is a thread (announcement, public or private). */\n public isThread(): boolean {\n return this.type === 10 || this.type === 11 || this.type === 12;\n }\n\n /** Whether messages can be sent in this channel (text, DM, announcement, voice, stage, threads). */\n public isTextBased(): boolean {\n return [0, 1, 2, 3, 5, 10, 11, 12, 13].includes(this.type);\n }\n\n /** Whether this is a voice or stage channel. */\n public isVoiceBased(): boolean {\n return this.type === 2 || this.type === 13;\n }\n\n /** Whether this is a direct-message channel (DM or group DM). */\n public isDMBased(): boolean {\n return this.type === 1 || this.type === 3;\n }\n\n /** Returns the Discord channel mention string. Used automatically in template literals. */\n public override toString(): string {\n return `<#${this.id}>`;\n }\n}\n\n// ─── Guild ────────────────────────────────────────────────────────────────────\n\nexport class Guild extends BaseStructure {\n public name: string;\n public icon: string | null;\n public splash: string | null;\n public banner: string | null;\n public description: string | null;\n public preferredLocale: string;\n public ownerId?: string;\n public features: string[];\n public verificationLevel: number;\n public premiumTier: number;\n public premiumSubscriptionCount: number;\n public memberCount?: number;\n public approximateMemberCount?: number;\n public approximatePresenceCount?: number;\n public vanityUrlCode: string | null;\n public nsfwLevel: number;\n public discoverySplash: string | null;\n /** ID of the rules channel for Community guilds. */\n public rulesChannelId: string | null;\n /** ID of the system messages channel. */\n public systemChannelId: string | null;\n /** Bitfield of system channel flags. */\n public systemChannelFlags: number;\n /** Maximum number of members the guild can have. */\n public maxMembers: number;\n /** Required MFA level for administrators. */\n public mfaLevel: number;\n /** Explicit content filter level. */\n public explicitContentFilter: number;\n /** Default notification level for new members. */\n public defaultMessageNotifications: number;\n /** AFK voice channel, or null. */\n public afkChannelId: string | null;\n /** Seconds before a silent member is moved to the AFK channel. */\n public afkTimeout?: number;\n /** Whether the server widget is on. */\n public widgetEnabled: boolean;\n /** Channel the widget invites to, or null. */\n public widgetChannelId: string | null;\n /** Application that created the guild, if a bot did. */\n public applicationId: string | null;\n /** Channel for Discord's community updates, or null. */\n public publicUpdatesChannelId: string | null;\n /** Channel for safety alerts, or null. */\n public safetyAlertsChannelId: string | null;\n /** Maximum presences (null for the default limit). */\n public maxPresences: number | null;\n /** Maximum users in a video channel. */\n public maxVideoChannelUsers?: number;\n /** Whether the boost progress bar shows. */\n public premiumProgressBarEnabled: boolean;\n\n public constructor(data: import(\"@lunibee/types\").APIGuild) {\n super(data.id);\n if (!data.name) throw new TypeError(\"A Discord guild requires a name.\");\n this.name = data.name;\n this.icon = data.icon ?? null;\n this.splash = data.splash ?? null;\n this.banner = data.banner ?? null;\n this.description = data.description ?? null;\n this.preferredLocale = data.preferred_locale ?? \"en-US\";\n this.ownerId = data.owner_id;\n this.features = data.features ?? [];\n this.verificationLevel = data.verification_level ?? 0;\n this.premiumTier = data.premium_tier ?? 0;\n this.premiumSubscriptionCount = data.premium_subscription_count ?? 0;\n this.memberCount = data.member_count ?? data.approximate_member_count;\n this.approximateMemberCount = data.approximate_member_count;\n this.approximatePresenceCount = data.approximate_presence_count;\n this.vanityUrlCode = data.vanity_url_code ?? null;\n this.nsfwLevel = data.nsfw_level ?? 0;\n this.discoverySplash = data.discovery_splash ?? null;\n this.rulesChannelId = data.rules_channel_id ?? null;\n this.systemChannelId = data.system_channel_id ?? null;\n this.systemChannelFlags = data.system_channel_flags ?? 0;\n this.maxMembers = data.max_members ?? 0;\n this.mfaLevel = data.mfa_level ?? 0;\n this.explicitContentFilter = data.explicit_content_filter ?? 0;\n this.defaultMessageNotifications =\n data.default_message_notifications ?? 0;\n this.afkChannelId = data.afk_channel_id ?? null;\n this.afkTimeout = data.afk_timeout;\n this.widgetEnabled = data.widget_enabled ?? false;\n this.widgetChannelId = data.widget_channel_id ?? null;\n this.applicationId = data.application_id ?? null;\n this.publicUpdatesChannelId = data.public_updates_channel_id ?? null;\n this.safetyAlertsChannelId = data.safety_alerts_channel_id ?? null;\n this.maxPresences = data.max_presences ?? null;\n this.maxVideoChannelUsers = data.max_video_channel_users;\n this.premiumProgressBarEnabled =\n data.premium_progress_bar_enabled ?? false;\n }\n\n /** Returns the guild icon URL, or null if no icon is set. */\n public iconURL(options: ImageURLOptions = {}): string | null {\n if (!this.icon) return null;\n return cdnURL(`/icons/${this.id}`, this.icon, options);\n }\n\n /** Returns the guild splash URL, or null if none. */\n public splashURL(options: ImageURLOptions = {}): string | null {\n if (!this.splash) return null;\n return cdnURL(`/splashes/${this.id}`, this.splash, options);\n }\n\n /** Returns the guild banner URL, or null if none. */\n public bannerURL(options: ImageURLOptions = {}): string | null {\n if (!this.banner) return null;\n return cdnURL(`/banners/${this.id}`, this.banner, options);\n }\n\n /** Returns the guild discovery splash URL, or null if none. */\n public discoverySplashURL(options: ImageURLOptions = {}): string | null {\n if (!this.discoverySplash) return null;\n return cdnURL(\n `/discovery-splashes/${this.id}`,\n this.discoverySplash,\n options,\n );\n }\n\n /** Returns a widget image URL for the guild.\n * @param style Widget style: shield (default), banner1, banner2, banner3, banner4.\n */\n public widgetImageURL(\n style:\n \"shield\" | \"banner1\" | \"banner2\" | \"banner3\" | \"banner4\" = \"shield\",\n ): string {\n return `https://discord.com/api/guilds/${this.id}/widget.png?style=${style}`;\n }\n\n /** Whether the guild has a given feature flag. @param feature Feature string, e.g. \"COMMUNITY\". */\n public hasFeature(feature: string): boolean {\n return this.features.includes(feature);\n }\n\n /** Whether the guild is a Community server. */\n public get isCommunity(): boolean {\n return this.hasFeature(\"COMMUNITY\");\n }\n\n /** Whether the guild is an official Discord Partner. */\n public get isPartnered(): boolean {\n return this.hasFeature(\"PARTNERED\");\n }\n\n /** Whether the guild is verified by Discord. */\n public get isVerified(): boolean {\n return this.hasFeature(\"VERIFIED\");\n }\n\n /** Whether the guild has any active boost subscriptions. */\n public get isBoosted(): boolean {\n return this.premiumSubscriptionCount > 0;\n }\n\n /** Alias for premiumTier — the guild's current Nitro boost level (0–3). */\n public get boostLevel(): number {\n return this.premiumTier;\n }\n\n /** Whether the guild has a vanity URL. */\n public get hasVanityUrl(): boolean {\n return this.vanityUrlCode !== null;\n }\n\n /** Returns the guild's vanity invite URL, or null if none. */\n public get vanityURL(): string | null {\n return this.vanityUrlCode\n ? `https://discord.gg/${this.vanityUrlCode}`\n : null;\n }\n\n /** Returns a human-readable string representation. */\n public override toString(): string {\n return this.name;\n }\n}\n",
9
9
  "/** Permission overwrite target types. */\nexport enum PermissionOverwriteEnum {\n Role = 0,\n Member = 1,\n}\n\n/** Lunibee idiomatic camelCase permission bitflags. */\nexport const Permission = {\n createInstantInvite: 1n << 0n,\n kickMembers: 1n << 1n,\n banMembers: 1n << 2n,\n administrator: 1n << 3n,\n manageChannels: 1n << 4n,\n manageGuild: 1n << 5n,\n addReactions: 1n << 6n,\n viewAuditLog: 1n << 7n,\n prioritySpeaker: 1n << 8n,\n stream: 1n << 9n,\n viewChannel: 1n << 10n,\n sendMessages: 1n << 11n,\n sendTTSMessages: 1n << 12n,\n manageMessages: 1n << 13n,\n embedLinks: 1n << 14n,\n attachFiles: 1n << 15n,\n readMessageHistory: 1n << 16n,\n mentionEveryone: 1n << 17n,\n useExternalEmojis: 1n << 18n,\n viewGuildInsights: 1n << 19n,\n connect: 1n << 20n,\n speak: 1n << 21n,\n muteMembers: 1n << 22n,\n deafenMembers: 1n << 23n,\n moveMembers: 1n << 24n,\n useVAD: 1n << 25n,\n changeNickname: 1n << 26n,\n manageNicknames: 1n << 27n,\n manageRoles: 1n << 28n,\n manageWebhooks: 1n << 29n,\n manageGuildExpressions: 1n << 30n,\n useApplicationCommands: 1n << 31n,\n requestToSpeak: 1n << 32n,\n manageEvents: 1n << 33n,\n manageThreads: 1n << 34n,\n createPublicThreads: 1n << 35n,\n createPrivateThreads: 1n << 36n,\n useExternalStickers: 1n << 37n,\n sendMessagesInThreads: 1n << 38n,\n useEmbeddedActivities: 1n << 39n,\n moderateMembers: 1n << 40n,\n viewCreatorMonetizationAnalytics: 1n << 41n,\n useSoundboard: 1n << 42n,\n createGuildExpressions: 1n << 43n,\n createEvents: 1n << 44n,\n useExternalSounds: 1n << 45n,\n sendVoiceMessages: 1n << 46n,\n setVoiceChannelStatus: 1n << 48n,\n sendPolls: 1n << 49n,\n useExternalApps: 1n << 50n,\n pinMessages: 1n << 51n,\n bypassSlowmode: 1n << 52n,\n} as const;\n\n/** Canonical PascalCase permission constants. */\nexport const Permissions = {\n CreateInstantInvite: 1n << 0n,\n KickMembers: 1n << 1n,\n BanMembers: 1n << 2n,\n Administrator: 1n << 3n,\n ManageChannels: 1n << 4n,\n ManageGuild: 1n << 5n,\n AddReactions: 1n << 6n,\n ViewAuditLog: 1n << 7n,\n PrioritySpeaker: 1n << 8n,\n Stream: 1n << 9n,\n ViewChannel: 1n << 10n,\n SendMessages: 1n << 11n,\n SendTTSMessages: 1n << 12n,\n ManageMessages: 1n << 13n,\n EmbedLinks: 1n << 14n,\n AttachFiles: 1n << 15n,\n ReadMessageHistory: 1n << 16n,\n MentionEveryone: 1n << 17n,\n UseExternalEmojis: 1n << 18n,\n ViewGuildInsights: 1n << 19n,\n Connect: 1n << 20n,\n Speak: 1n << 21n,\n MuteMembers: 1n << 22n,\n DeafenMembers: 1n << 23n,\n MoveMembers: 1n << 24n,\n UseVAD: 1n << 25n,\n ChangeNickname: 1n << 26n,\n ManageNicknames: 1n << 27n,\n ManageRoles: 1n << 28n,\n ManageWebhooks: 1n << 29n,\n ManageGuildExpressions: 1n << 30n,\n UseApplicationCommands: 1n << 31n,\n RequestToSpeak: 1n << 32n,\n ManageEvents: 1n << 33n,\n ManageThreads: 1n << 34n,\n CreatePublicThreads: 1n << 35n,\n CreatePrivateThreads: 1n << 36n,\n UseExternalStickers: 1n << 37n,\n SendMessagesInThreads: 1n << 38n,\n UseEmbeddedActivities: 1n << 39n,\n ModerateMembers: 1n << 40n,\n ViewCreatorMonetizationAnalytics: 1n << 41n,\n UseSoundboard: 1n << 42n,\n CreateGuildExpressions: 1n << 43n,\n CreateEvents: 1n << 44n,\n UseExternalSounds: 1n << 45n,\n SendVoiceMessages: 1n << 46n,\n SetVoiceChannelStatus: 1n << 48n,\n SendPolls: 1n << 49n,\n UseExternalApps: 1n << 50n,\n PinMessages: 1n << 51n,\n BypassSlowmode: 1n << 52n,\n} as const;\n\n/**\n * Permission flag bits with **bigint** values matching Discord's permission bit\n * positions. Mirrors discord.js's `PermissionFlagsBits`, so bitwise composition\n * (`Flags.A | Flags.B`) OR-s the bits instead of concatenating strings.\n *\n * The same object as {@link Permissions}, under the discord.js name. It is a\n * `const` object rather than a TS `enum` because enums cannot hold bigint\n * members; the same-named union type below preserves usage as a type.\n */\nexport const PermissionFlagsBits = Permissions;\n\n/** Union of {@link PermissionFlagsBits} values (all bigint). */\nexport type PermissionFlagsBits =\n (typeof PermissionFlagsBits)[keyof typeof PermissionFlagsBits];\n\nexport type PermissionName =\n | keyof typeof Permission\n | keyof typeof Permissions\n | keyof typeof PermissionFlagsBits;\n\nexport type PermissionResolvable =\n | bigint\n | number\n | string\n | PermissionName\n | PermissionFlagsBits\n | PermissionSet\n | PermissionResolvable[];\n\n/** Immutable permission bitfield with direct boolean getters. */\nexport class PermissionSet {\n /** Permission bitfield. */\n public readonly bitfield: bigint;\n\n /** Creates a permission set. @param value Initial permission bitfield or array of permissions. @throws {RangeError} If the value is negative. @throws {TypeError} If the value is not a valid integer. */\n public constructor(value: PermissionResolvable = 0n) {\n try {\n if (Array.isArray(value)) {\n let bit = 0n;\n for (const item of value) {\n bit |= this.#resolve(item);\n }\n this.bitfield = bit;\n } else if (value instanceof PermissionSet) {\n this.bitfield = value.bitfield;\n } else {\n const bitfield = this.#resolve(value);\n if (bitfield < 0n)\n throw new RangeError(\n \"Permission bitfield cannot be negative.\",\n );\n this.bitfield = bitfield;\n }\n } catch (error) {\n if (error instanceof RangeError) throw error;\n throw new TypeError(\n \"Permission bitfield must be a valid non-negative integer.\",\n { cause: error },\n );\n }\n }\n\n // Direct camelCase boolean getters for instant checks (e.g. member.permissions.kickMembers)\n public get createInstantInvite(): boolean {\n return (\n (this.bitfield & Permission.createInstantInvite) ===\n Permission.createInstantInvite\n );\n }\n public get kickMembers(): boolean {\n return (\n (this.bitfield & Permission.kickMembers) === Permission.kickMembers\n );\n }\n public get banMembers(): boolean {\n return (\n (this.bitfield & Permission.banMembers) === Permission.banMembers\n );\n }\n public get administrator(): boolean {\n return (\n (this.bitfield & Permission.administrator) ===\n Permission.administrator\n );\n }\n public get manageChannels(): boolean {\n return (\n (this.bitfield & Permission.manageChannels) ===\n Permission.manageChannels\n );\n }\n public get manageGuild(): boolean {\n return (\n (this.bitfield & Permission.manageGuild) === Permission.manageGuild\n );\n }\n public get addReactions(): boolean {\n return (\n (this.bitfield & Permission.addReactions) ===\n Permission.addReactions\n );\n }\n public get viewAuditLog(): boolean {\n return (\n (this.bitfield & Permission.viewAuditLog) ===\n Permission.viewAuditLog\n );\n }\n public get prioritySpeaker(): boolean {\n return (\n (this.bitfield & Permission.prioritySpeaker) ===\n Permission.prioritySpeaker\n );\n }\n public get stream(): boolean {\n return (this.bitfield & Permission.stream) === Permission.stream;\n }\n public get viewChannel(): boolean {\n return (\n (this.bitfield & Permission.viewChannel) === Permission.viewChannel\n );\n }\n public get sendMessages(): boolean {\n return (\n (this.bitfield & Permission.sendMessages) ===\n Permission.sendMessages\n );\n }\n public get sendTTSMessages(): boolean {\n return (\n (this.bitfield & Permission.sendTTSMessages) ===\n Permission.sendTTSMessages\n );\n }\n public get manageMessages(): boolean {\n return (\n (this.bitfield & Permission.manageMessages) ===\n Permission.manageMessages\n );\n }\n public get embedLinks(): boolean {\n return (\n (this.bitfield & Permission.embedLinks) === Permission.embedLinks\n );\n }\n public get attachFiles(): boolean {\n return (\n (this.bitfield & Permission.attachFiles) === Permission.attachFiles\n );\n }\n public get readMessageHistory(): boolean {\n return (\n (this.bitfield & Permission.readMessageHistory) ===\n Permission.readMessageHistory\n );\n }\n public get mentionEveryone(): boolean {\n return (\n (this.bitfield & Permission.mentionEveryone) ===\n Permission.mentionEveryone\n );\n }\n public get useExternalEmojis(): boolean {\n return (\n (this.bitfield & Permission.useExternalEmojis) ===\n Permission.useExternalEmojis\n );\n }\n public get viewGuildInsights(): boolean {\n return (\n (this.bitfield & Permission.viewGuildInsights) ===\n Permission.viewGuildInsights\n );\n }\n public get connect(): boolean {\n return (this.bitfield & Permission.connect) === Permission.connect;\n }\n public get speak(): boolean {\n return (this.bitfield & Permission.speak) === Permission.speak;\n }\n public get muteMembers(): boolean {\n return (\n (this.bitfield & Permission.muteMembers) === Permission.muteMembers\n );\n }\n public get deafenMembers(): boolean {\n return (\n (this.bitfield & Permission.deafenMembers) ===\n Permission.deafenMembers\n );\n }\n public get moveMembers(): boolean {\n return (\n (this.bitfield & Permission.moveMembers) === Permission.moveMembers\n );\n }\n public get useVAD(): boolean {\n return (this.bitfield & Permission.useVAD) === Permission.useVAD;\n }\n public get changeNickname(): boolean {\n return (\n (this.bitfield & Permission.changeNickname) ===\n Permission.changeNickname\n );\n }\n public get manageNicknames(): boolean {\n return (\n (this.bitfield & Permission.manageNicknames) ===\n Permission.manageNicknames\n );\n }\n public get manageRoles(): boolean {\n return (\n (this.bitfield & Permission.manageRoles) === Permission.manageRoles\n );\n }\n public get manageWebhooks(): boolean {\n return (\n (this.bitfield & Permission.manageWebhooks) ===\n Permission.manageWebhooks\n );\n }\n public get manageGuildExpressions(): boolean {\n return (\n (this.bitfield & Permission.manageGuildExpressions) ===\n Permission.manageGuildExpressions\n );\n }\n public get useApplicationCommands(): boolean {\n return (\n (this.bitfield & Permission.useApplicationCommands) ===\n Permission.useApplicationCommands\n );\n }\n public get requestToSpeak(): boolean {\n return (\n (this.bitfield & Permission.requestToSpeak) ===\n Permission.requestToSpeak\n );\n }\n public get manageEvents(): boolean {\n return (\n (this.bitfield & Permission.manageEvents) ===\n Permission.manageEvents\n );\n }\n public get manageThreads(): boolean {\n return (\n (this.bitfield & Permission.manageThreads) ===\n Permission.manageThreads\n );\n }\n public get createPublicThreads(): boolean {\n return (\n (this.bitfield & Permission.createPublicThreads) ===\n Permission.createPublicThreads\n );\n }\n public get createPrivateThreads(): boolean {\n return (\n (this.bitfield & Permission.createPrivateThreads) ===\n Permission.createPrivateThreads\n );\n }\n public get useExternalStickers(): boolean {\n return (\n (this.bitfield & Permission.useExternalStickers) ===\n Permission.useExternalStickers\n );\n }\n public get sendMessagesInThreads(): boolean {\n return (\n (this.bitfield & Permission.sendMessagesInThreads) ===\n Permission.sendMessagesInThreads\n );\n }\n public get useEmbeddedActivities(): boolean {\n return (\n (this.bitfield & Permission.useEmbeddedActivities) ===\n Permission.useEmbeddedActivities\n );\n }\n public get moderateMembers(): boolean {\n return (\n (this.bitfield & Permission.moderateMembers) ===\n Permission.moderateMembers\n );\n }\n public get viewCreatorMonetizationAnalytics(): boolean {\n return (\n (this.bitfield & Permission.viewCreatorMonetizationAnalytics) ===\n Permission.viewCreatorMonetizationAnalytics\n );\n }\n public get useSoundboard(): boolean {\n return (\n (this.bitfield & Permission.useSoundboard) ===\n Permission.useSoundboard\n );\n }\n public get createGuildExpressions(): boolean {\n return (\n (this.bitfield & Permission.createGuildExpressions) ===\n Permission.createGuildExpressions\n );\n }\n public get createEvents(): boolean {\n return (\n (this.bitfield & Permission.createEvents) ===\n Permission.createEvents\n );\n }\n public get useExternalSounds(): boolean {\n return (\n (this.bitfield & Permission.useExternalSounds) ===\n Permission.useExternalSounds\n );\n }\n public get sendVoiceMessages(): boolean {\n return (\n (this.bitfield & Permission.sendVoiceMessages) ===\n Permission.sendVoiceMessages\n );\n }\n public get sendPolls(): boolean {\n return (this.bitfield & Permission.sendPolls) === Permission.sendPolls;\n }\n public get useExternalApps(): boolean {\n return (\n (this.bitfield & Permission.useExternalApps) ===\n Permission.useExternalApps\n );\n }\n\n /** Checks whether all supplied permissions are present (AND). @param permissions Permission names or raw bits. @returns True when every requested permission is present. */\n public has(...permissions: PermissionResolvable[]): boolean {\n if (permissions.length === 1) {\n const p = permissions[0]!;\n const bit = typeof p === \"bigint\" ? p : this.#resolve(p);\n return (this.bitfield & bit) === bit;\n }\n for (let i = 0; i < permissions.length; i++) {\n const p = permissions[i]!;\n const bit = typeof p === \"bigint\" ? p : this.#resolve(p);\n if ((this.bitfield & bit) !== bit) return false;\n }\n return true;\n }\n\n /** Checks whether any supplied permission is present (OR). @param permissions Permission names or raw bits. @returns True when at least one requested permission is present. */\n public any(...permissions: PermissionResolvable[]): boolean {\n if (permissions.length === 1) {\n const p = permissions[0]!;\n const bit = typeof p === \"bigint\" ? p : this.#resolve(p);\n return (this.bitfield & bit) !== 0n;\n }\n for (let i = 0; i < permissions.length; i++) {\n const p = permissions[i]!;\n const bit = typeof p === \"bigint\" ? p : this.#resolve(p);\n if ((this.bitfield & bit) !== 0n) return true;\n }\n return false;\n }\n\n /** Returns the known permission names from `permissions` that this set lacks. @param permissions Permission names or raw bits. */\n public missing(...permissions: PermissionResolvable[]): PermissionName[] {\n const wanted = new PermissionSet(permissions);\n return wanted.toArray().filter((name) => !this.has(name));\n }\n\n /** Returns a new set with permissions added. @param permissions Permission names or raw bits. @returns A new permission set. */\n public add(...permissions: PermissionResolvable[]): PermissionSet {\n let bitfield = this.bitfield;\n for (let i = 0; i < permissions.length; i++) {\n const p = permissions[i]!;\n bitfield |= typeof p === \"bigint\" ? p : this.#resolve(p);\n }\n return new PermissionSet(bitfield);\n }\n\n /** Returns a new set with permissions removed. @param permissions Permission names or raw bits. @returns A new permission set. */\n public remove(...permissions: PermissionResolvable[]): PermissionSet {\n let bitfield = this.bitfield;\n for (let i = 0; i < permissions.length; i++) {\n const p = permissions[i]!;\n bitfield &= ~(typeof p === \"bigint\" ? p : this.#resolve(p));\n }\n return new PermissionSet(bitfield);\n }\n\n /** Checks equality with another permission set or raw bitfield. @param other Permission set or raw bitfield. @returns True when both values are equal. */\n public equals(other: PermissionSet | bigint | number | string): boolean {\n return (\n this.bitfield ===\n (other instanceof PermissionSet\n ? other.bitfield\n : new PermissionSet(other).bitfield)\n );\n }\n\n /** Returns the decimal permission representation. @returns Decimal permission bitfield. */\n public toString(): string {\n return this.bitfield.toString();\n }\n\n /** Returns known permission names contained in this set. @returns Names of all enabled known permissions. */\n public toArray(): PermissionName[] {\n return (Object.keys(Permission) as PermissionName[]).filter(\n (permission) => this.has(permission),\n );\n }\n\n /** Resolves a named permission, enum value, or raw bit. @param permission Permission name, enum value, or raw bit. @returns Numeric permission bit. */\n /** Names Discord has since renamed, still accepted when resolving a permission by name. */\n static readonly #renamed: Readonly<Record<string, string>> = {\n ManageEmojisAndStickers: \"ManageGuildExpressions\",\n ManageEmojis: \"ManageGuildExpressions\",\n UseSlashCommands: \"UseApplicationCommands\",\n };\n\n #resolve(permission: PermissionResolvable): bigint {\n if (typeof permission === \"bigint\") return permission;\n if (typeof permission === \"number\") return BigInt(permission);\n if (permission instanceof PermissionSet) return permission.bitfield;\n if (Array.isArray(permission)) {\n let bit = 0n;\n for (let i = 0; i < permission.length; i++)\n bit |= this.#resolve(permission[i]!);\n return bit;\n }\n if (typeof permission === \"string\") {\n const renamed = PermissionSet.#renamed[permission];\n if (renamed) return this.#resolve(renamed);\n if (permission in Permission)\n return (Permission as Record<string, bigint>)[permission]!;\n if (permission in Permissions)\n return (Permissions as Record<string, bigint>)[permission]!;\n if (permission in PermissionFlagsBits)\n return (PermissionFlagsBits as Record<string, bigint>)[\n permission\n ]!;\n const lower =\n permission.charAt(0).toLowerCase() + permission.slice(1);\n if (lower in Permission)\n return (Permission as Record<string, bigint>)[lower]!;\n return BigInt(permission);\n }\n return 0n;\n }\n}\n\n/** Permissions bitfield alias. */\nexport class PermissionsBitField extends PermissionSet {\n /** Permission flags bits enum. */\n public static readonly Flags = PermissionFlagsBits;\n /** Permission flags constants. */\n public static readonly All = Permission;\n\n /** Creates a permissions bitfield. @param value Initial permission bitfield or array of permissions. */\n public constructor(value: PermissionResolvable = 0n) {\n super(value);\n }\n}\n\n/** Every known permission bit. */\nconst ALL_PERMISSIONS = Object.values(Permission).reduce(\n (bits, bit) => bits | bit,\n 0n,\n);\n\n/** A role as needed for permission resolution. */\nexport interface PermissionRole {\n id: string;\n permissions: PermissionResolvable;\n}\n\n/** A channel permission overwrite (Discord's `permission_overwrites` entry). */\nexport interface PermissionOverwrite {\n id: string;\n type: PermissionOverwriteEnum | number;\n allow: PermissionResolvable;\n deny: PermissionResolvable;\n}\n\n/** Everything needed to resolve a member's permissions. */\nexport interface PermissionContext {\n guildId: string;\n /** Guild owner ID; the owner has every permission. */\n ownerId?: string;\n memberId: string;\n memberRoleIds: readonly string[];\n /** Guild roles, including `@everyone` (whose ID is the guild ID). */\n roles: readonly PermissionRole[];\n /** Channel overwrites; omit for guild-level permissions. */\n overwrites?: readonly PermissionOverwrite[];\n /** When the member's timeout ends; while it is in the future the member keeps only View Channel and Read Message History. */\n timedOutUntil?: Date | number | null;\n}\n\nconst bits = (value: PermissionResolvable): bigint =>\n new PermissionSet(value).bitfield;\n\n/**\n * Resolves a member's effective permissions the way Discord does: base\n * permissions from `@everyone` and the member's roles (owner and\n * administrator get everything), then the channel's `@everyone` overwrite,\n * the combined role overwrites, and finally the member overwrite. A member\n * whose timeout has not ended keeps only View Channel and Read Message History.\n */\nexport function computePermissions(context: PermissionContext): PermissionSet {\n if (context.ownerId !== undefined && context.ownerId === context.memberId)\n return new PermissionSet(ALL_PERMISSIONS);\n const roles = new Map(context.roles.map((role) => [role.id, role]));\n let base = bits(roles.get(context.guildId)?.permissions ?? 0n);\n for (const id of context.memberRoleIds)\n base |= bits(roles.get(id)?.permissions ?? 0n);\n if (base & Permission.administrator)\n return new PermissionSet(ALL_PERMISSIONS);\n const overwrites = context.overwrites ?? [];\n const everyone = overwrites.find((o) => o.id === context.guildId);\n if (everyone) base = (base & ~bits(everyone.deny)) | bits(everyone.allow);\n const memberRoles = new Set(context.memberRoleIds);\n let allow = 0n;\n let deny = 0n;\n for (const overwrite of overwrites)\n if (\n overwrite.type === PermissionOverwriteEnum.Role &&\n overwrite.id !== context.guildId &&\n memberRoles.has(overwrite.id)\n ) {\n allow |= bits(overwrite.allow);\n deny |= bits(overwrite.deny);\n }\n base = (base & ~deny) | allow;\n const member = overwrites.find(\n (o) =>\n o.type === PermissionOverwriteEnum.Member &&\n o.id === context.memberId,\n );\n if (member) base = (base & ~bits(member.deny)) | bits(member.allow);\n // Discord: timed-out members lose every permission except View Channel and\n // Read Message History. Owners and administrators returned above.\n const until =\n context.timedOutUntil instanceof Date\n ? context.timedOutUntil.getTime()\n : (context.timedOutUntil ?? 0);\n if (until > Date.now())\n base &= Permission.viewChannel | Permission.readMessageHistory;\n return new PermissionSet(base);\n}\n",
@@ -11,7 +11,7 @@
11
11
  "/** Discord application command option types. */\nexport const ApplicationCommandOptionEnum = {\n Subcommand: 1,\n SubcommandGroup: 2,\n String: 3,\n Integer: 4,\n Boolean: 5,\n User: 6,\n Channel: 7,\n Role: 8,\n Mentionable: 9,\n Number: 10,\n Attachment: 11,\n} as const;\n/** One {@link ApplicationCommandOptionEnum} value, usable as a type. */\nexport type ApplicationCommandOptionEnum =\n (typeof ApplicationCommandOptionEnum)[keyof typeof ApplicationCommandOptionEnum];\n\n/** Throws unless every context is 0 (guilds), 1 (the bot's DMs) or 2 (other DMs). */\nfunction validateContexts(contexts: number[]): void {\n if (\n contexts.some(\n (context) => context !== 0 && context !== 1 && context !== 2,\n )\n )\n throw new RangeError(\"Command contexts must be 0, 1 or 2.\");\n}\n\n/** Builds Discord application command payloads. */\nexport class CreateSlashCommand {\n readonly #data: Record<string, unknown> = { type: 1 };\n /** Sets the command name. */\n public setName(name: string): this {\n validateName(name, \"Command name\");\n this.#data.name = name;\n return this;\n }\n /** Sets the command description. */\n public setDescription(description: string): this {\n validateText(description, \"Command description\", 100);\n this.#data.description = description;\n return this;\n }\n /** Sets where the command can be used: 0 guilds, 1 the bot's DMs, 2 other DMs. @throws {RangeError} For any other value. */\n public setContexts(...contexts: number[]): this {\n validateContexts(contexts);\n this.#data.contexts = [...contexts];\n return this;\n }\n /** Sets whether the command is NSFW. */\n public setNSFW(nsfw = true): this {\n this.#data.nsfw = nsfw;\n return this;\n }\n /** Sets whether the command is NSFW. */\n public setNsfw(nsfw = true): this {\n return this.setNSFW(nsfw);\n }\n /** Sets command default member permissions. */\n public setDefaultMemberPermissions(\n permissions: bigint | number | string | null,\n ): this {\n this.#data.default_member_permissions =\n permissions === null ? null : BigInt(permissions).toString();\n return this;\n }\n /** Sets command integration types. */\n public setIntegrationTypes(...types: number[]): this {\n this.#data.integration_types = [...types];\n return this;\n }\n /** Adds a string option. */\n public addStringOption(\n configure: (option: CreateStringOption) => CreateStringOption,\n ): this {\n return this.addOption(configure(new CreateStringOption()));\n }\n /** Adds an integer option. */\n public addIntegerOption(\n configure: (option: CreateIntegerOption) => CreateIntegerOption,\n ): this {\n return this.addOption(configure(new CreateIntegerOption()));\n }\n /** Adds a number option. */\n public addNumberOption(\n configure: (option: CreateNumberOption) => CreateNumberOption,\n ): this {\n return this.addOption(configure(new CreateNumberOption()));\n }\n /** Adds a boolean option. */\n public addBooleanOption(\n configure: (option: CreateBooleanOption) => CreateBooleanOption,\n ): this {\n return this.addOption(configure(new CreateBooleanOption()));\n }\n /** Adds a user option. */\n public addUserOption(\n configure: (option: CreateUserOption) => CreateUserOption,\n ): this {\n return this.addOption(configure(new CreateUserOption()));\n }\n /** Adds a channel option. */\n public addChannelOption(\n configure: (option: CreateChannelOption) => CreateChannelOption,\n ): this {\n return this.addOption(configure(new CreateChannelOption()));\n }\n /** Adds a role option. */\n public addRoleOption(\n configure: (option: CreateRoleOption) => CreateRoleOption,\n ): this {\n return this.addOption(configure(new CreateRoleOption()));\n }\n /** Adds a mentionable option. */\n public addMentionableOption(\n configure: (option: CreateMentionableOption) => CreateMentionableOption,\n ): this {\n return this.addOption(configure(new CreateMentionableOption()));\n }\n /** Adds an attachment option. */\n public addAttachmentOption(\n configure: (option: CreateAttachmentOption) => CreateAttachmentOption,\n ): this {\n return this.addOption(configure(new CreateAttachmentOption()));\n }\n /** Adds a subcommand. */\n public addSubcommand(\n configure: (option: CreateSubcommand) => CreateSubcommand,\n ): this {\n return this.addOption(configure(new CreateSubcommand()));\n }\n /** Adds a subcommand group. */\n public addSubcommandGroup(\n configure: (option: CreateSubcommandGroup) => CreateSubcommandGroup,\n ): this {\n return this.addOption(configure(new CreateSubcommandGroup()));\n }\n /** Serializes the command payload. */\n public toJSON(): Record<string, unknown> {\n return structuredClone(this.#data);\n }\n /** Adds a validated top-level command option. */\n protected addOption(option: CreateCommandOption): this {\n this.#data.options = appendOption(\n this.#data.options,\n option.toJSON(),\n \"application command\",\n );\n return this;\n }\n}\n\n/** Base builder for command options. */\nexport class CreateCommandOption {\n protected readonly data: Record<string, unknown>;\n /** Creates an option builder. */\n public constructor(type: number) {\n this.data = { type };\n }\n /** Sets the option name. */\n public setName(name: string): this {\n validateName(name, \"Option name\");\n this.data.name = name;\n return this;\n }\n /** Sets the option description. */\n public setDescription(description: string): this {\n validateText(description, \"Option description\", 100);\n this.data.description = description;\n return this;\n }\n /** Makes the option required or optional. */\n public setRequired(required = true): this {\n this.data.required = required;\n return this;\n }\n /** Sets autocomplete. */\n public setAutocomplete(enabled = true): this {\n this.data.autocomplete = enabled;\n return this;\n }\n /** Serializes the option payload. */\n public toJSON(): Record<string, unknown> {\n return structuredClone(this.data);\n }\n}\n\n/** Builds a string command option. */\nexport class CreateStringOption extends CreateCommandOption {\n /** Creates a string option. */\n public constructor() {\n super(ApplicationCommandOptionEnum.String);\n }\n /** Adds string choices. */\n public addChoices(\n ...choices: Array<{ name: string; value: string }>\n ): this {\n for (const choice of choices)\n validateText(choice.value, \"Choice value\", 100);\n appendChoices(this.data, choices);\n return this;\n }\n /** Sets the minimum string length. */\n public setMinLength(value: number): this {\n validateIntegerRange(value, 0, 6000, \"min_length\");\n this.data.min_length = value;\n return this;\n }\n /** Sets the maximum string length. */\n public setMaxLength(value: number): this {\n validateIntegerRange(value, 1, 6000, \"max_length\");\n if (\n (this.data.min_length as number | undefined) !== undefined &&\n (this.data.min_length as number) > value\n )\n throw new RangeError(\"min_length cannot exceed max_length.\");\n this.data.max_length = value;\n return this;\n }\n}\n/** Shared bounds, choices and autocomplete rules of integer and number options. */\nexport class CreateNumericOption extends CreateCommandOption {\n readonly #integer: boolean;\n /** Creates a numeric option; `integer` rejects fractional bounds. */\n public constructor(type: number, integer: boolean) {\n super(type);\n this.#integer = integer;\n }\n /** Sets the minimum value. */\n public setMinValue(value: number): this {\n validateNumberRange(\n value,\n -MAX_SAFE,\n MAX_SAFE,\n \"min_value\",\n this.#integer,\n );\n this.data.min_value = value;\n return this;\n }\n /** Sets the maximum value; it cannot be below the minimum. */\n public setMaxValue(value: number): this {\n validateNumberRange(\n value,\n -MAX_SAFE,\n MAX_SAFE,\n \"max_value\",\n this.#integer,\n );\n const min = this.data.min_value as number | undefined;\n if (min !== undefined && min > value)\n throw new RangeError(\"min_value cannot exceed max_value.\");\n this.data.max_value = value;\n return this;\n }\n /** Enables autocomplete; options with choices cannot use it. */\n public override setAutocomplete(enabled = true): this {\n if (\n enabled &&\n Array.isArray(this.data.choices) &&\n this.data.choices.length\n )\n throw new RangeError(\"Autocomplete options cannot define choices.\");\n this.data.autocomplete = enabled;\n return this;\n }\n /** Adds fixed choices. */\n public addChoices(...choices: { name: string; value: number }[]): this {\n appendChoices(this.data, choices);\n return this;\n }\n}\n/** Builds an integer command option. */\nexport class CreateIntegerOption extends CreateNumericOption {\n public constructor() {\n super(ApplicationCommandOptionEnum.Integer, true);\n }\n}\n/** Builds a number command option. */\nexport class CreateNumberOption extends CreateNumericOption {\n public constructor() {\n super(ApplicationCommandOptionEnum.Number, false);\n }\n}\n/** Builds a boolean command option. */\nexport class CreateBooleanOption extends CreateCommandOption {\n /** Creates a boolean option. */\n public constructor() {\n super(ApplicationCommandOptionEnum.Boolean);\n }\n}\n/** Builds a user command option. */\nexport class CreateUserOption extends CreateCommandOption {\n /** Creates a user option. */\n public constructor() {\n super(ApplicationCommandOptionEnum.User);\n }\n}\n/** Builds a role command option. */\nexport class CreateRoleOption extends CreateCommandOption {\n /** Creates a role option. */\n public constructor() {\n super(ApplicationCommandOptionEnum.Role);\n }\n}\n/** Builds a mentionable command option. */\nexport class CreateMentionableOption extends CreateCommandOption {\n /** Creates a mentionable option. */\n public constructor() {\n super(ApplicationCommandOptionEnum.Mentionable);\n }\n}\n/** Builds an attachment command option. */\nexport class CreateAttachmentOption extends CreateCommandOption {\n /** Creates an attachment option. */\n public constructor() {\n super(ApplicationCommandOptionEnum.Attachment);\n }\n}\n/** Builds a channel command option. */\nexport class CreateChannelOption extends CreateCommandOption {\n /** Creates a channel option. */\n public constructor() {\n super(ApplicationCommandOptionEnum.Channel);\n }\n /** Restricts accepted channel types. */\n public addChannelTypes(...types: number[]): this {\n if (types.some((type) => !Number.isInteger(type) || type < 0))\n throw new RangeError(\n \"Channel types must be non-negative integers.\",\n );\n this.data.channel_types = [...new Set(types)];\n return this;\n }\n}\n/** Builds a nested subcommand. */\nexport class CreateSubcommand extends CreateCommandOption {\n /** Creates a subcommand. */\n public constructor() {\n super(ApplicationCommandOptionEnum.Subcommand);\n }\n /** Adds a string option to this subcommand. */\n public addStringOption(\n configure: (option: CreateStringOption) => CreateStringOption,\n ): this {\n return this.addChildOption(configure(new CreateStringOption()));\n }\n /** Adds an integer option to this subcommand. */\n public addIntegerOption(\n configure: (option: CreateIntegerOption) => CreateIntegerOption,\n ): this {\n return this.addChildOption(configure(new CreateIntegerOption()));\n }\n /** Adds a number option to this subcommand. */\n public addNumberOption(\n configure: (option: CreateNumberOption) => CreateNumberOption,\n ): this {\n return this.addChildOption(configure(new CreateNumberOption()));\n }\n /** Adds a boolean option to this subcommand. */\n public addBooleanOption(\n configure: (option: CreateBooleanOption) => CreateBooleanOption,\n ): this {\n return this.addChildOption(configure(new CreateBooleanOption()));\n }\n /** Adds a user option to this subcommand. */\n public addUserOption(\n configure: (option: CreateUserOption) => CreateUserOption,\n ): this {\n return this.addChildOption(configure(new CreateUserOption()));\n }\n /** Adds a channel option to this subcommand. */\n public addChannelOption(\n configure: (option: CreateChannelOption) => CreateChannelOption,\n ): this {\n return this.addChildOption(configure(new CreateChannelOption()));\n }\n /** Adds a role option to this subcommand. */\n public addRoleOption(\n configure: (option: CreateRoleOption) => CreateRoleOption,\n ): this {\n return this.addChildOption(configure(new CreateRoleOption()));\n }\n /** Adds a mentionable option to this subcommand. */\n public addMentionableOption(\n configure: (option: CreateMentionableOption) => CreateMentionableOption,\n ): this {\n return this.addChildOption(configure(new CreateMentionableOption()));\n }\n /** Adds an attachment option to this subcommand. */\n public addAttachmentOption(\n configure: (option: CreateAttachmentOption) => CreateAttachmentOption,\n ): this {\n return this.addChildOption(configure(new CreateAttachmentOption()));\n }\n protected addChildOption(option: CreateCommandOption): this {\n const options =\n (this.data.options as OptionPayload[] | undefined) ?? [];\n if (\n options.some(\n (existing) =>\n existing.type === ApplicationCommandOptionEnum.Subcommand,\n )\n )\n throw new RangeError(\n \"Subcommands cannot contain nested subcommands.\",\n );\n this.data.options = appendOption(\n options,\n option.toJSON(),\n \"subcommand\",\n );\n return this;\n }\n}\n/** Builds a nested subcommand group. */\nexport class CreateSubcommandGroup extends CreateCommandOption {\n /** Creates a subcommand group. */\n public constructor() {\n super(ApplicationCommandOptionEnum.SubcommandGroup);\n }\n /** Adds a subcommand to this group. */\n public addSubcommand(\n configure: (option: CreateSubcommand) => CreateSubcommand,\n ): this {\n const options = (this.data.options as unknown[] | undefined) ?? [];\n if (options.length >= 25)\n throw new RangeError(\n \"A subcommand group cannot contain more than 25 subcommands.\",\n );\n const payload = configure(new CreateSubcommand()).toJSON();\n if (\n options.some(\n (existing) =>\n (existing as Record<string, unknown>).name === payload.name,\n )\n )\n throw new RangeError(\n `Duplicate subcommand name: ${String(payload.name)}.`,\n );\n if (payload.type !== ApplicationCommandOptionEnum.Subcommand)\n throw new TypeError(\n \"Subcommand group children must be subcommands.\",\n );\n options.push(payload);\n this.data.options = options;\n return this;\n }\n}\nconst MAX_SAFE = Number.MAX_SAFE_INTEGER;\n\n/** A serialized option as stored on its parent. */\ntype OptionPayload = Record<string, unknown>;\n\n/**\n * Returns `options` with `payload` appended, enforcing Discord's limit of 25,\n * unique names, and required options before optional ones.\n */\nfunction appendOption(\n options: unknown,\n payload: OptionPayload,\n parent: \"application command\" | \"subcommand\",\n): OptionPayload[] {\n const list = (options as OptionPayload[] | undefined) ?? [];\n if (list.length >= 25)\n throw new RangeError(\n `${parent === \"subcommand\" ? \"A subcommand\" : \"An application command\"} cannot contain more than 25 options.`,\n );\n if (list.some((existing) => existing.name === payload.name))\n throw new RangeError(`Duplicate option name: ${String(payload.name)}.`);\n if (\n payload.required === true &&\n list.some((existing) => existing.required !== true)\n )\n throw new RangeError(\n `Required ${parent} options must be placed before optional options.`,\n );\n return [...list, payload];\n}\n\n/** Appends choices to an option, enforcing the shared choice rules. */\nfunction appendChoices(\n data: OptionPayload,\n choices: { name: string; value: string | number }[],\n): void {\n const current = (data.choices as unknown[] | undefined) ?? [];\n if (!choices.length)\n throw new TypeError(\"At least one choice is required.\");\n if (data.autocomplete === true)\n throw new RangeError(\"Autocomplete options cannot define choices.\");\n if (current.length + choices.length > 25)\n throw new RangeError(\"An option cannot contain more than 25 choices.\");\n for (const choice of choices) validateText(choice.name, \"Choice name\", 100);\n data.choices = [...current, ...choices.map((choice) => ({ ...choice }))];\n}\n\nfunction validateName(value: string, field: string): void {\n if (!/^[\\p{L}\\p{N}_-]{1,32}$/u.test(value) || value !== value.toLowerCase())\n throw new RangeError(\n `${field} must contain 1-32 lowercase letters, numbers, underscores, or hyphens.`,\n );\n}\nfunction validateText(value: string, field: string, max: number): void {\n if (typeof value !== \"string\" || !value.trim() || value.length > max)\n throw new RangeError(`${field} must contain 1-${max} characters.`);\n}\nfunction validateIntegerRange(\n value: number,\n minimum: number,\n maximum: number,\n field: string,\n): void {\n if (!Number.isInteger(value) || value < minimum || value > maximum)\n throw new RangeError(\n `${field} must be an integer between ${minimum} and ${maximum}.`,\n );\n}\nfunction validateNumberRange(\n value: number,\n minimum: number,\n maximum: number,\n field: string,\n integerOnly = true,\n): void {\n if (\n !Number.isFinite(value) ||\n (integerOnly && !Number.isInteger(value)) ||\n value < minimum ||\n value > maximum\n )\n throw new RangeError(\n `${field} must be ${integerOnly ? \"an integer\" : \"a number\"} between ${minimum} and ${maximum}.`,\n );\n}\n\n// ─── Context Menu Builders ────────────────────────────────────────────────────\n\n/** Builder for application commands that appear in right-click context menus.\n * Discord.js-familiar: `new CreateContextMenuCommand().setName(\"x\").setType(2)`. */\nexport class CreateContextMenuCommand {\n protected readonly data: Record<string, unknown>;\n public constructor(type: 2 | 3 = 2) {\n this.data = { type };\n }\n /** Sets the command type: 2 = USER, 3 = MESSAGE. @throws {RangeError} For any other type. */\n public setType(type: 2 | 3): this {\n if (type !== 2 && type !== 3)\n throw new RangeError(\"Context menu command type must be 2 or 3.\");\n this.data.type = type;\n return this;\n }\n /** Sets the command name (shown in the right-click menu).\n * Unlike CHAT_INPUT commands, USER (type 2) and MESSAGE (type 3) context-menu\n * command names may contain uppercase letters and spaces, so no lowercase/charset\n * validation is applied here — only Discord's 1-32 length limit and a non-empty\n * (non-whitespace) requirement are enforced.\n * @param name Display name; 1-32 characters, mixed case and spaces allowed.\n * @throws {RangeError} If the name is empty/whitespace-only or exceeds 32 characters.\n */\n public setName(name: string): this {\n if (\n typeof name !== \"string\" ||\n name.trim().length < 1 ||\n name.length > 32\n )\n throw new RangeError(\"Command name must contain 1-32 characters.\");\n this.data.name = name;\n return this;\n }\n /** Sets default member permissions required to see this command. */\n public setDefaultMemberPermissions(\n permissions: bigint | number | string | null,\n ): this {\n this.data.default_member_permissions =\n permissions === null ? null : BigInt(permissions).toString();\n return this;\n }\n /** Sets command integration types (0 guild install, 1 user install). */\n public setIntegrationTypes(...types: number[]): this {\n this.data.integration_types = [...types];\n return this;\n }\n /** Sets where the command can be used: 0 guilds, 1 the bot's DMs, 2 other DMs. @throws {RangeError} For any other value. */\n public setContexts(...contexts: number[]): this {\n validateContexts(contexts);\n this.data.contexts = [...contexts];\n return this;\n }\n /** Sets the name shown per locale, e.g. `{ \"es-ES\": \"Ver perfil\" }`; null clears it. */\n public setNameLocalizations(\n localizations: Record<string, string> | null,\n ): this {\n this.data.name_localizations =\n localizations === null ? null : { ...localizations };\n return this;\n }\n /** Serializes the command payload for the Discord API. */\n public toJSON(): Record<string, unknown> {\n return structuredClone(this.data);\n }\n}\n\n/** Builds a User context menu command (appears when right-clicking a user, type 2).\n * @example\n * new CreateUserCommand().setName(\"View Profile\").toJSON()\n */\nexport class CreateUserCommand extends CreateContextMenuCommand {\n public constructor() {\n super(2);\n }\n}\n\n/** Builds a Message context menu command (appears when right-clicking a message, type 3).\n * @example\n * new CreateMessageCommand().setName(\"Translate Message\").toJSON()\n */\nexport class CreateMessageCommand extends CreateContextMenuCommand {\n public constructor() {\n super(3);\n }\n}\n",
12
12
  "/**\n * Prefix-command arguments: split a message into arguments and read them as\n * typed values, the way slash-command options are read.\n */\n\n/** What an argument is read as. `rest` takes everything left, as one string. */\nexport type PrefixArgType =\n | \"string\"\n | \"integer\"\n | \"number\"\n | \"boolean\"\n | \"user\"\n | \"role\"\n | \"channel\"\n | \"mentionable\"\n | \"rest\";\n\n/** One expected argument. */\nexport interface PrefixArgSpec {\n name: string;\n type: PrefixArgType;\n required?: boolean;\n /** For `string` and `rest`: the longest accepted value. */\n maxLength?: number;\n /** For `integer` and `number`: the accepted range. */\n min?: number;\n max?: number;\n /** Accepted values (compared case-insensitively for strings). */\n choices?: readonly (string | number)[];\n}\n\n/** A mentionable argument: which kind of mention it was. An unmarked ID reads as a user. */\nexport interface MentionableArg {\n id: string;\n type: \"user\" | \"role\";\n}\n\nexport type PrefixArgValue = string | number | boolean | MentionableArg;\n\nexport type PrefixArgsResult =\n | { ok: true; values: Record<string, PrefixArgValue | undefined> }\n | { ok: false; arg: string; error: string };\n\nconst SNOWFLAKE = /^\\d{16,22}$/;\nconst TRUE_WORDS = new Set([\n \"true\",\n \"yes\",\n \"y\",\n \"on\",\n \"1\",\n \"enable\",\n \"enabled\",\n]);\nconst FALSE_WORDS = new Set([\n \"false\",\n \"no\",\n \"n\",\n \"off\",\n \"0\",\n \"disable\",\n \"disabled\",\n]);\n\n/**\n * Splits text into arguments on whitespace. \"Double quotes\" or 'single quotes'\n * keep spaces inside one argument; a backslash escapes the next character.\n * @example tokenizeArgs(`ban @user \"being rude\"`) // [\"ban\", \"@user\", \"being rude\"]\n */\nexport function tokenizeArgs(input: string): string[] {\n const args: string[] = [];\n let current = \"\";\n let quote: string | null = null;\n let started = false;\n for (let index = 0; index < input.length; index++) {\n const character = input[index]!;\n if (character === \"\\\\\" && index + 1 < input.length) {\n current += input[++index];\n started = true;\n } else if (quote) {\n if (character === quote) quote = null;\n else current += character;\n } else if (character === '\"' || character === \"'\") {\n // A quote opens a quoted part only at the start of an argument.\n if (started) current += character;\n else {\n quote = character;\n started = true;\n }\n } else if (/\\s/.test(character)) {\n if (started) args.push(current);\n current = \"\";\n started = false;\n } else {\n current += character;\n started = true;\n }\n }\n if (started) args.push(current);\n return args;\n}\n\n/** The user ID in `<@id>`, `<@!id>` or a bare ID; null otherwise. */\nexport function parseUserMention(value: string): string | null {\n const id = /^<@!?(\\d{16,22})>$/.exec(value)?.[1] ?? value;\n return SNOWFLAKE.test(id) ? id : null;\n}\n\n/** The role ID in `<@&id>` or a bare ID; null otherwise. */\nexport function parseRoleMention(value: string): string | null {\n const id = /^<@&(\\d{16,22})>$/.exec(value)?.[1] ?? value;\n return SNOWFLAKE.test(id) ? id : null;\n}\n\n/** The channel ID in `<#id>` or a bare ID; null otherwise. */\nexport function parseChannelMention(value: string): string | null {\n const id = /^<#(\\d{16,22})>$/.exec(value)?.[1] ?? value;\n return SNOWFLAKE.test(id) ? id : null;\n}\n\n/** A user or role mention; a bare ID reads as a user. Null otherwise. */\nexport function parseMentionable(value: string): MentionableArg | null {\n const role = /^<@&(\\d{16,22})>$/.exec(value)?.[1];\n if (role) return { id: role, type: \"role\" };\n const user = parseUserMention(value);\n return user ? { id: user, type: \"user\" } : null;\n}\n\nfunction readOne(\n spec: PrefixArgSpec,\n raw: string,\n): { value: PrefixArgValue } | { error: string } {\n switch (spec.type) {\n case \"string\":\n case \"rest\":\n if (spec.maxLength !== undefined && raw.length > spec.maxLength)\n return {\n error: `must be at most ${spec.maxLength} characters`,\n };\n if (\n spec.choices &&\n !spec.choices.some(\n (choice) =>\n String(choice).toLowerCase() === raw.toLowerCase(),\n )\n )\n return { error: `must be one of: ${spec.choices.join(\", \")}` };\n return { value: raw };\n case \"integer\":\n case \"number\": {\n const value = Number(raw);\n if (!Number.isFinite(value) || raw.trim() === \"\")\n return { error: \"must be a number\" };\n if (spec.type === \"integer\" && !Number.isSafeInteger(value))\n return { error: \"must be a whole number\" };\n if (spec.min !== undefined && value < spec.min)\n return { error: `must be at least ${spec.min}` };\n if (spec.max !== undefined && value > spec.max)\n return { error: `must be at most ${spec.max}` };\n if (spec.choices && !spec.choices.includes(value))\n return { error: `must be one of: ${spec.choices.join(\", \")}` };\n return { value };\n }\n case \"boolean\": {\n const word = raw.toLowerCase();\n if (TRUE_WORDS.has(word)) return { value: true };\n if (FALSE_WORDS.has(word)) return { value: false };\n return { error: \"must be yes or no\" };\n }\n case \"user\": {\n const id = parseUserMention(raw);\n return id\n ? { value: id }\n : { error: \"must be a user mention or ID\" };\n }\n case \"role\": {\n const id = parseRoleMention(raw);\n return id\n ? { value: id }\n : { error: \"must be a role mention or ID\" };\n }\n case \"channel\": {\n const id = parseChannelMention(raw);\n return id\n ? { value: id }\n : { error: \"must be a channel mention or ID\" };\n }\n case \"mentionable\": {\n const value = parseMentionable(raw);\n return value\n ? { value }\n : { error: \"must be a user or role mention\" };\n }\n }\n}\n\n/**\n * Reads prefix-command arguments by position, as typed values. IDs come back\n * as strings (user, role, channel); a mentionable says which it was. The first\n * problem is reported, with the argument's name, instead of throwing.\n * @param input The text after the command name, or already-split arguments.\n * @param specs The expected arguments in order; a `rest` argument must be last.\n * @example\n * const result = parsePrefixArgs(`@user 10 spamming links`, [\n * { name: \"user\", type: \"user\", required: true },\n * { name: \"minutes\", type: \"integer\", min: 1, max: 40320 },\n * { name: \"reason\", type: \"rest\" },\n * ]);\n * if (!result.ok) return message.reply(`\\`${result.arg}\\` ${result.error}.`);\n */\nexport function parsePrefixArgs(\n input: string | readonly string[],\n specs: readonly PrefixArgSpec[],\n): PrefixArgsResult {\n const args = typeof input === \"string\" ? tokenizeArgs(input) : [...input];\n const values: Record<string, PrefixArgValue | undefined> = {};\n let position = 0;\n for (const [index, spec] of specs.entries()) {\n if (spec.type === \"rest\" && index !== specs.length - 1)\n throw new TypeError(\n `The rest argument \"${spec.name}\" must be the last one.`,\n );\n const raw =\n spec.type === \"rest\"\n ? args.slice(position).join(\" \")\n : args[position];\n if (raw === undefined || raw === \"\") {\n if (spec.required)\n return { ok: false, arg: spec.name, error: \"is required\" };\n values[spec.name] = undefined;\n continue;\n }\n const read = readOne(spec, raw);\n if (\"error\" in read)\n return { ok: false, arg: spec.name, error: read.error };\n values[spec.name] = read.value;\n position += spec.type === \"rest\" ? args.length : 1;\n }\n return { ok: true, values };\n}\n\n/** Discord application-command option types, as numbers (this package has no dependencies). */\nconst OPTION_TYPES: Readonly<Record<number, PrefixArgType>> = {\n 3: \"string\",\n 4: \"integer\",\n 5: \"boolean\",\n 6: \"user\",\n 7: \"channel\",\n 8: \"role\",\n 9: \"mentionable\",\n 10: \"number\",\n};\n\n/**\n * Argument specs from a slash command's options, so one definition serves both\n * `/command` and the prefix command. Subcommands and attachments are left out.\n * @param options The command's (or subcommand's) `options` as sent to Discord.\n * @param restLast Read the last string option as the rest of the message (handy for reasons).\n */\nexport function argsFromCommandOptions(\n options: readonly {\n name: string;\n type: number;\n required?: boolean;\n min_value?: number;\n max_value?: number;\n max_length?: number;\n choices?: readonly { value: string | number }[];\n }[],\n restLast = false,\n): PrefixArgSpec[] {\n const specs: PrefixArgSpec[] = [];\n for (const option of options) {\n const type = OPTION_TYPES[option.type];\n if (!type) continue;\n specs.push({\n name: option.name,\n type,\n required: option.required,\n min: option.min_value,\n max: option.max_value,\n maxLength: option.max_length,\n choices: option.choices?.map((choice) => choice.value),\n });\n }\n const last = specs[specs.length - 1];\n if (restLast && last?.type === \"string\") last.type = \"rest\";\n return specs;\n}\n",
13
13
  "/** Retention policy for a {@link Collection}. */\nexport interface CollectionOptions<K = unknown, V = unknown> {\n /** Default sliding TTL in ms for `set()`. Omit for no expiry. */\n ttl?: number;\n /** Fraction (0-1) of each entry's TTL randomly shaved off when it is set, so entries stored together do not expire together. Default `0`. */\n jitter?: number;\n /** Maximum entries stored with `set()`; least-recently-used are evicted past it. `setWithoutTTL()` entries are never counted or evicted. */\n maxSize?: number;\n /** Whether a read restarts a TTL entry's window. Default `true`; with `false` an entry lapses a fixed time after it was set, and reads only mark it recently used. */\n slide?: boolean;\n /** Called after an entry is removed automatically, with why. Exceptions are swallowed. */\n onEvict?: (key: K, value: V, reason: EvictionReason) => void;\n}\n\n/** Why an entry was removed automatically. */\nexport type EvictionReason = \"expired\" | \"evicted\";\n\n/** Cumulative counters from {@link Collection.stats}. */\nexport interface CollectionStats {\n /** `get`/`peek` calls that found a value. */\n hits: number;\n /** `get`/`peek` calls that found nothing. */\n misses: number;\n /** Entries removed because their TTL lapsed. */\n expired: number;\n /** Entries removed to stay within `maxSize`. */\n evicted: number;\n}\n\n/**\n * Everything the policy knows about one key: its place in the recency list\n * and its TTL. One record serves the policy map, the recency list and the\n * expiry heap, so a write touches one extra hash entry and allocates once.\n */\ntype Entry<K, V> = {\n key: K;\n /** A copy of the stored value, so a read needs one lookup instead of two. */\n value: V;\n /** Neighbours in recency order; meaningful while `inLru`. */\n older: Entry<K, V> | undefined;\n newer: Entry<K, V> | undefined;\n inLru: boolean;\n /** TTL window in ms; 0 when the entry does not expire (and is not in the heap). */\n window: number;\n /** The current deadline; a read moves it later without touching the heap. */\n deadline: number;\n /** The deadline the heap ordered this record by. */\n filed: number;\n};\n\n/**\n * Everything a retention policy needs. A Collection without `ttl` or\n * `maxSize` never creates one, so plain collections cost no more than a Map.\n */\ntype Policy<K, V> = {\n /** Keys with a TTL or a recency position; `setWithoutTTL()` keys are absent. */\n entries: Map<K, Entry<K, V>>;\n /** Oldest and newest entries of the recency list; only `maxSize` collections use it. */\n oldest: Entry<K, V> | undefined;\n newest: Entry<K, V> | undefined;\n lruSize: number;\n /** Min-heap by `filed`. A record that is no longer its key's map value is stale and dropped lazily. */\n heap: Entry<K, V>[];\n timer?: ReturnType<typeof setTimeout>;\n armedFor: number;\n /** The options, kept here so a plain collection carries none of them. */\n ttl: number | undefined;\n jitter: number;\n maxSize: number | undefined;\n slide: boolean;\n /** Stored loosely typed so the callback does not make Collection invariant in K and V. */\n onEvict:\n | ((key: unknown, value: unknown, reason: EvictionReason) => void)\n | undefined;\n expired: number;\n evicted: number;\n};\n\nfunction newPolicy<K, V>(options: CollectionOptions<K, V> = {}): Policy<K, V> {\n return {\n ttl: options.ttl,\n jitter: options.jitter ?? 0,\n maxSize: options.maxSize,\n slide: options.slide ?? true,\n onEvict: options.onEvict as Policy<K, V>[\"onEvict\"],\n expired: 0,\n evicted: 0,\n entries: new Map(),\n oldest: undefined,\n newest: undefined,\n lruSize: 0,\n heap: [],\n armedFor: Infinity,\n };\n}\n\n/**\n * Reads a value that `has()` just confirmed, without touching a Lunibee\n * collection's TTL, recency or hit counters.\n */\nfunction peekOf<K, V>(\n collection: ReadonlyCollection<K, V>,\n key: K,\n): V | undefined {\n return collection instanceof Map\n ? (Map.prototype.get.call(collection, key) as V | undefined)\n : collection.get(key);\n}\n\n/** What `merge()` does with one entry: keep it with `value`, or drop it. */\nexport type Keep<V> = { keep: true; value: V } | { keep: false };\n\n/**\n * A `Collection` seen read-only: every method that does not change it. Pass it\n * where you only read, so a function cannot add to or remove from the cache.\n */\nexport type ReadonlyCollection<K, V> = Omit<\n Collection<K, V>,\n | \"set\"\n | \"setWithoutTTL\"\n | \"delete\"\n | \"clear\"\n | \"ensure\"\n | \"forEach\"\n | \"get\"\n | \"sweep\"\n | \"purge\"\n> &\n ReadonlyMap<K, V>;\n\nconst mapGet = Map.prototype.get as (\n this: Map<unknown, unknown>,\n key: unknown,\n) => unknown;\nconst mapHas = Map.prototype.has as (\n this: Map<unknown, unknown>,\n key: unknown,\n) => boolean;\n\n/** Picks `amount` different items at random (a partial Fisher-Yates shuffle of `items`). */\nfunction sample<T>(items: T[], amount: number): T[] {\n if (!Number.isInteger(amount) || amount < 0)\n throw new RangeError(\"amount must be a non-negative integer.\");\n const count = Math.min(amount, items.length);\n for (let i = 0; i < count; i++) {\n const j = i + Math.floor(Math.random() * (items.length - i));\n [items[i], items[j]] = [items[j]!, items[i]!];\n }\n items.length = count;\n return items;\n}\n\nfunction assertTTL(ttl: number | undefined): void {\n if (ttl !== undefined && (!Number.isFinite(ttl) || ttl <= 0))\n throw new RangeError(\"TTL must be a positive number, or omitted.\");\n}\n\n/**\n * A keyed collection for Lunibee resource and application state.\n *\n * - `set(key, value, ttl?)` is TTL/LRU-aware: with a TTL (the default from\n * options, or per call) the entry expires after that window of inactivity,\n * and with `maxSize` it counts toward the cap.\n * - `setWithoutTTL(key, value)` never expires: no deadline, no heap record,\n * no timer, and exempt from `maxSize` eviction. Resource managers store\n * Discord state this way, so it leaves only through an explicit delete.\n *\n * Without options, a Collection behaves like a plain Map.\n */\nexport class Collection<K, V> extends Map<K, V> {\n /** Created with any option, or by the first `set()` with its own TTL. */\n #policy?: Policy<K, V>;\n #hits = 0;\n #misses = 0;\n\n /**\n * @param entries Initial entries, stored with `set()`.\n * @param options TTL and size policy.\n * @throws {RangeError} On a non-positive TTL or non-integer `maxSize`.\n */\n public constructor(\n entries?: Iterable<readonly [K, V]> | null,\n options: CollectionOptions<K, V> = {},\n ) {\n super();\n assertTTL(options.ttl);\n if (\n options.jitter !== undefined &&\n !(options.jitter >= 0 && options.jitter <= 1)\n )\n throw new RangeError(\"jitter must be between 0 and 1.\");\n if (\n options.maxSize !== undefined &&\n (!Number.isInteger(options.maxSize) || options.maxSize <= 0)\n )\n throw new RangeError(\"maxSize must be a positive integer.\");\n if (\n options.ttl !== undefined ||\n options.jitter !== undefined ||\n options.maxSize !== undefined ||\n options.slide !== undefined ||\n options.onEvict !== undefined\n )\n this.#policy = newPolicy(options);\n if (entries) for (const [key, value] of entries) this.set(key, value);\n }\n\n /**\n * Stores an entry with the default TTL (or `ttl`), marks it most recently\n * used, and enforces `maxSize`. Turns a `setWithoutTTL()` entry back into\n * a TTL/LRU entry.\n */\n public override set(key: K, value: V, ttl?: number): this {\n if (ttl === undefined && this.#policy === undefined)\n return super.set(key, value);\n assertTTL(ttl);\n let window = ttl ?? this.#policy?.ttl;\n const jitter = this.#policy?.jitter ?? 0;\n if (window !== undefined && jitter > 0)\n window = Math.max(1, window * (1 - jitter * Math.random()));\n if (window !== undefined) this.#policy ??= newPolicy();\n if (!this.#policy) return super.set(key, value);\n // A key that was not stored before has no policy entry.\n const before = super.size;\n super.set(key, value);\n this.#track(key, value, window, super.size !== before);\n this.#enforceCap();\n return this;\n }\n\n /**\n * Stores an entry that never expires: no deadline, heap record, timer or\n * LRU eviction. It leaves only through `delete`, `sweep`, `clear`, or a\n * later `set()`.\n */\n public setWithoutTTL(key: K, value: V): this {\n if (this.#policy) this.#forget(key);\n super.set(key, value);\n return this;\n }\n\n /** Reads a value; restarts a TTL entry's window and promotes it. */\n public override get(key: K): V | undefined {\n const policy = this.#policy;\n const entry = policy?.entries.get(key);\n if (entry === undefined) {\n const value = mapGet.call(this, key) as V | undefined;\n if (value !== undefined || mapHas.call(this, key)) {\n this.#hits++;\n return value;\n }\n this.#misses++;\n return undefined;\n }\n const timed = entry.window !== 0;\n const now = timed ? Date.now() : 0;\n if (timed && entry.deadline <= now) {\n this.#expire(entry);\n this.#misses++;\n return undefined;\n }\n this.#hits++;\n if (timed && policy!.slide) entry.deadline = now + entry.window;\n if (entry.inLru) this.#promote(policy!, entry);\n return entry.value;\n }\n\n /**\n * Returns the value for `key`, or stores and returns what `factory`\n * makes. Reading counts as a `get()` (restarts the TTL, marks it recently\n * used); creating stores with `set()`, so `ttl` applies.\n * @param factory Called with the key and this collection only when the key is missing; if it throws, nothing is stored.\n * @param ttl TTL in ms for a created entry; defaults to the collection's.\n */\n public ensure(\n key: K,\n factory: (key: K, collection: this) => V,\n ttl?: number,\n ): V {\n const existing = this.get(key);\n if (existing !== undefined || super.has(key)) return existing as V;\n const created = factory(key, this);\n this.set(key, created, ttl);\n return created;\n }\n\n /** Reads a value without restarting its TTL or promoting it. */\n public peek(key: K): V | undefined {\n if (!this.#expireIfDue(key) && super.has(key)) {\n this.#hits++;\n return super.get(key);\n }\n this.#misses++;\n return undefined;\n }\n\n /** Cumulative hit, miss, expiry and eviction counters (a copy). */\n public get stats(): CollectionStats {\n return {\n hits: this.#hits,\n misses: this.#misses,\n expired: this.#policy?.expired ?? 0,\n evicted: this.#policy?.evicted ?? 0,\n };\n }\n\n public override has(key: K): boolean {\n return !this.#expireIfDue(key) && super.has(key);\n }\n\n public override delete(key: K): boolean {\n if (this.#expireIfDue(key)) return false;\n if (this.#policy) this.#forget(key);\n return super.delete(key);\n }\n\n public override clear(): void {\n super.clear();\n const policy = this.#policy;\n if (!policy) return;\n policy.entries.clear();\n policy.oldest = policy.newest = undefined;\n policy.lruSize = 0;\n policy.heap.length = 0;\n if (policy.timer) clearTimeout(policy.timer);\n policy.timer = undefined;\n policy.armedFor = Infinity;\n }\n\n public override get size(): number {\n this.purge();\n return super.size;\n }\n\n public override entries(): MapIterator<[K, V]> {\n this.purge();\n return super.entries();\n }\n\n public override keys(): MapIterator<K> {\n this.purge();\n return super.keys();\n }\n\n public override values(): MapIterator<V> {\n this.purge();\n return super.values();\n }\n\n public override [Symbol.iterator](): MapIterator<[K, V]> {\n return this.entries();\n }\n\n public override forEach(\n callback: (value: V, key: K, map: Map<K, V>) => void,\n thisArg?: unknown,\n ): void {\n this.purge();\n super.forEach(callback, thisArg);\n }\n\n /** Milliseconds until a key lapses, or undefined if absent or never expiring. */\n public ttlRemaining(key: K): number | undefined {\n if (this.#expireIfDue(key)) return undefined;\n const entry = this.#policy?.entries.get(key);\n return entry && entry.window !== 0\n ? Math.max(0, entry.deadline - Date.now())\n : undefined;\n }\n\n /** Drops every lapsed TTL entry now. @returns How many were dropped. */\n public purge(): number {\n if (!this.#policy || this.#policy.heap.length === 0) return 0;\n const now = Date.now();\n let removed = 0;\n const dropped: [K, V][] = [];\n while (this.#nextDeadline() <= now) {\n const due = this.#heapPop()!;\n dropped.push([due.key, super.get(due.key)!]);\n this.#remove(due.key);\n removed++;\n }\n this.#policy!.expired += removed;\n this.#rearm();\n for (const [key, value] of dropped) this.#notify(key, value, \"expired\");\n return removed;\n }\n\n /** Drops `key` if its TTL has lapsed. @returns Whether it did. */\n #expireIfDue(key: K): boolean {\n const entry = this.#policy?.entries.get(key);\n if (!entry || entry.window === 0 || entry.deadline > Date.now())\n return false;\n this.#expire(entry);\n return true;\n }\n\n #expire(entry: Entry<K, V>): void {\n const value = super.get(entry.key)!;\n this.#remove(entry.key);\n this.#policy!.expired++;\n this.#notify(entry.key, value, \"expired\");\n }\n\n #remove(key: K): void {\n this.#forget(key);\n super.delete(key);\n }\n\n /** Drops a key's policy entry (recency position and TTL); its heap record goes stale. */\n #forget(key: K): void {\n const policy = this.#policy!;\n const entry = policy.entries.get(key);\n if (!entry) return;\n if (entry.inLru) this.#unlink(policy, entry);\n policy.entries.delete(key);\n }\n\n /**\n * Brings the key's policy entry in line with a `set()`: marks it newest\n * when `maxSize` is set, and gives it a deadline when `window` is defined.\n * Only a new entry, or a deadline that moves earlier, gets a heap record;\n * a later one is a number write and the record re-files itself when it\n * surfaces.\n */\n #track(key: K, value: V, window: number | undefined, isNew: boolean): void {\n const policy = this.#policy!;\n const lru = policy.maxSize !== undefined;\n let entry = isNew ? undefined : policy.entries.get(key);\n if (entry) entry.value = value;\n if (window === undefined) {\n if (entry && entry.window !== 0) {\n // It had a TTL and now has none.\n if (lru) entry = this.#replace(policy, entry, 0, 0);\n else {\n this.#forget(key);\n return;\n }\n }\n if (!entry && lru) {\n entry = this.#create(key, value, 0, 0);\n policy.entries.set(key, entry);\n }\n if (entry) this.#promote(policy, entry);\n return;\n }\n const deadline = Date.now() + window;\n if (entry && entry.window !== 0) {\n entry.deadline = deadline;\n entry.window = window;\n if (deadline < entry.filed)\n // The heap cannot move a record up, and this one is in it: file a fresh one.\n entry = this.#replace(policy, entry, window, deadline);\n else {\n if (entry.inLru) this.#promote(policy, entry);\n return;\n }\n } else if (entry) {\n entry.window = window;\n entry.deadline = entry.filed = deadline;\n } else {\n entry = this.#create(key, value, window, deadline);\n policy.entries.set(key, entry);\n }\n if (lru) this.#promote(policy, entry);\n this.#heapPush(entry);\n if (policy.heap.length > 2 * policy.entries.size + 8) this.#compact();\n // A timer already armed for an earlier deadline stays correct.\n if (policy.timer === undefined || deadline < policy.armedFor)\n this.#rearm();\n }\n\n #create(key: K, value: V, window: number, deadline: number): Entry<K, V> {\n return {\n key,\n value,\n older: undefined,\n newer: undefined,\n inLru: false,\n window,\n deadline,\n filed: deadline,\n };\n }\n\n /** Swaps in a fresh record for `old` (which goes stale), keeping its recency position. */\n #replace(\n policy: Policy<K, V>,\n old: Entry<K, V>,\n window: number,\n deadline: number,\n ): Entry<K, V> {\n const entry: Entry<K, V> = {\n ...old,\n window,\n deadline,\n filed: deadline,\n };\n if (old.inLru) {\n if (old.older) old.older.newer = entry;\n else policy.oldest = entry;\n if (old.newer) old.newer.older = entry;\n else policy.newest = entry;\n }\n policy.entries.set(old.key, entry);\n return entry;\n }\n\n /** Makes an entry the newest in recency order, linking it first if needed. */\n #promote(policy: Policy<K, V>, entry: Entry<K, V>): void {\n if (entry === policy.newest) return;\n if (entry.inLru) this.#unlink(policy, entry, true);\n else {\n entry.inLru = true;\n policy.lruSize++;\n }\n entry.older = policy.newest;\n entry.newer = undefined;\n if (policy.newest) policy.newest.newer = entry;\n else policy.oldest = entry;\n policy.newest = entry;\n }\n\n /** Takes an entry out of the recency list; `keepCount` is for a move within it. */\n #unlink(policy: Policy<K, V>, entry: Entry<K, V>, keepCount = false): void {\n if (entry.older) entry.older.newer = entry.newer;\n else policy.oldest = entry.newer;\n if (entry.newer) entry.newer.older = entry.older;\n else policy.newest = entry.older;\n entry.older = entry.newer = undefined;\n if (!keepCount) {\n entry.inLru = false;\n policy.lruSize--;\n }\n }\n\n /**\n * An empty collection of the species this one derives, so `filter()` and\n * friends on a subclass return the subclass. It is built without\n * arguments and carries no `ttl` or `maxSize`.\n */\n #derived(): this {\n const Species =\n (\n this.constructor as {\n [Symbol.species]?: new () => Collection<K, V>;\n }\n )[Symbol.species] ?? Collection;\n return new Species() as this;\n }\n\n #notify(key: K, value: V, reason: EvictionReason): void {\n const onEvict = this.#policy?.onEvict;\n if (!onEvict) return;\n try {\n onEvict(key, value, reason);\n } catch {\n // A throwing callback must not leave the collection inconsistent.\n }\n }\n\n /** Evicts least-recently-used `set()` entries past `maxSize`; O(1) per eviction. */\n #enforceCap(): void {\n const policy = this.#policy!;\n const max = policy.maxSize;\n if (max === undefined) return;\n // Lapsed entries must not push live ones out.\n if (\n policy.lruSize > max &&\n policy.heap.length > 0 &&\n policy.heap[0]!.filed <= Date.now()\n )\n this.purge();\n while (policy.lruSize > max) {\n const oldest = policy.oldest!;\n const value = policy.onEvict ? super.get(oldest.key) : undefined;\n this.#unlink(policy, oldest);\n policy.entries.delete(oldest.key);\n super.delete(oldest.key);\n policy.evicted++;\n this.#notify(oldest.key, value as V, \"evicted\");\n }\n }\n\n #nextDeadline(): number {\n const policy = this.#policy!;\n for (;;) {\n const top = policy.heap[0];\n if (top === undefined) return Infinity;\n if (policy.entries.get(top.key) !== top) {\n this.#heapPop();\n continue;\n }\n if (top.deadline === top.filed) return top.filed;\n // Extended since it was filed: re-file for its current deadline.\n this.#heapPop();\n top.filed = top.deadline;\n this.#heapPush(top);\n }\n }\n\n #compact(): void {\n const policy = this.#policy!;\n const live = policy.heap.filter(\n (entry) => policy.entries.get(entry.key) === entry,\n );\n policy.heap.length = 0;\n for (const entry of live) this.#heapPush(entry);\n }\n\n #rearm(): void {\n const p = this.#policy!;\n const deadline = this.#nextDeadline();\n if (p.timer !== undefined && deadline >= p.armedFor) return;\n if (p.timer) clearTimeout(p.timer);\n p.timer = undefined;\n p.armedFor = deadline;\n if (deadline === Infinity) return;\n p.timer = setTimeout(\n () => {\n p.timer = undefined;\n p.armedFor = Infinity;\n this.purge();\n },\n Math.max(0, deadline - Date.now()),\n );\n // Pending expiry is housekeeping, not a reason to keep the process alive.\n (p.timer as { unref?: () => void }).unref?.();\n }\n\n #heapPush(entry: Entry<K, V>): void {\n const heap = this.#policy!.heap;\n let i = heap.length;\n heap.push(entry);\n while (i > 0) {\n const parent = (i - 1) >> 1;\n const above = heap[parent]!;\n if (above.filed <= entry.filed) break;\n heap[i] = above;\n i = parent;\n }\n heap[i] = entry;\n }\n\n #heapPop(): Entry<K, V> | undefined {\n const heap = this.#policy!.heap;\n const top = heap[0];\n const last = heap.pop();\n if (top === undefined || heap.length === 0) return top;\n let i = 0;\n for (;;) {\n let child = 2 * i + 1;\n if (child >= heap.length) break;\n if (\n child + 1 < heap.length &&\n heap[child + 1]!.filed < heap[child]!.filed\n )\n child++;\n if (heap[child]!.filed >= last!.filed) break;\n heap[i] = heap[child]!;\n i = child;\n }\n heap[i] = last!;\n return top;\n }\n\n /** Returns the first stored value. */\n public first(): V | undefined {\n for (const value of this.values()) return value;\n return undefined;\n }\n /** Returns the first stored key. */\n public firstKey(): K | undefined {\n for (const key of this.keys()) return key;\n return undefined;\n }\n /** Returns the last stored value. */\n public last(): V | undefined {\n let value: V | undefined;\n for (const item of this.values()) value = item;\n return value;\n }\n /** Returns the last stored key. */\n public lastKey(): K | undefined {\n let key: K | undefined;\n for (const item of this.keys()) key = item;\n return key;\n }\n /** Finds the first value matching a predicate. */\n public find(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): V | undefined {\n for (const [key, value] of this) {\n if (predicate(value, key, this)) return value;\n }\n return undefined;\n }\n /** Finds the last value (in insertion order) matching a predicate. */\n public findLast(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): V | undefined {\n const keys = this.keyArray();\n const values = this.array();\n for (let i = values.length - 1; i >= 0; i--)\n if (predicate(values[i]!, keys[i]!, this)) return values[i];\n return undefined;\n }\n /** Finds the key of the last entry (in insertion order) matching a predicate. */\n public findLastKey(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): K | undefined {\n const keys = this.keyArray();\n const values = this.array();\n for (let i = values.length - 1; i >= 0; i--)\n if (predicate(values[i]!, keys[i]!, this)) return keys[i];\n return undefined;\n }\n /** Returns all values matching a predicate. */\n public filter(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): this {\n const result = this.#derived();\n // forEach walks the entries without allocating a [key, value] pair each.\n this.forEach((value, key) => {\n if (predicate(value, key, this)) result.set(key, value);\n });\n return result;\n }\n /** Transforms every stored value into an array. */\n public map<T>(transform: (value: V, key: K, collection: this) => T): T[] {\n const result: T[] = [];\n this.forEach((value, key) => {\n result.push(transform(value, key, this));\n });\n return result;\n }\n /** Returns whether at least one stored value satisfies a predicate. */\n public some(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): boolean {\n for (const [key, value] of this) {\n if (predicate(value, key, this)) return true;\n }\n return false;\n }\n /** Returns whether every stored value satisfies a predicate. */\n public every(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): boolean {\n for (const [key, value] of this) {\n if (!predicate(value, key, this)) return false;\n }\n return true;\n }\n /** Runs a callback for each stored value. */\n public each(\n callback: (value: V, key: K, collection: this) => unknown,\n ): this {\n this.forEach((value, key) => {\n callback(value, key, this);\n });\n return this;\n }\n /** Returns an array containing all stored values. */\n public array(): V[] {\n return [...this.values()];\n }\n /** Returns an array containing all stored keys. */\n public keyArray(): K[] {\n return [...this.keys()];\n }\n /** Returns a plain array of entries. */\n public entriesArray(): [K, V][] {\n return [...this.entries()];\n }\n /** Returns a new collection with the same entries. */\n public clone(): this {\n const copy = this.#derived();\n this.forEach((value, key) => {\n copy.set(key, value);\n });\n return copy;\n }\n /** Returns whether all supplied keys are present. */\n public hasAll(...keys: K[]): boolean {\n for (let i = 0; i < keys.length; i++) {\n if (!this.has(keys[i]!)) return false;\n }\n return true;\n }\n /** Returns whether at least one supplied key is present. */\n public hasAny(...keys: K[]): boolean {\n for (let i = 0; i < keys.length; i++) {\n if (this.has(keys[i]!)) return true;\n }\n return false;\n }\n /** Returns the first key whose value matches a predicate. */\n public findKey(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): K | undefined {\n for (const [key, value] of this) {\n if (predicate(value, key, this)) return key;\n }\n return undefined;\n }\n /** Returns whether a predicate matches at least one entry. */\n public someEntry(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): boolean {\n return this.some(predicate);\n }\n /** Removes every entry matching a predicate and returns the number removed. */\n public sweep(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): number {\n let removed = 0;\n for (const [key, value] of this) {\n if (predicate(value, key, this) && this.delete(key)) removed++;\n }\n return removed;\n }\n /** Returns the collection's first matching entry as a tuple. */\n public firstEntry(): [K, V] | undefined {\n for (const [key, value] of this) return [key, value];\n return undefined;\n }\n /** Returns the collection's last matching entry as a tuple. */\n public lastEntry(): [K, V] | undefined {\n let entry: [K, V] | undefined;\n for (const item of this) entry = [item[0], item[1]];\n return entry;\n }\n /** Returns a new Collection containing elements from both collections. */\n public union(other: ReadonlyCollection<K, V>): this {\n const result = this.clone();\n for (const [key, value] of other) result.set(key, value);\n return result;\n }\n /** Returns a new Collection containing only elements present in both collections. */\n public intersection(other: ReadonlyCollection<K, V>): this {\n const result = this.#derived();\n for (const [key, value] of this) {\n if (other.has(key)) result.set(key, value);\n }\n return result;\n }\n /** Returns a new Collection containing elements present in this collection but not the other. */\n public difference(other: ReadonlyCollection<K, V>): this {\n const result = this.#derived();\n for (const [key, value] of this) {\n if (!other.has(key)) result.set(key, value);\n }\n return result;\n }\n /** Splits the collection into two collections based on a predicate.\n * @returns `[passing, failing]` — entries satisfying the predicate, then those that don't.\n */\n public partition(\n predicate: (value: V, key: K, collection: this) => boolean,\n ): [this, this] {\n const pass = this.#derived();\n const fail = this.#derived();\n for (const [key, value] of this) {\n if (predicate(value, key, this)) pass.set(key, value);\n else fail.set(key, value);\n }\n return [pass, fail];\n }\n /** Returns the entries of both collections that are in only one of them. */\n public symmetricDifference(other: ReadonlyCollection<K, V>): this {\n const result = this.#derived();\n for (const [key, value] of this)\n if (!other.has(key)) result.set(key, value);\n for (const [key, value] of other)\n if (!this.has(key)) result.set(key, value);\n return result;\n }\n /** Returns a new Collection with this one's entries followed by each of `collections`'; a later collection wins a shared key. */\n public concat(...collections: ReadonlyCollection<K, V>[]): this {\n const result = this.clone();\n for (const collection of collections)\n for (const [key, value] of collection) result.set(key, value);\n return result;\n }\n /**\n * Combines this collection with `other`, deciding per key what to keep.\n * @param whenInSelf Called for a key only in this collection.\n * @param whenInOther Called for a key only in `other`.\n * @param whenInBoth Called for a key in both.\n * @returns A new collection of the entries the callbacks kept, in this collection's order, then `other`'s new keys.\n */\n public merge<T, R>(\n other: ReadonlyCollection<K, T>,\n whenInSelf: (value: V, key: K) => Keep<R>,\n whenInOther: (valueOther: T, key: K) => Keep<R>,\n whenInBoth: (value: V, valueOther: T, key: K) => Keep<R>,\n ): Collection<K, R> {\n const result = this.#derived() as unknown as Collection<K, R>;\n const decide = (key: K, kept: Keep<R>): void => {\n if (kept.keep) result.set(key, kept.value);\n };\n for (const [key, value] of this) {\n decide(\n key,\n other.has(key)\n ? whenInBoth(value, peekOf(other, key) as T, key)\n : whenInSelf(value, key),\n );\n }\n for (const [key, value] of other)\n if (!this.has(key)) decide(key, whenInOther(value, key));\n return result;\n }\n /** Returns whether `other` holds the same keys with `===` equal values. Neither side's TTL or recency is touched. */\n public equals(other: ReadonlyCollection<K, V> | null | undefined): boolean {\n if (!other) return false;\n if (other === (this as unknown)) return true;\n if (this.size !== other.size) return false;\n for (const [key, value] of this) {\n if (!other.has(key)) return false;\n if (value !== peekOf(other, key)) return false;\n }\n return true;\n }\n /** Returns a new collection with the same keys and each value passed through `transform`. */\n public mapValues<T>(\n transform: (value: V, key: K, collection: this) => T,\n ): Collection<K, T> {\n const result = this.#derived() as unknown as Collection<K, T>;\n for (const [key, value] of this)\n result.set(key, transform(value, key, this));\n return result;\n }\n /** Returns a new collection with the entries in reverse order. */\n public toReversed(): this {\n const result = this.#derived();\n const entries = [...this];\n for (let i = entries.length - 1; i >= 0; i--)\n result.set(entries[i]![0], entries[i]![1]);\n return result;\n }\n /** Same as {@link sorted}, under the name the standard `Array` uses. */\n public toSorted(\n comparator?: (a: V, b: V, aKey: K, bKey: K) => number,\n ): this {\n return this.sorted(comparator);\n }\n /**\n * Groups `items` by the key `keySelector` returns for each.\n * @param keySelector Called with each item and its index.\n * @returns A new collection from each key to its items, in input order.\n */\n public static override groupBy<K, V>(\n items: Iterable<V>,\n keySelector: (item: V, index: number) => K,\n ): Collection<K, V[]> {\n const groups = new Collection<K, V[]>();\n let index = 0;\n for (const item of items)\n groups.ensure(keySelector(item, index++), () => []).push(item);\n return groups;\n }\n /**\n * Builds a collection from `entries`, merging values that share a key.\n * @param combine Called with the value stored so far, the new one and the key; its result is stored.\n */\n public static combineEntries<K, V>(\n entries: Iterable<readonly [K, V]>,\n combine: (firstValue: V, secondValue: V, key: K) => V,\n ): Collection<K, V> {\n const result = new Collection<K, V>();\n for (const [key, value] of entries)\n result.set(\n key,\n result.has(key) ? combine(result.get(key)!, value, key) : value,\n );\n return result;\n }\n /** Calls `fn` with this collection and returns the collection unchanged.\n * Useful for inserting debug side-effects in a chain without breaking the flow.\n */\n public tap(fn: (collection: this) => unknown): this {\n fn(this);\n return this;\n }\n /** Maps every entry to an array of items and flattens the results. */\n public flatMap<T>(\n transform: (value: V, key: K, collection: this) => T[],\n ): T[] {\n const result: T[] = [];\n for (const [key, value] of this) {\n for (const item of transform(value, key, this)) result.push(item);\n }\n return result;\n }\n /** Reduces the collection to a single value. */\n public reduce<T>(\n fn: (accumulator: T, value: V, key: K, collection: this) => T,\n initialValue: T,\n ): T {\n let acc = initialValue;\n for (const [key, value] of this) acc = fn(acc, value, key, this);\n return acc;\n }\n /** Like {@link reduce}, but from the last entry to the first. */\n public reduceRight<T>(\n fn: (accumulator: T, value: V, key: K, collection: this) => T,\n initialValue: T,\n ): T {\n const entries = [...this];\n let acc = initialValue;\n for (let i = entries.length - 1; i >= 0; i--)\n acc = fn(acc, entries[i]![1], entries[i]![0], this);\n return acc;\n }\n /** Returns a new Collection with entries sorted by comparator (non-mutating).\n * Defaults to insertion order if no comparator is provided.\n */\n public sorted(comparator?: (a: V, b: V, aKey: K, bKey: K) => number): this {\n // Decorate with the original insertion index and use it as a tiebreaker so\n // equal elements keep their insertion order (a stable sort independent of\n // the engine's Array.prototype.sort stability guarantees).\n const decorated = [...this.entries()].map((entry, index) => ({\n entry,\n index,\n }));\n if (comparator)\n decorated.sort((x, y) => {\n const comparison = comparator(\n x.entry[1],\n y.entry[1],\n x.entry[0],\n y.entry[0],\n );\n return comparison !== 0 ? comparison : x.index - y.index;\n });\n const result = this.#derived();\n for (const { entry } of decorated) result.set(entry[0], entry[1]);\n return result;\n }\n /**\n * Returns a random value, or `undefined` if empty. With `amount`, returns\n * that many different values (all of them, shuffled, when `amount` is at\n * least the size).\n * Uses `Math.random()` (fast, non-cryptographic): do not use it for\n * security-sensitive selection such as tokens or secrets.\n * @param amount How many values to pick.\n * @throws {RangeError} If `amount` is not a non-negative integer.\n */\n public random(): V | undefined;\n public random(amount: number): V[];\n public random(amount?: number): V | V[] | undefined {\n if (amount !== undefined) return sample([...this.values()], amount);\n const size = this.size;\n if (size === 0) return undefined;\n let index = Math.floor(Math.random() * size);\n for (const value of this.values()) if (index-- === 0) return value;\n return undefined;\n }\n /**\n * Returns a random key, or `undefined` if empty. With `amount`, returns\n * that many different keys. Same randomness and errors as {@link random}.\n * @param amount How many keys to pick.\n */\n public randomKey(): K | undefined;\n public randomKey(amount: number): K[];\n public randomKey(amount?: number): K | K[] | undefined {\n if (amount !== undefined) return sample([...this.keys()], amount);\n const size = this.size;\n if (size === 0) return undefined;\n let index = Math.floor(Math.random() * size);\n for (const key of this.keys()) if (index-- === 0) return key;\n return undefined;\n }\n /** Returns the value at a given insertion-order index (supports negative indices). */\n public at(index: number): V | undefined {\n let remaining = index < 0 ? this.size + index : index;\n if (remaining < 0) return undefined;\n for (const value of this.values()) if (remaining-- === 0) return value;\n return undefined;\n }\n /** Returns the key at a given insertion-order index (supports negative indices). */\n public keyAt(index: number): K | undefined {\n let remaining = index < 0 ? this.size + index : index;\n if (remaining < 0) return undefined;\n for (const key of this.keys()) if (remaining-- === 0) return key;\n return undefined;\n }\n /** Serializes the collection to a plain `[key, value]` array. */\n public toJSON(): [K, V][] {\n return [...this.entries()];\n }\n}\n\nexport { Cache } from \"./cache.js\";\nexport type { CacheOptions } from \"./cache.js\";\n",
14
- "/** Library version and source URL used for the Discord-compliant User-Agent. */\nimport packageJson from \"../package.json\" with { type: \"json\" };\nconst LIBRARY_VERSION = packageJson.version;\nconst LIBRARY_URL = \"https://github.com/Ekretos/lunibee\";\nconst USER_AGENT = `DiscordBot (${LIBRARY_URL}, ${LIBRARY_VERSION})`;\n\nimport { MemoryRateLimitStore, type RateLimitStore } from \"./store.js\";\nimport { RESTError, abortError, redactPath, sleep } from \"./errors.js\";\nimport {\n createRouteKey,\n serializeQuery,\n type RESTQuery,\n type RouteKey,\n} from \"./route.js\";\nimport { RequestScheduler } from \"./scheduler.js\";\nimport { RateLimiter } from \"./limiter.js\";\nimport { HttpTransport, TransportError } from \"./transport.js\";\nimport { ResponseDecoder } from \"./decoder.js\";\n\n/**\n * Refuses a path that URL normalization would rewrite into another route:\n * `.` / `..` segments (also percent-encoded) and backslashes. Without this,\n * an id like `../../channels/1` sends the bot token to a route the caller\n * never asked for. The path is left out of the message: it may hold a token.\n */\nfunction assertSafePath(path: string): void {\n const end = path.search(/[?#]/);\n const pathname = end === -1 ? path : path.slice(0, end);\n if (pathname.includes(\"\\\\\"))\n throw new TypeError(\"REST paths must not contain backslashes.\");\n for (const segment of pathname.split(\"/\")) {\n let decoded: string;\n try {\n decoded = decodeURIComponent(segment);\n } catch {\n throw new TypeError(\"REST path has malformed percent-encoding.\");\n }\n if (decoded === \".\" || decoded === \"..\")\n throw new TypeError(\n \"REST paths must not contain '.' or '..' segments.\",\n );\n }\n}\n\n/** A file attachment sent as part of a multipart REST request. */\nexport interface RESTFileAttachment {\n name: string;\n data: Blob | Uint8Array | ArrayBuffer;\n contentType?: string;\n}\n\n/**\n * Options controlling an individual REST request.\n *\n * Lunibee's canonical call shape is positional (`post(path, body)`); these options\n * add the Discord.js-`@discordjs/rest`-familiar per-request knobs (`query`, `headers`,\n * `reason`, `auth`) additively without changing that positional default.\n */\nexport interface RESTRequestOptions {\n /** Abort signal used to cancel the request and any queued wait. */\n signal?: AbortSignal;\n /** Query-string appended to the path (`URLSearchParams`, a record, or a raw string). */\n query?: RESTQuery;\n /** Extra request headers merged in (library headers such as `Authorization` win). */\n headers?: Record<string, string>;\n /** Audit-log reason, sent as the `X-Audit-Log-Reason` header. */\n reason?: string;\n /** Set `false` to omit the `Authorization` header for this request. Defaults to `true`. */\n auth?: boolean;\n}\n\n/**\n * Discord.js-familiar request payload. Superset of {@link RESTRequestOptions} that also\n * carries the `body` and `files`, matching `@discordjs/rest`'s `RequestData`. It powers the\n * additive `method(path, { body })` overloads without breaking the positional signature.\n */\nexport interface RequestData extends RESTRequestOptions {\n /** Request body. Encoded as JSON unless `files` are present (then multipart). */\n body?: unknown;\n /** File attachments; presence upgrades the request to `multipart/form-data`. */\n files?: RESTFileAttachment[];\n}\n\n/** Keys recognised on a {@link RequestData} wrapper, used to disambiguate it from a raw body. */\nconst REQUEST_DATA_KEYS = new Set([\n \"body\",\n \"files\",\n \"query\",\n \"headers\",\n \"reason\",\n \"auth\",\n \"signal\",\n]);\n\n/**\n * Detects a Discord.js-style `RequestData` wrapper so verb helpers can accept both\n * `post(path, rawBody)` (Lunibee-canonical) and `post(path, { body })` (Discord.js-familiar).\n * Conservative by design: only a plain object whose every own key is a known `RequestData`\n * key counts — a raw Discord payload (which always carries entity keys like `content`/`name`)\n * never matches, so existing positional callers are unaffected.\n */\nfunction isRequestData(value: unknown): value is RequestData {\n if (value === null || typeof value !== \"object\") return false;\n if (Array.isArray(value)) return false;\n if (typeof FormData !== \"undefined\" && value instanceof FormData)\n return false;\n if (\n value instanceof Blob ||\n value instanceof Uint8Array ||\n value instanceof ArrayBuffer\n )\n return false;\n const keys = Object.keys(value);\n if (keys.length === 0) return false;\n return keys.every((key) => REQUEST_DATA_KEYS.has(key));\n}\n\n// ─── REST Hooks ───────────────────────────────────────────────────────────────\n\n/** Context passed to `RESTHooks.onRequest`. */\nexport interface RESTRequestContext {\n method: string;\n path: string;\n attempt: number;\n}\n/** Context passed to `RESTHooks.onResponse`. */\nexport interface RESTResponseContext {\n method: string;\n path: string;\n status: number;\n durationMs: number;\n}\n/** Context passed to `RESTHooks.onRateLimit`. */\nexport interface RESTRateLimitContext {\n method: string;\n path: string;\n retryAfterMs: number;\n global: boolean;\n bucket?: string;\n}\n/** Context passed to `RESTHooks.onRetry`. */\nexport interface RESTRetryContext {\n method: string;\n path: string;\n attempt: number;\n status: number;\n delayMs: number;\n}\n\n/**\n * Lightweight callback hooks for REST request lifecycle events.\n *\n * All callbacks are optional. They are dispatched in isolation from the request\n * path (deferred to a microtask, and any thrown error is swallowed), so a hook\n * cannot break or reject a request. Even so, hooks **must** be fast and\n * non-blocking: heavy synchronous work in a hook still steals event-loop time\n * from in-flight requests. Offload anything expensive to your own queue.\n */\nexport interface RESTHooks {\n /** Called just before every HTTP attempt. */\n onRequest?(ctx: RESTRequestContext): void;\n /** Called after a successful or failed response is received. */\n onResponse?(ctx: RESTResponseContext): void;\n /** Called when a rate limit is encountered and the client is about to wait. */\n onRateLimit?(ctx: RESTRateLimitContext): void;\n /** Called when a failed request is about to be retried. */\n onRetry?(ctx: RESTRetryContext): void;\n}\n\n/** Configures which REST failures may be retried. */\nexport interface RetryPolicy {\n /** Maximum retry attempts. */\n maxRetries: number;\n /** Determines whether a request may be retried. @param method HTTP method. @param status HTTP status, or zero for transport errors. @returns True when retrying is safe under the policy. */\n shouldRetry(method: string, status: number): boolean;\n /** Calculates delay before the next attempt. @param attempt Zero-based retry number. @param retryAfter Server-provided delay, when available. @returns Delay in milliseconds. */\n getDelay(attempt: number, retryAfter?: number): number;\n}\n/** Creates the default conservative REST retry policy. @param maxRetries Maximum retries. @returns Retry policy that retries rate limits and idempotent transient failures. @throws {TypeError} If maxRetries is negative or not finite. */\nexport function createRetryPolicy(maxRetries = 2): RetryPolicy {\n if (!Number.isFinite(maxRetries) || maxRetries < 0)\n throw new TypeError(\"maxRetries must be a non-negative finite number.\");\n return {\n maxRetries: Math.floor(maxRetries),\n shouldRetry(method, status) {\n const normalized = method.toUpperCase();\n const idempotent = [\"GET\", \"HEAD\", \"PUT\", \"DELETE\"].includes(\n normalized,\n );\n // Status 0 is a transport failure (DNS, reset connection, TLS):\n // no response reached Discord's handlers, so replaying an\n // idempotent request is as safe as replaying it after a 5xx.\n if (status === 0) return idempotent;\n return (\n status === 429 || (status >= 500 && status <= 599 && idempotent)\n );\n },\n getDelay(attempt, retryAfter) {\n if (retryAfter !== undefined) return Math.max(0, retryAfter * 1000);\n return Math.min(10_000, 500 * 2 ** attempt) + Math.random() * 250;\n },\n };\n}\n\n/** Bun-native REST transport with Discord bucket-aware rate limiting, retries, and cancellation. */\nexport class REST {\n readonly #retryPolicy: RetryPolicy;\n #token?: string;\n /** Rate-limit state: when a request may go out, what a response says. */\n readonly #limiter: RateLimiter;\n /** Per-bucket ordering. */\n readonly #scheduler = new RequestScheduler();\n /** One HTTP attempt, with timeout and cancellation. */\n readonly #transport: HttpTransport;\n /** Response payload and error metadata. */\n readonly #decoder = new ResponseDecoder();\n #hooks: RESTHooks = {};\n /** Whether requests on a known bucket may run concurrently. */\n readonly #concurrentBuckets: boolean;\n /** Creates a REST transport. @param options Transport configuration. @throws {TypeError} If retry configuration is invalid. */\n public constructor(\n options: {\n token?: string;\n timeout?: number;\n retries?: number;\n retryPolicy?: RetryPolicy;\n baseURL?: string;\n hooks?: RESTHooks;\n store?: RateLimitStore;\n /** Replaces the HTTP stage, e.g. with a recording transport in tests. */\n transport?: HttpTransport;\n /**\n * Lets requests on a bucket whose limit is known run concurrently, up\n * to its `remaining` allowance, instead of one at a time. Requires a\n * store with `reserve`. Requests on one bucket may then complete out\n * of order (e.g. messages to one channel). Defaults to `false`.\n */\n concurrentBuckets?: boolean;\n } = {},\n ) {\n this.#token = options.token;\n this.#retryPolicy =\n options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);\n this.#transport =\n options.transport ??\n new HttpTransport({\n baseURL: options.baseURL,\n timeout: options.timeout,\n });\n const store = options.store ?? new MemoryRateLimitStore();\n this.#concurrentBuckets =\n (options.concurrentBuckets ?? true) &&\n typeof store.reserve === \"function\";\n this.#limiter = new RateLimiter(store, {\n concurrent: this.#concurrentBuckets,\n });\n if (options.hooks) this.#hooks = options.hooks;\n }\n /** The rate-limit store backing this transport. */\n public get store(): RateLimitStore {\n return this.#limiter.store;\n }\n /** Replaces the current hook set. Pass an empty object to clear all hooks. */\n public setHooks(hooks: RESTHooks): this {\n this.#hooks = hooks;\n return this;\n }\n /** Sets the authentication token. @param token Discord bot token. @returns Nothing. @throws {TypeError} If token is empty. */\n public setToken(token: string): void {\n if (!token.trim())\n throw new TypeError(\"A Discord bot token is required.\");\n this.#token = token;\n }\n /** Executes an HTTP request with Discord bucket/global rate limits, retry handling, and AbortSignal support.\n * Accepts a JSON body, a FormData body (for file uploads), or undefined.\n * @param method HTTP method.\n * @param path API path.\n * @param body Optional JSON body, FormData for file uploads, or undefined.\n * @param options Request options: cancellation `signal` plus the Discord.js-familiar\n * `query`, `headers`, `reason`, `auth`, and `files` (which upgrade to multipart).\n * @returns Decoded response body.\n * @throws {RESTError} If Discord or transport rejects the request.\n */\n public async request<T>(\n method: string,\n path: string,\n body?: unknown,\n options: RequestData = {},\n ): Promise<T> {\n if (!method.toUpperCase())\n throw new TypeError(\"REST method is required.\");\n if (!path.startsWith(\"/\"))\n throw new TypeError(\"REST paths must start with '/'.\");\n assertSafePath(path);\n const route = createRouteKey(method, path);\n // Upgrade to multipart when file attachments are supplied via options.\n const files = options.files;\n if (files && files.length > 0 && !(body instanceof FormData))\n body = this.#fileForm(body, files);\n const query = serializeQuery(options.query);\n const requestPath = query ? `${path}${query}` : path;\n\n // Discord scopes a rate limit by (bucket hash, major parameter): two\n // channels sharing an endpoint have independent limits, while two\n // different routes can share one hash. The key is therefore resolved\n // again once the slot is acquired, so a request that queued under its\n // route-derived key joins the shared bucket's queue if one was\n // discovered while it waited.\n return this.#scheduler.runResolved(\n () => this.#limiter.resolveBucketKey(route.route, route.major),\n options.signal,\n path,\n (bucketKey, release) =>\n this.#attempts<T>(\n route,\n requestPath,\n body,\n options,\n bucketKey,\n release,\n ),\n );\n }\n /**\n * Runs the retry loop for one scheduled request: acquire limits, send,\n * record what the response says, decode, and decide whether to try again.\n */\n async #attempts<T>(\n route: RouteKey,\n requestPath: string,\n body: unknown,\n options: RequestData,\n initialBucketKey: string,\n release: () => void,\n ): Promise<T> {\n const { method, path } = route;\n let bucketKey = initialBucketKey;\n for (\n let attempt = 0;\n attempt <= this.#retryPolicy.maxRetries;\n attempt++\n ) {\n await this.#limiter.acquire(bucketKey, options.signal, path);\n // The reservation already claimed this request's unit, so once the\n // bucket's window is known the next request need not wait for our\n // response. An unknown bucket stays serial until a response\n // reveals its limit.\n if (this.#concurrentBuckets) {\n const bucket = await this.#limiter.store.getBucket(bucketKey);\n if (bucket && bucket.resetAt > Date.now()) release();\n }\n this.#emit(this.#hooks.onRequest, { method, path, attempt });\n const requestStart = Date.now();\n let response: Response;\n try {\n response = await this.#transport.send({\n method,\n path: requestPath,\n headers: this.#headers(body, options),\n body: this.#encodeBody(body),\n signal: options.signal,\n });\n } catch (error) {\n if (error instanceof RESTError) throw error;\n if (options.signal?.aborted)\n throw abortError(path, options.signal.reason);\n const failure =\n error instanceof TransportError\n ? error\n : new TransportError(\n \"Discord REST request failed\",\n false,\n error,\n );\n if (\n !failure.timedOut &&\n attempt < this.#retryPolicy.maxRetries &&\n this.#retryPolicy.shouldRetry(method, 0)\n ) {\n await sleep(\n this.#retryPolicy.getDelay(attempt),\n options.signal,\n path,\n );\n continue;\n }\n throw new RESTError(failure.message, 0, undefined, undefined, {\n method,\n path,\n cause: failure.cause ?? failure,\n kind: failure.timedOut ? \"timeout\" : \"network\",\n });\n }\n this.#emit(this.#hooks.onResponse, {\n method,\n path,\n status: response.status,\n durationMs: Date.now() - requestStart,\n });\n bucketKey = await this.#limiter.applyResponse(\n response,\n bucketKey,\n route.route,\n route.major,\n );\n const payload = await this.#decoder.read(response);\n if (response.ok)\n return (response.status === 204 ? undefined : payload) as T;\n\n const data = this.#decoder.errorData(payload);\n const retryAfter =\n response.status === 429\n ? this.#decoder.retryAfter(response, data)\n : undefined;\n if (data.global && retryAfter !== undefined)\n await this.#limiter.noteGlobalReset(\n Date.now() + retryAfter * 1000,\n );\n // Fire for ANY 429, including a global limit that arrives without a\n // Retry-After header; fall back to 1s in that case.\n if (response.status === 429)\n this.#emit(this.#hooks.onRateLimit, {\n method,\n path,\n retryAfterMs: (retryAfter ?? 1) * 1000,\n global: data.global ?? false,\n bucket: await this.#limiter.store.getBucketHash(\n route.route,\n ),\n });\n if (\n attempt < this.#retryPolicy.maxRetries &&\n this.#retryPolicy.shouldRetry(method, response.status)\n ) {\n const delay = this.#retryPolicy.getDelay(attempt, retryAfter);\n this.#emit(this.#hooks.onRetry, {\n method,\n path,\n attempt,\n status: response.status,\n delayMs: delay,\n });\n await sleep(delay, options.signal, path);\n continue;\n }\n throw new RESTError(\n data.message ??\n response.statusText ??\n `Discord REST request failed with status ${response.status}`,\n response.status,\n data.code,\n payload,\n { method, path },\n );\n }\n throw new RESTError(\n \"Discord REST request exhausted its retry attempts\",\n 0,\n undefined,\n undefined,\n { method, path },\n );\n }\n /** Builds the header set for one attempt; library headers win over caller headers. */\n #headers(body: unknown, options: RequestData): Record<string, string> {\n const isFormData =\n typeof FormData !== \"undefined\" && body instanceof FormData;\n const headers: Record<string, string> = { ...options.headers };\n // Header names are case-insensitive: drop the caller's spelling first,\n // or `authorization` and `Authorization` are joined into one value.\n const set = (name: string, value: string): void => {\n for (const key of Object.keys(headers))\n if (key.toLowerCase() === name.toLowerCase())\n delete headers[key];\n headers[name] = value;\n };\n if (options.auth !== false && this.#token)\n set(\"Authorization\", `Bot ${this.#token}`);\n set(\"User-Agent\", USER_AGENT);\n if (options.reason !== undefined)\n set(\"X-Audit-Log-Reason\", encodeURIComponent(options.reason));\n if (!isFormData) set(\"Content-Type\", \"application/json\");\n return headers;\n }\n /** Encodes a body as multipart (passed through) or JSON. */\n #encodeBody(body: unknown): BodyInit | undefined {\n if (typeof FormData !== \"undefined\" && body instanceof FormData)\n return body;\n return body === undefined ? undefined : JSON.stringify(body);\n }\n /**\n * Normalizes a verb helper's second argument into `(body, options)`.\n * Supports both `verb(path, rawBody, options?)` (Lunibee-canonical positional) and the\n * Discord.js-familiar `verb(path, { body, query, headers, reason, files })` wrapper.\n */\n #dispatch<T>(\n method: string,\n path: string,\n bodyOrOptions?: unknown,\n options?: RequestData,\n ): Promise<T> {\n if (options === undefined && isRequestData(bodyOrOptions))\n return this.request<T>(\n method,\n path,\n bodyOrOptions.body,\n bodyOrOptions,\n );\n return this.request<T>(method, path, bodyOrOptions, options);\n }\n /** Sends a GET request. @param path API path. @param options Request options (`query`, `headers`, `signal`, …). @returns Decoded response. @throws {RESTError} When the request fails. */\n public get<T>(path: string, options?: RequestData): Promise<T> {\n return this.request<T>(\"GET\", path, options?.body, options);\n }\n /** Sends a POST request. Accepts a raw body (`post(path, body)`) or a Discord.js-familiar `post(path, { body })`. @param path API path. @param body Optional JSON body, or a `RequestData` wrapper. @param options Request options. @returns Decoded response. @throws {RESTError} When the request fails. */\n public post<T>(path: string, options: RequestData): Promise<T>;\n public post<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T>;\n public post<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T> {\n return this.#dispatch<T>(\"POST\", path, body, options);\n }\n /** Sends a PATCH request. Accepts a raw body (`patch(path, body)`) or a Discord.js-familiar `patch(path, { body })`. @param path API path. @param body Optional JSON body, or a `RequestData` wrapper. @param options Request options. @returns Decoded response. @throws {RESTError} When the request fails. */\n public patch<T>(path: string, options: RequestData): Promise<T>;\n public patch<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T>;\n public patch<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T> {\n return this.#dispatch<T>(\"PATCH\", path, body, options);\n }\n /** Sends a PUT request. Accepts a raw body (`put(path, body)`) or a Discord.js-familiar `put(path, { body })`. @param path API path. @param body Optional JSON body, or a `RequestData` wrapper. @param options Request options. @returns Decoded response. @throws {RESTError} When the request fails. */\n public put<T>(path: string, options: RequestData): Promise<T>;\n public put<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T>;\n public put<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T> {\n return this.#dispatch<T>(\"PUT\", path, body, options);\n }\n /** Sends a DELETE request. @param path API path. @param options Request options (`reason`, `headers`, `signal`, …). @returns Decoded response. @throws {RESTError} When the request fails. */\n public delete<T>(path: string, options?: RequestData): Promise<T> {\n return this.request<T>(\"DELETE\", path, options?.body, options);\n }\n /**\n * Builds a multipart form with a JSON payload and file attachments.\n */\n #fileForm(payload: unknown, files: RESTFileAttachment[]): FormData {\n const form = new FormData();\n form.append(\"payload_json\", JSON.stringify(payload));\n for (let i = 0; i < files.length; i++) {\n const file = files[i]!;\n const blob =\n file.data instanceof Blob\n ? file.data\n : new Blob([file.data as ArrayBuffer], {\n type: file.contentType ?? \"application/octet-stream\",\n });\n form.append(`files[${i}]`, blob, file.name);\n }\n return form;\n }\n\n /**\n * Sends a multipart/form-data POST request for file uploads.\n * Attaches a JSON payload as `payload_json` and files as additional fields.\n * @param path API path.\n * @param payload JSON payload (e.g. message content, embeds).\n * @param files Array of file attachments.\n * @param options Cancellation options.\n * @returns Decoded response.\n * @throws {RESTError} When the request fails.\n */\n public postWithFiles<T>(\n path: string,\n payload: unknown,\n files: RESTFileAttachment[],\n options?: RESTRequestOptions,\n ): Promise<T> {\n return this.request<T>(\n \"POST\",\n path,\n this.#fileForm(payload, files),\n options,\n );\n }\n\n /**\n * Sends a multipart/form-data PATCH request for editing messages with files.\n * @param path API path.\n * @param payload JSON payload.\n * @param files Array of file attachments.\n * @param options Cancellation options.\n * @returns Decoded response.\n * @throws {RESTError} When the request fails.\n */\n public patchWithFiles<T>(\n path: string,\n payload: unknown,\n files: RESTFileAttachment[],\n options?: RESTRequestOptions,\n ): Promise<T> {\n return this.request<T>(\n \"PATCH\",\n path,\n this.#fileForm(payload, files),\n options,\n );\n }\n /** Dispatches a lifecycle hook in isolation: deferred to a microtask so it\n * cannot block the request path, with any thrown error swallowed so a faulty\n * hook cannot reject the request. @param hook Optional callback. @param ctx Event context. */\n #emit<C>(hook: ((ctx: C) => void) | undefined, ctx: C): void {\n if (!hook) return;\n const withPath = ctx as { path?: unknown };\n if (typeof withPath.path === \"string\")\n ctx = { ...ctx, path: redactPath(withPath.path) };\n Promise.resolve()\n .then(() => hook(ctx))\n .catch(() => {});\n }\n}\n\nexport { Routes } from \"./routes.js\";\nexport {\n RESTError,\n abortError,\n redactPath,\n type RESTErrorKind,\n} from \"./errors.js\";\nexport { RESTErrorCode } from \"./error-codes.js\";\nexport {\n createRouteKey,\n normalizeRoutePath,\n majorParameter,\n scopeBucket,\n serializeQuery,\n type RouteKey,\n type RESTQuery,\n} from \"./route.js\";\nexport { RequestScheduler } from \"./scheduler.js\";\nexport { RateLimiter } from \"./limiter.js\";\nexport {\n HttpTransport,\n TransportError,\n type TransportRequest,\n type FetchLike,\n} from \"./transport.js\";\nexport { ResponseDecoder, type DecodedError } from \"./decoder.js\";\nexport {\n type RateLimitStore,\n type BucketState,\n type Reservation,\n MemoryRateLimitStore,\n} from \"./store.js\";\nexport {\n RedisRateLimitStore,\n type RedisRateLimitStoreOptions,\n type MinimalRedisClient,\n} from \"./redis.js\";\n\nexport {\n WebhookClient,\n type WebhookClientOptions,\n type WebhookMessageOptions,\n} from \"./webhook.js\";\n\n/**\n * Discord.js-familiar alias for {@link RESTError}. `@discordjs/rest` throws `DiscordAPIError`\n * on a Discord-rejected request; Lunibee keeps `RESTError` as the canonical class and exports\n * this alias so Discord.js-shaped `catch (err) { if (err instanceof DiscordAPIError) … }` works.\n */\nexport { RESTError as DiscordAPIError };\n",
14
+ "/** Library version and source URL used for the Discord-compliant User-Agent. */\nimport packageJson from \"../package.json\" with { type: \"json\" };\nconst LIBRARY_VERSION = packageJson.version;\nconst LIBRARY_URL = \"https://github.com/Ekretos/lunibee\";\nconst USER_AGENT = `DiscordBot (${LIBRARY_URL}, ${LIBRARY_VERSION})`;\n\nimport { MemoryRateLimitStore, type RateLimitStore } from \"./store.js\";\nimport { RESTError, abortError, redactPath, sleep } from \"./errors.js\";\nimport {\n createRouteKey,\n serializeQuery,\n type RESTQuery,\n type RouteKey,\n} from \"./route.js\";\nimport { RequestScheduler } from \"./scheduler.js\";\nimport { RateLimiter } from \"./limiter.js\";\nimport { HttpTransport, TransportError } from \"./transport.js\";\nimport { ResponseDecoder } from \"./decoder.js\";\n\n/**\n * Refuses a path that URL normalization would rewrite into another route:\n * `.` / `..` segments (also percent-encoded) and backslashes. Without this,\n * an id like `../../channels/1` sends the bot token to a route the caller\n * never asked for. The path is left out of the message: it may hold a token.\n */\nfunction assertSafePath(path: string): void {\n const end = path.search(/[?#]/);\n const pathname = end === -1 ? path : path.slice(0, end);\n if (pathname.includes(\"\\\\\"))\n throw new TypeError(\"REST paths must not contain backslashes.\");\n for (const segment of pathname.split(\"/\")) {\n let decoded: string;\n try {\n decoded = decodeURIComponent(segment);\n } catch {\n throw new TypeError(\"REST path has malformed percent-encoding.\");\n }\n if (decoded === \".\" || decoded === \"..\")\n throw new TypeError(\n \"REST paths must not contain '.' or '..' segments.\",\n );\n }\n}\n\n/** A file attachment sent as part of a multipart REST request. */\nexport interface RESTFileAttachment {\n name: string;\n data: Blob | Uint8Array | ArrayBuffer;\n contentType?: string;\n}\n\n/**\n * Options controlling an individual REST request.\n *\n * Lunibee's canonical call shape is positional (`post(path, body)`); these options\n * add the Discord.js-`@discordjs/rest`-familiar per-request knobs (`query`, `headers`,\n * `reason`, `auth`) additively without changing that positional default.\n */\nexport interface RESTRequestOptions {\n /** Abort signal used to cancel the request and any queued wait. */\n signal?: AbortSignal;\n /** Query-string appended to the path (`URLSearchParams`, a record, or a raw string). */\n query?: RESTQuery;\n /** Extra request headers merged in (library headers such as `Authorization` win). */\n headers?: Record<string, string>;\n /** Audit-log reason, sent as the `X-Audit-Log-Reason` header. */\n reason?: string;\n /** Set `false` to omit the `Authorization` header for this request. Defaults to `true`. */\n auth?: boolean;\n}\n\n/**\n * Discord.js-familiar request payload. Superset of {@link RESTRequestOptions} that also\n * carries the `body` and `files`, matching `@discordjs/rest`'s `RequestData`. It powers the\n * additive `method(path, { body })` overloads without breaking the positional signature.\n */\nexport interface RequestData extends RESTRequestOptions {\n /** Request body. Encoded as JSON unless `files` are present (then multipart). */\n body?: unknown;\n /** File attachments; presence upgrades the request to `multipart/form-data`. */\n files?: RESTFileAttachment[];\n}\n\n/** Keys recognised on a {@link RequestData} wrapper, used to disambiguate it from a raw body. */\nconst REQUEST_DATA_KEYS = new Set([\n \"body\",\n \"files\",\n \"query\",\n \"headers\",\n \"reason\",\n \"auth\",\n \"signal\",\n]);\n\n/**\n * Detects a Discord.js-style `RequestData` wrapper so verb helpers can accept both\n * `post(path, rawBody)` (Lunibee-canonical) and `post(path, { body })` (Discord.js-familiar).\n * Conservative by design: only a plain object whose every own key is a known `RequestData`\n * key counts — a raw Discord payload (which always carries entity keys like `content`/`name`)\n * never matches, so existing positional callers are unaffected.\n */\nfunction isRequestData(value: unknown): value is RequestData {\n if (value === null || typeof value !== \"object\") return false;\n if (Array.isArray(value)) return false;\n if (typeof FormData !== \"undefined\" && value instanceof FormData)\n return false;\n if (\n value instanceof Blob ||\n value instanceof Uint8Array ||\n value instanceof ArrayBuffer\n )\n return false;\n const keys = Object.keys(value);\n if (keys.length === 0) return false;\n return keys.every((key) => REQUEST_DATA_KEYS.has(key));\n}\n\n// ─── REST Hooks ───────────────────────────────────────────────────────────────\n\n/** Context passed to `RESTHooks.onRequest`. */\nexport interface RESTRequestContext {\n method: string;\n path: string;\n attempt: number;\n}\n/** Context passed to `RESTHooks.onResponse`. */\nexport interface RESTResponseContext {\n method: string;\n path: string;\n status: number;\n durationMs: number;\n}\n/** Context passed to `RESTHooks.onRateLimit`. */\nexport interface RESTRateLimitContext {\n method: string;\n path: string;\n retryAfterMs: number;\n global: boolean;\n bucket?: string;\n}\n/** Context passed to `RESTHooks.onRetry`. */\nexport interface RESTRetryContext {\n method: string;\n path: string;\n attempt: number;\n status: number;\n delayMs: number;\n}\n\n/**\n * Lightweight callback hooks for REST request lifecycle events.\n *\n * All callbacks are optional. They are dispatched in isolation from the request\n * path (deferred to a microtask, and any thrown error is swallowed), so a hook\n * cannot break or reject a request. Even so, hooks **must** be fast and\n * non-blocking: heavy synchronous work in a hook still steals event-loop time\n * from in-flight requests. Offload anything expensive to your own queue.\n */\nexport interface RESTHooks {\n /** Called just before every HTTP attempt. */\n onRequest?(ctx: RESTRequestContext): void;\n /** Called after a successful or failed response is received. */\n onResponse?(ctx: RESTResponseContext): void;\n /** Called when a rate limit is encountered and the client is about to wait. */\n onRateLimit?(ctx: RESTRateLimitContext): void;\n /** Called when a failed request is about to be retried. */\n onRetry?(ctx: RESTRetryContext): void;\n}\n\n/** Configures which REST failures may be retried. */\nexport interface RetryPolicy {\n /** Maximum retry attempts. */\n maxRetries: number;\n /** Determines whether a request may be retried. @param method HTTP method. @param status HTTP status, or zero for transport errors. @returns True when retrying is safe under the policy. */\n shouldRetry(method: string, status: number): boolean;\n /** Calculates delay before the next attempt. @param attempt Zero-based retry number. @param retryAfter Server-provided delay, when available. @returns Delay in milliseconds. */\n getDelay(attempt: number, retryAfter?: number): number;\n}\n/** Creates the default conservative REST retry policy. @param maxRetries Maximum retries. @returns Retry policy that retries rate limits and idempotent transient failures. @throws {TypeError} If maxRetries is negative or not finite. */\nexport function createRetryPolicy(maxRetries = 2): RetryPolicy {\n if (!Number.isFinite(maxRetries) || maxRetries < 0)\n throw new TypeError(\"maxRetries must be a non-negative finite number.\");\n return {\n maxRetries: Math.floor(maxRetries),\n shouldRetry(method, status) {\n const normalized = method.toUpperCase();\n const idempotent = [\"GET\", \"HEAD\", \"PUT\", \"DELETE\"].includes(\n normalized,\n );\n // Status 0 is a transport failure (DNS, reset connection, TLS):\n // no response reached Discord's handlers, so replaying an\n // idempotent request is as safe as replaying it after a 5xx.\n if (status === 0) return idempotent;\n return (\n status === 429 || (status >= 500 && status <= 599 && idempotent)\n );\n },\n getDelay(attempt, retryAfter) {\n if (retryAfter !== undefined) return Math.max(0, retryAfter * 1000);\n return Math.min(10_000, 500 * 2 ** attempt) + Math.random() * 250;\n },\n };\n}\n\n/** Bun-native REST transport with Discord bucket-aware rate limiting, retries, and cancellation. */\nexport class REST {\n readonly #retryPolicy: RetryPolicy;\n #token?: string;\n /** Rate-limit state: when a request may go out, what a response says. */\n readonly #limiter: RateLimiter;\n /** Per-bucket ordering. */\n readonly #scheduler = new RequestScheduler();\n /** One HTTP attempt, with timeout and cancellation. */\n readonly #transport: HttpTransport;\n /** Response payload and error metadata. */\n readonly #decoder = new ResponseDecoder();\n #hooks: RESTHooks = {};\n /** Whether requests on a known bucket may run concurrently. */\n readonly #concurrentBuckets: boolean;\n /** Creates a REST transport. @param options Transport configuration. @throws {TypeError} If retry configuration is invalid. */\n public constructor(\n options: {\n token?: string;\n timeout?: number;\n retries?: number;\n retryPolicy?: RetryPolicy;\n baseURL?: string;\n /** Allow a cleartext `http://` base URL on a network you trust (a REST proxy). Default false. */\n allowInsecureBaseURL?: boolean;\n hooks?: RESTHooks;\n store?: RateLimitStore;\n /** Replaces the HTTP stage, e.g. with a recording transport in tests. */\n transport?: HttpTransport;\n /**\n * Lets requests on a bucket whose limit is known run concurrently, up\n * to its `remaining` allowance, instead of one at a time. Requires a\n * store with `reserve`. Requests on one bucket may then complete out\n * of order (e.g. messages to one channel). Defaults to `false`.\n */\n concurrentBuckets?: boolean;\n } = {},\n ) {\n this.#token = options.token;\n this.#retryPolicy =\n options.retryPolicy ?? createRetryPolicy(options.retries ?? 2);\n this.#transport =\n options.transport ??\n new HttpTransport({\n baseURL: options.baseURL,\n allowInsecureBaseURL: options.allowInsecureBaseURL,\n timeout: options.timeout,\n });\n const store = options.store ?? new MemoryRateLimitStore();\n this.#concurrentBuckets =\n (options.concurrentBuckets ?? true) &&\n typeof store.reserve === \"function\";\n this.#limiter = new RateLimiter(store, {\n concurrent: this.#concurrentBuckets,\n });\n if (options.hooks) this.#hooks = options.hooks;\n }\n /** The rate-limit store backing this transport. */\n public get store(): RateLimitStore {\n return this.#limiter.store;\n }\n /** Replaces the current hook set. Pass an empty object to clear all hooks. */\n public setHooks(hooks: RESTHooks): this {\n this.#hooks = hooks;\n return this;\n }\n /** Sets the authentication token. @param token Discord bot token. @returns Nothing. @throws {TypeError} If token is empty. */\n public setToken(token: string): void {\n if (!token.trim())\n throw new TypeError(\"A Discord bot token is required.\");\n this.#token = token;\n }\n /** Executes an HTTP request with Discord bucket/global rate limits, retry handling, and AbortSignal support.\n * Accepts a JSON body, a FormData body (for file uploads), or undefined.\n * @param method HTTP method.\n * @param path API path.\n * @param body Optional JSON body, FormData for file uploads, or undefined.\n * @param options Request options: cancellation `signal` plus the Discord.js-familiar\n * `query`, `headers`, `reason`, `auth`, and `files` (which upgrade to multipart).\n * @returns Decoded response body.\n * @throws {RESTError} If Discord or transport rejects the request.\n */\n public async request<T>(\n method: string,\n path: string,\n body?: unknown,\n options: RequestData = {},\n ): Promise<T> {\n if (!method.toUpperCase())\n throw new TypeError(\"REST method is required.\");\n if (!path.startsWith(\"/\"))\n throw new TypeError(\"REST paths must start with '/'.\");\n assertSafePath(path);\n const route = createRouteKey(method, path);\n // Upgrade to multipart when file attachments are supplied via options.\n const files = options.files;\n if (files && files.length > 0 && !(body instanceof FormData))\n body = this.#fileForm(body, files);\n const query = serializeQuery(options.query);\n const requestPath = query ? `${path}${query}` : path;\n\n // Discord scopes a rate limit by (bucket hash, major parameter): two\n // channels sharing an endpoint have independent limits, while two\n // different routes can share one hash. The key is therefore resolved\n // again once the slot is acquired, so a request that queued under its\n // route-derived key joins the shared bucket's queue if one was\n // discovered while it waited.\n return this.#scheduler.runResolved(\n () => this.#limiter.resolveBucketKey(route.route, route.major),\n options.signal,\n path,\n (bucketKey, release) =>\n this.#attempts<T>(\n route,\n requestPath,\n body,\n options,\n bucketKey,\n release,\n ),\n );\n }\n /**\n * Runs the retry loop for one scheduled request: acquire limits, send,\n * record what the response says, decode, and decide whether to try again.\n */\n async #attempts<T>(\n route: RouteKey,\n requestPath: string,\n body: unknown,\n options: RequestData,\n initialBucketKey: string,\n release: () => void,\n ): Promise<T> {\n const { method, path } = route;\n let bucketKey = initialBucketKey;\n for (\n let attempt = 0;\n attempt <= this.#retryPolicy.maxRetries;\n attempt++\n ) {\n await this.#limiter.acquire(bucketKey, options.signal, path);\n // The reservation already claimed this request's unit, so once the\n // bucket's window is known the next request need not wait for our\n // response. An unknown bucket stays serial until a response\n // reveals its limit.\n if (this.#concurrentBuckets) {\n const bucket = await this.#limiter.store.getBucket(bucketKey);\n if (bucket && bucket.resetAt > Date.now()) release();\n }\n this.#emit(this.#hooks.onRequest, { method, path, attempt });\n const requestStart = Date.now();\n let response: Response;\n try {\n response = await this.#transport.send({\n method,\n path: requestPath,\n headers: this.#headers(body, options),\n body: this.#encodeBody(body),\n signal: options.signal,\n });\n } catch (error) {\n if (error instanceof RESTError) throw error;\n if (options.signal?.aborted)\n throw abortError(path, options.signal.reason);\n const failure =\n error instanceof TransportError\n ? error\n : new TransportError(\n \"Discord REST request failed\",\n false,\n error,\n );\n if (\n !failure.timedOut &&\n attempt < this.#retryPolicy.maxRetries &&\n this.#retryPolicy.shouldRetry(method, 0)\n ) {\n await sleep(\n this.#retryPolicy.getDelay(attempt),\n options.signal,\n path,\n );\n continue;\n }\n throw new RESTError(failure.message, 0, undefined, undefined, {\n method,\n path,\n cause: failure.cause ?? failure,\n kind: failure.timedOut ? \"timeout\" : \"network\",\n });\n }\n this.#emit(this.#hooks.onResponse, {\n method,\n path,\n status: response.status,\n durationMs: Date.now() - requestStart,\n });\n bucketKey = await this.#limiter.applyResponse(\n response,\n bucketKey,\n route.route,\n route.major,\n );\n const payload = await this.#decoder.read(response);\n if (response.ok)\n return (response.status === 204 ? undefined : payload) as T;\n\n const data = this.#decoder.errorData(payload);\n const retryAfter =\n response.status === 429\n ? this.#decoder.retryAfter(response, data)\n : undefined;\n if (data.global && retryAfter !== undefined)\n await this.#limiter.noteGlobalReset(\n Date.now() + retryAfter * 1000,\n );\n // Fire for ANY 429, including a global limit that arrives without a\n // Retry-After header; fall back to 1s in that case.\n if (response.status === 429)\n this.#emit(this.#hooks.onRateLimit, {\n method,\n path,\n retryAfterMs: (retryAfter ?? 1) * 1000,\n global: data.global ?? false,\n bucket: await this.#limiter.store.getBucketHash(\n route.route,\n ),\n });\n if (\n attempt < this.#retryPolicy.maxRetries &&\n this.#retryPolicy.shouldRetry(method, response.status)\n ) {\n const delay = this.#retryPolicy.getDelay(attempt, retryAfter);\n this.#emit(this.#hooks.onRetry, {\n method,\n path,\n attempt,\n status: response.status,\n delayMs: delay,\n });\n await sleep(delay, options.signal, path);\n continue;\n }\n throw new RESTError(\n data.message ??\n response.statusText ??\n `Discord REST request failed with status ${response.status}`,\n response.status,\n data.code,\n payload,\n { method, path },\n );\n }\n throw new RESTError(\n \"Discord REST request exhausted its retry attempts\",\n 0,\n undefined,\n undefined,\n { method, path },\n );\n }\n /** Builds the header set for one attempt; library headers win over caller headers. */\n #headers(body: unknown, options: RequestData): Record<string, string> {\n const isFormData =\n typeof FormData !== \"undefined\" && body instanceof FormData;\n const headers: Record<string, string> = { ...options.headers };\n // Header names are case-insensitive: drop the caller's spelling first,\n // or `authorization` and `Authorization` are joined into one value.\n const set = (name: string, value: string): void => {\n for (const key of Object.keys(headers))\n if (key.toLowerCase() === name.toLowerCase())\n delete headers[key];\n headers[name] = value;\n };\n if (options.auth !== false && this.#token)\n set(\"Authorization\", `Bot ${this.#token}`);\n set(\"User-Agent\", USER_AGENT);\n if (options.reason !== undefined)\n set(\"X-Audit-Log-Reason\", encodeURIComponent(options.reason));\n if (!isFormData) set(\"Content-Type\", \"application/json\");\n return headers;\n }\n /** Encodes a body as multipart (passed through) or JSON. */\n #encodeBody(body: unknown): BodyInit | undefined {\n if (typeof FormData !== \"undefined\" && body instanceof FormData)\n return body;\n return body === undefined ? undefined : JSON.stringify(body);\n }\n /**\n * Normalizes a verb helper's second argument into `(body, options)`.\n * Supports both `verb(path, rawBody, options?)` (Lunibee-canonical positional) and the\n * Discord.js-familiar `verb(path, { body, query, headers, reason, files })` wrapper.\n */\n #dispatch<T>(\n method: string,\n path: string,\n bodyOrOptions?: unknown,\n options?: RequestData,\n ): Promise<T> {\n if (options === undefined && isRequestData(bodyOrOptions))\n return this.request<T>(\n method,\n path,\n bodyOrOptions.body,\n bodyOrOptions,\n );\n return this.request<T>(method, path, bodyOrOptions, options);\n }\n /** Sends a GET request. @param path API path. @param options Request options (`query`, `headers`, `signal`, …). @returns Decoded response. @throws {RESTError} When the request fails. */\n public get<T>(path: string, options?: RequestData): Promise<T> {\n return this.request<T>(\"GET\", path, options?.body, options);\n }\n /** Sends a POST request. Accepts a raw body (`post(path, body)`) or a Discord.js-familiar `post(path, { body })`. @param path API path. @param body Optional JSON body, or a `RequestData` wrapper. @param options Request options. @returns Decoded response. @throws {RESTError} When the request fails. */\n public post<T>(path: string, options: RequestData): Promise<T>;\n public post<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T>;\n public post<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T> {\n return this.#dispatch<T>(\"POST\", path, body, options);\n }\n /** Sends a PATCH request. Accepts a raw body (`patch(path, body)`) or a Discord.js-familiar `patch(path, { body })`. @param path API path. @param body Optional JSON body, or a `RequestData` wrapper. @param options Request options. @returns Decoded response. @throws {RESTError} When the request fails. */\n public patch<T>(path: string, options: RequestData): Promise<T>;\n public patch<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T>;\n public patch<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T> {\n return this.#dispatch<T>(\"PATCH\", path, body, options);\n }\n /** Sends a PUT request. Accepts a raw body (`put(path, body)`) or a Discord.js-familiar `put(path, { body })`. @param path API path. @param body Optional JSON body, or a `RequestData` wrapper. @param options Request options. @returns Decoded response. @throws {RESTError} When the request fails. */\n public put<T>(path: string, options: RequestData): Promise<T>;\n public put<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T>;\n public put<T>(\n path: string,\n body?: unknown,\n options?: RequestData,\n ): Promise<T> {\n return this.#dispatch<T>(\"PUT\", path, body, options);\n }\n /** Sends a DELETE request. @param path API path. @param options Request options (`reason`, `headers`, `signal`, …). @returns Decoded response. @throws {RESTError} When the request fails. */\n public delete<T>(path: string, options?: RequestData): Promise<T> {\n return this.request<T>(\"DELETE\", path, options?.body, options);\n }\n /**\n * Builds a multipart form with a JSON payload and file attachments.\n */\n #fileForm(payload: unknown, files: RESTFileAttachment[]): FormData {\n const form = new FormData();\n form.append(\"payload_json\", JSON.stringify(payload));\n for (let i = 0; i < files.length; i++) {\n const file = files[i]!;\n const blob =\n file.data instanceof Blob\n ? file.data\n : new Blob([file.data as ArrayBuffer], {\n type: file.contentType ?? \"application/octet-stream\",\n });\n form.append(`files[${i}]`, blob, file.name);\n }\n return form;\n }\n\n /**\n * Sends a multipart/form-data POST request for file uploads.\n * Attaches a JSON payload as `payload_json` and files as additional fields.\n * @param path API path.\n * @param payload JSON payload (e.g. message content, embeds).\n * @param files Array of file attachments.\n * @param options Cancellation options.\n * @returns Decoded response.\n * @throws {RESTError} When the request fails.\n */\n public postWithFiles<T>(\n path: string,\n payload: unknown,\n files: RESTFileAttachment[],\n options?: RESTRequestOptions,\n ): Promise<T> {\n return this.request<T>(\n \"POST\",\n path,\n this.#fileForm(payload, files),\n options,\n );\n }\n\n /**\n * Sends a multipart/form-data PATCH request for editing messages with files.\n * @param path API path.\n * @param payload JSON payload.\n * @param files Array of file attachments.\n * @param options Cancellation options.\n * @returns Decoded response.\n * @throws {RESTError} When the request fails.\n */\n public patchWithFiles<T>(\n path: string,\n payload: unknown,\n files: RESTFileAttachment[],\n options?: RESTRequestOptions,\n ): Promise<T> {\n return this.request<T>(\n \"PATCH\",\n path,\n this.#fileForm(payload, files),\n options,\n );\n }\n /** Dispatches a lifecycle hook in isolation: deferred to a microtask so it\n * cannot block the request path, with any thrown error swallowed so a faulty\n * hook cannot reject the request. @param hook Optional callback. @param ctx Event context. */\n #emit<C>(hook: ((ctx: C) => void) | undefined, ctx: C): void {\n if (!hook) return;\n const withPath = ctx as { path?: unknown };\n if (typeof withPath.path === \"string\")\n ctx = { ...ctx, path: redactPath(withPath.path) };\n Promise.resolve()\n .then(() => hook(ctx))\n .catch(() => {});\n }\n}\n\nexport { Routes } from \"./routes.js\";\nexport {\n RESTError,\n abortError,\n redactPath,\n type RESTErrorKind,\n} from \"./errors.js\";\nexport { RESTErrorCode } from \"./error-codes.js\";\nexport {\n createRouteKey,\n normalizeRoutePath,\n majorParameter,\n scopeBucket,\n serializeQuery,\n type RouteKey,\n type RESTQuery,\n} from \"./route.js\";\nexport { RequestScheduler } from \"./scheduler.js\";\nexport { RateLimiter } from \"./limiter.js\";\nexport {\n HttpTransport,\n TransportError,\n type TransportRequest,\n type FetchLike,\n} from \"./transport.js\";\nexport { ResponseDecoder, type DecodedError } from \"./decoder.js\";\nexport {\n type RateLimitStore,\n type BucketState,\n type Reservation,\n MemoryRateLimitStore,\n} from \"./store.js\";\nexport {\n RedisRateLimitStore,\n type RedisRateLimitStoreOptions,\n type MinimalRedisClient,\n} from \"./redis.js\";\n\nexport {\n WebhookClient,\n type WebhookClientOptions,\n type WebhookMessageOptions,\n} from \"./webhook.js\";\n\n/**\n * Discord.js-familiar alias for {@link RESTError}. `@discordjs/rest` throws `DiscordAPIError`\n * on a Discord-rejected request; Lunibee keeps `RESTError` as the canonical class and exports\n * this alias so Discord.js-shaped `catch (err) { if (err instanceof DiscordAPIError) … }` works.\n */\nexport { RESTError as DiscordAPIError };\n",
15
15
  "import { Manager, ResourceManager, splitReason } from \"./base.js\";\nexport { Manager, ResourceManager } from \"./base.js\";\n/** Discord.js-familiar alias for {@link ResourceManager}. */\nexport { ResourceManager as CachedManager } from \"./base.js\";\nimport { REST, Routes } from \"@lunibee/rest\";\nimport type {\n AllowedMentions,\n APIForumTag,\n ForumTagInput,\n MessageCreateOptions,\n MessageEditOptions,\n} from \"@lunibee/types\";\nimport {\n Channel,\n createChannel,\n Message,\n User,\n type ResourceContext,\n} from \"@lunibee/structures\";\nimport {\n MessageManager,\n toRequest,\n type MessageCacheOptions,\n} from \"./message.js\";\nimport { ThreadManager } from \"./thread.js\";\n\nexport { UserManager } from \"./user.js\";\nexport { GuildManager } from \"./guild.js\";\nexport type { AuditLogEntry, AuditLogResponse } from \"./guild.js\";\nexport { AuditLogEvent } from \"./guild.js\";\n\nexport type { MessageCreateOptions, MessageEditOptions } from \"./message.js\";\n/** Discord only bulk-deletes messages younger than two weeks. */\nconst BULK_DELETE_MAX_AGE_MS = 14 * 24 * 60 * 60_000;\n/** Creation time (ms) encoded in a snowflake. */\nfunction snowflakeTime(id: string): number {\n return Number((BigInt(id) >> 22n) + 1_420_070_400_000n);\n}\nexport interface MessageFetchOptions {\n cache?: boolean;\n}\nexport interface MessageQueryOptions {\n before?: string;\n after?: string;\n around?: string;\n limit?: number;\n}\nexport interface MessageThreadOptions {\n name: string;\n autoArchiveDuration?: 60 | 1440 | 4320 | 10080;\n rateLimitPerUser?: number;\n}\nexport interface ReactionFetchOptions {\n limit?: number;\n after?: string;\n}\n\nexport interface ChannelCreateOptions extends Record<string, unknown> {\n /** Audit-log reason. */\n reason?: string;\n name: string;\n type: number;\n guild_id?: string;\n parent_id?: string | null;\n}\nexport type ChannelEditOptions = Record<string, unknown> & {\n /** Audit-log reason. */\n reason?: string;\n};\n\nimport { PermissionOverwriteManager } from \"./guild-resources.js\";\n\nexport class ChannelManager extends Manager<string, Channel> {\n readonly #rest: REST;\n readonly #context: ResourceContext;\n readonly #messageManagers = new Map<string, MessageManager>();\n readonly #messageCache?: MessageCacheOptions;\n readonly #allowedMentions?: AllowedMentions;\n /**\n * @param options.messageCache Enables a bounded per-channel message cache; messages are not cached by default.\n * @param options.allowedMentions Default `allowed_mentions` for sends and edits without their own.\n */\n public constructor(\n rest: REST,\n options: {\n messageCache?: MessageCacheOptions;\n allowedMentions?: AllowedMentions;\n } = {},\n ) {\n super();\n this.#rest = rest;\n this.#messageCache = options.messageCache;\n this.#allowedMentions = options.allowedMentions;\n this.#context = {\n sendMessage: (channelId, options) => this.send(channelId, options),\n editMessage: (channelId, messageId, options) =>\n this.editMessage(channelId, messageId, options),\n deleteMessage: (channelId, messageId, reason) =>\n this.deleteMessage(channelId, messageId, reason),\n crosspostMessage: (channelId, messageId) =>\n this.crosspostMessage(channelId, messageId),\n editChannel: (channelId, options) => this.edit(channelId, options),\n deleteChannel: (channelId, reason) =>\n this.remove(channelId, reason),\n addReaction: (channelId, messageId, emoji) =>\n this.addReaction(channelId, messageId, emoji),\n removeOwnReaction: (channelId, messageId, emoji) =>\n this.removeOwnReaction(channelId, messageId, emoji),\n removeReaction: (channelId, messageId, emoji, userId) =>\n this.removeReaction(channelId, messageId, emoji, userId),\n removeAllReactions: (channelId, messageId) =>\n this.removeAllReactions(channelId, messageId),\n pinMessage: (channelId, messageId, reason) =>\n this.pinMessage(channelId, messageId, reason),\n unpinMessage: (channelId, messageId, reason) =>\n this.unpinMessage(channelId, messageId, reason),\n };\n }\n public messages(channelId: string): MessageManager {\n let manager = this.#messageManagers.get(channelId);\n if (!manager) {\n manager = new MessageManager(\n this.#rest,\n this.#context,\n channelId,\n this.#messageCache,\n this.#allowedMentions,\n );\n this.#messageManagers.set(channelId, manager);\n }\n return manager;\n }\n /** Permission overwrite manager for a channel. */\n public permissionOverwrites(channelId: string): PermissionOverwriteManager {\n return new PermissionOverwriteManager(\n this.#rest,\n channelId,\n () => this.get(channelId)?.permissionOverwrites,\n );\n }\n public threads(channelId: string): ThreadManager {\n return new ThreadManager(this.#rest, this.#context, channelId);\n }\n /** Fetches a channel; concurrent fetches share a request and a stale result never overwrites newer state. */\n public fetch(channelId: string): Promise<Channel> {\n return this.fetchOnce(\n channelId,\n async () =>\n createChannel(\n await this.#rest.get<\n ConstructorParameters<typeof Channel>[0]\n >(Routes.channel(channelId)),\n this.#context,\n ),\n (channel) => this.#merge(channel),\n );\n }\n public async resolve(channelId: string): Promise<Channel> {\n return this.get(channelId) ?? this.fetch(channelId);\n }\n public upsert(data: ConstructorParameters<typeof Channel>[0]): Channel {\n return this.#merge(createChannel(data, this.#context));\n }\n /** Merges into the cached instance; a type change (e.g. text -> announcement) needs the new subclass. */\n #merge(channel: Channel): Channel {\n const existing = this.get(channel.id);\n if (existing && existing.type === channel.type) {\n Object.assign(existing, channel);\n return existing;\n }\n this.set(channel.id, channel);\n return channel;\n }\n public update(channel: Channel): this {\n return this.set(channel.id, channel);\n }\n /** Removes every cached channel that belongs to a guild. @returns Number removed. */\n public deleteGuildChannels(guildId: string): number {\n let removed = 0;\n for (const channel of this.values())\n if (channel.guildId === guildId && this.delete(channel.id))\n removed++;\n return removed;\n }\n /** Ends a poll now; returns the updated message. */\n public async endPoll(\n channelId: string,\n messageId: string,\n ): Promise<Message> {\n return this.messages(channelId).upsert(\n await this.#rest.post<ConstructorParameters<typeof Message>[0]>(\n Routes.pollExpire(channelId, messageId),\n ),\n );\n }\n /** Fetches users who voted for one poll answer. @param options.limit 1-100 (default 25). */\n public async fetchPollVoters(\n channelId: string,\n messageId: string,\n answerId: number,\n options: { after?: string; limit?: number } = {},\n ): Promise<User[]> {\n if (\n options.limit !== undefined &&\n (!Number.isInteger(options.limit) ||\n options.limit < 1 ||\n options.limit > 100)\n )\n throw new RangeError(\"Poll voter limit must be 1-100.\");\n const params = new URLSearchParams();\n if (options.after) params.set(\"after\", options.after);\n if (options.limit !== undefined)\n params.set(\"limit\", String(options.limit));\n const suffix = params.toString();\n const { users } = await this.#rest.get<{\n users: ConstructorParameters<typeof User>[0][];\n }>(\n `${Routes.pollAnswerVoters(channelId, messageId, answerId)}${suffix ? `?${suffix}` : \"\"}`,\n );\n return users.map((user) => new User(user));\n }\n /** Plays a soundboard sound in a voice channel the bot is connected to. */\n public async sendSoundboardSound(\n channelId: string,\n options: { soundId: string; sourceGuildId?: string },\n ): Promise<void> {\n await this.#rest.post(Routes.sendSoundboardSound(channelId), {\n sound_id: options.soundId,\n source_guild_id: options.sourceGuildId,\n });\n }\n /** Returns the message manager for a channel only if one already exists. */\n public cachedMessages(channelId: string): MessageManager | undefined {\n return this.#messageManagers.get(channelId);\n }\n public async create(\n guildId: string,\n options: ChannelCreateOptions,\n ): Promise<Channel> {\n const [payload, reason] = splitReason(options);\n return this.upsert(\n await this.#rest.post<ConstructorParameters<typeof Channel>[0]>(\n Routes.guildChannels(guildId),\n payload,\n { reason },\n ),\n );\n }\n public async edit(\n channelId: string,\n options: ChannelEditOptions,\n ): Promise<Channel> {\n const [payload, reason] = splitReason(options);\n return this.upsert(\n await this.#rest.patch<ConstructorParameters<typeof Channel>[0]>(\n Routes.channel(channelId),\n payload,\n { reason },\n ),\n );\n }\n\n /** Deletes a channel (or closes a DM). @param reason Audit-log reason. */\n public async remove(channelId: string, reason?: string): Promise<void> {\n await this.#rest.delete(Routes.channel(channelId), { reason });\n this.delete(channelId);\n }\n /** Evicts a channel, dropping its per-channel message manager with it.\n * Overrides {@link Manager.delete} so cache eviction driven by a Gateway\n * `CHANNEL_DELETE`/`THREAD_DELETE` — which calls `delete` rather than\n * {@link remove} — releases the message manager and its cached\n * messages too, instead of retaining them for the client's lifetime. */\n public override delete(channelId: string): boolean {\n this.#messageManagers.delete(channelId);\n return super.delete(channelId);\n }\n public send(\n channelId: string,\n options: MessageCreateOptions,\n ): Promise<Message> {\n return this.messages(channelId).send(options);\n }\n public async fetchMessage(\n channelId: string,\n messageId: string,\n options: MessageFetchOptions = {},\n ): Promise<Message> {\n void options;\n return this.messages(channelId).fetch(messageId);\n }\n public fetchMessages(\n channelId: string,\n query: MessageQueryOptions | Iterable<string>,\n ): Promise<Message[]> {\n if (isMessageQuery(query)) {\n const params = new URLSearchParams();\n if (query.before) params.set(\"before\", query.before);\n if (query.after) params.set(\"after\", query.after);\n if (query.around) params.set(\"around\", query.around);\n if (query.limit !== undefined)\n params.set(\"limit\", String(query.limit));\n const suffix = params.toString();\n return this.#rest\n .get<ConstructorParameters<typeof Message>[0][]>(\n `${Routes.channelMessages(channelId)}${suffix ? `?${suffix}` : \"\"}`,\n )\n .then((data) =>\n data.map((item) => this.messages(channelId).upsert(item)),\n );\n }\n return this.messages(channelId).fetchMany(query);\n }\n public upsertMessage(\n data: ConstructorParameters<typeof Message>[0],\n ): Message {\n return this.messages(data.channel_id).upsert(data);\n }\n public async editMessage(\n channelId: string,\n messageId: string,\n options: MessageEditOptions,\n ): Promise<Message> {\n return this.messages(channelId).upsert(\n await this.#rest.patch<ConstructorParameters<typeof Message>[0]>(\n Routes.message(channelId, messageId),\n toRequest(options, this.#allowedMentions),\n ),\n );\n }\n /** Deletes a message. @param reason Audit-log reason (shown when deleting someone else's message). */\n public async deleteMessage(\n channelId: string,\n messageId: string,\n reason?: string,\n ): Promise<void> {\n await this.#rest.delete(Routes.message(channelId, messageId), {\n reason,\n });\n this.messages(channelId).delete(messageId);\n }\n public deleteCachedMessage(channelId: string, messageId: string): boolean {\n return this.messages(channelId).delete(messageId);\n }\n public async crosspostMessage(\n channelId: string,\n messageId: string,\n ): Promise<Message> {\n return this.messages(channelId).upsert(\n await this.#rest.post<ConstructorParameters<typeof Message>[0]>(\n Routes.crosspostMessage(channelId, messageId),\n ),\n );\n }\n /** Bulk-deletes messages in a channel. Duplicate IDs are ignored and a\n * single ID falls back to a normal delete.\n * @throws {RangeError} If no IDs or more than 100 are provided, or any\n * message is older than 14 days (Discord refuses to bulk-delete those). */\n public async bulkDeleteMessages(\n channelId: string,\n messageIds: Iterable<string>,\n reason?: string,\n ): Promise<void> {\n const ids = [...new Set(messageIds)];\n if (ids.length < 1 || ids.length > 100)\n throw new RangeError(\n \"bulkDelete requires between 1 and 100 message IDs.\",\n );\n if (ids.length === 1)\n return this.deleteMessage(channelId, ids[0]!, reason);\n const cutoff = Date.now() - BULK_DELETE_MAX_AGE_MS;\n const tooOld = ids.filter((id) => snowflakeTime(id) < cutoff);\n if (tooOld.length)\n throw new RangeError(\n `bulkDelete cannot delete messages older than 14 days: ${tooOld.join(\", \")}`,\n );\n await this.#rest.post(\n Routes.channelBulkDelete(channelId),\n { messages: ids },\n { reason },\n );\n for (const id of ids) this.messages(channelId).delete(id);\n }\n /**\n * Iterates a channel's history newest-first, 100 messages per request,\n * starting before `before` (or the latest message) and stopping after\n * `limit` messages or at the start of the channel.\n */\n public async *iterateMessages(\n channelId: string,\n options: { before?: string; limit?: number } = {},\n ): AsyncGenerator<Message, void, undefined> {\n let before = options.before;\n let remaining = options.limit ?? Infinity;\n while (remaining > 0) {\n const page = await this.fetchMessages(channelId, {\n before,\n limit: Math.min(100, remaining),\n });\n for (const message of page) yield message;\n remaining -= page.length;\n if (page.length < 100) return;\n before = page.at(-1)!.id;\n }\n }\n public async addReaction(\n channelId: string,\n messageId: string,\n emoji: string,\n ): Promise<void> {\n // Discord's create-reaction endpoint is\n // PUT .../reactions/{emoji}/@me — the bare .../reactions/{emoji} path\n // is not a valid target for PUT.\n await this.#rest.put(\n Routes.messageReactionMe(channelId, messageId, emoji),\n );\n }\n public async fetchReactions(\n channelId: string,\n messageId: string,\n emoji: string,\n options: ReactionFetchOptions = {},\n ): Promise<User[]> {\n const params = new URLSearchParams();\n if (options.limit !== undefined)\n params.set(\"limit\", String(options.limit));\n if (options.after) params.set(\"after\", options.after);\n const suffix = params.toString();\n const data = await this.#rest.get<\n ConstructorParameters<typeof User>[0][]\n >(\n `${Routes.messageReactions(channelId, messageId, emoji)}${suffix ? `?${suffix}` : \"\"}`,\n );\n return data.map((user) => new User(user));\n }\n public async removeOwnReaction(\n channelId: string,\n messageId: string,\n emoji: string,\n ): Promise<void> {\n await this.#rest.delete(\n Routes.messageReactionMe(channelId, messageId, emoji),\n );\n }\n public async removeReaction(\n channelId: string,\n messageId: string,\n emoji: string,\n userId: string,\n ): Promise<void> {\n await this.#rest.delete(\n Routes.messageReactionUser(channelId, messageId, emoji, userId),\n );\n }\n public async removeAllReactions(\n channelId: string,\n messageId: string,\n ): Promise<void> {\n await this.#rest.delete(\n Routes.messageReactionsAll(channelId, messageId),\n );\n }\n /** Fetches the newest pinned messages (one page, up to 50, like the endpoint it replaces). */\n public async fetchPinnedMessages(channelId: string): Promise<Message[]> {\n const data = await this.#rest.get<{\n items: { message: ConstructorParameters<typeof Message>[0] }[];\n }>(Routes.channelMessagesPins(channelId));\n return data.items.map((item) =>\n this.messages(channelId).upsert(item.message),\n );\n }\n /** Fetches pinned messages a page at a time, newest first (`limit` 1-50, `before` an ISO timestamp from the previous page). */\n public async fetchPins(\n channelId: string,\n options: { before?: string; limit?: number } = {},\n ): Promise<{ messages: Message[]; hasMore: boolean }> {\n const params = new URLSearchParams();\n if (options.before) params.set(\"before\", options.before);\n if (options.limit !== undefined)\n params.set(\n \"limit\",\n String(Math.min(50, Math.max(1, options.limit))),\n );\n const query = params.toString();\n const data = await this.#rest.get<{\n items: {\n pinned_at: string;\n message: ConstructorParameters<typeof Message>[0];\n }[];\n has_more: boolean;\n }>(\n `${Routes.channelMessagesPins(channelId)}${query ? `?${query}` : \"\"}`,\n );\n return {\n messages: data.items.map((item) =>\n this.messages(channelId).upsert(item.message),\n ),\n hasMore: data.has_more,\n };\n }\n /** Shows \"the bot is typing…\" for about ten seconds, or until it sends a message. */\n public async triggerTyping(channelId: string): Promise<void> {\n await this.#rest.post(Routes.channelTyping(channelId));\n }\n /** Adds a tag to a forum or media channel and returns the channel's tags. @param reason Audit-log reason. */\n public async createForumTag(\n channelId: string,\n tag: ForumTagInput,\n reason?: string,\n ): Promise<APIForumTag[]> {\n const tags = await this.#forumTags(channelId);\n return this.#saveForumTags(channelId, [...tags, tag], reason);\n }\n /** Changes a forum tag and returns the channel's tags. */\n public async editForumTag(\n channelId: string,\n tagId: string,\n changes: Partial<ForumTagInput>,\n reason?: string,\n ): Promise<APIForumTag[]> {\n const tags = await this.#forumTags(channelId);\n if (!tags.some((tag) => tag.id === tagId))\n throw new Error(`Channel ${channelId} has no tag ${tagId}.`);\n return this.#saveForumTags(\n channelId,\n tags.map((tag) =>\n tag.id === tagId ? { ...tag, ...changes } : tag,\n ),\n reason,\n );\n }\n /** Removes a forum tag and returns the channel's remaining tags. */\n public async removeForumTag(\n channelId: string,\n tagId: string,\n reason?: string,\n ): Promise<APIForumTag[]> {\n const tags = await this.#forumTags(channelId);\n if (!tags.some((tag) => tag.id === tagId))\n throw new Error(`Channel ${channelId} has no tag ${tagId}.`);\n return this.#saveForumTags(\n channelId,\n tags.filter((tag) => tag.id !== tagId),\n reason,\n );\n }\n /** The channel's current tags, read fresh so edits never clobber newer ones. */\n async #forumTags(channelId: string): Promise<APIForumTag[]> {\n const channel = await this.#rest.get<{\n available_tags?: APIForumTag[];\n }>(Routes.channel(channelId));\n return channel.available_tags ?? [];\n }\n /** Discord replaces the whole list; tags that keep their `id` are kept. */\n async #saveForumTags(\n channelId: string,\n tags: Array<ForumTagInput & { id?: string }>,\n reason?: string,\n ): Promise<APIForumTag[]> {\n const updated = await this.#rest.patch<{\n available_tags?: APIForumTag[];\n }>(Routes.channel(channelId), { available_tags: tags }, { reason });\n return updated.available_tags ?? [];\n }\n /** Pins a message. @param reason Audit-log reason. */\n public async pinMessage(\n channelId: string,\n messageId: string,\n reason?: string,\n ): Promise<void> {\n await this.#rest.put(\n Routes.channelMessagesPin(channelId, messageId),\n undefined,\n { reason },\n );\n }\n /** Unpins a message. @param reason Audit-log reason. */\n public async unpinMessage(\n channelId: string,\n messageId: string,\n reason?: string,\n ): Promise<void> {\n await this.#rest.delete(\n Routes.channelMessagesPin(channelId, messageId),\n { reason },\n );\n }\n /** Creates a webhook in a channel. @param options Name, optional avatar data URI, and audit-log `reason`. */\n public async createWebhook(\n channelId: string,\n options: { name: string; avatar?: string | null; reason?: string },\n ): Promise<import(\"@lunibee/types\").APIWebhook> {\n const [payload, reason] = splitReason(options);\n return this.#rest.post(Routes.channelWebhooks(channelId), payload, {\n reason,\n });\n }\n public createThreadFromMessage(\n channelId: string,\n messageId: string,\n options: MessageThreadOptions,\n ): Promise<Channel> {\n return this.threads(channelId).createFromMessage(messageId, options);\n }\n /**\n * Fetches a page of messages with cursor metadata.\n * @param channelId Channel to fetch from.\n * @param options Page options — `before`, `after`, `around`, and `limit` (max 100).\n * @returns `{ messages, before, after, hasMore }` — use `before` or `after` as cursors for the next page.\n */\n public async fetchPage(\n channelId: string,\n options: MessageQueryOptions = {},\n ): Promise<{\n messages: Message[];\n before?: string;\n after?: string;\n hasMore: boolean;\n }> {\n const limit = Math.min(100, Math.max(1, options.limit ?? 50));\n const messages = await this.fetchMessages(channelId, {\n ...options,\n limit,\n });\n return {\n messages,\n before: messages[0]?.id,\n after: messages[messages.length - 1]?.id,\n hasMore: messages.length === limit,\n };\n }\n public override clear(): void {\n this.#messageManagers.clear();\n super.clear();\n }\n}\n\nfunction isMessageQuery(\n value: MessageQueryOptions | Iterable<string>,\n): value is MessageQueryOptions {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !(Symbol.iterator in value)\n );\n}\nexport type CreateMessageOptions = MessageCreateOptions;\nexport {\n MessageManager,\n toRequest,\n type MessageCacheOptions,\n type MessageFile,\n} from \"./message.js\";\nexport { ThreadManager } from \"./thread.js\";\nexport {\n RoleManager,\n type RoleCreateOptions,\n type RoleEditOptions,\n} from \"./role.js\";\nexport {\n GuildMemberManager,\n type MemberEditOptions,\n type BanOptions,\n} from \"./member.js\";\nexport { ApplicationCommandManager } from \"./application.js\";\nexport {\n GuildBanManager,\n GuildScheduledEventManager,\n StageInstanceManager,\n PermissionOverwriteManager,\n type APIBan,\n type GuildBanCreateOptions,\n type GuildScheduledEventOptions,\n type StageInstanceCreateOptions,\n type PermissionOverwriteOptions,\n type PermissionOverwriteTargetType,\n type PermissionOverwriteUpdate,\n} from \"./guild-resources.js\";\nexport {\n EmojiManager,\n type EmojiCreateOptions,\n type EmojiEditOptions,\n} from \"./emoji.js\";\nexport {\n GuildStickerManager,\n GuildSoundboardManager,\n MonetizationManager,\n ApplicationEmojiManager,\n type APIEntitlement,\n type APISKU,\n type APISoundboardSound,\n type APISubscription,\n type EntitlementQuery,\n type GuildStickerCreateOptions,\n type SoundboardSoundCreateOptions,\n} from \"./advanced.js\";\n",
16
16
  "/**\n * Discord Gateway close codes.\n *\n * Discord.js-familiar names and numeric values, matching the Discord Gateway\n * protocol. Exposed so consumers can branch on named codes instead of magic\n * numbers; {@link Gateway} uses them internally to decide resume/identify/stop.\n */\nexport const GatewayCloseCodes = {\n UnknownError: 4000,\n UnknownOpcode: 4001,\n DecodeError: 4002,\n NotAuthenticated: 4003,\n AuthenticationFailed: 4004,\n AlreadyAuthenticated: 4005,\n InvalidSeq: 4007,\n RateLimited: 4008,\n SessionTimedOut: 4009,\n InvalidShard: 4010,\n ShardingRequired: 4011,\n InvalidAPIVersion: 4012,\n InvalidIntents: 4013,\n DisallowedIntents: 4014,\n} as const;\n",
17
17
  "/**\n * Reconnect policy and scheduling for one Gateway connection.\n *\n * Owns how a close code is classified, whether another attempt is allowed, how\n * long to wait, and the coordination that keeps at most one logical connection\n * attempt in flight. It owns no socket, no session state, no heartbeat and no\n * event emission: it is told a close code and whether a resume is possible, and\n * answers with a decision.\n */\nimport { GatewayCloseCodes } from \"./close-codes.js\";\n\n/** What a closed connection should do next. */\nexport type CloseAction =\n /** Reconnect and RESUME the existing session. */\n | \"resume\"\n /** Reconnect, but IDENTIFY afresh: the session cannot be resumed. */\n | \"identify\"\n /** Do not reconnect; the condition will not fix itself. */\n | \"stop\";\n\n/**\n * Close codes that no reconnect can recover from: bad token, bad shard\n * configuration, bad API version, or intents the application is not approved\n * for. Retrying these hammers Discord with a request that cannot succeed.\n */\nexport const FATAL_CLOSE_CODES: readonly number[] = [\n GatewayCloseCodes.AuthenticationFailed,\n GatewayCloseCodes.InvalidShard,\n GatewayCloseCodes.ShardingRequired,\n GatewayCloseCodes.InvalidAPIVersion,\n GatewayCloseCodes.InvalidIntents,\n GatewayCloseCodes.DisallowedIntents,\n];\n\n/**\n * Close codes that keep the connection recoverable but destroy the session, so\n * the next connection must IDENTIFY rather than RESUME.\n */\nexport const IDENTIFY_CLOSE_CODES: readonly number[] = [\n GatewayCloseCodes.InvalidSeq,\n GatewayCloseCodes.SessionTimedOut,\n];\n\n/**\n * Classifies a close code.\n *\n * Any code outside the two tables above is **reconnectable**: transport-level\n * closes (`1006`), server restarts (`1001`), Discord's own `4000`, and the\n * `1000` the Gateway itself sends after a non-resumable `op 9` all belong here.\n * Whether that reconnect resumes or identifies is not a property of the code —\n * it is whether a session survives, which is why `canResume` is a parameter\n * rather than something this module works out for itself.\n *\n * @param code WebSocket close code.\n * @param canResume Whether the session can still be resumed.\n */\nexport function classifyCloseCode(\n code: number,\n canResume: boolean,\n): CloseAction {\n if (FATAL_CLOSE_CODES.includes(code)) return \"stop\";\n if (IDENTIFY_CLOSE_CODES.includes(code)) return \"identify\";\n return canResume ? \"resume\" : \"identify\";\n}\n\n/** Why a reconnect was not scheduled. */\nexport type ScheduleRefusal =\n /** Automatic reconnect is disabled for this Gateway. */\n | \"disabled\"\n /** A reconnect is already armed; a second would open a second socket. */\n | \"pending\"\n /** The attempt budget is spent. */\n | \"exhausted\";\n\n/** Outcome of asking for a reconnect. */\nexport type ScheduleResult =\n | {\n scheduled: true;\n delayMs: number;\n attempt: number;\n /** Set when reconnects are coming too fast: the delay was raised. */\n unstable?: { reconnects: number };\n }\n | { scheduled: false; reason: ScheduleRefusal };\n\n/** Configuration for {@link GatewayReconnect}. */\nexport interface ReconnectOptions {\n /** Whether automatic reconnect is permitted at all. */\n enabled: boolean;\n /** Maximum consecutive attempts before giving up. */\n maxAttempts: number;\n /** Delay for the first attempt, doubled per attempt. */\n baseDelay: number;\n /** Ceiling for the backoff. */\n maxDelay: number;\n /** Randomness source for jitter; injected in tests. */\n random?: () => number;\n /** Reconnects inside `stormWindow` before the link counts as unstable (default 5). */\n stormLimit?: number;\n /** The span over which reconnects are counted, in ms (default 60,000). */\n stormWindow?: number;\n /** Clock in ms; injected in tests. */\n now?: () => number;\n}\n\n/** Reconnect scheduling, backoff and attempt coordination. */\nexport class GatewayReconnect {\n readonly #options: ReconnectOptions;\n readonly #random: () => number;\n #attempt = 0;\n /** When recent reconnects were scheduled. A successful connection does not clear it, so a link that connects and drops again keeps backing off. */\n readonly #recent: number[] = [];\n #timer?: ReturnType<typeof setTimeout>;\n #promise?: Promise<void>;\n #resolve?: () => void;\n #reject?: (error: Error) => void;\n\n public constructor(options: ReconnectOptions) {\n this.#options = options;\n this.#random = options.random ?? Math.random;\n }\n\n /** Whether automatic reconnect is permitted. */\n public get enabled(): boolean {\n return this.#options.enabled;\n }\n\n /** Consecutive attempts made since the last {@link reset}. */\n public get attempts(): number {\n return this.#attempt;\n }\n\n /** Whether a reconnect timer is currently armed. */\n public get pending(): boolean {\n return this.#timer !== undefined;\n }\n\n /** Whether the attempt budget is spent. */\n public get exhausted(): boolean {\n return this.#attempt >= this.#options.maxAttempts;\n }\n\n /** Whether a connection attempt is currently in flight. */\n public get connecting(): boolean {\n return this.#promise !== undefined;\n }\n\n /** The in-flight attempt, or undefined when none is active. */\n public get attemptPromise(): Promise<void> | undefined {\n return this.#promise;\n }\n\n /** Classifies a close code against this Gateway's policy. */\n public classifyClose(code: number, canResume: boolean): CloseAction {\n return classifyCloseCode(code, canResume);\n }\n\n /** Whether another reconnect is currently allowed. */\n public canReconnect(): boolean {\n return this.#options.enabled && !this.exhausted;\n }\n\n /**\n * Arms a reconnect, unless one is already armed or no attempt is allowed.\n *\n * Refusing while `pending` is what keeps repeated closes from stacking\n * timers: several closes in a row produce one reconnect, not several\n * competing sockets.\n */\n public schedule(run: () => void): ScheduleResult {\n if (!this.#options.enabled)\n return { scheduled: false, reason: \"disabled\" };\n if (this.#timer) return { scheduled: false, reason: \"pending\" };\n if (this.exhausted) return { scheduled: false, reason: \"exhausted\" };\n\n const attempt = this.#attempt;\n const now = (this.#options.now ?? Date.now)();\n const window = this.#options.stormWindow ?? 60_000;\n const limit = this.#options.stormLimit ?? 5;\n while (this.#recent.length && now - this.#recent[0]! >= window)\n this.#recent.shift();\n const over = this.#recent.length - limit;\n this.#recent.push(now);\n let delay = Math.min(\n this.#options.maxDelay,\n this.#options.baseDelay * 2 ** this.#attempt++,\n );\n const unstable = over >= 0;\n // Past the limit, every further reconnect in the window doubles the\n // wait, even when each connection succeeded and reset the attempt count.\n if (unstable)\n delay = Math.min(\n this.#options.maxDelay,\n Math.max(delay, this.#options.baseDelay * 2 ** (over + 1)),\n );\n // Jitter must scale with the delay: a fixed ceiling would reconnect\n // every shard inside the same narrow window after a gateway-wide\n // restart, which is exactly when decorrelation matters.\n const jitter = this.#random() * Math.max(1, delay * 0.25);\n const delayMs = delay + jitter;\n this.#timer = setTimeout(() => {\n this.#timer = undefined;\n run();\n }, delayMs);\n return {\n scheduled: true,\n delayMs,\n attempt,\n ...(unstable\n ? { unstable: { reconnects: this.#recent.length } }\n : {}),\n };\n }\n\n /** Cancels an armed reconnect. Safe to call when none is armed. */\n public cancel(): void {\n if (this.#timer) clearTimeout(this.#timer);\n this.#timer = undefined;\n }\n\n /** Clears the attempt counter after a connection succeeds. */\n public reset(): void {\n this.#attempt = 0;\n }\n\n /**\n * Coordinates one logical connection attempt.\n *\n * Callers that ask while an attempt is in flight join it instead of\n * starting a second: three `connect()` calls produce one socket attempt and\n * one shared promise.\n *\n * @param start Invoked exactly once, when this call begins a new attempt.\n */\n public attempt(start: () => void): Promise<void> {\n if (this.#promise) return this.#promise;\n this.#promise = new Promise<void>((resolve, reject) => {\n this.#resolve = resolve;\n this.#reject = reject;\n start();\n });\n return this.#promise;\n }\n\n /** Settles the in-flight attempt, resolving it or rejecting with `error`. */\n public settle(error?: Error): void {\n const resolve = this.#resolve;\n const reject = this.#reject;\n this.#resolve = undefined;\n this.#reject = undefined;\n this.#promise = undefined;\n if (error) reject?.(error);\n else resolve?.();\n }\n}\n",
@@ -25,6 +25,6 @@
25
25
  "/** Resource structures for Discord messages and related entities. */\nimport type { MessageCreateOptions, MessageEditOptions } from \"@lunibee/types\";\nimport { BaseStructure, Channel, User } from \"./base.js\";\nimport { GuildMember } from \"./resources.js\";\nimport type { ComponentInteraction } from \"./interactions.js\";\nimport type { Collector, CollectorOptions } from \"@lunibee/core\";\n\n/** Interaction tokens last 15 minutes, so a component collector with no limit of its own stops then. */\nconst COMPONENT_COLLECTOR_DEFAULT_TIME = 15 * 60_000;\n\n/** Options for {@link Message.createComponentCollector}. */\nexport interface ComponentCollectorOptions extends CollectorOptions<ComponentInteraction> {\n /** Only this component type (2 button, 3 string select...). */\n componentType?: number;\n}\nimport type { ResourceContext } from \"./base.js\";\n\n/** discord.js user-authored (non-system) message types: Default, Reply,\n * ChatInputCommand and ContextMenuCommand. Any other type is a system message. */\nconst NON_SYSTEM_MESSAGE_TYPES = new Set([0, 19, 20, 23]);\n\nexport {\n BaseStructure,\n Channel,\n Guild,\n User,\n CDN_BASE,\n cdnURL,\n} from \"./base.js\";\nexport type { ResourceContext, ImageURLOptions } from \"./base.js\";\n\nexport class Message extends BaseStructure {\n public content: string;\n public author: User;\n public channelId: string;\n public readonly channel: Channel;\n public guildId?: string;\n public flags: number;\n /** Message creation timestamp (from timestamp field or snowflake). */\n public readonly timestamp: Date;\n public readonly attachments: import(\"@lunibee/types\").APIAttachment[];\n public readonly embeds: import(\"@lunibee/types\").APIEmbed[];\n public readonly mentions: User[];\n public readonly mentionRoles: string[];\n public pinned: boolean;\n public mentionEveryone: boolean;\n public type: number;\n public readonly reference?: import(\"@lunibee/types\").APIMessageReference;\n public readonly components: import(\"@lunibee/types\").APIMessageComponent[];\n public readonly referencedMessage?: Message | null;\n /** Stickers sent with the message (`sticker_items`). */\n public readonly stickers: import(\"@lunibee/types\").APIStickerItem[];\n /** The poll, when the message is one. */\n public readonly poll: import(\"@lunibee/types\").APIPoll | null;\n /** For a forwarded message, the snapshot of the original. */\n public readonly messageSnapshots: import(\"@lunibee/types\").APIMessageSnapshot[];\n /**\n * The author as a guild member, on guild messages from the Gateway. Its\n * `permissions` are computed from the cached roles; `member.kick()` and the\n * other member actions work when the message came from a client.\n */\n public member: GuildMember | null;\n readonly #context?: ResourceContext;\n\n /** Creates a message structure from Discord message data. */\n public constructor(\n data: import(\"@lunibee/types\").APIMessage,\n context?: ResourceContext,\n ) {\n super(data.id);\n if (!/^\\d{1,20}$/.test(data.channel_id))\n throw new TypeError(\n \"Message channel_id must be a valid snowflake.\",\n );\n this.content = data.content ?? \"\";\n this.author = new User(data.author);\n this.channelId = data.channel_id;\n this.guildId = data.guild_id;\n this.flags = data.flags ?? 0;\n this.timestamp = new Date(\n data.timestamp ?? Number((BigInt(this.id) >> 22n) + 1420070400000n),\n );\n this.attachments = data.attachments ?? [];\n this.embeds = data.embeds ?? [];\n this.components = data.components ?? [];\n this.mentions = (data.mentions ?? []).map((user) => new User(user));\n this.mentionRoles = data.mention_roles ?? [];\n this.pinned = data.pinned ?? false;\n this.mentionEveryone = data.mention_everyone ?? false;\n this.type = data.type ?? 0;\n this.reference = data.message_reference;\n this.stickers =\n data.sticker_items ??\n (data.stickers ?? []).map(({ id, name, format_type }) => ({\n id,\n name,\n format_type,\n }));\n this.poll = data.poll ?? null;\n this.messageSnapshots = data.message_snapshots ?? [];\n this.referencedMessage = data.referenced_message\n ? new Message(data.referenced_message, context)\n : data.referenced_message === null\n ? null\n : undefined;\n this.#context = context;\n this.member =\n data.member && data.guild_id\n ? new GuildMember(\n {\n ...data.member,\n user: data.member.user ?? data.author,\n guild_id: data.guild_id,\n },\n context,\n )\n : null;\n this.channel = new Channel(\n { id: data.channel_id, type: 0, guild_id: data.guild_id },\n context,\n );\n }\n\n /** Whether embeds are suppressed on this message. */\n public get embedsSuppressed(): boolean {\n return (this.flags & 4) !== 0;\n }\n\n /** Unix timestamp (ms) at which the message was created — discord.js parity.\n * Derived from the message `timestamp`, which itself falls back to the id snowflake. */\n public override get createdTimestamp(): number {\n return this.timestamp.getTime();\n }\n\n /** Date at which the message was created — discord.js parity. Overrides the\n * snowflake-only `BaseStructure.createdAt` to honour the message `timestamp`. */\n public override get createdAt(): Date {\n return this.timestamp;\n }\n\n /** Edits this message. @param options Message fields to change. @returns The updated message. @throws {Error} If the message is not attached to a client. */\n public edit(options: MessageEditOptions): Promise<Message> {\n if (!this.#context)\n throw new Error(\"This message is not attached to a client.\");\n return this.#context.editMessage(this.channelId, this.id, options);\n }\n\n /** Updates this message using the same resource operation as edit. @param options Message fields to change. @returns The updated message. @throws {Error} If the message is not attached to a client. */\n public update(options: MessageEditOptions): Promise<Message> {\n return this.edit(options);\n }\n\n /** Deletes this message. @param reason Audit-log reason (shown when deleting someone else's message). @returns A promise fulfilled when Discord confirms deletion. @throws {Error} If the message is not attached to a client. */\n public delete(reason?: string): Promise<void> {\n if (!this.#context)\n throw new Error(\"This message is not attached to a client.\");\n return this.#context.deleteMessage(this.channelId, this.id, reason);\n }\n\n /** Replies to this message. @param options Message content or payload. @returns The created reply message. @throws {Error} If the message is not attached to a client. */\n public reply(options: string | MessageCreateOptions): Promise<Message> {\n if (!this.#context)\n throw new Error(\"This message is not attached to a client.\");\n const payload =\n typeof options === \"string\" ? { content: options } : { ...options };\n payload.message_reference = {\n message_id: this.id,\n channel_id: this.channelId,\n guild_id: this.guildId,\n };\n return this.#context.sendMessage(this.channelId, payload);\n }\n\n /** Crossposts this message. @returns The resulting message. @throws {Error} If the message is not attached to a client. */\n public crosspost(): Promise<Message> {\n if (!this.#context)\n throw new Error(\"This message is not attached to a client.\");\n return this.#context.crosspostMessage(this.channelId, this.id);\n }\n\n /**\n * Collects clicks and picks on this message's components. It always ends:\n * after `time`, or 15 minutes (an interaction token's life) when neither\n * `time` nor `idle` is set.\n * @example\n * const collector = message.createComponentCollector({ filter: (i) => i.user?.id === authorId, time: 60_000 });\n * collector.on(\"collect\", (i) => i.update({ content: `Picked ${i.customId}` }));\n */\n public createComponentCollector(\n options: ComponentCollectorOptions = {},\n ): Collector<string, ComponentInteraction> {\n const collect = this.#context?.collectInteractions;\n if (!collect)\n throw new Error(\"This message is not attached to a client.\");\n const { componentType, filter, ...rest } = options;\n return collect({\n ...rest,\n time:\n rest.time ??\n (rest.idle ? undefined : COMPONENT_COLLECTOR_DEFAULT_TIME),\n filter: async (interaction) =>\n interaction.isMessageComponent() &&\n interaction.messageId === this.id &&\n (componentType === undefined ||\n interaction.componentType === componentType) &&\n (filter ? await filter(interaction) : true),\n }) as unknown as Collector<string, ComponentInteraction>;\n }\n\n /**\n * Waits for the next click or pick on this message's components.\n * @param options `time` (default 15 minutes), `filter`, `componentType`.\n * @returns The interaction. @throws {Error} When time runs out first.\n */\n public awaitComponent(\n options: Omit<ComponentCollectorOptions, \"max\"> = {},\n ): Promise<ComponentInteraction> {\n return this.createComponentCollector({ ...options, max: 1 }).next();\n }\n\n /** Adds a reaction to this message. @param emoji Emoji identifier. @returns A promise fulfilled when the reaction is added. @throws {Error} If reactions are unavailable. */\n public react(emoji: string): Promise<void> {\n if (!this.#context?.addReaction)\n throw new Error(\"This message is not attached to a client.\");\n return this.#context.addReaction(this.channelId, this.id, emoji);\n }\n\n /** Removes a reaction from this message. @param emoji Emoji identifier. @param userId User whose reaction should be removed; omit to remove the current user's reaction. @returns A promise fulfilled when the reaction is removed. @throws {Error} If reaction operations are unavailable. */\n public removeReaction(emoji: string, userId?: string): Promise<void> {\n if (!this.#context)\n throw new Error(\"This message is not attached to a client.\");\n return userId\n ? (this.#context.removeReaction?.(\n this.channelId,\n this.id,\n emoji,\n userId,\n ) ??\n Promise.reject(\n new Error(\"Reaction operations are unavailable.\"),\n ))\n : (this.#context.removeOwnReaction?.(\n this.channelId,\n this.id,\n emoji,\n ) ??\n Promise.reject(\n new Error(\"Reaction operations are unavailable.\"),\n ));\n }\n\n /** Removes all reactions from this message. @returns A promise fulfilled when reactions are removed. @throws {Error} If reaction operations are unavailable. */\n public removeAllReactions(): Promise<void> {\n if (!this.#context?.removeAllReactions)\n throw new Error(\"This message is not attached to a client.\");\n return this.#context.removeAllReactions(this.channelId, this.id);\n }\n\n /** Pins this message. @param reason Audit-log reason. @returns A promise fulfilled when the message is pinned. @throws {Error} If pin operations are unavailable. */\n public pin(reason?: string): Promise<void> {\n if (!this.#context?.pinMessage)\n throw new Error(\"This message is not attached to a client.\");\n return this.#context.pinMessage(this.channelId, this.id, reason);\n }\n\n /** Unpins this message. @param reason Audit-log reason. @returns A promise fulfilled when the message is unpinned. @throws {Error} If pin operations are unavailable. */\n public unpin(reason?: string): Promise<void> {\n if (!this.#context?.unpinMessage)\n throw new Error(\"This message is not attached to a client.\");\n return this.#context.unpinMessage(this.channelId, this.id, reason);\n }\n\n // ─── Flag-based getters ───────────────────────────────────────────────────────\n\n /** Whether this message was sent ephemerally (only visible to the recipient). */\n public get isEphemeral(): boolean {\n return (this.flags & 64) !== 0;\n }\n\n /** Whether this message is currently pinned in its channel. */\n public get isPinned(): boolean {\n return this.pinned;\n }\n\n /** Whether this is a system-generated message (join, boost, etc). Mirrors\n * discord.js: a message is \"system\" unless its type is one of the user-authored\n * types — Default (0), Reply (19), ChatInputCommand (20) or ContextMenuCommand (23). */\n public get isSystemMessage(): boolean {\n return !NON_SYSTEM_MESSAGE_TYPES.has(this.type);\n }\n\n /** Whether this message was crossposted from an announcement channel. */\n public get isCrosspost(): boolean {\n return (this.flags & 2) !== 0;\n }\n\n /** Whether this message has file attachments. */\n public get hasAttachments(): boolean {\n return this.attachments.length > 0;\n }\n\n /** Whether this message contains embeds. */\n public get hasEmbeds(): boolean {\n return this.embeds.length > 0;\n }\n\n /** Whether this message contains interactive components. */\n public get hasComponents(): boolean {\n return this.components.length > 0;\n }\n\n /** Edits this message to suppress its embeds. @returns The updated message. */\n public suppressEmbeds(): Promise<Message> {\n return this.edit({ flags: this.flags | 4 });\n }\n\n /** Returns a plain-object snapshot of all message fields. */\n public toJSON(): Record<string, unknown> {\n return {\n id: this.id,\n content: this.content,\n channelId: this.channelId,\n guildId: this.guildId,\n flags: this.flags,\n pinned: this.pinned,\n mentionEveryone: this.mentionEveryone,\n type: this.type,\n timestamp: this.timestamp.toISOString(),\n author: { id: this.author.id, username: this.author.username },\n attachments: [...this.attachments],\n embeds: [...this.embeds],\n components: [...this.components],\n mentions: this.mentions.map((u) => u.id),\n mentionRoles: [...this.mentionRoles],\n reference: this.reference,\n referencedMessage: this.referencedMessage\n ? this.referencedMessage.id\n : this.referencedMessage,\n };\n }\n}\n\nexport {\n GuildMember,\n Role,\n TextChannel,\n Invite,\n Webhook,\n WebhookEnum,\n Emoji,\n AutoModerationRule,\n WelcomeScreenChannel,\n GuildWelcomeScreen,\n OnboardingPromptOption,\n OnboardingPrompt,\n GuildOnboarding,\n} from \"./resources.js\";\nexport {\n NewsChannel,\n DMChannel,\n VoiceChannel,\n StageChannel,\n CategoryChannel,\n ThreadChannel,\n ForumChannel,\n MediaChannel,\n createChannel,\n type ChannelOfKind,\n type ChannelsByKind,\n} from \"./channels.js\";\nexport * from \"./interactions.js\";\nexport { Embed } from \"./embed.js\";\nexport {\n AuditLog,\n AuditLogEntry,\n type AuditLogAutoModInfo,\n type AuditLogInfo,\n type AuditLogInfoByType,\n type AuditLogOverwriteInfo,\n type AuditLogQuery,\n} from \"./audit-log.js\";\nexport { Sticker, stickerURL } from \"./sticker.js\";\nexport { PermissionsBitField } from \"@lunibee/core\";\n"
26
26
  ],
27
27
  "mappings": "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",
28
- "debugId": "CCB1FE6D82188C4D64756E2164756E21",
28
+ "debugId": "1F2A9DC9E180B64C64756E2164756E21",
29
29
  "names": []
30
30
  }