npm
Source
Description
A powerful HTTP framework for building your Discord bots, powered bynode:http, @discordjs/rest, and @sapphire/pieces.
Features
- Support for reloading and unloading commands
- Built-in Hot Module Reloading for every store
- Built-in logger, extendable by plugins
- Support for attachment responses
- Seamless integration with low-level libraries
- Thin wrapper on top of raw data for maximum performance
Usage
This library can handle both HTTP interactions and registering commands both globally and per guild using an integrated design powered by decorators.Command
The Command is a piece that runs for all chat input and context menu interactions, including auto-complete (since this one is sort of part of the former). Registering the commands happens with decorators:Registering commands without decorators
If you don’t want to rely on TS decorators (for example, when writing plain JavaScript, or@sapphire/framework-style codebases), you can instead override the registerApplicationCommands method, which receives a per-command registry with the same capabilities as the decorators above:
Note: this is an alternative to the decorators, not a replacement — both approaches share the same underlying registry and can be mixed across different commands in the same project.
Utility decorators
Besides theRegister* decorators, the framework ships a set of utility decorators for configuring pieces and gating
methods.
ApplyOptions
Sets the options of any Piece — Command, Listener, or InteractionHandler — without writing a constructor. The
decorator’s values are merged on top of the options the piece is constructed with, so they win on conflicting keys.
Note:ApplyOptionsreturns aProxywrapping the class, so it must be applied above (outside of) any other class decorator that keys metadata by class identity, such asRegisterCommand— as in the example above. A decorator applied aboveApplyOptionswould run after it and register against the proxy, but instances constructed from the exported class still resolve.constructorto the original, unproxied class, so that metadata could never be found.
RequiresGuildContext / RequiresDMContext
Restrict a method to interactions received from a guild, or to interactions received outside of one (DMs and
user-installed app contexts). Both take an optional fallback that receives the same arguments as the decorated method;
without one, the method is silently skipped and resolves to undefined.
RequiresUserPermissions / RequiresClientPermissions
Check the permissions of the invoking user (member.permissions) or of the application (app_permissions) in the
channel the interaction was sent from. Permissions are given as PermissionFlagsBits values, as flag names, or as any
nested array of both.
PreconditionError is thrown, identified by Identifiers.PreconditionUserPermissions or
Identifiers.PreconditionClientPermissions, with context: { missing, missingNames } describing the missing
permissions. Errors thrown from a command are emitted as commandError (and as interactionHandlerError for
interaction handlers), which is the idiomatic place to turn them into a user-facing reply:
- Members with
Administratorimplicitly satisfy every check. RequiresUserPermissionspasses for interactions received outside of a guild, since there are no guild permissions to check. Combine it withRequiresGuildContextwhen the method must be guild-only.RequiresClientPermissionspasses whenapp_permissionsis absent from the payload, since there is nothing to check against.
Enumerable / EnumerableMethod
Control whether a field or a method shows up in Object.keys, JSON.stringify, and console output.
Note:Enumerableinstalls a setter on the prototype, which is bypassed by theObject.definePropertycall thatuseDefineForClassFields(enabled by@sapphire/ts-config, and the default fromES2022onwards) emits for class fields. Mark the field asdeclareso no field definition is emitted, and assign it in the constructor.
Building your own decorators
createClassDecorator, createMethodDecorator, createProxy, and createFunctionPrecondition are the primitives the
decorators above — and the Register* ones — are built on, and are exported so you can build your own.
Note:createFunctionPreconditionreplaces the decorated method with anasyncone, so a decorated method always returns aPromise, even when both the precondition and the method are synchronous.
Client
TheClient class contains the HTTP server, powered by node:http, it also registers a handler that processes whether or not the HTTP request comes from Discord and processes the information accordingly, handling the heavyweight in the background.
Logger
The framework ships a minimal logger, available ascontainer.logger (and as client.logger) as soon as
@wolfstar/http-framework is imported. It writes to the matching console method and filters entries by
LogLevel, which defaults to LogLevel.Info:
ILogger to options.logger.instance from a
preGenericsInitialization hook:
ILogger interface, the rest of the framework — including Hot Module
Reloading and the command router — keeps logging through container.logger without any change.
Client events
TheClient extends an event emitter typed by the ClientEvents interface. Every event name is also available as a
member of the Events enum, which is the recommended way to reference them, as the plain strings remain valid:
The Hot Module Reloading events are listed in their own section.
Listeners declared as pieces can use the enum too:
Hot Module Reloading
@wolfstar/http-framework ships with Hot Module Reloading (HMR) as a core feature, no plugin required. When enabled,
every path registered in every store is watched, and pieces are loaded, reloaded, and unloaded in place as their files
are created, changed, and deleted, without restarting the process.
hmr option accepts all of chokidar’s options, plus:
The reloader is exposed as
client.hmr, which is null when HMR is disabled, and can be stopped at any time:
Client, as long as the stores are registered in the container:
Note: unloading a command also removes its entry from the ApplicationCommandRegistry, so a reloaded command is
registered exactly once. HMR does not push the updated commands to Discord on its own, subscribe to the events above
if you want that behaviour.
Project configuration (stars.config.*)
@wolfstar/http-framework owns the typed project configuration consumed by the stars CLI — the
defineConfig helper and the config loader live here, not in the CLI, so any tool can resolve a project’s
configuration without pulling in @wolfstar/cli.
@wolfstar/http-framework/config has no side effects — importing it (or a stars.config.ts that imports it) never
starts the bot. loadStarsConfig discovers stars.config.{ts,mts,cts,js,mjs,cjs} from a directory, applies defaults,
validates every option and resolves all paths to absolute ones.
dev.url, the URL stars dev shows and health-checks the bot on, needs no configuration either: it is detected the
way Vite’s and Nuxt’s dev servers are, from HTTP_PORT (env var, src/.env*/.env*, or dev.env) or 3000, and
localhost is swapped for 127.0.0.1 at runtime if that is what is actually reachable. Set dev.url explicitly only
to override it, e.g. for a LAN address: dev: { url: 'http://192.168.1.5:3000' }.
dev.banner replaces the default Stars wordmark in the interactive CLI. Use a string or an array of lines, for
example dev: { banner: ['★ STARYL', 'Twitch notifications'] }, or false to hide it. Up to four lines fit the
compact panel. Application logs and standalone banners are available behind l, not printed over the dev panel.
dev also carries optional overrides for the dev loop. Press t in the TUI to toggle a quick tunnel without any
configuration:
tunnel.updateEndpoint writes the public URL to the Discord application’s interactions_endpoint_url; it is opt-in
because it edits a live application, and needs DISCORD_TOKEN in the environment or the project’s .env.
The build (tsdown)
tsdown is the bundler stars build and stars dev use, and it is configured from stars.config itself. A base
project configures nothing at all — this is a complete build:
Aliases
The four prefixes Nuxt gives every project work out of the box, pointing at the same two places its own do:paths (the scaffold writes them, and
examples/basic shows them):
tsdown.alias is added to these rather than replacing them, and a target written as a relative path
('./src/lib') is resolved against the project root, the way every other path in stars.config is — a module id
('preact/compat') is left alone.
So the block is for what the defaults cannot know — an extra alias, declaration output, or a target. Conventional
src/locales assets are copied to dist/locales automatically by the CLI:
tsdown wins over the defaults, and plugins are appended rather than replaced.
With future.compatibilityVersion: 3 (legacy mode) a tsdown.config.* in the project root is still loaded and
tsdown is merged over it, so a project can move its options across one at a time. With 4 the block is the whole
configuration, and a leftover tsdown.config.* is reported instead of being silently ignored.
vite: {} works the same way for build.tool: 'vite' (see experimental flags): it is merged
into the project’s own vite.config.*, the way vite: {} in a Nuxt config is.
Compatibility version
future.compatibilityVersion selects the build-default generation. Version 4 is the default; version 3 remains as
an explicit migration mode for projects that still have a standalone tsdown.config.*.
- Auto imports are on with the
tsdownbuild tool, and theautoImports()plugin is wired into the build bystarsitself instead of by the project’s own configuration file — the framework’s exports and the project’ssrc/lib/**,src/utils/**are usable without animportstatement, the same way Nuxt’s own are. tsdownis configured fromstars.configalone. Atsdown.config.*(or apackage.json#tsdownfield) raisesTSDOWN_CONFIG_FILE_UNSUPPORTED, because a build that quietly dropped the plugins such a file declares would be far harder to diagnose than an error naming it.build.tool: 'auto'resolves totsdownfor any TypeScript entry, rather than looking for atsdown.config.*or atsdowndependency first.tscstays available as an explicit choice.
stars info prints the version in effect.
Experimental flags
experimental is the same kind of block Nuxt’s own experimental is: opt-in booleans, all false by default, each
guarding work that is still landing.
ConfigError with a stable code, the offending option path, the file it came from, and
an actionable hint. See the @wolfstar/cli README for the full option reference and how the
stars commands (dev, build, info, codegen, prepare, commands) use it.
See Configuration Errors for every stars.config.* diagnostic code.
ApplicationCommandRegistry
TheApplicationCommandRegistry is @wolfstar/http-framework’s centralized registry and uses @discordjs/rest to register them in Discord.
Note: callingapplicationCommandRegistry.setup()is not needed if you are using theClientclass because it is already called automatically for you.