lunibee 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -24
- package/dist/builders/commands.d.ts +37 -0
- package/dist/builders/components.d.ts +127 -12
- package/dist/builders/embed.d.ts +3 -1
- package/dist/builders/index.d.ts +2 -2
- package/dist/builders/index.js +319 -16
- package/dist/builders/index.js.map +8 -7
- package/dist/collection/index.d.ts +36 -0
- package/dist/collection/index.js +110 -6
- package/dist/collection/index.js.map +4 -4
- package/dist/core/events.d.ts +69 -3
- package/dist/core/index.d.ts +158 -90
- package/dist/core/index.js +1543 -1169
- package/dist/core/index.js.map +24 -23
- package/dist/core/permissions.d.ts +112 -53
- package/dist/formatters/index.d.ts +43 -3
- package/dist/formatters/index.js +64 -1
- package/dist/formatters/index.js.map +3 -3
- package/dist/handlers/index.js.map +2 -2
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3882 -1588
- package/dist/index.js.map +43 -35
- package/dist/managers/emoji.d.ts +38 -0
- package/dist/managers/guild.d.ts +141 -0
- package/dist/managers/index.d.ts +86 -75
- package/dist/managers/index.js +1084 -67
- package/dist/managers/index.js.map +21 -16
- package/dist/managers/message.d.ts +7 -12
- package/dist/managers/user.d.ts +20 -0
- package/dist/rest/index.d.ts +173 -1
- package/dist/rest/index.js +343 -47
- package/dist/rest/index.js.map +8 -6
- package/dist/rest/redis.d.ts +37 -0
- package/dist/rest/routes.d.ts +26 -1
- package/dist/rest/store.d.ts +38 -0
- package/dist/rest/webhook.d.ts +2 -2
- package/dist/sharding/cluster.d.ts +61 -0
- package/dist/sharding/index.d.ts +8 -0
- package/dist/sharding/index.js +289 -18
- package/dist/sharding/index.js.map +8 -7
- package/dist/structures/audit-log.d.ts +26 -0
- package/dist/structures/base.d.ts +131 -48
- package/dist/structures/embed.d.ts +65 -0
- package/dist/structures/index.d.ts +74 -26
- package/dist/structures/index.js +1528 -59
- package/dist/structures/index.js.map +14 -8
- package/dist/structures/interactions.d.ts +72 -17
- package/dist/structures/resources.d.ts +199 -7
- package/dist/types/index.d.ts +605 -84
- package/dist/types/index.js +71 -1
- package/dist/types/index.js.map +3 -3
- package/dist/utils/index.js.map +2 -2
- package/dist/voice/index.d.ts +88 -0
- package/dist/voice/index.js +204 -5
- package/dist/voice/index.js.map +3 -3
- package/dist/ws/index.d.ts +40 -1
- package/dist/ws/index.js +105 -18
- package/dist/ws/index.js.map +4 -4
- package/package.json +16 -16
- package/dist/structures/permissions.d.ts +0 -73
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../packages/types/src/index.ts", "../../packages/ws/src/index.ts", "../../packages/sharding/src/bus.ts", "../../packages/sharding/src/index.ts"],
|
|
3
|
+
"sources": ["../../packages/types/src/index.ts", "../../packages/ws/src/index.ts", "../../packages/sharding/src/bus.ts", "../../packages/sharding/src/cluster.ts", "../../packages/sharding/src/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/** Discord API snowflake identifier. */\r\nexport type Snowflake = string;\r\n\r\n/** Gateway intent bit flags supported by Discord (PascalCase). */\r\nexport const GatewayIntentBits = {\r\n Guilds: 1 << 0,\r\n GuildMembers: 1 << 1,\r\n GuildModeration: 1 << 2,\r\n GuildExpressions: 1 << 3,\r\n GuildIntegrations: 1 << 4,\r\n GuildWebhooks: 1 << 5,\r\n GuildInvites: 1 << 6,\r\n GuildVoiceStates: 1 << 7,\r\n GuildPresences: 1 << 8,\r\n GuildMessages: 1 << 9,\r\n GuildMessageReactions: 1 << 10,\r\n GuildMessageTyping: 1 << 11,\r\n DirectMessages: 1 << 12,\r\n DirectMessageReactions: 1 << 13,\r\n DirectMessageTyping: 1 << 14,\r\n MessageContent: 1 << 15,\r\n GuildScheduledEvents: 1 << 16,\r\n AutoModerationConfiguration: 1 << 20,\r\n AutoModerationExecution: 1 << 21,\r\n GuildMessagePolls: 1 << 24,\r\n DirectMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\r\nexport const IntentBits = {\r\n guild: 1 << 0,\r\n guilds: 1 << 0,\r\n guildMembers: 1 << 1,\r\n guildModeration: 1 << 2,\r\n guildBans: 1 << 2,\r\n guildExpressions: 1 << 3,\r\n guildEmojis: 1 << 3,\r\n guildEmojisAndStickers: 1 << 3,\r\n guildIntegrations: 1 << 4,\r\n guildWebhooks: 1 << 5,\r\n guildInvites: 1 << 6,\r\n guildVoiceStates: 1 << 7,\r\n guildPresences: 1 << 8,\r\n guildMessages: 1 << 9,\r\n guildMessage: 1 << 9,\r\n guildMessageReactions: 1 << 10,\r\n guildMessageTyping: 1 << 11,\r\n directMessages: 1 << 12,\r\n directMessageReactions: 1 << 13,\r\n directMessageTyping: 1 << 14,\r\n messageContent: 1 << 15,\r\n guildScheduledEvents: 1 << 16,\r\n autoModerationConfiguration: 1 << 20,\r\n autoModerationExecution: 1 << 21,\r\n guildMessagePolls: 1 << 24,\r\n directMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Alias for IntentBits. */\r\nexport const Intents = IntentBits;\r\n\r\n/** Gateway intent resolvable value (single bitfield, enum key, or array of bitfields/strings). */\r\nexport type GatewayIntentResolvable =\r\n | number\r\n | keyof typeof GatewayIntentBits\r\n | keyof typeof IntentBits\r\n | (\r\n number | keyof typeof GatewayIntentBits | keyof typeof IntentBits | string\r\n )[];\r\n\r\n/** Resolves any GatewayIntentResolvable into a raw bitfield integer. */\r\nexport function resolveGatewayIntents(\r\n intents: GatewayIntentResolvable,\r\n): number {\r\n if (typeof intents === \"number\") return intents;\r\n if (Array.isArray(intents)) {\r\n return intents.reduce<number>((acc, intent) => {\r\n return acc | resolveGatewayIntents(intent as any);\r\n }, 0);\r\n }\r\n if (typeof intents === \"string\") {\r\n if (intents in GatewayIntentBits)\r\n return (GatewayIntentBits as Record<string, number>)[intents]!;\r\n if (intents in IntentBits)\r\n return (IntentBits as Record<string, number>)[intents]!;\r\n const lower = intents.charAt(0).toLowerCase() + intents.slice(1);\r\n if (lower in IntentBits)\r\n return (IntentBits as Record<string, number>)[lower]!;\r\n const num = Number(intents);\r\n if (!Number.isNaN(num)) return num;\r\n }\r\n return 0;\r\n}\r\n\r\n/** Gateway connection properties (OS, browser, device). */\r\nexport interface GatewayProperties {\r\n os?: string;\r\n browser?: string;\r\n device?: string;\r\n [key: string]: unknown;\r\n}\r\n/** Gateway presence activity. */\r\nexport interface GatewayPresenceActivity {\r\n name: string;\r\n type?: number;\r\n url?: string;\r\n state?: string;\r\n}\r\n/** Gateway presence data. */\r\nexport interface GatewayPresence {\r\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\r\n activities?: GatewayPresenceActivity[];\r\n afk?: boolean;\r\n since?: number | null;\r\n}\r\n/** Client configuration. */\r\nexport interface ClientOptions {\r\n token: string;\r\n intents: GatewayIntentResolvable;\r\n gateway?: GatewayOptions;\r\n rest?: RESTOptions;\r\n}\r\n/** Gateway connection configuration. */\r\nexport interface GatewayOptions {\r\n intents?: GatewayIntentResolvable;\r\n reconnect?: boolean;\r\n maxReconnectAttempts?: number;\r\n reconnectBaseDelay?: number;\r\n reconnectMaxDelay?: number;\r\n shardId?: number;\r\n shardCount?: number;\r\n properties?: GatewayProperties;\r\n presence?: GatewayPresence;\r\n}\r\n\r\n/** REST request configuration. */\r\nexport interface RESTOptions {\r\n timeout?: number;\r\n retries?: number;\r\n baseURL?: string;\r\n}\r\n/** Discord user object. */\r\nexport interface UserData {\r\n id: Snowflake;\r\n username: string;\r\n discriminator?: string;\r\n global_name?: string | null;\r\n avatar?: string | null;\r\n bot?: boolean;\r\n system?: boolean;\r\n public_flags?: number;\r\n}\r\n/** Current authenticated bot user. */\r\nexport type ClientUser = UserData;\r\n/** Discord Gateway payload envelope. */\r\nexport interface GatewayPayload<T = unknown> {\r\n op: number;\r\n d: T | null;\r\n s: number | null;\r\n t: string | null;\r\n}\r\n/** Generic Discord API error payload. */\r\nexport interface APIError {\r\n code?: number;\r\n message?: string;\r\n errors?: Record<string, unknown>;\r\n}\r\n/** Discord message flags. */\r\nexport const MessageFlags = {\r\n Crossposted: 1,\r\n IsCrosspost: 2,\r\n SuppressEmbeds: 4,\r\n SourceMessageDeleted: 8,\r\n Urgent: 16,\r\n HasThread: 32,\r\n Ephemeral: 64,\r\n Loading: 128,\r\n IsComponentsV2: 32768,\r\n} as const;\r\n/** Discord channel types. */\r\nexport const ChannelType = {\r\n GuildText: 0,\r\n DM: 1,\r\n GuildVoice: 2,\r\n GroupDM: 3,\r\n GuildCategory: 4,\r\n GuildAnnouncement: 5,\r\n AnnouncementThread: 10,\r\n PublicThread: 11,\r\n PrivateThread: 12,\r\n GuildStageVoice: 13,\r\n GuildDirectory: 14,\r\n GuildForum: 15,\r\n GuildMedia: 16,\r\n} as const;\r\n\r\n/** Raw Discord embed object. */\r\nexport interface APIEmbed {\r\n title?: string;\r\n type?: string;\r\n description?: string;\r\n url?: string;\r\n timestamp?: string;\r\n color?: number;\r\n footer?: Record<string, unknown>;\r\n image?: Record<string, unknown>;\r\n thumbnail?: Record<string, unknown>;\r\n video?: Record<string, unknown>;\r\n provider?: Record<string, unknown>;\r\n author?: Record<string, unknown>;\r\n fields?: Array<Record<string, unknown>>;\r\n}\r\n/** Raw Discord attachment object. */\r\nexport interface APIAttachment {\r\n id: Snowflake;\r\n filename: string;\r\n description?: string | null;\r\n content_type?: string;\r\n size: number;\r\n url: string;\r\n proxy_url: string;\r\n height?: number | null;\r\n width?: number | null;\r\n ephemeral?: boolean;\r\n duration_secs?: number;\r\n waveform?: string;\r\n flags?: number;\r\n}\r\n/** Raw Discord message component object. */\r\nexport interface APIMessageComponent {\r\n type: number;\r\n id?: number;\r\n custom_id?: string;\r\n style?: number;\r\n label?: string;\r\n disabled?: boolean;\r\n emoji?: Record<string, unknown>;\r\n url?: string;\r\n options?: Array<Record<string, unknown>>;\r\n components?: APIMessageComponent[];\r\n}\r\n/** Raw Discord message reference. */\r\nexport interface APIMessageReference {\r\n type?: number;\r\n message_id?: Snowflake;\r\n channel_id?: Snowflake;\r\n guild_id?: Snowflake;\r\n fail_if_not_exists?: boolean;\r\n}\r\n/** Raw Discord message reaction. */\r\nexport interface APIMessageReaction {\r\n count: number;\r\n count_details?: Record<string, number>;\r\n me?: boolean;\r\n me_burst?: boolean;\r\n emoji: Record<string, unknown>;\r\n burst_colors?: string[];\r\n}\r\n/** Raw Discord message object. */\r\nexport interface APIMessage {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n author: UserData;\r\n content: string;\r\n timestamp?: string;\r\n edited_timestamp?: string | null;\r\n tts?: boolean;\r\n mention_everyone?: boolean;\r\n mentions?: UserData[];\r\n mention_roles?: Snowflake[];\r\n mention_channels?: Array<Record<string, unknown>>;\r\n attachments?: APIAttachment[];\r\n embeds?: APIEmbed[];\r\n reactions?: APIMessageReaction[];\r\n nonce?: string | number | null;\r\n pinned?: boolean;\r\n webhook_id?: Snowflake;\r\n type?: number;\r\n activity?: Record<string, unknown>;\r\n application?: Record<string, unknown>;\r\n application_id?: Snowflake;\r\n message_reference?: APIMessageReference;\r\n flags?: number;\r\n referenced_message?: APIMessage | null;\r\n interaction_metadata?: Record<string, unknown>;\r\n thread?: Record<string, unknown>;\r\n components?: APIMessageComponent[];\r\n position?: number;\r\n role_subscription_data?: Record<string, unknown>;\r\n purchase_notification?: Record<string, unknown>;\r\n poll?: Record<string, unknown>;\r\n guild_id?: Snowflake;\r\n}\r\n/** Raw Discord channel object. */\r\nexport interface APIChannel {\r\n id: Snowflake;\r\n type: number;\r\n guild_id?: Snowflake;\r\n position?: number;\r\n permission_overwrites?: Array<Record<string, unknown>>;\r\n name?: string | null;\r\n topic?: string | null;\r\n nsfw?: boolean;\r\n last_message_id?: Snowflake | null;\r\n bitrate?: number;\r\n user_limit?: number;\r\n rate_limit_per_user?: number;\r\n recipients?: UserData[];\r\n icon?: string | null;\r\n owner_id?: Snowflake;\r\n application_id?: Snowflake;\r\n parent_id?: Snowflake | null;\r\n last_pin_timestamp?: string | null;\r\n rtc_region?: string | null;\r\n video_quality_mode?: number;\r\n message_count?: number;\r\n member_count?: number;\r\n thread_metadata?: Record<string, unknown>;\r\n default_auto_archive_duration?: number;\r\n flags?: number;\r\n total_message_sent?: number;\r\n available_tags?: Array<Record<string, unknown>>;\r\n default_reaction_emoji?: Record<string, unknown> | null;\r\n default_thread_rate_limit_per_user?: number;\r\n default_sort_order?: number | null;\r\n default_forum_layout?: number;\r\n}\r\n/** Raw Discord guild object. */\r\nexport interface APIGuild {\r\n id: Snowflake;\r\n name: string;\r\n icon?: string | null;\r\n icon_hash?: string | null;\r\n splash?: string | null;\r\n discovery_splash?: string | null;\r\n owner_id?: Snowflake;\r\n afk_channel_id?: Snowflake | null;\r\n afk_timeout?: number;\r\n widget_enabled?: boolean;\r\n widget_channel_id?: Snowflake | null;\r\n verification_level?: number;\r\n default_message_notifications?: number;\r\n explicit_content_filter?: number;\r\n roles?: Array<Record<string, unknown>>;\r\n emojis?: Array<Record<string, unknown>>;\r\n features?: string[];\r\n mfa_level?: number;\r\n application_id?: Snowflake | null;\r\n system_channel_id?: Snowflake | null;\r\n system_channel_flags?: number;\r\n rules_channel_id?: Snowflake | null;\r\n max_presences?: number | null;\r\n max_members?: number;\r\n vanity_url_code?: string | null;\r\n description?: string | null;\r\n banner?: string | null;\r\n premium_tier?: number;\r\n preferred_locale?: string;\r\n public_updates_channel_id?: Snowflake | null;\r\n max_video_channel_users?: number;\r\n approximate_member_count?: number;\r\n approximate_presence_count?: number;\r\n nsfw_level?: number;\r\n safety_alerts_channel_id?: Snowflake | null;\r\n}\r\n/** Raw Discord guild member object. */\r\nexport interface APIGuildMember {\r\n user: UserData;\r\n nick?: string | null;\r\n avatar?: string | null;\r\n roles: Snowflake[];\r\n joined_at: string;\r\n premium_since?: string | null;\r\n deaf?: boolean;\r\n mute?: boolean;\r\n flags?: number;\r\n pending?: boolean;\r\n permissions?: string;\r\n communication_disabled_until?: string | null;\r\n guild_id?: Snowflake;\r\n}\r\n/** Raw Discord reaction event payload. */\r\nexport interface APIMessageReactionEvent {\r\n user_id: Snowflake;\r\n channel_id: Snowflake;\r\n message_id: Snowflake;\r\n guild_id?: Snowflake;\r\n member?: APIGuildMember;\r\n emoji: Record<string, unknown>;\r\n message_author_id?: Snowflake;\r\n burst?: boolean;\r\n type?: number;\r\n}\r\n/** Raw Discord message deletion event payload. */\r\nexport interface APIMessageDeleteEvent {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n/** Raw Discord bulk message deletion event payload. */\r\nexport interface APIMessageDeleteBulkEvent {\r\n ids: Snowflake[];\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n/** Raw Discord channel deletion event payload. */\r\nexport interface APIChannelDeleteEvent extends APIChannel {}\r\n/** Raw Discord guild deletion event payload. */\r\nexport interface APIGuildDeleteEvent {\r\n id: Snowflake;\r\n unavailable?: boolean;\r\n}\r\n/** Raw Discord thread lifecycle payload. */\r\nexport interface APIThreadEvent extends APIChannel {\r\n guild_id: Snowflake;\r\n member?: Record<string, unknown>;\r\n}\r\n/** Raw Discord READY event payload. */\r\nexport interface APIReadyEvent {\r\n v: number;\r\n user: UserData;\r\n guilds: Array<{ id: Snowflake; unavailable?: boolean }>;\r\n session_id: string;\r\n resume_gateway_url: string;\r\n shard?: [number, number];\r\n application?: { id: Snowflake; flags: number; [key: string]: unknown };\r\n}\r\n\r\n/** Discord application command option types. */\r\nexport const ApplicationCommandOptionType = {\r\n SubCommand: 1,\r\n SubCommandGroup: 2,\r\n String: 3,\r\n Integer: 4,\r\n Boolean: 5,\r\n User: 6,\r\n Channel: 7,\r\n Role: 8,\r\n Mentionable: 9,\r\n Number: 10,\r\n Attachment: 11,\r\n} as const;\r\n\r\n/** Discord application command types. */\r\nexport const ApplicationCommandType = {\r\n ChatInput: 1,\r\n User: 2,\r\n Message: 3,\r\n} as const;\r\n\r\n/** Raw Discord application command option. */\r\nexport interface APIApplicationCommandOption {\r\n type: number;\r\n name: string;\r\n description: string;\r\n required?: boolean;\r\n choices?: Array<{ name: string; value: string | number }>;\r\n options?: APIApplicationCommandOption[];\r\n min_value?: number;\r\n max_value?: number;\r\n min_length?: number;\r\n max_length?: number;\r\n autocomplete?: boolean;\r\n channel_types?: number[];\r\n}\r\n\r\n/** Raw Discord application command object (from API). */\r\nexport interface APIApplicationCommand {\r\n id: Snowflake;\r\n type?: number;\r\n application_id: Snowflake;\r\n guild_id?: Snowflake;\r\n name: string;\r\n description: string;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n version: Snowflake;\r\n}\r\n\r\n/** Data for creating/overwriting an application command. */\r\nexport interface ApplicationCommandData {\r\n name: string;\r\n description: string;\r\n type?: number;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n}\r\n\r\n/** Raw Discord role object. */\r\nexport interface APIRole {\r\n id: Snowflake;\r\n name: string;\r\n color: number;\r\n hoist: boolean;\r\n icon?: string | null;\r\n unicode_emoji?: string | null;\r\n position: number;\r\n permissions: string;\r\n managed: boolean;\r\n mentionable: boolean;\r\n tags?: Record<string, unknown>;\r\n flags?: number;\r\n}\r\n\r\n/** Raw Discord guild role event payload. */\r\nexport interface APIGuildRoleEvent {\r\n guild_id: Snowflake;\r\n role: APIRole;\r\n}\r\n\r\n/** Raw Discord guild role delete event payload. */\r\nexport interface APIGuildRoleDeleteEvent {\r\n guild_id: Snowflake;\r\n role_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord guild ban event payload. */\r\nexport interface APIGuildBanEvent {\r\n guild_id: Snowflake;\r\n user: UserData;\r\n}\r\n\r\n/** Raw Discord emoji object. */\r\nexport interface APIEmoji {\r\n id: Snowflake | null;\r\n name: string | null;\r\n roles?: Snowflake[];\r\n user?: UserData;\r\n require_colons?: boolean;\r\n managed?: boolean;\r\n animated?: boolean;\r\n available?: boolean;\r\n}\r\n\r\n/** Raw Discord guild emojis update event payload. */\r\nexport interface APIGuildEmojisUpdateEvent {\r\n guild_id: Snowflake;\r\n emojis: APIEmoji[];\r\n}\r\n",
|
|
6
|
-
"import {\n resolveGatewayIntents,\n type GatewayPayload,\n type GatewayProperties,\n type GatewayPresence,\n type GatewayIntentResolvable,\n} from \"@lunibee/types\";\n\n/** Discord Gateway opcodes. */\nexport const GatewayOpcodes = {\n Dispatch: 0,\n Heartbeat: 1,\n Identify: 2,\n PresenceUpdate: 3,\n VoiceStateUpdate: 4,\n Resume: 6,\n Reconnect: 7,\n RequestGuildMembers: 8,\n InvalidSession: 9,\n Hello: 10,\n HeartbeatAck: 11,\n} as const;\n/** Gateway connection lifecycle states. */\nexport enum GatewayState {\n /** Initial connection state. */ Connect = \"CONNECT\",\n /** Gateway HELLO received state. */ Hello = \"HELLO\",\n /** IDENTIFY operation in progress. */ Identify = \"IDENTIFY\",\n /** RESUME operation in progress. */ Resume = \"RESUME\",\n /** Gateway READY state. */ Ready = \"READY\",\n /** Gateway dispatch processing state. */ Dispatch = \"DISPATCH\",\n /** Heartbeat processing state. */ Heartbeat = \"HEARTBEAT\",\n /** Reconnect in progress. */ Reconnect = \"RECONNECT\",\n /** Gateway is permanently closed. */ Closed = \"CLOSED\",\n}\n/** Gateway protocol error. */\nexport class GatewayError extends Error {\n /** Gateway close/error code. */ public readonly code?: number;\n /** Creates a Gateway error. @param message Error message. @param code Optional Gateway code. @param options Optional error metadata. */ public constructor(\n message: string,\n code?: number,\n options?: ErrorOptions,\n ) {\n super(message, options);\n this.name = \"GatewayError\";\n this.code = code;\n }\n}\n/** Gateway connection configuration. */\nexport interface GatewayOptions {\n /** Authentication token. */ token: string;\n /** Gateway intent bitfield or resolvable. */ intents: GatewayIntentResolvable;\n /** Shard identifier. */ shardId?: number;\n /** Total shard count. */ shardCount?: number;\n /** Whether automatic reconnect is enabled. */ reconnect?: boolean;\n /** Maximum reconnect attempts. */ maxReconnectAttempts?: number;\n /** Initial reconnect delay. */ reconnectBaseDelay?: number;\n /** Maximum reconnect delay. */ reconnectMaxDelay?: number;\n /** Heartbeat acknowledgement timeout. */ heartbeatAckTimeout?: number;\n /** Zombie connection timeout. */ zombieTimeout?: number;\n /** Identification properties. */ properties?: GatewayProperties;\n /** Presence data. */ presence?: GatewayPresence;\n}\n/** Gateway event listener. */\ntype GatewayListener = (data: unknown) => unknown;\n/** Manages a Discord Gateway connection. */\nexport class Gateway {\n /** Current Gateway lifecycle state. */\n public state: GatewayState = GatewayState.Connect;\n #options: Required<Omit<GatewayOptions, \"properties\" | \"presence\">> & {\n properties?: GatewayProperties;\n presence?: GatewayPresence;\n };\n #ws?: WebSocket;\n #sequence: number | null = null;\n #sessionId?: string;\n #resumeURL?: string;\n #heartbeatTimer?: ReturnType<typeof setInterval>;\n #initialHeartbeat?: ReturnType<typeof setTimeout>;\n #heartbeatAckTimer?: ReturnType<typeof setTimeout>;\n #heartbeatACK = true;\n #heartbeatInterval = 0;\n #heartbeatSentAt = 0;\n #lastMessageAt = 0;\n #zombieTimer?: ReturnType<typeof setInterval>;\n #zombieReported = false;\n #closed = false;\n #attempt = 0;\n #reconnectTimer?: ReturnType<typeof setTimeout>;\n #connectPromise?: Promise<void>;\n #resolveConnect?: () => void;\n #rejectConnect?: (error: GatewayError) => void;\n readonly #listeners = new Map<string, Set<GatewayListener>>();\n readonly #sendTimestamps: number[] = [];\n /** Creates a Gateway connection manager. @param options Gateway configuration. @throws {TypeError|RangeError} If configuration is invalid. */\n public constructor(options: GatewayOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Gateway token is required.\");\n const resolvedIntents = resolveGatewayIntents(options.intents);\n if (!Number.isInteger(resolvedIntents) || resolvedIntents < 0)\n throw new TypeError(\"Gateway intents must be a non-negative integer.\");\n this.#options = {\n shardId: 0,\n shardCount: 1,\n reconnect: true,\n maxReconnectAttempts: Infinity,\n reconnectBaseDelay: 1000,\n reconnectMaxDelay: 30000,\n heartbeatAckTimeout: 10000,\n zombieTimeout: 30000,\n properties: {\n os: \"Android\",\n browser: \"Discord Android\",\n device: \"Discord Android\",\n },\n presence: { status: \"online\", activities: [], afk: false, since: null },\n ...options,\n };\n if (\n this.#options.shardId < 0 ||\n this.#options.shardId >= this.#options.shardCount\n )\n throw new RangeError(\n \"Gateway shardId must be within the configured shard count.\",\n );\n if (this.#options.zombieTimeout <= this.#options.heartbeatAckTimeout)\n throw new RangeError(\n \"Gateway zombieTimeout must be greater than heartbeatAckTimeout\",\n );\n }\n\n /** Opens the Gateway connection. @param url Gateway WebSocket URL. @returns Promise fulfilled when the socket opens. @throws {GatewayError} If permanently closed or unable to connect. */\n public connect(\n url = \"wss://gateway.discord.gg/?v=10&encoding=json\",\n ): Promise<void> {\n if (this.state === GatewayState.Closed)\n throw new GatewayError(\"Gateway has been permanently closed.\");\n if (this.#connectPromise) return this.#connectPromise;\n this.#closed = false;\n this.#setState(GatewayState.Connect);\n this.#connectPromise = new Promise<void>((resolve, reject) => {\n this.#resolveConnect = resolve;\n this.#rejectConnect = reject;\n this.#open(url);\n });\n return this.#connectPromise;\n }\n /** Permanently closes the Gateway connection. */\n public close(): void {\n this.#closed = true;\n this.#clearTimers();\n this.#settleConnect(\n new GatewayError(\"Gateway connection closed before socket open.\"),\n );\n const ws = this.#ws;\n this.#ws = undefined;\n try {\n ws?.close(1000, \"Client closed connection\");\n } catch (error) {\n this.#emitError(error);\n }\n this.#setState(GatewayState.Closed);\n }\n /** Registers a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\n /** Emits an event to all registered listeners. @param event Event name. @param data Event payload. */\n public emit(event: string, data?: unknown): void {\n this.#emit(event, data);\n }\n public on(event: string, listener: GatewayListener): this {\n if (!event || typeof listener !== \"function\")\n throw new TypeError(\"Gateway event and listener are required.\");\n let listeners = this.#listeners.get(event);\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\n listeners.add(listener);\n return this;\n }\n /** Removes a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\n public off(event: string, listener: GatewayListener): this {\n this.#listeners.get(event)?.delete(listener);\n return this;\n }\n /** Sends a Gateway payload. @param payload Gateway payload. @returns Whether it was sent. */\n public send(payload: GatewayPayload): boolean {\n if (!payload || !Number.isInteger(payload.op))\n throw new TypeError(\"Gateway payload must contain an integer opcode.\");\n if (this.#ws?.readyState !== WebSocket.OPEN) return false;\n const now = Date.now();\n while (\n this.#sendTimestamps.length &&\n now - this.#sendTimestamps[0]! >= 60000\n )\n this.#sendTimestamps.shift();\n if (this.#sendTimestamps.length >= 115) {\n this.#emitError(new GatewayError(\"Gateway send rate budget exhausted.\"));\n return false;\n }\n try {\n this.#ws.send(JSON.stringify(payload));\n this.#sendTimestamps.push(now);\n return true;\n } catch (error) {\n this.#emitError(error);\n return false;\n }\n }\n /** Sends a presence update. @param data Presence payload. @returns Whether it was sent. */\n public setPresence(data: GatewayPresence): boolean {\n this.#options.presence = { ...this.#options.presence, ...data };\n const payload = {\n since: this.#options.presence.since ?? null,\n activities: this.#options.presence.activities ?? [],\n status: this.#options.presence.status ?? \"online\",\n afk: Boolean(this.#options.presence.afk),\n };\n return this.send({\n op: GatewayOpcodes.PresenceUpdate,\n d: payload,\n s: null,\n t: null,\n });\n }\n /** Sends a voice state update. @param data Voice state payload. @returns Whether it was sent. */\n public setVoiceState(data: Record<string, unknown>): boolean {\n return this.send({\n op: GatewayOpcodes.VoiceStateUpdate,\n d: data,\n s: null,\n t: null,\n });\n }\n /** Requests guild members. @param data Guild member request payload. @returns Whether it was sent. */\n public requestGuildMembers(data: Record<string, unknown>): boolean {\n return this.send({\n op: GatewayOpcodes.RequestGuildMembers,\n d: data,\n s: null,\n t: null,\n });\n }\n #open(url: string): void {\n if (this.#closed) return;\n let ws: WebSocket;\n try {\n ws = new WebSocket(url);\n } catch (error) {\n const failure = this.#normalizeError(error);\n this.#emitError(failure);\n if (this.#options.reconnect) {\n this.#setState(GatewayState.Reconnect);\n this.#scheduleReconnect();\n } else this.#settleConnect(failure);\n return;\n }\n this.#ws = ws;\n ws.addEventListener(\"open\", () => {\n this.#lastMessageAt = Date.now();\n this.#zombieReported = false;\n this.#startZombieDetection();\n this.#emit(\"open\", undefined);\n this.#settleConnect();\n });\n ws.addEventListener(\"message\", (event) => {\n this.#lastMessageAt = Date.now();\n this.#zombieReported = false;\n this.#message(ws, String(event.data));\n });\n ws.addEventListener(\"close\", (event) => this.#close(ws, event.code));\n ws.addEventListener(\"error\", () =>\n this.#emitError(new GatewayError(\"Gateway WebSocket error\")),\n );\n }\n #startZombieDetection(): void {\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\n const interval = Math.max(\n 1,\n Math.min(\n 250,\n this.#options.heartbeatAckTimeout,\n this.#options.zombieTimeout / 2,\n ),\n );\n this.#zombieTimer = setInterval(() => {\n if (\n this.#closed ||\n this.#ws?.readyState !== WebSocket.OPEN ||\n this.#lastMessageAt === 0\n )\n return;\n const silentFor = Date.now() - this.#lastMessageAt;\n const deadline = Math.max(\n this.#options.zombieTimeout,\n this.#heartbeatInterval + this.#options.heartbeatAckTimeout,\n );\n if (silentFor < deadline || this.#zombieReported) return;\n this.#zombieReported = true;\n const error = new GatewayError(\n `Gateway connection appears stale after ${silentFor}ms without traffic.`,\n );\n this.#emit(\"zombie\", { silentFor, timeout: deadline });\n this.#emitError(error);\n try {\n this.#ws.close(1001, \"Zombie Gateway connection\");\n } catch (closeError) {\n this.#emitError(closeError);\n }\n }, interval);\n }\n #message(ws: WebSocket, raw: string): void {\n let payload: GatewayPayload;\n try {\n payload = JSON.parse(raw) as GatewayPayload;\n } catch (error) {\n this.#emitError(\n new GatewayError(\"Gateway returned invalid JSON\", undefined, {\n cause: error,\n }),\n );\n ws.close(1002, \"Invalid JSON\");\n return;\n }\n if (!payload || typeof payload.op !== \"number\") {\n this.#emitError(new GatewayError(\"Gateway returned an invalid payload\"));\n ws.close(1002, \"Invalid payload\");\n return;\n }\n if (typeof payload.s === \"number\") this.#sequence = payload.s;\n switch (payload.op) {\n case GatewayOpcodes.Dispatch:\n this.#setState(GatewayState.Dispatch);\n this.#handleDispatch(payload.t, payload.d);\n break;\n case GatewayOpcodes.Hello:\n this.#handleHello(payload.d);\n break;\n case GatewayOpcodes.Heartbeat:\n this.#setState(GatewayState.Heartbeat);\n this.#sendHeartbeat();\n break;\n case GatewayOpcodes.HeartbeatAck:\n this.#heartbeatACK = true;\n this.#clearHeartbeatAckTimer();\n this.#emit(\"heartbeatAck\", payload.d);\n break;\n case GatewayOpcodes.Reconnect:\n ws.close(1001, \"Server requested reconnect\");\n break;\n case GatewayOpcodes.InvalidSession:\n this.#sessionId = undefined;\n this.#sequence = null;\n this.#emit(\"invalidSession\", payload.d);\n ws.close(1000, \"Invalid session\");\n break;\n }\n }\n #handleDispatch(event: string | null, data: unknown): void {\n if (event === \"READY\") {\n const ready = data as {\n session_id?: unknown;\n resume_gateway_url?: unknown;\n };\n if (\n typeof ready?.session_id !== \"string\" ||\n typeof ready?.resume_gateway_url !== \"string\"\n ) {\n this.#emitError(\n new GatewayError(\n \"Gateway READY payload is missing session information\",\n ),\n );\n return;\n }\n this.#sessionId = ready.session_id;\n this.#resumeURL = ready.resume_gateway_url;\n this.#attempt = 0;\n this.#setState(GatewayState.Ready);\n this.#emit(\"ready\", data);\n }\n this.#emit(event ?? \"dispatch\", data);\n }\n #handleHello(data: unknown): void {\n const interval = (data as { heartbeat_interval?: unknown })\n ?.heartbeat_interval;\n if (\n typeof interval !== \"number\" ||\n !Number.isFinite(interval) ||\n interval <= 0\n ) {\n this.#emitError(\n new GatewayError(\n \"Gateway HELLO payload contains an invalid heartbeat interval\",\n ),\n );\n this.#ws?.close(1002, \"Invalid heartbeat interval\");\n return;\n }\n this.#heartbeatInterval = interval;\n this.#setState(\n this.#sessionId && this.#sequence !== null && this.#resumeURL\n ? GatewayState.Resume\n : GatewayState.Hello,\n );\n this.#startHeartbeat(interval);\n this.#identifyOrResume();\n }\n #identifyOrResume(): void {\n if (this.#sessionId && this.#sequence !== null && this.#resumeURL) {\n this.#setState(GatewayState.Resume);\n this.send({\n op: GatewayOpcodes.Resume,\n d: {\n token: this.#options.token,\n session_id: this.#sessionId,\n seq: this.#sequence,\n },\n s: null,\n t: null,\n });\n return;\n }\n this.#setState(GatewayState.Identify);\n const props = this.#options.properties ?? {\n os: \"Android\",\n browser: \"Discord Android\",\n device: \"Discord Android\",\n };\n const os = props.os ?? \"Android\";\n const browser = props.browser ?? \"Discord Android\";\n const device = props.device ?? \"Discord Android\";\n this.send({\n op: GatewayOpcodes.Identify,\n d: {\n token: this.#options.token,\n intents: this.#options.intents,\n properties: {\n os,\n browser,\n device,\n $os: os,\n $browser: browser,\n $device: device,\n ...props,\n },\n presence: {\n since: this.#options.presence?.since ?? null,\n activities: this.#options.presence?.activities ?? [],\n status: this.#options.presence?.status ?? \"online\",\n afk: Boolean(this.#options.presence?.afk),\n },\n shard: [this.#options.shardId, this.#options.shardCount],\n },\n s: null,\n t: null,\n });\n }\n #startHeartbeat(interval: number): void {\n this.#clearHeartbeatTimers();\n this.#heartbeatACK = true;\n this.#initialHeartbeat = setTimeout(\n () => this.#sendHeartbeat(),\n Math.random() * interval,\n );\n this.#heartbeatTimer = setInterval(() => this.#sendHeartbeat(), interval);\n }\n #sendHeartbeat(): void {\n this.#heartbeatACK = false;\n this.#heartbeatSentAt = Date.now();\n if (\n !this.send({\n op: GatewayOpcodes.Heartbeat,\n d: this.#sequence,\n s: null,\n t: null,\n })\n ) {\n this.#emitError(\n new GatewayError(\n \"Unable to send Gateway heartbeat because the WebSocket is not open.\",\n ),\n );\n return;\n }\n this.#clearHeartbeatAckTimer();\n this.#heartbeatAckTimer = setTimeout(() => {\n if (!this.#heartbeatACK) {\n const elapsed = Date.now() - this.#heartbeatSentAt;\n this.#emitError(\n new GatewayError(\n `Gateway heartbeat acknowledgement timed out after ${elapsed}ms.`,\n ),\n );\n try {\n this.#ws?.close(1001, \"Heartbeat timeout\");\n } catch (error) {\n this.#emitError(error);\n }\n }\n }, this.#options.heartbeatAckTimeout);\n }\n #close(ws: WebSocket, code: number): void {\n if (this.#ws !== ws) return;\n this.#ws = undefined;\n this.#clearTimers();\n const action = this.#closeAction(code);\n this.#emit(\"close\", { code, action });\n if (this.#closed || !this.#options.reconnect || action === \"stop\") {\n this.#setState(this.#closed ? GatewayState.Closed : GatewayState.Connect);\n if (this.#connectPromise)\n this.#settleConnect(\n new GatewayError(`Gateway closed before READY (code ${code}).`, code),\n );\n return;\n }\n if (action === \"identify\") {\n this.#sequence = null;\n this.#sessionId = undefined;\n }\n this.#setState(GatewayState.Reconnect);\n this.#scheduleReconnect();\n }\n #closeAction(code: number): \"resume\" | \"identify\" | \"stop\" {\n if ([4004, 4010, 4011, 4012, 4013, 4014].includes(code)) return \"stop\";\n if ([4007, 4009].includes(code)) return \"identify\";\n return this.#sessionId && this.#sequence !== null ? \"resume\" : \"identify\";\n }\n #scheduleReconnect(): void {\n if (this.#closed || !this.#options.reconnect || this.#reconnectTimer)\n return;\n if (this.#attempt >= this.#options.maxReconnectAttempts) {\n this.#settleConnect(\n new GatewayError(\"Gateway reconnect attempts exhausted.\"),\n );\n this.#setState(GatewayState.Connect);\n return;\n }\n const delay = Math.min(\n this.#options.reconnectMaxDelay,\n this.#options.reconnectBaseDelay * 2 ** this.#attempt++,\n );\n const jitter = Math.random() * Math.min(250, Math.max(1, delay * 0.25));\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = undefined;\n this.#open(\n this.#resumeURL ?? \"wss://gateway.discord.gg/?v=10&encoding=json\",\n );\n }, delay + jitter);\n }\n #settleConnect(error?: GatewayError): void {\n const resolve = this.#resolveConnect;\n const reject = this.#rejectConnect;\n this.#resolveConnect = undefined;\n this.#rejectConnect = undefined;\n this.#connectPromise = undefined;\n if (error) reject?.(error);\n else resolve?.();\n }\n #clearHeartbeatAckTimer(): void {\n if (this.#heartbeatAckTimer) clearTimeout(this.#heartbeatAckTimer);\n this.#heartbeatAckTimer = undefined;\n }\n #clearHeartbeatTimers(): void {\n if (this.#heartbeatTimer) clearInterval(this.#heartbeatTimer);\n if (this.#initialHeartbeat) clearTimeout(this.#initialHeartbeat);\n this.#clearHeartbeatAckTimer();\n this.#heartbeatTimer = undefined;\n this.#initialHeartbeat = undefined;\n }\n #clearTimers(): void {\n if (this.#reconnectTimer) clearTimeout(this.#reconnectTimer);\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\n this.#reconnectTimer = undefined;\n this.#zombieTimer = undefined;\n this.#clearHeartbeatTimers();\n }\n #normalizeError(error: unknown): GatewayError {\n return error instanceof GatewayError\n ? error\n : new GatewayError(\"Gateway connection failed.\", undefined, {\n cause: error,\n });\n }\n #emitError(error: unknown): void {\n this.#emit(\n \"error\",\n error instanceof Error ? error : new GatewayError(String(error)),\n );\n }\n #emit(event: string, data: unknown): void {\n for (const listener of this.#listeners.get(event) ?? []) {\n try {\n const result = listener(data);\n if (\n result &&\n typeof (result as PromiseLike<unknown>).then === \"function\"\n )\n void Promise.resolve(result).catch((error) => {\n if (event !== \"error\") this.#emitError(error);\n });\n } catch (error) {\n if (event !== \"error\") this.#emitError(error);\n }\n }\n }\n /** Changes the Gateway lifecycle state. @param next Next lifecycle state. */\n #setState(next: GatewayState): void {\n if (this.state === next) return;\n const previous = this.state;\n this.state = next;\n this.#emit(\"stateChange\", { previous, next });\n }\n}\n",
|
|
7
|
-
"/** Message envelope exchanged between Lunibee shards. */\nexport interface ShardMessage<T = unknown> {\n
|
|
8
|
-
"import { Gateway } from \"@lunibee/ws\";\nexport { ShardBus } from \"./bus.js\";\nexport type { ShardMessage, ShardMessageHandler } from \"./bus.js\";\n\n/** Configuration for a sharded Gateway client. */\nexport interface ShardManagerOptions {\n /** Bot token. */ token: string;\n /** Gateway intents. */ intents: number;\n /** Number of shards. Use `\"auto\"` to request Discord's recommended count. */ shardCount?:\n number | \"auto\";\n /** Gateway reconnect behavior. */ reconnect?: boolean;\n /** Delay between shard starts in milliseconds. */ spawnDelay?: number;\n}\n/** Runtime state for a managed shard. */\nexport interface ShardInfo {\n /** Shard identifier. */ id: number;\n /** Gateway instance. */ gateway: Gateway;\n}\n\n/** Manages independent Discord Gateway shards with explicit destruction and reinitialization semantics. */\nexport class ShardManager {\n /** Active Gateway shards indexed by shard identifier. */ public readonly shards =\n new Map<number, Gateway>();\n /** Number of shards managed by this instance after initialization. */ public get shardCount(): number {\n return this.shards.size;\n }\n readonly #options: ShardManagerOptions;\n /** Whether the shard set has been initialized. */ #resolved = false;\n /** Whether this manager is currently destroyed. */ #destroyed = false;\n /** Creates a shard manager. @param options Sharding configuration. @throws {TypeError} If token or intents are invalid. @throws {RangeError} If shard count is invalid. */\n public constructor(options: ShardManagerOptions) {\n if (!options.token?.trim())\n throw new TypeError(\"A Discord bot token is required.\");\n if (!Number.isInteger(options.intents) || options.intents < 0)\n throw new TypeError(\"Gateway intents must be a non-negative integer.\");\n const count =\n options.shardCount === \"auto\" || options.shardCount === undefined\n ? 1\n : options.shardCount;\n if (!Number.isInteger(count) || count < 1)\n throw new RangeError(\"Shard count must be a positive integer.\");\n this.#options = { ...options };\n if (options.shardCount !== \"auto\") this.#initialize(count);\n }\n /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */\n public async fetchRecommendedShardCount(): Promise<number> {\n const response = await fetch(\"https://discord.com/api/v10/gateway/bot\", {\n headers: {\n Authorization: `Bot ${this.#options.token}`,\n \"User-Agent\": \"Lunibee/0.1.0\",\n },\n });\n if (!response.ok)\n throw new Error(\n `Gateway discovery failed with status ${response.status}`,\n );\n const data = (await response.json()) as { shards?: unknown };\n if (\n typeof data.shards !== \"number\" ||\n !Number.isInteger(data.shards) ||\n data.shards < 1\n )\n throw new Error(\"Gateway discovery returned an invalid shard count.\");\n return data.shards;\n }\n /** Connects all shards sequentially. A destroyed manager is reinitialized before connecting. @returns A promise fulfilled after all shards connect. @throws {Error} If a shard fails to connect. */\n public async connect(): Promise<void> {\n await this.#ensureInitialized();\n for (const [id, shard] of this.shards) {\n try {\n await shard.connect();\n } catch (error) {\n this.destroy();\n throw new Error(`Failed to connect shard ${id}.`, { cause: error });\n }\n if (\n id + 1 < this.shardCount &&\n this.#options.spawnDelay &&\n this.#options.spawnDelay > 0\n )\n await Bun.sleep(this.#options.spawnDelay);\n }\n }\n /** Permanently closes the current shard set and releases Gateway resources. The next connect recreates them. @returns Nothing. */\n public destroy(): void {\n for (const shard of this.shards.values()) shard.close();\n this.shards.clear();\n this.#resolved = false;\n this.#destroyed = true;\n }\n /** Calculates the target shard ID for a Discord guild snowflake. @param guildId Guild snowflake. @returns Shard identifier. */\n public getShardIdForGuild(guildId: string): number {\n const id = BigInt(guildId);\n return Number((id >> 22n) % BigInt(this.shardCount || 1));\n }\n /** Gets a shard by ID. @param id Shard identifier. @returns Gateway instance or undefined. */ public get(\n id: number,\n ): Gateway | undefined {\n return this.shards.get(id);\n }\n /** Returns information for all managed shards. @returns Shard information snapshots. */ public values(): ShardInfo[] {\n return [...this.shards].map(([id, gateway]) => ({ id, gateway }));\n }\n /** Ensures a live shard set exists. @returns A promise fulfilled after initialization. @throws {Error} If initialization fails. */\n async #ensureInitialized(): Promise<void> {\n if (this.#resolved) return;\n if (this.#destroyed) this.#destroyed = false;\n const count =\n this.#options.shardCount === \"auto\"\n ? await this.fetchRecommendedShardCount()\n : (this.#options.shardCount ?? 1);\n this.#initialize(count);\n }\n /** Creates every shard for a resolved shard count. @param count Number of shards. @returns Nothing. */\n #initialize(count: number): void {\n if (this.#resolved) return;\n for (let id = 0; id < count; id++)\n this.shards.set(id, this.#createShard(id, count));\n this.#resolved = true;\n }\n /** Creates one Gateway instance with its shard identity. @param id Shard identifier. @param count Total shard count. @returns Configured Gateway. */\n #createShard(id: number, count: number): Gateway {\n return new Gateway({\n token: this.#options.token,\n intents: this.#options.intents,\n shardId: id,\n shardCount: count,\n reconnect: this.#options.reconnect ?? true,\n });\n }\n}\n"
|
|
5
|
+
"/** Discord API snowflake identifier. */\r\nexport type Snowflake = string;\r\n\r\n// ─── Gateway Intents ─────────────────────────────────────────────────────────\r\n\r\n/** Gateway intent bit flags supported by Discord (PascalCase). */\r\nexport const GatewayIntentBits = {\r\n Guilds: 1 << 0,\r\n GuildMembers: 1 << 1,\r\n GuildModeration: 1 << 2,\r\n GuildExpressions: 1 << 3,\r\n GuildIntegrations: 1 << 4,\r\n GuildWebhooks: 1 << 5,\r\n GuildInvites: 1 << 6,\r\n GuildVoiceStates: 1 << 7,\r\n GuildPresences: 1 << 8,\r\n GuildMessages: 1 << 9,\r\n GuildMessageReactions: 1 << 10,\r\n GuildMessageTyping: 1 << 11,\r\n DirectMessages: 1 << 12,\r\n DirectMessageReactions: 1 << 13,\r\n DirectMessageTyping: 1 << 14,\r\n MessageContent: 1 << 15,\r\n GuildScheduledEvents: 1 << 16,\r\n AutoModerationConfiguration: 1 << 20,\r\n AutoModerationExecution: 1 << 21,\r\n GuildMessagePolls: 1 << 24,\r\n DirectMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Idiomatic camelCase Gateway intent bit flags supported by Discord. */\r\nexport const IntentBits = {\r\n guild: 1 << 0,\r\n guilds: 1 << 0,\r\n guildMembers: 1 << 1,\r\n guildModeration: 1 << 2,\r\n guildBans: 1 << 2,\r\n guildExpressions: 1 << 3,\r\n guildEmojis: 1 << 3,\r\n guildEmojisAndStickers: 1 << 3,\r\n guildIntegrations: 1 << 4,\r\n guildWebhooks: 1 << 5,\r\n guildInvites: 1 << 6,\r\n guildVoiceStates: 1 << 7,\r\n guildPresences: 1 << 8,\r\n guildMessages: 1 << 9,\r\n guildMessage: 1 << 9,\r\n guildMessageReactions: 1 << 10,\r\n guildMessageTyping: 1 << 11,\r\n directMessages: 1 << 12,\r\n directMessageReactions: 1 << 13,\r\n directMessageTyping: 1 << 14,\r\n messageContent: 1 << 15,\r\n guildScheduledEvents: 1 << 16,\r\n autoModerationConfiguration: 1 << 20,\r\n autoModerationExecution: 1 << 21,\r\n guildMessagePolls: 1 << 24,\r\n directMessagePolls: 1 << 25,\r\n} as const;\r\n\r\n/** Alias for IntentBits. */\r\nexport const Intents = IntentBits;\r\n\r\n/** Gateway intent resolvable value (single bitfield, enum key, or array of bitfields/strings). */\r\nexport type GatewayIntentResolvable =\r\n | number\r\n | keyof typeof GatewayIntentBits\r\n | keyof typeof IntentBits\r\n | (\r\n | number\r\n | keyof typeof GatewayIntentBits\r\n | keyof typeof IntentBits\r\n | string\r\n )[];\r\n\r\n/** Resolves any GatewayIntentResolvable into a raw bitfield integer. */\r\nexport function resolveGatewayIntents(\r\n intents: GatewayIntentResolvable,\r\n): number {\r\n if (typeof intents === \"number\") return intents;\r\n if (Array.isArray(intents)) {\r\n return intents.reduce<number>((acc, intent) => {\r\n return acc | resolveGatewayIntents(intent as any);\r\n }, 0);\r\n }\r\n if (typeof intents === \"string\") {\r\n if (intents in GatewayIntentBits)\r\n return (GatewayIntentBits as Record<string, number>)[intents]!;\r\n if (intents in IntentBits)\r\n return (IntentBits as Record<string, number>)[intents]!;\r\n const lower = intents.charAt(0).toLowerCase() + intents.slice(1);\r\n if (lower in IntentBits)\r\n return (IntentBits as Record<string, number>)[lower]!;\r\n const num = Number(intents);\r\n if (!Number.isNaN(num)) return num;\r\n }\r\n return 0;\r\n}\r\n\r\n// ─── Gateway Config ───────────────────────────────────────────────────────────\r\n\r\n/** Gateway connection properties (OS, browser, device). */\r\nexport interface GatewayProperties {\r\n os?: string;\r\n browser?: string;\r\n device?: string;\r\n [key: string]: unknown;\r\n}\r\n/** Gateway presence activity. */\r\nexport interface GatewayPresenceActivity {\r\n name: string;\r\n type?: number;\r\n url?: string;\r\n state?: string;\r\n}\r\n/** Gateway presence data. */\r\nexport interface GatewayPresence {\r\n status?: \"online\" | \"dnd\" | \"idle\" | \"invisible\" | \"offline\";\r\n activities?: GatewayPresenceActivity[];\r\n afk?: boolean;\r\n since?: number | null;\r\n}\r\n/** Client configuration. */\r\nexport interface ClientOptions {\r\n token: string;\r\n intents: GatewayIntentResolvable;\r\n gateway?: GatewayOptions;\r\n rest?: RESTOptions;\r\n}\r\n/** Gateway connection configuration. */\r\nexport interface GatewayOptions {\r\n intents?: GatewayIntentResolvable;\r\n reconnect?: boolean;\r\n maxReconnectAttempts?: number;\r\n reconnectBaseDelay?: number;\r\n reconnectMaxDelay?: number;\r\n shardId?: number;\r\n shardCount?: number;\r\n properties?: GatewayProperties;\r\n presence?: GatewayPresence;\r\n}\r\n\r\n/** REST request configuration. */\r\nexport interface RESTOptions {\r\n timeout?: number;\r\n retries?: number;\r\n baseURL?: string;\r\n}\r\n\r\n// ─── Core Discord Objects ─────────────────────────────────────────────────────\r\n\r\n/** Discord user object. */\r\nexport interface UserData {\r\n id: Snowflake;\r\n username: string;\r\n discriminator?: string;\r\n global_name?: string | null;\r\n avatar?: string | null;\r\n bot?: boolean;\r\n system?: boolean;\r\n public_flags?: number;\r\n}\r\n/** Current authenticated bot user. */\r\nexport type ClientUser = UserData;\r\n/** Discord Gateway payload envelope. */\r\nexport interface GatewayPayload<T = unknown> {\r\n op: number;\r\n d: T | null;\r\n s: number | null;\r\n t: string | null;\r\n}\r\n/** Generic Discord API error payload. */\r\nexport interface APIError {\r\n code?: number;\r\n message?: string;\r\n errors?: Record<string, unknown>;\r\n}\r\n\r\n// ─── Discord Flags & Enums ────────────────────────────────────────────────────\r\n\r\n/** Discord message flags. */\r\nexport const MessageFlags = {\r\n Crossposted: 1,\r\n IsCrosspost: 2,\r\n SuppressEmbeds: 4,\r\n SourceMessageDeleted: 8,\r\n Urgent: 16,\r\n HasThread: 32,\r\n Ephemeral: 64,\r\n Loading: 128,\r\n IsComponentsV2: 32768,\r\n} as const;\r\n\r\n/** Discord channel types. */\r\nexport const ChannelType = {\r\n GuildText: 0,\r\n DM: 1,\r\n GuildVoice: 2,\r\n GroupDM: 3,\r\n GuildCategory: 4,\r\n GuildAnnouncement: 5,\r\n AnnouncementThread: 10,\r\n PublicThread: 11,\r\n PrivateThread: 12,\r\n GuildStageVoice: 13,\r\n GuildDirectory: 14,\r\n GuildForum: 15,\r\n GuildMedia: 16,\r\n} as const;\r\n\r\n/** Discord component types. */\r\nexport const ComponentType = {\r\n ActionRow: 1,\r\n Button: 2,\r\n StringSelect: 3,\r\n TextInput: 4,\r\n UserSelect: 5,\r\n RoleSelect: 6,\r\n MentionableSelect: 7,\r\n ChannelSelect: 8,\r\n Section: 9,\r\n TextDisplay: 10,\r\n Thumbnail: 11,\r\n MediaGallery: 12,\r\n File: 13,\r\n Separator: 14,\r\n ContentInventoryEntry: 16,\r\n Container: 17,\r\n} as const;\r\n\r\n/** Discord button style types. */\r\nexport const ButtonStyle = {\r\n Primary: 1,\r\n Secondary: 2,\r\n Success: 3,\r\n Danger: 4,\r\n Link: 5,\r\n Premium: 6,\r\n} as const;\r\n\r\n/** Discord text input style types. */\r\nexport const TextInputStyle = {\r\n Short: 1,\r\n Paragraph: 2,\r\n} as const;\r\n\r\n/** Discord interaction response types. */\r\nexport const InteractionResponseType = {\r\n Pong: 1,\r\n ChannelMessage: 4,\r\n DeferredChannelMessage: 5,\r\n DeferredMessageUpdate: 6,\r\n MessageUpdate: 7,\r\n Autocomplete: 8,\r\n Modal: 9,\r\n} as const;\r\n\r\n/** Discord sticker format types. */\r\nexport const StickerFormatType = {\r\n PNG: 1,\r\n APNG: 2,\r\n Lottie: 3,\r\n GIF: 4,\r\n} as const;\r\n\r\n/** Discord sticker types. */\r\nexport const StickerType = {\r\n Standard: 1,\r\n Guild: 2,\r\n} as const;\r\n\r\n/** Discord verification levels. */\r\nexport const VerificationLevel = {\r\n None: 0,\r\n Low: 1,\r\n Medium: 2,\r\n High: 3,\r\n VeryHigh: 4,\r\n} as const;\r\n\r\n/** Discord premium tiers. */\r\nexport const PremiumTier = {\r\n None: 0,\r\n Tier1: 1,\r\n Tier2: 2,\r\n Tier3: 3,\r\n} as const;\r\n\r\n// ─── Embeds ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord embed footer object. */\r\nexport interface APIEmbedFooter {\r\n text: string;\r\n icon_url?: string;\r\n proxy_icon_url?: string;\r\n}\r\n/** Raw Discord embed image/thumbnail/video object. */\r\nexport interface APIEmbedMedia {\r\n url: string;\r\n proxy_url?: string;\r\n height?: number;\r\n width?: number;\r\n}\r\n/** Raw Discord embed provider object. */\r\nexport interface APIEmbedProvider {\r\n name?: string;\r\n url?: string;\r\n}\r\n/** Raw Discord embed author object. */\r\nexport interface APIEmbedAuthor {\r\n name: string;\r\n url?: string;\r\n icon_url?: string;\r\n proxy_icon_url?: string;\r\n}\r\n/** Raw Discord embed field object. */\r\nexport interface APIEmbedField {\r\n name: string;\r\n value: string;\r\n inline?: boolean;\r\n}\r\n/** Raw Discord embed object. */\r\nexport interface APIEmbed {\r\n title?: string;\r\n type?: string;\r\n description?: string;\r\n url?: string;\r\n timestamp?: string;\r\n color?: number;\r\n footer?: APIEmbedFooter;\r\n image?: APIEmbedMedia;\r\n thumbnail?: APIEmbedMedia;\r\n video?: APIEmbedMedia;\r\n provider?: APIEmbedProvider;\r\n author?: APIEmbedAuthor;\r\n fields?: APIEmbedField[];\r\n}\r\n\r\n// ─── Message Components ───────────────────────────────────────────────────────\r\n\r\n/** Raw Discord select menu option. */\r\nexport interface APISelectMenuOption {\r\n label: string;\r\n value: string;\r\n description?: string;\r\n emoji?: APIPartialEmoji;\r\n default?: boolean;\r\n}\r\n/** Raw Discord emoji (partial, as used in components). */\r\nexport interface APIPartialEmoji {\r\n id?: Snowflake | null;\r\n name?: string | null;\r\n animated?: boolean;\r\n}\r\n/** Raw Discord button component. */\r\nexport interface APIButtonComponent {\r\n type: 2;\r\n style: number;\r\n label?: string;\r\n emoji?: APIPartialEmoji;\r\n custom_id?: string;\r\n url?: string;\r\n sku_id?: string;\r\n disabled?: boolean;\r\n}\r\n/** Raw Discord select menu component. */\r\nexport interface APISelectMenuComponent {\r\n type: 3 | 5 | 6 | 7 | 8;\r\n custom_id: string;\r\n options?: APISelectMenuOption[];\r\n channel_types?: number[];\r\n placeholder?: string;\r\n min_values?: number;\r\n max_values?: number;\r\n disabled?: boolean;\r\n}\r\n/** Raw Discord text input component. */\r\nexport interface APITextInputComponent {\r\n type: 4;\r\n custom_id: string;\r\n style: number;\r\n label: string;\r\n min_length?: number;\r\n max_length?: number;\r\n required?: boolean;\r\n value?: string;\r\n placeholder?: string;\r\n}\r\n/** Union of all raw message component types. */\r\nexport type APIMessageActionRowComponent =\r\n APIButtonComponent | APISelectMenuComponent | APITextInputComponent;\r\n/** Raw Discord action row component. */\r\nexport interface APIActionRowComponent {\r\n type: 1;\r\n id?: number;\r\n components: APIMessageActionRowComponent[];\r\n}\r\n/** Union including action rows and V2 components. */\r\nexport type APIMessageComponent =\r\n APIActionRowComponent | { type: Exclude<number, 1>; [key: string]: any };\r\n\r\n// ─── Attachments ─────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord attachment object. */\r\nexport interface APIAttachment {\r\n id: Snowflake;\r\n filename: string;\r\n description?: string | null;\r\n content_type?: string;\r\n size: number;\r\n url: string;\r\n proxy_url: string;\r\n height?: number | null;\r\n width?: number | null;\r\n ephemeral?: boolean;\r\n duration_secs?: number;\r\n waveform?: string;\r\n flags?: number;\r\n}\r\n\r\n// ─── Stickers ────────────────────────────────────────────────────────────────\r\n\r\n/** Compact sticker item as included in messages. */\r\nexport interface APIStickerItem {\r\n id: Snowflake;\r\n name: string;\r\n format_type: number;\r\n}\r\n/** Full Discord sticker object. */\r\nexport interface APISticker {\r\n id: Snowflake;\r\n pack_id?: Snowflake;\r\n name: string;\r\n description?: string | null;\r\n tags?: string;\r\n type: number;\r\n format_type: number;\r\n available?: boolean;\r\n guild_id?: Snowflake;\r\n user?: UserData;\r\n sort_value?: number;\r\n}\r\n\r\n// ─── Messages ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord message reference. */\r\nexport interface APIMessageReference {\r\n type?: number;\r\n message_id?: Snowflake;\r\n channel_id?: Snowflake;\r\n guild_id?: Snowflake;\r\n fail_if_not_exists?: boolean;\r\n}\r\n/** Raw Discord message reaction. */\r\nexport interface APIMessageReaction {\r\n count: number;\r\n count_details?: Record<string, number>;\r\n me?: boolean;\r\n me_burst?: boolean;\r\n emoji: APIPartialEmoji;\r\n burst_colors?: string[];\r\n}\r\n/** Raw Discord message object. */\r\nexport interface APIMessage {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n author: UserData;\r\n content: string;\r\n timestamp?: string;\r\n edited_timestamp?: string | null;\r\n tts?: boolean;\r\n mention_everyone?: boolean;\r\n mentions?: UserData[];\r\n mention_roles?: Snowflake[];\r\n mention_channels?: Array<Record<string, unknown>>;\r\n attachments?: APIAttachment[];\r\n embeds?: APIEmbed[];\r\n reactions?: APIMessageReaction[];\r\n nonce?: string | number | null;\r\n pinned?: boolean;\r\n webhook_id?: Snowflake;\r\n type?: number;\r\n activity?: Record<string, unknown>;\r\n application?: Record<string, unknown>;\r\n application_id?: Snowflake;\r\n message_reference?: APIMessageReference;\r\n flags?: number;\r\n referenced_message?: APIMessage | null;\r\n interaction_metadata?: Record<string, unknown>;\r\n thread?: Record<string, unknown>;\r\n components?: APIMessageComponent[];\r\n sticker_items?: APIStickerItem[];\r\n stickers?: APISticker[];\r\n position?: number;\r\n role_subscription_data?: Record<string, unknown>;\r\n purchase_notification?: Record<string, unknown>;\r\n poll?: Record<string, unknown>;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n// ─── Channels ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord channel object. */\r\nexport interface APIChannel {\r\n id: Snowflake;\r\n type: number;\r\n guild_id?: Snowflake;\r\n position?: number;\r\n permission_overwrites?: Array<Record<string, unknown>>;\r\n name?: string | null;\r\n topic?: string | null;\r\n nsfw?: boolean;\r\n last_message_id?: Snowflake | null;\r\n bitrate?: number;\r\n user_limit?: number;\r\n rate_limit_per_user?: number;\r\n recipients?: UserData[];\r\n icon?: string | null;\r\n owner_id?: Snowflake;\r\n application_id?: Snowflake;\r\n parent_id?: Snowflake | null;\r\n last_pin_timestamp?: string | null;\r\n rtc_region?: string | null;\r\n video_quality_mode?: number;\r\n message_count?: number;\r\n member_count?: number;\r\n thread_metadata?: APIThreadMetadata;\r\n default_auto_archive_duration?: number;\r\n flags?: number;\r\n total_message_sent?: number;\r\n available_tags?: APIForumTag[];\r\n default_reaction_emoji?: APIPartialEmoji | null;\r\n default_thread_rate_limit_per_user?: number;\r\n default_sort_order?: number | null;\r\n default_forum_layout?: number;\r\n}\r\n\r\n/** Raw Discord thread metadata. */\r\nexport interface APIThreadMetadata {\r\n archived: boolean;\r\n auto_archive_duration: number;\r\n archive_timestamp: string;\r\n locked: boolean;\r\n invitable?: boolean;\r\n create_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord forum tag. */\r\nexport interface APIForumTag {\r\n id: Snowflake;\r\n name: string;\r\n moderated: boolean;\r\n emoji_id?: Snowflake | null;\r\n emoji_name?: string | null;\r\n}\r\n\r\n/** Raw Discord thread member. */\r\nexport interface APIThreadMember {\r\n id?: Snowflake;\r\n user_id?: Snowflake;\r\n join_timestamp: string;\r\n flags: number;\r\n member?: APIGuildMember;\r\n}\r\n\r\n// ─── Roles ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord role object. */\r\nexport interface APIRole {\r\n id: Snowflake;\r\n name: string;\r\n color: number;\r\n hoist: boolean;\r\n icon?: string | null;\r\n unicode_emoji?: string | null;\r\n position: number;\r\n permissions: string;\r\n managed: boolean;\r\n mentionable: boolean;\r\n tags?: Record<string, unknown>;\r\n flags?: number;\r\n}\r\n\r\n// ─── Emoji ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord emoji object. */\r\nexport interface APIEmoji {\r\n id: Snowflake | null;\r\n name: string | null;\r\n roles?: Snowflake[];\r\n user?: UserData;\r\n require_colons?: boolean;\r\n managed?: boolean;\r\n animated?: boolean;\r\n available?: boolean;\r\n}\r\n\r\n// ─── Guilds ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild object. */\r\nexport interface APIGuild {\r\n id: Snowflake;\r\n name: string;\r\n icon?: string | null;\r\n icon_hash?: string | null;\r\n splash?: string | null;\r\n discovery_splash?: string | null;\r\n owner?: boolean;\r\n owner_id?: Snowflake;\r\n permissions?: string;\r\n afk_channel_id?: Snowflake | null;\r\n afk_timeout?: number;\r\n widget_enabled?: boolean;\r\n widget_channel_id?: Snowflake | null;\r\n verification_level?: number;\r\n default_message_notifications?: number;\r\n explicit_content_filter?: number;\r\n roles?: APIRole[];\r\n emojis?: APIEmoji[];\r\n stickers?: APISticker[];\r\n features?: string[];\r\n mfa_level?: number;\r\n application_id?: Snowflake | null;\r\n system_channel_id?: Snowflake | null;\r\n system_channel_flags?: number;\r\n rules_channel_id?: Snowflake | null;\r\n max_presences?: number | null;\r\n max_members?: number;\r\n vanity_url_code?: string | null;\r\n description?: string | null;\r\n banner?: string | null;\r\n premium_tier?: number;\r\n premium_subscription_count?: number;\r\n preferred_locale?: string;\r\n public_updates_channel_id?: Snowflake | null;\r\n max_video_channel_users?: number;\r\n approximate_member_count?: number;\r\n approximate_presence_count?: number;\r\n nsfw_level?: number;\r\n safety_alerts_channel_id?: Snowflake | null;\r\n member_count?: number;\r\n}\r\n\r\n// ─── Welcome Screen ───────────────────────────────────────────────────────────\r\n\r\nexport interface APIWelcomeScreenChannel {\r\n channel_id: Snowflake;\r\n description: string;\r\n emoji_id: Snowflake | null;\r\n emoji_name: string | null;\r\n}\r\n\r\nexport interface APIGuildWelcomeScreen {\r\n description: string | null;\r\n welcome_channels: APIWelcomeScreenChannel[];\r\n}\r\n\r\n// ─── Guild Onboarding ─────────────────────────────────────────────────────────\r\n\r\nexport interface APIOnboardingPromptOption {\r\n id: Snowflake;\r\n channel_ids: Snowflake[];\r\n role_ids: Snowflake[];\r\n emoji?: APIPartialEmoji;\r\n title: string;\r\n description: string | null;\r\n}\r\n\r\nexport interface APIOnboardingPrompt {\r\n id: Snowflake;\r\n type: number;\r\n options: APIOnboardingPromptOption[];\r\n title: string;\r\n single_select: boolean;\r\n required: boolean;\r\n in_onboarding: boolean;\r\n}\r\n\r\nexport interface APIGuildOnboarding {\r\n guild_id: Snowflake;\r\n prompts: APIOnboardingPrompt[];\r\n default_channel_ids: Snowflake[];\r\n enabled: boolean;\r\n mode: number;\r\n}\r\n\r\n/** Raw Discord guild member object. */\r\nexport interface APIGuildMember {\r\n user: UserData;\r\n nick?: string | null;\r\n avatar?: string | null;\r\n roles: Snowflake[];\r\n joined_at: string;\r\n premium_since?: string | null;\r\n deaf?: boolean;\r\n mute?: boolean;\r\n flags?: number;\r\n pending?: boolean;\r\n permissions?: string;\r\n communication_disabled_until?: string | null;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n// ─── Voice ────────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord voice state object. */\r\nexport interface APIVoiceState {\r\n guild_id?: Snowflake;\r\n channel_id?: Snowflake | null;\r\n user_id: Snowflake;\r\n member?: APIGuildMember;\r\n session_id: string;\r\n deaf: boolean;\r\n mute: boolean;\r\n self_deaf: boolean;\r\n self_mute: boolean;\r\n self_stream?: boolean;\r\n self_video: boolean;\r\n suppress: boolean;\r\n request_to_speak_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord voice server update event. */\r\nexport interface APIVoiceServerUpdate {\r\n token: string;\r\n guild_id: Snowflake;\r\n endpoint?: string | null;\r\n}\r\n\r\n// ─── Presence ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord activity object. */\r\nexport interface APIActivity {\r\n name: string;\r\n type: number;\r\n url?: string | null;\r\n created_at: number;\r\n state?: string | null;\r\n details?: string | null;\r\n emoji?: APIPartialEmoji | null;\r\n}\r\n\r\n/** Raw Discord presence update event. */\r\nexport interface APIPresenceUpdate {\r\n user: { id: Snowflake } & Partial<UserData>;\r\n guild_id: Snowflake;\r\n status: string;\r\n activities: APIActivity[];\r\n client_status: { desktop?: string; mobile?: string; web?: string };\r\n}\r\n\r\n// ─── Typing ───────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord typing start event. */\r\nexport interface APITypingStart {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n user_id: Snowflake;\r\n timestamp: number;\r\n member?: APIGuildMember;\r\n}\r\n\r\n// ─── Guilds ──────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild preview. */\r\nexport interface APIGuildPreview {\r\n id: Snowflake;\r\n name: string;\r\n icon: string | null;\r\n splash: string | null;\r\n discovery_splash: string | null;\r\n emojis: unknown[];\r\n features: string[];\r\n approximate_member_count: number;\r\n approximate_presence_count: number;\r\n description: string | null;\r\n stickers: unknown[];\r\n}\r\n\r\n// ─── Invites ─────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord invite. */\r\nexport interface APIInvite {\r\n code: string;\r\n guild?: Partial<APIGuild>;\r\n channel: Partial<APIChannel>;\r\n inviter?: UserData;\r\n target_type?: number;\r\n target_user?: UserData;\r\n approximate_presence_count?: number;\r\n approximate_member_count?: number;\r\n}\r\n\r\n/** Raw Discord invite create event. */\r\nexport interface APIInviteCreate {\r\n channel_id: Snowflake;\r\n code: string;\r\n created_at: string;\r\n guild_id?: Snowflake;\r\n inviter?: UserData;\r\n max_age: number;\r\n max_uses: number;\r\n target_type?: number;\r\n target_user?: UserData;\r\n temporary: boolean;\r\n uses: number;\r\n}\r\n\r\n/** Raw Discord invite delete event. */\r\nexport interface APIInviteDelete {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n code: string;\r\n}\r\n\r\n// ─── Webhooks ────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord webhooks update event. */\r\nexport interface APIWebhooksUpdate {\r\n guild_id: Snowflake;\r\n channel_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord webhook object. */\r\nexport interface APIWebhook {\r\n id: Snowflake;\r\n type: number;\r\n guild_id?: Snowflake | null;\r\n channel_id?: Snowflake | null;\r\n user?: UserData;\r\n name?: string | null;\r\n avatar?: string | null;\r\n token?: string;\r\n application_id?: Snowflake | null;\r\n url?: string;\r\n}\r\n\r\n// ─── AutoMod ─────────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord AutoMod action. */\r\nexport interface APIAutoModerationAction {\r\n type: number;\r\n metadata?: {\r\n channel_id?: Snowflake;\r\n duration_seconds?: number;\r\n custom_message?: string;\r\n };\r\n}\r\n\r\n// ─── Audit Logs ──────────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord audit log change object. */\r\nexport interface APIAuditLogChange {\r\n new_value?: unknown;\r\n old_value?: unknown;\r\n key: string;\r\n}\r\n\r\n/** Raw Discord audit log entry options object. */\r\nexport interface APIAuditLogOptions {\r\n app_id?: Snowflake;\r\n auto_moderation_rule_name?: string;\r\n auto_moderation_rule_trigger_type?: string;\r\n channel_id?: Snowflake;\r\n count?: string;\r\n delete_member_days?: string;\r\n id?: Snowflake;\r\n members_removed?: string;\r\n message_id?: Snowflake;\r\n role_name?: string;\r\n type?: string;\r\n integration_type?: string;\r\n}\r\n\r\n/** Raw Discord audit log entry object. */\r\nexport interface APIAuditLogEntry {\r\n target_id: string | null;\r\n changes?: APIAuditLogChange[];\r\n user_id: Snowflake | null;\r\n id: Snowflake;\r\n action_type: number;\r\n options?: APIAuditLogOptions;\r\n reason?: string;\r\n}\r\n\r\n/** Raw Discord audit log object. */\r\nexport interface APIAuditLog {\r\n audit_log_entries: APIAuditLogEntry[];\r\n guild_scheduled_events: APIGuildScheduledEvent[];\r\n integrations: Array<Record<string, unknown>>;\r\n threads: APIChannel[];\r\n users: UserData[];\r\n webhooks: APIWebhook[];\r\n application_commands: APIApplicationCommand[];\r\n auto_moderation_rules: APIAutoModerationRule[];\r\n}\r\n\r\n/** Raw Discord AutoMod rule object. */\r\nexport interface APIAutoModerationRule {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n name: string;\r\n creator_id: Snowflake;\r\n event_type: number;\r\n trigger_type: number;\r\n trigger_metadata: Record<string, unknown>;\r\n actions: APIAutoModerationAction[];\r\n enabled: boolean;\r\n exempt_roles: Snowflake[];\r\n exempt_channels: Snowflake[];\r\n}\r\n\r\n/** Raw Discord AutoMod action execution event. */\r\nexport interface APIAutoModerationActionExecution {\r\n guild_id: Snowflake;\r\n action: APIAutoModerationAction;\r\n rule_id: Snowflake;\r\n rule_trigger_type: number;\r\n user_id: Snowflake;\r\n channel_id?: Snowflake;\r\n message_id?: Snowflake;\r\n alert_system_message_id?: Snowflake;\r\n content: string;\r\n matched_keyword?: string | null;\r\n matched_content?: string | null;\r\n}\r\n\r\n// ─── Scheduled Events ─────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord guild scheduled event entity metadata. */\r\nexport interface APIGuildScheduledEventEntityMetadata {\r\n location?: string;\r\n}\r\n\r\n/** Raw Discord guild scheduled event object. */\r\nexport interface APIGuildScheduledEvent {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n channel_id?: Snowflake | null;\r\n creator_id?: Snowflake | null;\r\n name: string;\r\n description?: string | null;\r\n scheduled_start_time: string;\r\n scheduled_end_time?: string | null;\r\n privacy_level: number;\r\n status: number;\r\n entity_type: number;\r\n entity_id?: Snowflake | null;\r\n entity_metadata?: APIGuildScheduledEventEntityMetadata | null;\r\n creator?: UserData;\r\n user_count?: number;\r\n image?: string | null;\r\n}\r\n\r\n// ─── Stage Instances ─────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord stage instance object. */\r\nexport interface APIStageInstance {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n channel_id: Snowflake;\r\n topic: string;\r\n privacy_level: number;\r\n discoverable_disabled?: boolean;\r\n guild_scheduled_event_id?: Snowflake | null;\r\n}\r\n\r\n// ─── Application Commands ─────────────────────────────────────────────────────\r\n\r\n/** Discord application command option types. */\r\nexport const ApplicationCommandOptionType = {\r\n SubCommand: 1,\r\n SubCommandGroup: 2,\r\n String: 3,\r\n Integer: 4,\r\n Boolean: 5,\r\n User: 6,\r\n Channel: 7,\r\n Role: 8,\r\n Mentionable: 9,\r\n Number: 10,\r\n Attachment: 11,\r\n} as const;\r\n\r\n/** Discord application command types. */\r\nexport const ApplicationCommandType = {\r\n ChatInput: 1,\r\n User: 2,\r\n Message: 3,\r\n} as const;\r\n\r\n/** Raw Discord application command option. */\r\nexport interface APIApplicationCommandOption {\r\n type: number;\r\n name: string;\r\n description: string;\r\n required?: boolean;\r\n choices?: Array<{ name: string; value: string | number }>;\r\n options?: APIApplicationCommandOption[];\r\n min_value?: number;\r\n max_value?: number;\r\n min_length?: number;\r\n max_length?: number;\r\n autocomplete?: boolean;\r\n channel_types?: number[];\r\n}\r\n\r\n/** Raw Discord application command object (from API). */\r\nexport interface APIApplicationCommand {\r\n id: Snowflake;\r\n type?: number;\r\n application_id: Snowflake;\r\n guild_id?: Snowflake;\r\n name: string;\r\n description: string;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n version: Snowflake;\r\n}\r\n\r\n/** Data for creating/overwriting an application command. */\r\nexport interface ApplicationCommandData {\r\n name: string;\r\n description: string;\r\n type?: number;\r\n options?: APIApplicationCommandOption[];\r\n default_member_permissions?: string | null;\r\n dm_permission?: boolean;\r\n nsfw?: boolean;\r\n}\r\n\r\n// ─── Gateway Events ───────────────────────────────────────────────────────────\r\n\r\n/** Raw Discord READY event payload. */\r\nexport interface APIReadyEvent {\r\n v: number;\r\n user: UserData;\r\n guilds: Array<{ id: Snowflake; unavailable?: boolean }>;\r\n session_id: string;\r\n resume_gateway_url: string;\r\n shard?: [number, number];\r\n application?: { id: Snowflake; flags: number; [key: string]: unknown };\r\n}\r\n\r\n/** Raw Discord reaction event payload. */\r\nexport interface APIMessageReactionEvent {\r\n user_id: Snowflake;\r\n channel_id: Snowflake;\r\n message_id: Snowflake;\r\n guild_id?: Snowflake;\r\n member?: APIGuildMember;\r\n emoji: APIPartialEmoji;\r\n message_author_id?: Snowflake;\r\n burst?: boolean;\r\n type?: number;\r\n}\r\n\r\n/** Raw Discord reaction remove emoji event payload. */\r\nexport interface APIMessageReactionRemoveEmojiEvent {\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n message_id: Snowflake;\r\n emoji: APIPartialEmoji;\r\n}\r\n\r\n/** Raw Discord message deletion event payload. */\r\nexport interface APIMessageDeleteEvent {\r\n id: Snowflake;\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n/** Raw Discord bulk message deletion event payload. */\r\nexport interface APIMessageDeleteBulkEvent {\r\n ids: Snowflake[];\r\n channel_id: Snowflake;\r\n guild_id?: Snowflake;\r\n}\r\n\r\n/** Raw Discord channel deletion event payload. */\r\nexport interface APIChannelDeleteEvent extends APIChannel {}\r\n\r\n/** Raw Discord guild deletion event payload. */\r\nexport interface APIGuildDeleteEvent {\r\n id: Snowflake;\r\n unavailable?: boolean;\r\n}\r\n\r\n/** Raw Discord thread lifecycle payload. */\r\nexport interface APIThreadEvent extends APIChannel {\r\n guild_id: Snowflake;\r\n member?: APIThreadMember;\r\n}\r\n\r\n/** Raw Discord thread list sync event. */\r\nexport interface APIThreadListSync {\r\n guild_id: Snowflake;\r\n channel_ids?: Snowflake[];\r\n threads: APIChannel[];\r\n members: APIThreadMember[];\r\n}\r\n\r\n/** Raw Discord thread members update event. */\r\nexport interface APIThreadMembersUpdate {\r\n id: Snowflake;\r\n guild_id: Snowflake;\r\n member_count: number;\r\n added_members?: APIThreadMember[];\r\n removed_member_ids?: Snowflake[];\r\n}\r\n\r\n/** Raw Discord guild role event payload. */\r\nexport interface APIGuildRoleEvent {\r\n guild_id: Snowflake;\r\n role: APIRole;\r\n}\r\n\r\n/** Raw Discord guild role delete event payload. */\r\nexport interface APIGuildRoleDeleteEvent {\r\n guild_id: Snowflake;\r\n role_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord guild ban event payload. */\r\nexport interface APIGuildBanEvent {\r\n guild_id: Snowflake;\r\n user: UserData;\r\n}\r\n\r\n/** Raw Discord guild emojis update event payload. */\r\nexport interface APIGuildEmojisUpdateEvent {\r\n guild_id: Snowflake;\r\n emojis: APIEmoji[];\r\n}\r\n\r\n/** Raw Discord guild stickers update event payload. */\r\nexport interface APIGuildStickersUpdateEvent {\r\n guild_id: Snowflake;\r\n stickers: APISticker[];\r\n}\r\n\r\n/** Raw Discord channel pins update event. */\r\nexport interface APIChannelPinsUpdate {\r\n guild_id?: Snowflake;\r\n channel_id: Snowflake;\r\n last_pin_timestamp?: string | null;\r\n}\r\n\r\n/** Raw Discord guild members chunk event. */\r\nexport interface APIGuildMembersChunk {\r\n guild_id: Snowflake;\r\n members: APIGuildMember[];\r\n chunk_index: number;\r\n chunk_count: number;\r\n not_found?: Snowflake[];\r\n presences?: APIPresenceUpdate[];\r\n nonce?: string;\r\n}\r\n\r\n/** Raw Discord guild scheduled event user add/remove event. */\r\nexport interface APIGuildScheduledEventUserEvent {\r\n guild_scheduled_event_id: Snowflake;\r\n user_id: Snowflake;\r\n guild_id: Snowflake;\r\n}\r\n\r\n/** Raw Discord message poll vote add/remove event. */\r\nexport interface APIMessagePollVoteEvent {\r\n user_id: Snowflake;\r\n channel_id: Snowflake;\r\n message_id: Snowflake;\r\n guild_id?: Snowflake;\r\n answer_id: number;\r\n}\r\n",
|
|
6
|
+
"import {\r\n resolveGatewayIntents,\r\n type GatewayPayload,\r\n type GatewayProperties,\r\n type GatewayPresence,\r\n type GatewayIntentResolvable,\r\n} from \"@lunibee/types\";\r\n\r\n/** Discord Gateway opcodes. */\r\nexport const GatewayOpcodes = {\r\n Dispatch: 0,\r\n Heartbeat: 1,\r\n Identify: 2,\r\n PresenceUpdate: 3,\r\n VoiceStateUpdate: 4,\r\n Resume: 6,\r\n Reconnect: 7,\r\n RequestGuildMembers: 8,\r\n InvalidSession: 9,\r\n Hello: 10,\r\n HeartbeatAck: 11,\r\n} as const;\r\n/**\r\n * Discord Gateway close codes.\r\n *\r\n * Discord.js-familiar names and numeric values, matching the Discord Gateway\r\n * protocol. Exposed so consumers can branch on named codes instead of magic\r\n * numbers; {@link Gateway} uses them internally to decide resume/identify/stop.\r\n */\r\nexport const GatewayCloseCodes = {\r\n UnknownError: 4000,\r\n UnknownOpcode: 4001,\r\n DecodeError: 4002,\r\n NotAuthenticated: 4003,\r\n AuthenticationFailed: 4004,\r\n AlreadyAuthenticated: 4005,\r\n InvalidSeq: 4007,\r\n RateLimited: 4008,\r\n SessionTimedOut: 4009,\r\n InvalidShard: 4010,\r\n ShardingRequired: 4011,\r\n InvalidAPIVersion: 4012,\r\n InvalidIntents: 4013,\r\n DisallowedIntents: 4014,\r\n} as const;\r\n/** Gateway connection lifecycle states. */\r\nexport enum GatewayState {\r\n /** Initial connection state. */ Connect = \"CONNECT\",\r\n /** Gateway HELLO received state. */ Hello = \"HELLO\",\r\n /** IDENTIFY operation in progress. */ Identify = \"IDENTIFY\",\r\n /** RESUME operation in progress. */ Resume = \"RESUME\",\r\n /** Gateway READY state. */ Ready = \"READY\",\r\n /** Gateway dispatch processing state. */ Dispatch = \"DISPATCH\",\r\n /** Heartbeat processing state. */ Heartbeat = \"HEARTBEAT\",\r\n /** Reconnect in progress. */ Reconnect = \"RECONNECT\",\r\n /** Gateway is permanently closed. */ Closed = \"CLOSED\",\r\n}\r\n/**\r\n * Discord.js-familiar alias for {@link GatewayState}.\r\n *\r\n * Discord.js exposes connection status via a `Status` enum. Lunibee's canonical\r\n * name is {@link GatewayState}; this is an additive alias so `discord.js` users\r\n * find the expected name. Note the *values* remain Lunibee's string states\r\n * (e.g. `\"READY\"`), not Discord.js's numeric `Status` members — an intentional\r\n * divergence documented in the compatibility matrix.\r\n */\r\nexport { GatewayState as Status };\r\n/** Gateway protocol error. */\r\nexport class GatewayError extends Error {\r\n /** Gateway close/error code. */ public readonly code?: number;\r\n /** Creates a Gateway error. @param message Error message. @param code Optional Gateway code. @param options Optional error metadata. */ public constructor(\r\n message: string,\r\n code?: number,\r\n options?: ErrorOptions,\r\n ) {\r\n super(message, options);\r\n this.name = \"GatewayError\";\r\n this.code = code;\r\n }\r\n}\r\n/** Gateway connection configuration. */\r\nexport interface GatewayOptions {\r\n /** Authentication token. */ token: string;\r\n /** Gateway intent bitfield or resolvable. */ intents: GatewayIntentResolvable;\r\n /** Shard identifier. */ shardId?: number;\r\n /** Total shard count. */ shardCount?: number;\r\n /** Whether automatic reconnect is enabled. */ reconnect?: boolean;\r\n /** Maximum reconnect attempts. */ maxReconnectAttempts?: number;\r\n /** Initial reconnect delay. */ reconnectBaseDelay?: number;\r\n /** Maximum reconnect delay. */ reconnectMaxDelay?: number;\r\n /** Heartbeat acknowledgement timeout. */ heartbeatAckTimeout?: number;\r\n /** Zombie connection timeout. */ zombieTimeout?: number;\r\n /** Identification properties. */ properties?: GatewayProperties;\r\n /** Presence data. */ presence?: GatewayPresence;\r\n /**\r\n * Whether to enable zlib-stream transport compression.\r\n * Uses Bun's native `DecompressionStream` — no external dependencies.\r\n * When enabled, appends `&compress=zlib-stream` to the Gateway URL.\r\n */\r\n compress?: boolean;\r\n}\r\n/** Gateway event listener. */\r\ntype GatewayListener = (data: unknown) => unknown;\r\n/** Manages a Discord Gateway connection. */\r\nexport class Gateway {\r\n /** Current Gateway lifecycle state. */\r\n public state: GatewayState = GatewayState.Connect;\r\n #options: Required<\r\n Omit<GatewayOptions, \"properties\" | \"presence\" | \"compress\">\r\n > & {\r\n properties?: GatewayProperties;\r\n presence?: GatewayPresence;\r\n compress?: boolean;\r\n };\r\n #ws?: WebSocket;\r\n #sequence: number | null = null;\r\n #sessionId?: string;\r\n #resumeURL?: string;\r\n #heartbeatTimer?: ReturnType<typeof setInterval>;\r\n #initialHeartbeat?: ReturnType<typeof setTimeout>;\r\n #heartbeatAckTimer?: ReturnType<typeof setTimeout>;\r\n #heartbeatACK = true;\r\n #heartbeatInterval = 0;\r\n #heartbeatSentAt = 0;\r\n #lastMessageAt = 0;\r\n #zombieTimer?: ReturnType<typeof setInterval>;\r\n #zombieReported = false;\r\n #closed = false;\r\n #attempt = 0;\r\n #reconnectTimer?: ReturnType<typeof setTimeout>;\r\n #connectPromise?: Promise<void>;\r\n #resolveConnect?: () => void;\r\n #rejectConnect?: (error: GatewayError) => void;\r\n readonly #listeners = new Map<string, Set<GatewayListener>>();\r\n readonly #sendTimestamps: number[] = [];\r\n public ping: number = -1;\r\n /** Pending zlib-stream decompressor, initialised lazily when compress is enabled. */\r\n #decompressor?: {\r\n writer: WritableStreamDefaultWriter<BufferSource>;\r\n reader: ReadableStreamDefaultReader<Uint8Array>;\r\n };\r\n /** Creates a Gateway connection manager. @param options Gateway configuration. @throws {TypeError|RangeError} If configuration is invalid. */\r\n public constructor(options: GatewayOptions) {\r\n if (!options.token?.trim())\r\n throw new TypeError(\"A Gateway token is required.\");\r\n const resolvedIntents = resolveGatewayIntents(options.intents);\r\n if (!Number.isInteger(resolvedIntents) || resolvedIntents < 0)\r\n throw new TypeError(\r\n \"Gateway intents must be a non-negative integer.\",\r\n );\r\n this.#options = {\r\n shardId: 0,\r\n shardCount: 1,\r\n reconnect: true,\r\n maxReconnectAttempts: Infinity,\r\n reconnectBaseDelay: 1000,\r\n reconnectMaxDelay: 30000,\r\n heartbeatAckTimeout: 10000,\r\n zombieTimeout: 30000,\r\n properties: {\r\n os: \"Android\",\r\n browser: \"Discord Android\",\r\n device: \"Discord Android\",\r\n },\r\n presence: {\r\n status: \"online\",\r\n activities: [],\r\n afk: false,\r\n since: null,\r\n },\r\n ...options,\r\n };\r\n if (\r\n this.#options.shardId < 0 ||\r\n this.#options.shardId >= this.#options.shardCount\r\n )\r\n throw new RangeError(\r\n \"Gateway shardId must be within the configured shard count.\",\r\n );\r\n if (this.#options.zombieTimeout <= this.#options.heartbeatAckTimeout)\r\n throw new RangeError(\r\n \"Gateway zombieTimeout must be greater than heartbeatAckTimeout\",\r\n );\r\n }\r\n\r\n /** Opens the Gateway connection. @param url Gateway WebSocket URL. @returns Promise fulfilled when the socket opens. @throws {GatewayError} If permanently closed or unable to connect. */\r\n public connect(\r\n url = \"wss://gateway.discord.gg/?v=10&encoding=json\",\r\n ): Promise<void> {\r\n if (this.state === GatewayState.Closed)\r\n throw new GatewayError(\"Gateway has been permanently closed.\");\r\n if (this.#connectPromise) return this.#connectPromise;\r\n this.#closed = false;\r\n this.#setState(GatewayState.Connect);\r\n // Append compression parameter if enabled\r\n const connectURL = this.#options.compress\r\n ? url.includes(\"compress=\")\r\n ? url\r\n : `${url}&compress=zlib-stream`\r\n : url;\r\n if (this.#options.compress) this.#initDecompressor();\r\n this.#connectPromise = new Promise<void>((resolve, reject) => {\r\n this.#resolveConnect = resolve;\r\n this.#rejectConnect = reject;\r\n this.#open(connectURL);\r\n });\r\n return this.#connectPromise;\r\n }\r\n /** Permanently closes the Gateway connection. */\r\n public close(): void {\r\n this.#closed = true;\r\n this.#clearTimers();\r\n this.#settleConnect(\r\n new GatewayError(\"Gateway connection closed before socket open.\"),\r\n );\r\n const ws = this.#ws;\r\n this.#ws = undefined;\r\n try {\r\n ws?.close(1000, \"Client closed connection\");\r\n } catch (error) {\r\n this.#emitError(error);\r\n }\r\n this.#setState(GatewayState.Closed);\r\n }\r\n /** Registers a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\r\n /** Emits an event to all registered listeners. @param event Event name. @param data Event payload. */\r\n public emit(event: string, data?: unknown): void {\r\n this.#emit(event, data);\r\n }\r\n public on(event: string, listener: GatewayListener): this {\r\n if (!event || typeof listener !== \"function\")\r\n throw new TypeError(\"Gateway event and listener are required.\");\r\n let listeners = this.#listeners.get(event);\r\n if (!listeners) this.#listeners.set(event, (listeners = new Set()));\r\n listeners.add(listener);\r\n return this;\r\n }\r\n /** Removes a Gateway event listener. @param event Event name. @param listener Event callback. @returns This Gateway. */\r\n public off(event: string, listener: GatewayListener): this {\r\n this.#listeners.get(event)?.delete(listener);\r\n return this;\r\n }\r\n /** Sends a Gateway payload. @param payload Gateway payload. @returns Whether it was sent. */\r\n public send(payload: GatewayPayload): boolean {\r\n if (!payload || !Number.isInteger(payload.op))\r\n throw new TypeError(\r\n \"Gateway payload must contain an integer opcode.\",\r\n );\r\n return this.#dispatch(payload, false);\r\n }\r\n /**\r\n * Writes a payload to the socket. Heartbeats pass `privileged` so they are\r\n * never starved by application traffic: the 115 budget sits below\r\n * Discord's real 120/60s limit precisely to leave room for them, so\r\n * spending that headroom on heartbeats is what it is reserved for. They\r\n * are still recorded, keeping the true total under Discord's limit.\r\n */\r\n #dispatch(payload: GatewayPayload, privileged: boolean): boolean {\r\n if (this.#ws?.readyState !== WebSocket.OPEN) return false;\r\n const now = Date.now();\r\n while (\r\n this.#sendTimestamps.length &&\r\n now - this.#sendTimestamps[0]! >= 60000\r\n )\r\n this.#sendTimestamps.shift();\r\n if (!privileged && this.#sendTimestamps.length >= 115) {\r\n this.#emitError(\r\n new GatewayError(\"Gateway send rate budget exhausted.\"),\r\n );\r\n return false;\r\n }\r\n try {\r\n this.#ws.send(JSON.stringify(payload));\r\n this.#sendTimestamps.push(now);\r\n return true;\r\n } catch (error) {\r\n this.#emitError(error);\r\n return false;\r\n }\r\n }\r\n /** Sends a presence update. @param data Presence payload. @returns Whether it was sent. */\r\n public setPresence(data: GatewayPresence): boolean {\r\n this.#options.presence = { ...this.#options.presence, ...data };\r\n const payload = {\r\n since: this.#options.presence.since ?? null,\r\n activities: this.#options.presence.activities ?? [],\r\n status: this.#options.presence.status ?? \"online\",\r\n afk: Boolean(this.#options.presence.afk),\r\n };\r\n return this.send({\r\n op: GatewayOpcodes.PresenceUpdate,\r\n d: payload,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n /** Sends a voice state update. @param data Voice state payload. @returns Whether it was sent. */\r\n public setVoiceState(data: Record<string, unknown>): boolean {\r\n return this.send({\r\n op: GatewayOpcodes.VoiceStateUpdate,\r\n d: data,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n /** Requests guild members. @param data Guild member request payload. @returns Whether it was sent. */\r\n public requestGuildMembers(data: Record<string, unknown>): boolean {\r\n return this.send({\r\n op: GatewayOpcodes.RequestGuildMembers,\r\n d: data,\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n #open(url: string): void {\r\n if (this.#closed) return;\r\n let ws: WebSocket;\r\n try {\r\n ws = new WebSocket(url);\r\n } catch (error) {\r\n const failure = this.#normalizeError(error);\r\n this.#emitError(failure);\r\n if (this.#options.reconnect) {\r\n this.#setState(GatewayState.Reconnect);\r\n this.#scheduleReconnect();\r\n } else this.#settleConnect(failure);\r\n return;\r\n }\r\n this.#ws = ws;\r\n ws.addEventListener(\"open\", () => {\r\n this.#lastMessageAt = Date.now();\r\n this.#zombieReported = false;\r\n this.#startZombieDetection();\r\n this.#emit(\"open\", undefined);\r\n this.#settleConnect();\r\n });\r\n ws.addEventListener(\"message\", (event) => {\r\n this.#lastMessageAt = Date.now();\r\n this.#zombieReported = false;\r\n if (this.#options.compress && event.data instanceof ArrayBuffer) {\r\n this.#decompress(new Uint8Array(event.data))\r\n .then((text) => this.#message(ws, text))\r\n .catch((err) => this.#emitError(err));\r\n } else {\r\n this.#message(ws, String(event.data));\r\n }\r\n });\r\n ws.addEventListener(\"close\", (event) => this.#close(ws, event.code));\r\n ws.addEventListener(\"error\", () =>\r\n this.#emitError(new GatewayError(\"Gateway WebSocket error\")),\r\n );\r\n }\r\n #startZombieDetection(): void {\r\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\r\n // Poll granularity only needs to be a fraction of the staleness\r\n // deadline (heartbeatInterval + heartbeatAckTimeout, ~51s in\r\n // practice). A 1s ceiling keeps detection latency negligible while\r\n // costing one wakeup per second per shard instead of four.\r\n const interval = Math.max(\r\n 1,\r\n Math.min(\r\n 1000,\r\n this.#options.heartbeatAckTimeout,\r\n this.#options.zombieTimeout / 2,\r\n ),\r\n );\r\n this.#zombieTimer = setInterval(() => {\r\n if (\r\n this.#closed ||\r\n this.#ws?.readyState !== WebSocket.OPEN ||\r\n this.#lastMessageAt === 0\r\n )\r\n return;\r\n const silentFor = Date.now() - this.#lastMessageAt;\r\n const deadline = Math.max(\r\n this.#options.zombieTimeout,\r\n this.#heartbeatInterval + this.#options.heartbeatAckTimeout,\r\n );\r\n if (silentFor < deadline || this.#zombieReported) return;\r\n this.#zombieReported = true;\r\n const error = new GatewayError(\r\n `Gateway connection appears stale after ${silentFor}ms without traffic.`,\r\n );\r\n this.#emit(\"zombie\", { silentFor, timeout: deadline });\r\n this.#emitError(error);\r\n try {\r\n this.#ws.close(1001, \"Zombie Gateway connection\");\r\n } catch (closeError) {\r\n this.#emitError(closeError);\r\n }\r\n }, interval);\r\n }\r\n #message(ws: WebSocket, raw: string): void {\r\n let payload: GatewayPayload;\r\n try {\r\n payload = JSON.parse(raw) as GatewayPayload;\r\n } catch (error) {\r\n this.#emitError(\r\n new GatewayError(\"Gateway returned invalid JSON\", undefined, {\r\n cause: error,\r\n }),\r\n );\r\n ws.close(1002, \"Invalid JSON\");\r\n return;\r\n }\r\n if (!payload || typeof payload.op !== \"number\") {\r\n this.#emitError(\r\n new GatewayError(\"Gateway returned an invalid payload\"),\r\n );\r\n ws.close(1002, \"Invalid payload\");\r\n return;\r\n }\r\n if (typeof payload.s === \"number\") this.#sequence = payload.s;\r\n switch (payload.op) {\r\n case GatewayOpcodes.Dispatch:\r\n this.#setState(GatewayState.Dispatch);\r\n this.#handleDispatch(payload.t, payload.d);\r\n break;\r\n case GatewayOpcodes.Hello:\r\n this.#handleHello(payload.d);\r\n break;\r\n case GatewayOpcodes.Heartbeat:\r\n this.#setState(GatewayState.Heartbeat);\r\n this.#sendHeartbeat();\r\n break;\r\n case GatewayOpcodes.HeartbeatAck:\r\n this.#heartbeatACK = true;\r\n this.ping = Date.now() - this.#heartbeatSentAt;\r\n this.#clearHeartbeatAckTimer();\r\n this.#emit(\"heartbeatAck\", payload.d);\r\n break;\r\n case GatewayOpcodes.Reconnect:\r\n ws.close(1001, \"Server requested reconnect\");\r\n break;\r\n case GatewayOpcodes.InvalidSession: {\r\n // Op 9 `d` is a boolean: true = the session is resumable, so we\r\n // keep session state and RESUME on reconnect; false = the\r\n // session is dead and we must re-IDENTIFY from scratch. Matches\r\n // Discord/Discord.js semantics rather than always re-identifying.\r\n const resumable = payload.d === true;\r\n if (!resumable) {\r\n this.#sessionId = undefined;\r\n this.#sequence = null;\r\n this.#resumeURL = undefined;\r\n }\r\n this.#emit(\"invalidSession\", resumable);\r\n ws.close(\r\n resumable ? GatewayCloseCodes.UnknownError : 1000,\r\n resumable\r\n ? \"Invalid session (resumable)\"\r\n : \"Invalid session\",\r\n );\r\n break;\r\n }\r\n }\r\n }\r\n #handleDispatch(event: string | null, data: unknown): void {\r\n if (event === \"READY\") {\r\n const ready = data as {\r\n session_id?: unknown;\r\n resume_gateway_url?: unknown;\r\n };\r\n if (\r\n typeof ready?.session_id !== \"string\" ||\r\n typeof ready?.resume_gateway_url !== \"string\"\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Gateway READY payload is missing session information\",\r\n ),\r\n );\r\n return;\r\n }\r\n this.#sessionId = ready.session_id;\r\n this.#resumeURL = ready.resume_gateway_url;\r\n this.#attempt = 0;\r\n this.#setState(GatewayState.Ready);\r\n this.#emit(\"ready\", data);\r\n } else if (event === \"RESUMED\") {\r\n // A successful RESUME ends the reconnect cycle just like READY:\r\n // reset the backoff counter so a later disconnect starts from the\r\n // base delay, mark the connection READY, and surface a Discord.js-\r\n // familiar `resumed` event.\r\n this.#attempt = 0;\r\n this.#setState(GatewayState.Ready);\r\n this.#emit(\"resumed\", data);\r\n }\r\n this.#emit(\"RAW\", { event: event ?? \"UNKNOWN\", data });\r\n if (this.#closed) return;\r\n this.#emit(event ?? \"dispatch\", data);\r\n }\r\n #handleHello(data: unknown): void {\r\n const interval = (data as { heartbeat_interval?: unknown })\r\n ?.heartbeat_interval;\r\n if (\r\n typeof interval !== \"number\" ||\r\n !Number.isFinite(interval) ||\r\n interval <= 0\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Gateway HELLO payload contains an invalid heartbeat interval\",\r\n ),\r\n );\r\n this.#ws?.close(1002, \"Invalid heartbeat interval\");\r\n return;\r\n }\r\n this.#heartbeatInterval = interval;\r\n this.#setState(\r\n this.#sessionId && this.#sequence !== null && this.#resumeURL\r\n ? GatewayState.Resume\r\n : GatewayState.Hello,\r\n );\r\n this.#startHeartbeat(interval);\r\n this.#identifyOrResume();\r\n }\r\n #identifyOrResume(): void {\r\n if (this.#sessionId && this.#sequence !== null && this.#resumeURL) {\r\n this.#setState(GatewayState.Resume);\r\n this.send({\r\n op: GatewayOpcodes.Resume,\r\n d: {\r\n token: this.#options.token,\r\n session_id: this.#sessionId,\r\n seq: this.#sequence,\r\n },\r\n s: null,\r\n t: null,\r\n });\r\n return;\r\n }\r\n this.#setState(GatewayState.Identify);\r\n const props = this.#options.properties ?? {\r\n os: \"Android\",\r\n browser: \"Discord Android\",\r\n device: \"Discord Android\",\r\n };\r\n const os = props.os ?? \"Android\";\r\n const browser = props.browser ?? \"Discord Android\";\r\n const device = props.device ?? \"Discord Android\";\r\n this.send({\r\n op: GatewayOpcodes.Identify,\r\n d: {\r\n token: this.#options.token,\r\n intents: this.#options.intents,\r\n properties: {\r\n os,\r\n browser,\r\n device,\r\n $os: os,\r\n $browser: browser,\r\n $device: device,\r\n ...props,\r\n },\r\n presence: {\r\n since: this.#options.presence?.since ?? null,\r\n activities: this.#options.presence?.activities ?? [],\r\n status: this.#options.presence?.status ?? \"online\",\r\n afk: Boolean(this.#options.presence?.afk),\r\n },\r\n shard: [this.#options.shardId, this.#options.shardCount],\r\n },\r\n s: null,\r\n t: null,\r\n });\r\n }\r\n #startHeartbeat(interval: number): void {\r\n this.#clearHeartbeatTimers();\r\n this.#heartbeatACK = true;\r\n this.#initialHeartbeat = setTimeout(\r\n () => this.#sendHeartbeat(),\r\n Math.random() * interval,\r\n );\r\n this.#heartbeatTimer = setInterval(\r\n () => this.#sendHeartbeat(),\r\n interval,\r\n );\r\n }\r\n #sendHeartbeat(): void {\r\n this.#heartbeatACK = false;\r\n this.#heartbeatSentAt = Date.now();\r\n if (\r\n !this.#dispatch(\r\n {\r\n op: GatewayOpcodes.Heartbeat,\r\n d: this.#sequence,\r\n s: null,\r\n t: null,\r\n },\r\n true,\r\n )\r\n ) {\r\n this.#emitError(\r\n new GatewayError(\r\n \"Unable to send Gateway heartbeat because the WebSocket is not open.\",\r\n ),\r\n );\r\n return;\r\n }\r\n this.#clearHeartbeatAckTimer();\r\n this.#heartbeatAckTimer = setTimeout(() => {\r\n if (!this.#heartbeatACK) {\r\n const elapsed = Date.now() - this.#heartbeatSentAt;\r\n this.#emitError(\r\n new GatewayError(\r\n `Gateway heartbeat acknowledgement timed out after ${elapsed}ms.`,\r\n ),\r\n );\r\n try {\r\n this.#ws?.close(1001, \"Heartbeat timeout\");\r\n } catch (error) {\r\n this.#emitError(error);\r\n }\r\n }\r\n }, this.#options.heartbeatAckTimeout);\r\n }\r\n #close(ws: WebSocket, code: number): void {\r\n if (this.#ws !== ws) return;\r\n this.#ws = undefined;\r\n this.#clearTimers();\r\n const action = this.#closeAction(code);\r\n this.#emit(\"close\", { code, action });\r\n if (this.#closed || !this.#options.reconnect || action === \"stop\") {\r\n this.#setState(\r\n this.#closed ? GatewayState.Closed : GatewayState.Connect,\r\n );\r\n if (this.#connectPromise)\r\n this.#settleConnect(\r\n new GatewayError(\r\n `Gateway closed before READY (code ${code}).`,\r\n code,\r\n ),\r\n );\r\n return;\r\n }\r\n if (action === \"identify\") {\r\n // Fresh IDENTIFY: drop the resumable session AND its resume URL so\r\n // the reconnect targets the main Gateway, not a stale resume host.\r\n this.#sequence = null;\r\n this.#sessionId = undefined;\r\n this.#resumeURL = undefined;\r\n }\r\n this.#setState(GatewayState.Reconnect);\r\n this.#scheduleReconnect();\r\n }\r\n #closeAction(code: number): \"resume\" | \"identify\" | \"stop\" {\r\n // Fatal codes: the connection cannot recover by reconnecting.\r\n const fatal: number[] = [\r\n GatewayCloseCodes.AuthenticationFailed,\r\n GatewayCloseCodes.InvalidShard,\r\n GatewayCloseCodes.ShardingRequired,\r\n GatewayCloseCodes.InvalidAPIVersion,\r\n GatewayCloseCodes.InvalidIntents,\r\n GatewayCloseCodes.DisallowedIntents,\r\n ];\r\n if (fatal.includes(code)) return \"stop\";\r\n // Sequence/session invalidated: reconnect but IDENTIFY afresh.\r\n if (\r\n code === GatewayCloseCodes.InvalidSeq ||\r\n code === GatewayCloseCodes.SessionTimedOut\r\n )\r\n return \"identify\";\r\n // Otherwise resume when we still hold a live session + sequence.\r\n return this.#sessionId && this.#sequence !== null\r\n ? \"resume\"\r\n : \"identify\";\r\n }\r\n #scheduleReconnect(): void {\r\n if (this.#closed || !this.#options.reconnect || this.#reconnectTimer)\r\n return;\r\n if (this.#attempt >= this.#options.maxReconnectAttempts) {\r\n this.#settleConnect(\r\n new GatewayError(\"Gateway reconnect attempts exhausted.\"),\r\n );\r\n this.#setState(GatewayState.Connect);\r\n return;\r\n }\r\n const delay = Math.min(\r\n this.#options.reconnectMaxDelay,\r\n this.#options.reconnectBaseDelay * 2 ** this.#attempt++,\r\n );\r\n // Jitter must scale with the delay: a fixed ceiling would reconnect\r\n // every shard inside the same narrow window after a gateway-wide\r\n // restart, which is exactly when decorrelation matters.\r\n const jitter = Math.random() * Math.max(1, delay * 0.25);\r\n this.#reconnectTimer = setTimeout(() => {\r\n this.#reconnectTimer = undefined;\r\n this.#open(\r\n this.#resumeURL ??\r\n \"wss://gateway.discord.gg/?v=10&encoding=json\",\r\n );\r\n }, delay + jitter);\r\n }\r\n #settleConnect(error?: GatewayError): void {\r\n const resolve = this.#resolveConnect;\r\n const reject = this.#rejectConnect;\r\n this.#resolveConnect = undefined;\r\n this.#rejectConnect = undefined;\r\n this.#connectPromise = undefined;\r\n if (error) reject?.(error);\r\n else resolve?.();\r\n }\r\n #clearHeartbeatAckTimer(): void {\r\n if (this.#heartbeatAckTimer) clearTimeout(this.#heartbeatAckTimer);\r\n this.#heartbeatAckTimer = undefined;\r\n }\r\n #clearHeartbeatTimers(): void {\r\n if (this.#heartbeatTimer) clearInterval(this.#heartbeatTimer);\r\n if (this.#initialHeartbeat) clearTimeout(this.#initialHeartbeat);\r\n this.#clearHeartbeatAckTimer();\r\n this.#heartbeatTimer = undefined;\r\n this.#initialHeartbeat = undefined;\r\n }\r\n #clearTimers(): void {\r\n if (this.#reconnectTimer) clearTimeout(this.#reconnectTimer);\r\n if (this.#zombieTimer) clearInterval(this.#zombieTimer);\r\n this.#reconnectTimer = undefined;\r\n this.#zombieTimer = undefined;\r\n this.#clearHeartbeatTimers();\r\n }\r\n #normalizeError(error: unknown): GatewayError {\r\n return error instanceof GatewayError\r\n ? error\r\n : new GatewayError(\"Gateway connection failed.\", undefined, {\r\n cause: error,\r\n });\r\n }\r\n #emitError(error: unknown): void {\r\n this.#emit(\r\n \"error\",\r\n error instanceof Error ? error : new GatewayError(String(error)),\r\n );\r\n }\r\n #emit(event: string, data: unknown): void {\r\n for (const listener of this.#listeners.get(event) ?? []) {\r\n try {\r\n const result = listener(data);\r\n if (\r\n result &&\r\n typeof (result as PromiseLike<unknown>).then === \"function\"\r\n )\r\n void Promise.resolve(result).catch((error) => {\r\n if (event !== \"error\") this.#emitError(error);\r\n });\r\n } catch (error) {\r\n if (event !== \"error\") this.#emitError(error);\r\n }\r\n }\r\n }\r\n /** Changes the Gateway lifecycle state. @param next Next lifecycle state. */\r\n #setState(next: GatewayState): void {\r\n if (this.state === next) return;\r\n const previous = this.state;\r\n this.state = next;\r\n this.#emit(\"stateChange\", { previous, next });\r\n }\r\n /** Initialises a fresh zlib-stream decompressor using Bun's native DecompressionStream. */\r\n #initDecompressor(): void {\r\n const ds = new DecompressionStream(\"deflate-raw\");\r\n this.#decompressor = {\r\n writer: ds.writable.getWriter() as WritableStreamDefaultWriter<BufferSource>,\r\n reader: ds.readable.getReader() as ReadableStreamDefaultReader<Uint8Array>,\r\n };\r\n }\r\n /** Decompresses a zlib-stream chunk and returns the decoded JSON string. */\r\n async #decompress(chunk: Uint8Array): Promise<string> {\r\n if (!this.#decompressor) this.#initDecompressor();\r\n const { writer, reader } = this.#decompressor!;\r\n await writer.write(chunk as unknown as BufferSource);\r\n const parts: Uint8Array[] = [];\r\n // Drain all available output frames\r\n while (true) {\r\n const { done, value } = await Promise.race([\r\n reader.read(),\r\n // 50 ms fence so we don't hang on a partial payload\r\n new Promise<{ done: true; value: undefined }>((resolve) =>\r\n setTimeout(\r\n () => resolve({ done: true, value: undefined }),\r\n 50,\r\n ),\r\n ),\r\n ]);\r\n if (done || !value) break;\r\n parts.push(value);\r\n }\r\n const total = parts.reduce((n, p) => n + p.length, 0);\r\n const merged = new Uint8Array(total);\r\n let offset = 0;\r\n for (const p of parts) {\r\n merged.set(p, offset);\r\n offset += p.length;\r\n }\r\n return new TextDecoder().decode(merged);\r\n }\r\n}\r\n",
|
|
7
|
+
"/** Message envelope exchanged between Lunibee shards. */\r\nexport interface ShardMessage<T = unknown> {\r\n /** Sending shard ID. */ source: number;\r\n /** Target shard ID, or null for broadcast. */ target: number | null;\r\n /** Application message type. */ type: string;\r\n /** Message payload. */ data: T;\r\n /** Unique message ID. */ id: string;\r\n}\r\n/** Handler invoked for shard messages. @typeParam T Message payload type. */\r\nexport type ShardMessageHandler<T = unknown> = (\r\n message: ShardMessage<T>,\r\n) => unknown;\r\n/** Bun/Node-compatible cross-shard transport using BroadcastChannel. */\r\nexport class ShardBus {\r\n /** Underlying broadcast channel. */ readonly #channel: BroadcastChannel;\r\n /** Message handlers by message type. */ readonly #handlers = new Map<\r\n string,\r\n Set<ShardMessageHandler>\r\n >();\r\n /** Current shard ID. */ readonly #shardId: number;\r\n /** Application-specific channel namespace. */ readonly #namespace: string;\r\n /** BroadcastChannel name used by this bus. */ public readonly channelName: string;\r\n /** Monotonic message counter. */ #counter = 0;\r\n /** Creates a shard bus. @param shardId Shard ID. @param channelName Application-specific channel name. @throws {RangeError} If shard ID is invalid. @throws {TypeError} If channel name is empty. */\r\n public constructor(shardId: number, channelName: string) {\r\n if (!Number.isInteger(shardId) || shardId < 0)\r\n throw new RangeError(\"Shard ID must be a non-negative integer.\");\r\n if (!channelName?.trim())\r\n throw new TypeError(\r\n \"An application-specific shard channel name is required.\",\r\n );\r\n this.#shardId = shardId;\r\n this.#namespace = channelName.trim();\r\n this.channelName = this.#namespace;\r\n this.#channel = new BroadcastChannel(this.channelName);\r\n this.#channel.addEventListener(\"message\", (event) =>\r\n this.#dispatch(event.data as ShardMessage),\r\n );\r\n }\r\n /** Registers a message handler. @param type Message type. @param handler Handler callback. @returns This bus. @throws {TypeError} If type or handler is invalid. */ public on<\r\n T,\r\n >(type: string, handler: ShardMessageHandler<T>): this {\r\n if (!type.trim() || typeof handler !== \"function\")\r\n throw new TypeError(\"Shard message type and handler are required.\");\r\n let handlers = this.#handlers.get(type);\r\n if (!handlers) this.#handlers.set(type, (handlers = new Set()));\r\n handlers.add(handler as ShardMessageHandler);\r\n return this;\r\n }\r\n /** Removes a message handler. @param type Message type. @param handler Handler callback. @returns This bus. */ public off<\r\n T,\r\n >(type: string, handler: ShardMessageHandler<T>): this {\r\n this.#handlers.get(type)?.delete(handler as ShardMessageHandler);\r\n return this;\r\n }\r\n /** Sends a targeted shard message. @param target Target shard ID. @param type Message type. @param data Payload. @returns Unique message ID. */ public send<\r\n T,\r\n >(target: number, type: string, data: T): string {\r\n if (!Number.isInteger(target) || target < 0)\r\n throw new RangeError(\r\n \"Shard target must be a non-negative integer.\",\r\n );\r\n return this.#publish(target, type, data);\r\n }\r\n /** Broadcasts to all other shards. @param type Message type. @param data Payload. @returns Unique message ID. */ public broadcast<\r\n T,\r\n >(type: string, data: T): string {\r\n return this.#publish(null, type, data);\r\n }\r\n /** Closes the transport. @returns Nothing. */ public close(): void {\r\n this.#channel.close();\r\n this.#handlers.clear();\r\n }\r\n /** Publishes a message. @param target Target shard ID or null. @param type Message type. @param data Payload. @returns Unique message ID. */ #publish<\r\n T,\r\n >(target: number | null, type: string, data: T): string {\r\n if (!type.trim())\r\n throw new TypeError(\"Shard message type is required.\");\r\n const id = `${this.#namespace}:${this.#shardId}:${++this.#counter}`;\r\n this.#channel.postMessage({\r\n source: this.#shardId,\r\n target,\r\n type,\r\n data,\r\n id,\r\n } satisfies ShardMessage<T>);\r\n return id;\r\n }\r\n /** Routes an incoming message to matching handlers. @param message Message envelope. @returns Nothing. */ #dispatch(\r\n message: ShardMessage,\r\n ): void {\r\n if (\r\n !message ||\r\n typeof message !== \"object\" ||\r\n typeof message.type !== \"string\"\r\n )\r\n return;\r\n if (\r\n message.source === this.#shardId ||\r\n (message.target !== null && message.target !== this.#shardId)\r\n )\r\n return;\r\n for (const handler of this.#handlers.get(message.type) ?? []) {\r\n try {\r\n const result = handler(message);\r\n if (\r\n result &&\r\n typeof (result as PromiseLike<unknown>).then === \"function\"\r\n )\r\n void Promise.resolve(result).catch(() => undefined);\r\n } catch {\r\n /* Consumer errors are isolated. */\r\n }\r\n }\r\n }\r\n}\r\n",
|
|
8
|
+
"import { fork, type ChildProcess } from \"node:child_process\";\r\nimport { cpus } from \"node:os\";\r\n\r\n/** Runtime-agnostic delay used to stagger cluster spawns (works under Node and Bun). @param ms Milliseconds to wait. */\r\nconst sleep = (ms: number): Promise<void> =>\r\n new Promise<void>((resolve) => setTimeout(resolve, ms));\r\n\r\n/** Configuration for managing multiple shard clusters. */\r\nexport interface ClusterManagerOptions {\r\n /** Discord bot token. */\r\n token: string;\r\n /** Path to the worker script to execute for each cluster. */\r\n script: string;\r\n /** Number of shards. Use \"auto\" to request Discord's recommended count. */\r\n shardCount?: number | \"auto\";\r\n /** Number of clusters to spawn. Defaults to the number of logical CPUs. */\r\n clusterCount?: number;\r\n /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. */\r\n autoScaleInterval?: number;\r\n /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */\r\n onAutoScaleError?: (error: Error) => void;\r\n /** Grace period in milliseconds to await a cluster's clean exit after SIGTERM before force-killing. Defaults to 5000. */\r\n shutdownTimeout?: number;\r\n}\r\n\r\n/** Information about a running cluster. */\r\nexport interface ClusterInfo {\r\n /** Cluster identifier. */\r\n id: number;\r\n /** The child process running the cluster. */\r\n process: ChildProcess;\r\n /** The shard IDs managed by this cluster. */\r\n shards: number[];\r\n}\r\n\r\n/**\r\n * Manages multiple clusters of shards, distributing shards across child processes.\r\n *\r\n * **Runtime requirement:** clustering relies on Node.js `child_process.fork()`\r\n * and IPC. `fork()` is not fully supported under Bun, so run the cluster\r\n * manager under the Node.js runtime; `spawn()` will fail on a runtime whose\r\n * `fork()` is unavailable.\r\n */\r\nexport class ClusterManager {\r\n /** Active clusters indexed by cluster ID. */\r\n public readonly clusters = new Map<number, ClusterInfo>();\r\n /** Total number of shards across all clusters. */\r\n public shardCount = 0;\r\n\r\n readonly #options: ClusterManagerOptions;\r\n /** Whether the manager was created in auto shard-count mode. Preserved across respawns so auto-scaling keeps running. */\r\n readonly #auto: boolean;\r\n #autoScaleTimer?: ReturnType<typeof setInterval>;\r\n #spawned = false;\r\n\r\n /** Creates a cluster manager. @param options Clustering configuration. @throws {TypeError} If token or script is missing. */\r\n public constructor(options: ClusterManagerOptions) {\r\n if (!options.token?.trim()) throw new TypeError(\"A Discord bot token is required.\");\r\n if (!options.script?.trim()) throw new TypeError(\"A script path is required for clustering.\");\r\n if (\r\n options.autoScaleInterval !== undefined &&\r\n (!Number.isInteger(options.autoScaleInterval) || options.autoScaleInterval < 1000)\r\n ) {\r\n throw new RangeError(\"autoScaleInterval must be an integer of at least 1000 milliseconds.\");\r\n }\r\n\r\n this.#auto = options.shardCount === \"auto\";\r\n this.#options = { ...options };\r\n }\r\n\r\n /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */\r\n public async fetchRecommendedShardCount(): Promise<number> {\r\n const response = await fetch(\"https://discord.com/api/v10/gateway/bot\", {\r\n headers: {\r\n Authorization: `Bot ${this.#options.token}`,\r\n \"User-Agent\": \"Lunibee/0.1.0\",\r\n },\r\n });\r\n if (!response.ok) {\r\n throw new Error(`Gateway discovery failed with status ${response.status}`);\r\n }\r\n const data = (await response.json()) as { shards?: unknown };\r\n if (typeof data.shards !== \"number\" || !Number.isInteger(data.shards) || data.shards < 1) {\r\n throw new Error(\"Gateway discovery returned an invalid shard count.\");\r\n }\r\n return data.shards;\r\n }\r\n\r\n /** Spawns all clusters. @returns A promise fulfilled after clusters have launched. */\r\n public async spawn(): Promise<void> {\r\n if (this.#spawned) return;\r\n if (typeof process?.versions?.bun === \"string\") {\r\n throw new Error(\r\n \"ClusterManager requires the Node.js runtime; Bun does not support child_process.fork() for this clustering mode.\",\r\n );\r\n }\r\n this.#spawned = true;\r\n\r\n const count =\r\n this.#options.shardCount === \"auto\" || this.#options.shardCount === undefined\r\n ? await this.fetchRecommendedShardCount()\r\n : this.#options.shardCount;\r\n\r\n this.shardCount = count;\r\n const clusterCount = this.#options.clusterCount ?? cpus().length;\r\n\r\n // Chunk shards across clusters evenly\r\n const chunks = Array.from({ length: clusterCount }, () => [] as number[]);\r\n for (let i = 0; i < count; i++) {\r\n chunks[i % clusterCount].push(i);\r\n }\r\n\r\n for (let i = 0; i < clusterCount; i++) {\r\n if (chunks[i].length === 0) continue;\r\n\r\n const child = fork(this.#options.script, [], {\r\n env: {\r\n ...process.env,\r\n SHARD_LIST: chunks[i].join(\",\"),\r\n SHARD_COUNT: count.toString(),\r\n CLUSTER_ID: i.toString(),\r\n },\r\n });\r\n\r\n this.clusters.set(i, {\r\n id: i,\r\n process: child,\r\n shards: chunks[i],\r\n });\r\n\r\n // Stagger cluster creation\r\n await sleep(500);\r\n }\r\n\r\n if (this.#options.autoScaleInterval && this.#options.autoScaleInterval > 0) {\r\n this.#autoScaleTimer = setInterval(() => {\r\n void this.checkAutoScale();\r\n }, this.#options.autoScaleInterval);\r\n }\r\n }\r\n\r\n /** Checks if the recommended shard count has changed and respawns if so. */\r\n public async checkAutoScale(): Promise<void> {\r\n if (!this.#auto) return; // Only auto-scale in auto mode\r\n try {\r\n const recommended = await this.fetchRecommendedShardCount();\r\n if (recommended !== this.shardCount) {\r\n await this.respawn(recommended);\r\n }\r\n } catch (error) {\r\n // Surface the failure instead of silently swallowing it; the next\r\n // interval tick retries.\r\n const normalized = error instanceof Error ? error : new Error(String(error));\r\n this.#options.onAutoScaleError?.(normalized);\r\n }\r\n }\r\n\r\n /** Gracefully shuts the existing clusters down and respawns them with the new shard count. @param newShardCount The new total shard count. */\r\n public async respawn(newShardCount: number): Promise<void> {\r\n this.#options.shardCount = newShardCount;\r\n await this.shutdownAll();\r\n this.#spawned = false;\r\n await this.spawn();\r\n }\r\n\r\n /**\r\n * Gracefully stops all active clusters: sends SIGTERM, awaits each child's\r\n * clean exit, and force-kills (SIGKILL) only children that outlast the\r\n * shutdown timeout. Clears the auto-scale timer and cluster map.\r\n * @param timeoutMs Grace period per child before force-kill. Defaults to the configured `shutdownTimeout` (5000 ms).\r\n */\r\n public async shutdownAll(timeoutMs = this.#options.shutdownTimeout ?? 5000): Promise<void> {\r\n if (this.#autoScaleTimer) {\r\n clearInterval(this.#autoScaleTimer);\r\n this.#autoScaleTimer = undefined;\r\n }\r\n await Promise.all(\r\n [...this.clusters.values()].map((cluster) =>\r\n this.#shutdownCluster(cluster, timeoutMs),\r\n ),\r\n );\r\n this.clusters.clear();\r\n this.#spawned = false;\r\n }\r\n\r\n /** Gracefully terminates a single cluster child, escalating to SIGKILL after the timeout. */\r\n async #shutdownCluster(cluster: ClusterInfo, timeoutMs: number): Promise<void> {\r\n const child = cluster.process;\r\n if (child.exitCode !== null || child.signalCode !== null) return;\r\n await new Promise<void>((resolve) => {\r\n let timer: ReturnType<typeof setTimeout> | undefined;\r\n const finish = (): void => {\r\n if (timer) clearTimeout(timer);\r\n resolve();\r\n };\r\n child.once(\"exit\", finish);\r\n try {\r\n child.kill(\"SIGTERM\");\r\n } catch {\r\n child.off(\"exit\", finish);\r\n finish();\r\n return;\r\n }\r\n timer = setTimeout(() => {\r\n try {\r\n child.kill(\"SIGKILL\");\r\n } catch {\r\n // Ignore force-kill errors.\r\n }\r\n }, timeoutMs);\r\n });\r\n }\r\n\r\n /** Immediately force-kills all active cluster processes and clears the cluster map. Prefer {@link shutdownAll} for a graceful stop. */\r\n public killAll(): void {\r\n if (this.#autoScaleTimer) {\r\n clearInterval(this.#autoScaleTimer);\r\n this.#autoScaleTimer = undefined;\r\n }\r\n for (const cluster of this.clusters.values()) {\r\n try {\r\n cluster.process.kill();\r\n } catch {\r\n // Ignore kill errors\r\n }\r\n }\r\n this.clusters.clear();\r\n this.#spawned = false;\r\n }\r\n}\r\n",
|
|
9
|
+
"import { Gateway } from \"@lunibee/ws\";\r\n\r\n/** Runtime-agnostic delay used between shard starts (works under Node and Bun). @param ms Milliseconds to wait. */\r\nconst sleep = (ms: number): Promise<void> =>\r\n new Promise<void>((resolve) => setTimeout(resolve, ms));\r\n\r\nexport { ShardBus } from \"./bus.js\";\r\nexport type { ShardMessage, ShardMessageHandler } from \"./bus.js\";\r\n\r\nexport { ClusterManager } from \"./cluster.js\";\r\nexport type { ClusterManagerOptions, ClusterInfo } from \"./cluster.js\";\r\n\r\n/** Configuration for a sharded Gateway client. */\r\nexport interface ShardManagerOptions {\r\n /** Bot token. */ token: string;\r\n /** Gateway intents. */ intents: number;\r\n /** Number of shards. Use `\"auto\"` to request Discord's recommended count. */ shardCount?:\r\n number | \"auto\";\r\n /** Gateway reconnect behavior. */ reconnect?: boolean;\r\n /** Delay between shard starts in milliseconds. */ spawnDelay?: number;\r\n /** Interval in milliseconds to automatically check for recommended shard count and re-scale if needed. Must be an integer >= 1000. */ autoScaleInterval?: number;\r\n /** Optional handler invoked when a background auto-scale check fails. Receives the thrown error. */ onAutoScaleError?: (error: unknown) => void;\r\n}\r\n/** Runtime state for a managed shard. */\r\nexport interface ShardInfo {\r\n /** Shard identifier. */ id: number;\r\n /** Gateway instance. */ gateway: Gateway;\r\n}\r\n\r\n/** Manages independent Discord Gateway shards with explicit destruction and reinitialization semantics. */\r\nexport class ShardManager {\r\n /** Active Gateway shards indexed by shard identifier. */ public readonly shards =\r\n new Map<number, Gateway>();\r\n /** Number of shards managed by this instance after initialization. */ public get shardCount(): number {\r\n return this.shards.size;\r\n }\r\n readonly #options: ShardManagerOptions;\r\n /** Whether the shard set has been initialized. */ #resolved = false;\r\n /** Whether this manager is currently destroyed. */ #destroyed = false;\r\n /** Whether the manager was created in auto shard-count mode. Preserved across reshards so auto-scaling keeps running. */\r\n readonly #auto: boolean;\r\n #autoScaleTimer?: ReturnType<typeof setInterval>;\r\n /** Creates a shard manager. @param options Sharding configuration. @throws {TypeError} If token or intents are invalid. @throws {RangeError} If shard count is invalid. */\r\n public constructor(options: ShardManagerOptions) {\r\n if (!options.token?.trim())\r\n throw new TypeError(\"A Discord bot token is required.\");\r\n if (!Number.isInteger(options.intents) || options.intents < 0)\r\n throw new TypeError(\r\n \"Gateway intents must be a non-negative integer.\",\r\n );\r\n const count =\r\n options.shardCount === \"auto\" || options.shardCount === undefined\r\n ? 1\r\n : options.shardCount;\r\n if (!Number.isInteger(count) || count < 1)\r\n throw new RangeError(\"Shard count must be a positive integer.\");\r\n if (\r\n options.autoScaleInterval !== undefined &&\r\n (!Number.isInteger(options.autoScaleInterval) ||\r\n options.autoScaleInterval < 1000)\r\n )\r\n throw new RangeError(\r\n \"autoScaleInterval must be an integer of at least 1000 milliseconds.\",\r\n );\r\n this.#auto = options.shardCount === \"auto\";\r\n this.#options = { ...options };\r\n if (options.shardCount !== \"auto\") this.#initialize(count);\r\n }\r\n /** Retrieves Discord's recommended shard count. @returns Recommended shard count. @throws {Error} If discovery fails or returns invalid data. */\r\n public async fetchRecommendedShardCount(): Promise<number> {\r\n const response = await fetch(\r\n \"https://discord.com/api/v10/gateway/bot\",\r\n {\r\n headers: {\r\n Authorization: `Bot ${this.#options.token}`,\r\n \"User-Agent\": \"Lunibee/0.1.0\",\r\n },\r\n },\r\n );\r\n if (!response.ok)\r\n throw new Error(\r\n `Gateway discovery failed with status ${response.status}`,\r\n );\r\n const data = (await response.json()) as { shards?: unknown };\r\n if (\r\n typeof data.shards !== \"number\" ||\r\n !Number.isInteger(data.shards) ||\r\n data.shards < 1\r\n )\r\n throw new Error(\r\n \"Gateway discovery returned an invalid shard count.\",\r\n );\r\n return data.shards;\r\n }\r\n /** Connects all shards sequentially. A destroyed manager is reinitialized before connecting. @returns A promise fulfilled after all shards connect. @throws {Error} If a shard fails to connect. */\r\n public async connect(): Promise<void> {\r\n // Clear any timer from a previous connect() so a second call cannot leak one.\r\n if (this.#autoScaleTimer) {\r\n clearInterval(this.#autoScaleTimer);\r\n this.#autoScaleTimer = undefined;\r\n }\r\n await this.#ensureInitialized();\r\n for (const [id, shard] of this.shards) {\r\n try {\r\n await shard.connect();\r\n } catch (error) {\r\n this.destroy();\r\n throw new Error(`Failed to connect shard ${id}.`, {\r\n cause: error,\r\n });\r\n }\r\n if (\r\n id + 1 < this.shardCount &&\r\n this.#options.spawnDelay &&\r\n this.#options.spawnDelay > 0\r\n )\r\n await sleep(this.#options.spawnDelay);\r\n }\r\n if (this.#options.autoScaleInterval && !this.#autoScaleTimer) {\r\n this.#autoScaleTimer = setInterval(() => {\r\n void this.checkAutoScale();\r\n }, this.#options.autoScaleInterval);\r\n }\r\n }\r\n /** Checks if the recommended shard count has changed and reconnects if so. */\r\n public async checkAutoScale(): Promise<void> {\r\n if (!this.#auto) return;\r\n try {\r\n const recommended = await this.fetchRecommendedShardCount();\r\n if (recommended !== this.shardCount) {\r\n await this.reshard(recommended);\r\n }\r\n } catch (error) {\r\n // Surface the failure instead of silently swallowing it; the next\r\n // interval tick retries.\r\n this.#options.onAutoScaleError?.(error);\r\n }\r\n }\r\n /** Forces a reshard to a new shard count, replacing all active shards. @param count New shard count. */\r\n public async reshard(count: number): Promise<void> {\r\n this.#options.shardCount = count;\r\n this.destroy();\r\n await this.connect();\r\n }\r\n /** Permanently closes the current shard set and releases Gateway resources. The next connect recreates them. @returns Nothing. */\r\n public destroy(): void {\r\n for (const shard of this.shards.values()) shard.close();\r\n this.shards.clear();\r\n this.#resolved = false;\r\n this.#destroyed = true;\r\n if (this.#autoScaleTimer) {\r\n clearInterval(this.#autoScaleTimer);\r\n this.#autoScaleTimer = undefined;\r\n }\r\n }\r\n /** Calculates the target shard ID for a Discord guild snowflake. @param guildId Guild snowflake. @returns Shard identifier. */\r\n public getShardIdForGuild(guildId: string): number {\r\n const id = BigInt(guildId);\r\n return Number((id >> 22n) % BigInt(this.shardCount || 1));\r\n }\r\n /** Gets a shard by ID. @param id Shard identifier. @returns Gateway instance or undefined. */ public get(\r\n id: number,\r\n ): Gateway | undefined {\r\n return this.shards.get(id);\r\n }\r\n /** Returns information for all managed shards. @returns Shard information snapshots. */ public values(): ShardInfo[] {\r\n return [...this.shards].map(([id, gateway]) => ({ id, gateway }));\r\n }\r\n /** Ensures a live shard set exists. @returns A promise fulfilled after initialization. @throws {Error} If initialization fails. */\r\n async #ensureInitialized(): Promise<void> {\r\n if (this.#resolved) return;\r\n if (this.#destroyed) this.#destroyed = false;\r\n const count =\r\n this.#options.shardCount === \"auto\"\r\n ? await this.fetchRecommendedShardCount()\r\n : (this.#options.shardCount ?? 1);\r\n this.#initialize(count);\r\n }\r\n /** Creates every shard for a resolved shard count. @param count Number of shards. @returns Nothing. */\r\n #initialize(count: number): void {\r\n if (this.#resolved) return;\r\n for (let id = 0; id < count; id++)\r\n this.shards.set(id, this.#createShard(id, count));\r\n this.#resolved = true;\r\n }\r\n /** Creates one Gateway instance with its shard identity. @param id Shard identifier. @param count Total shard count. @returns Configured Gateway. */\r\n #createShard(id: number, count: number): Gateway {\r\n return new Gateway({\r\n token: this.#options.token,\r\n intents: this.#options.intents,\r\n shardId: id,\r\n shardCount: count,\r\n reconnect: this.#options.reconnect ?? true,\r\n });\r\n }\r\n}\r\n"
|
|
9
10
|
],
|
|
10
|
-
"mappings": "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",
|
|
11
|
-
"debugId": "
|
|
11
|
+
"mappings": "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",
|
|
12
|
+
"debugId": "F2AF359ADCA0121464756E2164756E21",
|
|
12
13
|
"names": []
|
|
13
14
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { BaseStructure } from "./base.js";
|
|
2
|
+
import type { APIAuditLog, APIAuditLogEntry, APIAuditLogChange, APIAuditLogOptions } from "@lunibee/types";
|
|
3
|
+
/** Represents a single entry in a Discord audit log. */
|
|
4
|
+
export declare class AuditLogEntry extends BaseStructure {
|
|
5
|
+
/** The ID of the affected entity. */
|
|
6
|
+
readonly targetId: string | null;
|
|
7
|
+
/** The ID of the user who made the changes. */
|
|
8
|
+
readonly userId: string | null;
|
|
9
|
+
/** The action type of this entry. */
|
|
10
|
+
readonly actionType: number;
|
|
11
|
+
/** The changes made to the target. */
|
|
12
|
+
readonly changes: APIAuditLogChange[];
|
|
13
|
+
/** Additional info for certain action types. */
|
|
14
|
+
readonly options: APIAuditLogOptions | null;
|
|
15
|
+
/** The reason for the change, if any. */
|
|
16
|
+
readonly reason: string | null;
|
|
17
|
+
/** Creates an audit log entry. */
|
|
18
|
+
constructor(data: APIAuditLogEntry);
|
|
19
|
+
}
|
|
20
|
+
/** Represents a Discord audit log. */
|
|
21
|
+
export declare class AuditLog {
|
|
22
|
+
/** The entries in the audit log. */
|
|
23
|
+
readonly entries: Map<string, AuditLogEntry>;
|
|
24
|
+
/** Creates an audit log. */
|
|
25
|
+
constructor(data: APIAuditLog);
|
|
26
|
+
}
|