Skip to content

plugin-gateway: port ApplicationEmoji (with its manager) and ApplicationCommand (discord.js parity) #207

Closed

Description

@RedStar071

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

  1. ApplicationEmoji + ApplicationEmojiManager.
    • Structure: id, name, animated, managed, requiresColons, available, author, application, plus edit(), setName(), delete(), fetchAuthor(), and equals(other).
    • Manager: create(), fetch(id?), edit(), delete(), fetchAuthor(), over the /applications/{id}/emojis routes.
    • It should share the Emoji base already used by GuildEmoji.
  2. ApplicationCommand. The command data (name and description with localizations, type, options, default member permissions, NSFW, integration types, contexts, handler, version, guild ID), plus edit(), the set*() helpers, delete(), and equals(command, enforceOptionOrder?) with the static optionsEqual(). Its actions go through the managers of plugin-gateway: port ApplicationCommandManager and ApplicationCommandPermissionsManager (discord.js parity) #209.
  3. equals() for both, ported from discord.js (v14 and v15 share the implementation):
  4. 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-cache needs a new entity type. The same question for commands is in plugin-gateway: port ApplicationCommandManager and ApplicationCommandPermissionsManager (discord.js parity) #209.

Questions

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

  1. pullfrog commented on Oct 5, 2026

    @pullfrog
    Contributor

    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-gateway and the installed @discordjs/core@2.6.0 and @wolfstar/http-framework@6.1.0.

    Findings

    • Nothing exists yet. src/ has no ClientApplication, no ApplicationEmoji, no ApplicationCommand, and no client.application. The only application-adjacent code is the Integration#application payload field. The READY dispatch (src/util/dispatch.ts:154) builds client.user but discards data.application.
    • The REST surface is complete in @discordjs/core. ApplicationsAPI has getCurrent, getEmojis, getEmoji, createEmoji, editEmoji and deleteEmoji. ApplicationCommandsAPI has the global and guild CRUD, bulkOverwriteGlobalCommands, bulkOverwriteGuildCommands, getGuildCommandPermissions, and editGuildCommandPermissions(userToken, ...). No raw Routes.* escape hatch should be needed.
    • There is a clear template for the emoji half. Emoji (src/structures/emojis/Emoji.ts) is already a shared base, with GuildEmoji as a subclass. GuildEmojiManager and GuildEmoji show the exact create, edit, delete, fetchAuthor shape, and the structure edit() and delete() conventions. GuildEmoji#equals is the model for ApplicationEmoji#equals.
    • The framework overlap is real. ApplicationCommandRegistry.pushGlobalCommands() in http-framework PUTs Routes.applicationCommands(clientId), which is a bulk overwrite. A client.application.commands.set() here would replace whatever the framework registered. This is the risk the issue's first question raises.
    • The cache story favours fetch-only. @wolfstar/plugin-cache already holds applicationCommandPermissions, a plain MemoryEntityCache outside cacheOptions. 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 is feat(plugin-gateway).

    • Add src/structures/applications/ClientApplication.ts, with id, flags (from READY's application), emojis and commands getters, and fetch() via api.applications.getCurrent().
    • Expose client.application: ClientApplication | null. It is set in the READY handler next to client.user, in the same way. The ID also falls back to client.id, so the REST-only managers work before READY.
    • Leave out owner/team, install params, role connection metadata, SKUs and entitlements. Each can be its own follow-up.

    PR 2: ApplicationEmoji and ApplicationEmojiManager.

    • ApplicationEmoji extends Emoji, with id, name, animated, author, application, edit(), setName(), delete(), fetchAuthor() and equals().
    • ApplicationEmojiManager is a plain BaseManager and fetch-only, like WebhookManager. It has fetch(id?), create() (reusing resolveImage), edit(), delete() and fetchAuthor().
    • It takes the application ID from ClientApplication. GatewayError codes go in errors/Messages.ts if needed.

    PR 3: ApplicationCommand and its managers.

    • ApplicationCommand structure with the fields listed in the issue, plus edit(), the set*() helpers, delete(), equals(command, enforceOptionOrder?) and the static optionsEqual(). Option comparison is recursive and ported from discord.js.
    • ApplicationCommandManager (global) and GuildApplicationCommandManager (guild.commands, a new instance per access like the other guild sub-managers). Both have create, fetch, edit, delete and set.
    • ApplicationCommandPermissionsManager with fetch, has and, for writes, set, add and remove. The writes take a bearer token option, passed through to editGuildCommandPermissions(userToken, ...). Without a token they throw a typed GatewayTypeError.
    • Reads can reuse the existing applicationCommandPermissions store, kept in sync by the gateway event. That is the only cached piece.
    • Tests live in packages/plugin-gateway/tests/, with the usual mocked core.api pattern. A type-level case goes in tests/types/*.ts if the resolvable types are public.

    Each PR also needs:

    • A changeset for @wolfstar/plugin-gateway (minor).
    • Exports through index.ts and managers/index.ts.
    • The four quality gates: pnpm lint, pnpm build, pnpm typecheck, pnpm test.
    • A README or API docs mention.

    Decisions needed before implementing

    1. Cached or fetch-only? I recommend fetch-only for emojis and commands, with the existing applicationCommandPermissions store as the only cached piece. It avoids new plugin-cache entity types, and a cache with no events to invalidate it would go stale.
    2. Framework overlap. I suggest keeping set() because the issue asks for parity, but documenting that it overwrites the http-framework registry, and not wiring anything automatically. Is that acceptable, or should set() be omitted or guarded?
    3. Separate issue for ClientApplication? I'd keep it as PR 1 under this issue, since it is small, and split further ClientApplication fields into their own issues.
    4. Permission writes need a bearer token. I'd accept an explicit token option 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.

    Pullfrog  | Implement plan ➔ | View workflow run | via Pullfrog | Using claude-sonnet-5-5 | 𝕏

  2. changed the title [-]plugin-gateway: port ApplicationEmoji and ApplicationCommand with their managers (discord.js parity)[/-] [+]plugin-gateway: port ApplicationEmoji (with its manager) and ApplicationCommand (discord.js parity)[/+] on Oct 5, 2026
  3. RedStar071 commented on Oct 17, 2026

    @RedStar071
    MemberAuthor

    Done: ApplicationEmoji and ApplicationEmojiManager in #250, ApplicationCommand with its managers in #252. Both are fetch-only, as discussed (no new plugin-cache entity).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions