Repository navigation
plugin-gateway: port ApplicationEmoji (with its manager) and ApplicationCommand (discord.js parity) #207
Description
Context
While aligning equals() with discord.js across @wolfstar/plugin-gateway (#203), two discord.js structures that define equals() turned out to have no counterpart here, so their comparison could not be ported:
| discord.js structure | discord.js managers | plugin-gateway |
|---|---|---|
ApplicationEmoji |
ApplicationEmojiManager (client.application.emojis) |
missing |
ApplicationCommand |
ApplicationCommandManager, GuildApplicationCommandManager, ApplicationCommandPermissionsManager (tracked in #209) |
missing |
Both hang off ClientApplication (client.application), which does not exist here either. It is tracked in #208 and is a prerequisite of this issue.
ApplicationCommand was left out on purpose so far (the parity scope excluded interaction-bound structures). This issue tracks bringing it in anyway, as a REST-managed entity: registering, fetching, editing and deleting commands does not depend on receiving interactions.
Scope
This issue covers the two structures and the emoji manager. The application command managers are in #209.
Proposal
ApplicationEmoji+ApplicationEmojiManager.- Structure:
id,name,animated,managed,requiresColons,available,author,application, plusedit(),setName(),delete(),fetchAuthor(), andequals(other). - Manager:
create(),fetch(id?),edit(),delete(),fetchAuthor(), over the/applications/{id}/emojisroutes. - It should share the
Emojibase already used byGuildEmoji.
- Structure:
ApplicationCommand. The command data (name and description with localizations, type, options, default member permissions, NSFW, integration types, contexts, handler, version, guild ID), plusedit(), theset*()helpers,delete(), andequals(command, enforceOptionOrder?)with the staticoptionsEqual(). Its actions go through the managers of plugin-gateway: port ApplicationCommandManager and ApplicationCommandPermissionsManager (discord.js parity) #209.equals()for both, ported from discord.js (v14 and v15 share the implementation):ApplicationEmoji#equals: against an emoji,animated,id,name,managed,requiresColons,available; against a raw emoji,idandname.ApplicationCommand#equals: the command fields plus a recursive option comparison, optionally order-sensitive.- Like the structures in plugin-gateway: structure compatibility of equals() across structures (discord.js parity) #203, return
falsefor a foreign argument instead of throwing.
- Cache. Decide whether application emojis are cached at all. discord.js caches them in the manager, but no gateway event keeps that cache in sync, so a fetch-only, uncached manager may fit the pluggable cache better. If they are cached,
@wolfstar/plugin-cacheneeds a new entity type. The same question for commands is in plugin-gateway: port ApplicationCommandManager and ApplicationCommandPermissionsManager (discord.js parity) #209.
Questions
- Cached or fetch-only for application emojis (see point 4)?
- Which of the two lands first: the
ApplicationCommandstructure here, or its managers in plugin-gateway: port ApplicationCommandManager and ApplicationCommandPermissionsManager (discord.js parity) #209? The structure'sedit()/delete()need the manager, and the manager returns the structure, so they may have to ship together.
Out of scope
ClientApplication (#208), the application command managers (#209), and interaction structures themselves (CommandInteraction, autocomplete, modals, and so on) with the interaction-bound resolvers.
Activity
pullfrog commentedon Oct 5, 2026
Assessment
Actionable, but I'd split it into three PRs, and one design decision needs a maintainer answer first (see "Decisions needed"). I checked the claims in the issue against
packages/plugin-gatewayand the installed@discordjs/core@2.6.0and@wolfstar/http-framework@6.1.0.Findings
src/has noClientApplication, noApplicationEmoji, noApplicationCommand, and noclient.application. The only application-adjacent code is theIntegration#applicationpayload field. TheREADYdispatch (src/util/dispatch.ts:154) buildsclient.userbut discardsdata.application.@discordjs/core.ApplicationsAPIhasgetCurrent,getEmojis,getEmoji,createEmoji,editEmojianddeleteEmoji.ApplicationCommandsAPIhas the global and guild CRUD,bulkOverwriteGlobalCommands,bulkOverwriteGuildCommands,getGuildCommandPermissions, andeditGuildCommandPermissions(userToken, ...). No rawRoutes.*escape hatch should be needed.Emoji(src/structures/emojis/Emoji.ts) is already a shared base, withGuildEmojias a subclass.GuildEmojiManagerandGuildEmojishow the exactcreate,edit,delete,fetchAuthorshape, and the structureedit()anddelete()conventions.GuildEmoji#equalsis the model forApplicationEmoji#equals.ApplicationCommandRegistry.pushGlobalCommands()inhttp-frameworkPUTsRoutes.applicationCommands(clientId), which is a bulk overwrite. Aclient.application.commands.set()here would replace whatever the framework registered. This is the risk the issue's first question raises.@wolfstar/plugin-cachealready holdsapplicationCommandPermissions, a plainMemoryEntityCacheoutsidecacheOptions. It is the only entity with a gateway event (APPLICATION_COMMAND_PERMISSIONS_UPDATE). Commands and application emojis have no gateway events, so a cache would go stale and nothing could repair it.Proposed split
PR 1:
ClientApplication(prerequisite). Scope isfeat(plugin-gateway).src/structures/applications/ClientApplication.ts, withid,flags(fromREADY'sapplication),emojisandcommandsgetters, andfetch()viaapi.applications.getCurrent().client.application: ClientApplication | null. It is set in theREADYhandler next toclient.user, in the same way. The ID also falls back toclient.id, so the REST-only managers work beforeREADY.PR 2:
ApplicationEmojiandApplicationEmojiManager.ApplicationEmoji extends Emoji, withid,name,animated,author,application,edit(),setName(),delete(),fetchAuthor()andequals().equals()argument isApplicationEmoji | APIEmoji-shaped. It returnsfalsefor a foreign argument, as plugin-gateway: structure compatibility of equals() across structures (discord.js parity) #203 does, and has norolescomparison.managed,requires_colonsoravailable. Either port discord.js' defaults or compare only the fields the payload carries; I'd lean towards the latter, and the PR should say which.ApplicationEmojiManageris a plainBaseManagerand fetch-only, likeWebhookManager. It hasfetch(id?),create()(reusingresolveImage),edit(),delete()andfetchAuthor().ClientApplication.GatewayErrorcodes go inerrors/Messages.tsif needed.PR 3:
ApplicationCommandand its managers.ApplicationCommandstructure with the fields listed in the issue, plusedit(), theset*()helpers,delete(),equals(command, enforceOptionOrder?)and the staticoptionsEqual(). Option comparison is recursive and ported from discord.js.ApplicationCommandManager(global) andGuildApplicationCommandManager(guild.commands, a new instance per access like the other guild sub-managers). Both havecreate,fetch,edit,deleteandset.ApplicationCommandPermissionsManagerwithfetch,hasand, for writes,set,addandremove. The writes take a bearertokenoption, passed through toeditGuildCommandPermissions(userToken, ...). Without a token they throw a typedGatewayTypeError.applicationCommandPermissionsstore, kept in sync by the gateway event. That is the only cached piece.packages/plugin-gateway/tests/, with the usual mockedcore.apipattern. A type-level case goes intests/types/*.tsif the resolvable types are public.Each PR also needs:
@wolfstar/plugin-gateway(minor).index.tsandmanagers/index.ts.pnpm lint,pnpm build,pnpm typecheck,pnpm test.Decisions needed before implementing
applicationCommandPermissionsstore as the only cached piece. It avoids newplugin-cacheentity types, and a cache with no events to invalidate it would go stale.set()because the issue asks for parity, but documenting that it overwrites thehttp-frameworkregistry, and not wiring anything automatically. Is that acceptable, or shouldset()be omitted or guarded?ClientApplication? I'd keep it as PR 1 under this issue, since it is small, and split furtherClientApplicationfields into their own issues.tokenoption and not add any OAuth handling. Is that enough?I haven't started implementing. Say which of these you want changed, or whether to begin with PR 1.
claude-sonnet-5-5| 𝕏[-]plugin-gateway: port ApplicationEmoji and ApplicationCommand with their managers (discord.js parity)[/-][+]plugin-gateway: port ApplicationEmoji (with its manager) and ApplicationCommand (discord.js parity)[/+]feat(plugin-gateway): add ApplicationEmoji and ApplicationEmojiManager
feat(plugin-gateway): add ApplicationEmoji and ApplicationEmojiManager
RedStar071 commentedon Oct 17, 2026
Done:
ApplicationEmojiandApplicationEmojiManagerin #250,ApplicationCommandwith its managers in #252. Both are fetch-only, as discussed (no new plugin-cache entity).