Skip to main content
This page collects the upgrade paths between Stars Components packages and their versions. Every section is self-contained — read the one matching the move you are making.

Migrating from v4 to v5

::: info Current releases The v5 build defaults are stable in @wolfstar/http-framework@5.0.0. The matching developer tooling is @wolfstar/cli@1.0.0. ::: v5 is a small, mostly opt-in release. The only breaking change to the framework’s public API is the removal of the previously-unannounced @wolfstar/http-framework/fetch submodule, replaced by a fetch() method on Client itself. Everything else is either purely additive (experimental.enableNitro) or an internal error-handling change (nostics diagnostics) that only matters to code that caught ConfigError/CliError by name.

Changes Included in v5

Upgrading the Packages

::: code-group
[pnpm]
[npm]
[yarn]
[bun]
::: No stars.config.* changes are required to move from v4 to v5 — the configuration shape is unchanged.

Replacing @wolfstar/http-framework/fetch with Client#fetch

The experimental, previously-unannounced @wolfstar/http-framework/fetch submodule is removed. Projects that imported createFetchHandler, FetchHandler or FetchHandlerOptions from it to adapt Client to a Fetch-based runtime switch to the new fetch() method on Client itself — a method on the instance you already have, rather than a separate adapter module to import and wire up:
Client#fetch(request, options?) runs the exact same signature verification, routing and replies listen()’s node:http server runs, without binding a port. The Discord public key given at construction is imported once and reused across every call, the same lifetime listen() gives its own signing key — use it for anything that speaks Fetch instead of node:http (Nitro, a Worker, Bun.serve, Deno.serve, Vite’s own dev middleware).

Building Through Nitro

experimental.enableNitro is now implemented: stars dev/stars build build and serve the bot through Nitro v3’s own Vite plugin instead of refusing with EXPERIMENT_UNAVAILABLE. It is purely opt-in and requires experimental.enableVite as well, since Nitro v3 is itself a Vite plugin rather than a separate build step:
Install vite and nitro as devDependencies. stars build now produces build.outDir (.output by default under Nitro, instead of dist) laid out for the configured preset rather than a node:http process; stars dev rebuilds and restarts on every change, the same as the other build tools. ::: warning The entry’s default export changes The generated Nitro server entry imports the entry file’s default export and calls .fetch(request) on it — the entry must export the already-load()ed Client instance, not call listen():
::: Vite’s native resolve.tsconfigPaths is turned on automatically under Nitro (see Nitro’s import alias guide), so a project’s own tsconfig.json#paths/package.json imports aliases — including the ones stars prepare’s generated .stars/tsconfig.json adds — keep working without a vite-tsconfig-paths plugin.

Catching Configuration and CLI Errors

ConfigError/ConfigErrorOptions (from @wolfstar/http-framework/config) and CliError/CliErrorOptions (from @wolfstar/cli) are removed. Every stars.config.* validation and load failure, and every condition @wolfstar/cli itself rejects, is now built from a catalog (configDiagnostics, cliDiagnostics, both still exported) and thrown as a nostics Diagnostic — a stable, typed code with a why, an actionable fix, and a docs link, instead of ad hoc code/hint/path/file fields:
The option path that used to live on .path is folded directly into the diagnostic’s message; the configuration file that used to live on .file is now in .sources. @wolfstar/cli itself still renders these the same way it always has (formatError/exitCodeOf keep their existing exports and behaviour) and still exits with code 2 for every stars.config.* diagnostic and 3 for BUILD_FAILED, unchanged from v4 — this only matters for code that imported ConfigError/CliError by name. See the configuration error reference and CLI error reference for the full code catalogs.

Checklist

  • @wolfstar/http-framework upgraded to v5 and @wolfstar/cli upgraded to v1
  • createFetchHandler/FetchHandler/FetchHandlerOptions imports from @wolfstar/http-framework/fetch replaced with client.fetch(request, options?)
  • instanceof ConfigError / instanceof CliError checks replaced with instanceof Diagnostic (from nostics)
  • .path/.file reads on caught errors replaced with .message/.sources
  • Optional: experimental.enableVite and experimental.enableNitro configured, vite and nitro installed as devDependencies, and the entry’s default export changed from calling listen() to the loaded Client instance

Migrating from v3 to v4

Current releasesThe v4 build defaults are stable in @wolfstar/http-framework@4.0.1. The matching developer tooling is @wolfstar/cli@0.6.0; projects generated by @wolfstar/create-http-framework@2.5.1 already use this layout.
v4 makes the convention-first workflow introduced during the later v3 releases the default. Most runtime APIs and imports from @wolfstar/http-framework are unchanged; the migration primarily consolidates development and build configuration around stars.config.* and the stars CLI.

Changes Included in v4

Upgrade the Framework and Add the stars CLI

The developer workflow moves into a separate package, @wolfstar/cli, which ships the stars binary. The framework itself keeps owning the configuration — the same split Nuxt has between nuxt/defineNuxtConfig and nuxi.
Node requirement@wolfstar/http-framework still supports Node.js 20 or newer. @wolfstar/cli requires Node.js 22 or newer, inherited from Ink, which renders the interactive stars dev UI. It is a devDependency, so it constrains the development environment, not the deployed bot.

Creating stars.config.ts

Add a stars.config.{ts,mts,cts,js,mjs,cjs} file at the project root. defineConfig comes from the framework’s new config subpath, not from the CLI:
Every option has a default: entry falls back to the first of src/main.ts, src/main.js, src/index.ts, src/index.js that exists. For a TypeScript entry, build.tool: 'auto' now selects tsdown; choose build: { tool: 'tsc' } explicitly if the project must keep the TypeScript compiler as its emitter. A minimal v4 project can export defineConfig({}); future.compatibilityVersion: 4 is no longer necessary. @wolfstar/http-framework/config is side-effect free — importing it, or a stars.config.ts that imports it, never starts the bot. Invalid options raise a ConfigError carrying a stable code, the offending option path, the file it came from, and an actionable hint; stars exits with code 2 on those. The resolved configuration is readable from any tool, without depending on the CLI:

Replacing the package.json Scripts

The per-language, per-build-tool script wiring collapses into two commands that read stars.config.*:
TypeScript projects built with tsc drop tsc-watch from their devDependenciesstars dev runs tsc -b --watch itself. JavaScript projects drop node --watch src/main.js and keep no build script at all (build.tool: 'none'). start is unchanged: production still runs the built entry point directly, stars is a development-only tool. Run stars prepare explicitly after installing development dependencies and before a standalone typecheck on a fresh checkout. Do not put it in a root postinstall: production-only installs such as npm ci --omit=dev still run that lifecycle script after omitting the development-only @wolfstar/cli, so the install would fail because stars is not available. stars dev and stars build already regenerate the files they need.

Move tsdown Configuration into stars.config.ts

Compatibility version 4 uses stars.config.* as the only build configuration. A standalone tsdown.config.* or a package.json#tsdown field now raises TSDOWN_CONFIG_FILE_UNSUPPORTED instead of being silently ignored. Most bot projects can delete tsdown.config.ts: Stars supplies entries for every source file next to the main entry, unbundled ESM output for Node, sourcemaps, treeshaking, external dependencies, the configured output directory and extension, the project tsconfig, Nuxt-style aliases, auto imports, and a copy of src/locales in dist/locales. Move only non-conventional overrides into the top-level tsdown block:
If the configuration cannot be migrated in one step, temporarily keep the old file with future: { compatibilityVersion: 3 }. In this legacy mode, Stars loads the standalone config and merges the stars.config.ts tsdown block over it. Remove the old file and the compatibility override together when finished.

The New Framework Subpaths

v3 exposed a single entry point. The package now has three export subpaths: No existing import changes: the root subpath keeps the same specifier and the same exports.

stars Commands

--config <file> points at a configuration file and --cwd <dir> changes the working directory; both work on every command. stars --help and stars --version never load the configuration machinery, so they stay fast. stars dev builds, starts the bot with STARS_DEV=1 and NODE_ENV=development, and restarts it after every successful build. Failed builds keep the previous process alive and wait for the next change. On a terminal it renders an interactive UI (lifecycle, uptime, restart reason, build state, URL, health, filtered logs, r restart, c clear, f/e filters, h help, q quit); it falls back to plain prefixed lines with --no-tui, STARS_TUI=plain, in CI, or when stdout is not a TTY. Both modes honour NO_COLOR and stop the bot cleanly on SIGINT/SIGTERM.
hmr and stars dev overlapThe bot runs as a child node process with STARS_DEV=1 in its environment, and stars dev restarts the whole process on every build. Leave the framework’s own hmr client option disabled while using stars dev.
stars commands covers the gap the registry cannot: renamed or deleted commands stay deployed on Discord until something removes them.
It reads DISCORD_TOKEN and DISCORD_APPLICATION_ID (or APPLICATION_ID) from the environment or the project’s .env, the same place the bot reads them from. Outside a terminal, clean refuses to run without --yes or --name.

Dev Loop Options

Three dev options round out the loop, all off by default except the log file:
  • dev.typecheck brings back the type safety a tsdown build skips. checker is tsc (watch mode), golar (golar tsc, watch mode), tsz (no watch mode, so re-run after every build), or auto — the default, golar when the project depends on it and tsc otherwise. Type errors are reported without blocking builds or restarts.
  • dev.tunnel exposes the interactions endpoint publicly: true opens a cloudflared quick tunnel (a new hostname on every run), a string is an https URL you already serve and the CLI only probes. dev.tunnel.updateEndpoint writes that URL to the Discord application’s interactions_endpoint_url — opt-in, because it edits a live application.
  • dev.logFile (default .stars/dev.log) mirrors the session’s logs to disk, so a run can be read back once the terminal UI is gone.
dev.url needs no configuration: it is detected from HTTP_PORT (environment variable, src/.env*/.env*, or dev.env) or 3000, the way Vite’s and Nuxt’s dev servers do, and localhost is swapped for 127.0.0.1 when that is what is actually reachable. Set it explicitly only to override, e.g. dev: { url: 'http://192.168.1.5:3000' }. Environment discovery now checks src/.env* as well as root .env* files. Conventional src/locales assets are copied to dist/locales automatically and watched during development, so remove custom copy/watch hooks that only do those jobs. In the TUI, press t to open or close a quick tunnel without setting dev.tunnel; keep dev.tunnel when the tunnel should open automatically at startup.

Auto Imports

Nuxt-style auto imports make the framework’s exports and the project’s own modules usable without an import statement. They are injected at build time by the autoImports() rolldown plugin, so they require the tsdown build tooltsc and none have no transform step to hook into. They are on by default with tsdown, and imports: false turns them off.
Client, Message, Plugin and Store are never auto-imported, even when a preset exports them: the names are generic enough that project code likely declares its own. Import them explicitly, as today.

Generated TypeScript Configuration

stars prepare now generates both the auto imports declaration and .stars/tsconfig.json. The generated config contains the Sapphire base, extra-strict and decorator options, plus bundler settings aligned with Nitro. For tsdown and Vite it also contains TypeScript paths matching ~/@ (the entry directory), ~~/@@ (the project root), and custom filesystem aliases from stars.config.ts. Replace manually duplicated compiler defaults, include entries and alias paths by extending it:
Keep project-specific compilerOptions next to extends. An explicit include or compilerOptions.paths replaces the inherited value, so remove those keys unless that is intentional. Do not edit .stars/tsconfig.json; stars dev and stars build regenerate it. Run stars prepare explicitly before editor/typecheck use on a fresh checkout. Use stars prepare --check in CI only after generation.

Plugin Registration and the Built-in Logger

Bundler builds now activate every @wolfstar/plugin-* package found in dependencies or optionalDependencies by injecting its /register side-effect entry before the application entry. Remove matching manual imports such as:
This automation applies to tsdown and Vite builds. It deliberately ignores devDependencies; a runtime plugin must be a runtime dependency. Projects using build.tool: 'tsc' or 'none' do not have a bundler transform and must keep explicit /register imports. The framework also provides container.logger without @wolfstar/logger. Remove the deprecated package and custom setup module when it only installed the standard logger. Configure the built-in logger through the client when needed:

Ignoring .stars/

The CLI writes generated TypeScript configuration, the auto imports declaration and the dev log into a .stars/ directory at the project root. Add it to .gitignore:

Checklist

  • @wolfstar/http-framework upgraded to v4 and @wolfstar/cli added as a devDependency
  • Development environment upgraded to Node.js 22 or newer
  • stars.config.ts created with defineConfig from @wolfstar/http-framework/config
  • Standalone tsdown.config.* or package.json#tsdown removed; custom options moved to stars.config.ts
  • dev / build scripts replaced with stars dev / stars build
  • watch, watch:start and the tsc-watch dependency removed
  • stars prepare runs before standalone typechecking on a fresh checkout, without a production postinstall
  • generate:i18n replaced with stars codegen, verified with stars codegen --check
  • The framework’s hmr client option disabled while developing with stars dev
  • tsconfig.json extends ./.stars/tsconfig.json; duplicated generated aliases/options removed
  • Redundant env discovery and src/locales copy/watch hooks removed
  • Manual @wolfstar/plugin-*/register imports removed for bundler builds only
  • Deprecated @wolfstar/logger setup replaced with the built-in container.logger
  • .stars/ added to .gitignore
  • stars info output reviewed — it prints the configuration exactly as the commands resolve it
  • stars prepare, the project typecheck and stars build pass
  • Optional: dev.typecheck, dev.tunnel and auto imports configured

Migrating to @wolfstar/plugin-i18next

@wolfstar/http-framework-i18n is deprecated. Its successor is @wolfstar/plugin-i18next, an official plugin for @wolfstar/http-framework that lives in the wolfstar-project/plugins repository. No further releases are planned for the old package.
Prerequisites@wolfstar/plugin-i18next requires @wolfstar/http-framework@^3.1.0 and pulls in i18next@^25.8.18.
The plugin keeps the same typed-key philosophy (T / FT, resolveKey, applyLocalizedBuilder) but replaces the manual load() + init() bootstrap with the framework’s plugin lifecycle, and moves the loaded state onto container.i18n.

Overview of the Changes

Swapping the Dependency

@wolfstar/http-framework is a peer dependency of the plugin, so keep it in your dependencies.

Bootstrap Moved to the Plugin Lifecycle

The old package required you to load the locales and initialize i18next yourself, before registering commands:
The plugin does all of that for you. Import the side-effecting register entrypoint before the client is created, and move the configuration into the i18n client option:
The plugin registers a preLoad hook that awaits container.i18n.init() before the stores load, so command builders can still be localized at registration time — the ordering the manual await init() used to guarantee.

Locales Directory Renamed to languages

The old package took whatever path you passed to load(); the plugin defaults to <root>/languages. Either rename the directory or keep your own path via defaultLanguageDirectory. The layout itself is unchanged: one directory per language, every nested .json file is a namespace.

Module Specifier Renamed

T, FT, resolveKey, resolveUserKey, getSupportedLanguageName, getSupportedUserLanguageName, getSupportedLanguageT, getSupportedUserLanguageT, supportedLanguages, isSupportedDiscordLocale, getLocalizedData, applyNameLocalizedBuilder, applyDescriptionLocalizedBuilder, applyLocalizedBuilder and createSelectMenuChoiceName keep the same names and signatures. For most files the migration is a single find-and-replace of the module specifier:

Removed APIs

New APIs

Per-Guild Languages

The old package could only read the locales Discord puts on the interaction payload. If you store a language per guild, you can now plug a resolver in and use the asynchronous fetch* helpers. The resolver can be declared up-front as part of the i18n client option:
Or assigned later on the handler, which is handy when the resolver depends on something only available after the client is created (a database connection, for example):
Both forms feed the same hook — the client option is applied to the handler on creation, and a later assignment overwrites it. Either way the asynchronous helpers pick it up:
resolveKey and resolveUserKey stay synchronous and keep reading the interaction payload only, so existing call sites do not change behaviour.

Hot Module Replacement

When enabled, the languages directory is watched with chokidar and container.i18n.reloadResources() runs on every change or deletion.

Upgrading i18next from 22 to 25

The plugin depends on i18next@^25. If your project pins i18next itself, bump it and review the i18next migration notes. In practice the typed-key helpers absorb most of the surface, but note that TFunction is now generic over namespace and key prefix, so explicitly annotated TFunction variables may need their type arguments updated.

Checklist

  • @wolfstar/http-framework-i18n removed from package.json
  • @wolfstar/plugin-i18next added, @wolfstar/http-framework on ^3.1.0 or newer
  • import '@wolfstar/plugin-i18next/register' is the first import of the entry point
  • load() / init() / addFormatters() calls removed, options moved to new Client({ i18n })
  • Locales directory named languages, or defaultLanguageDirectory set
  • Module specifiers updated across the codebase
  • getT / loadedLocales call sites moved to container.i18n

Migrating to @wolfstar/plugin-logger

@wolfstar/logger is deprecated. Replace it with @wolfstar/plugin-logger, which provides the same framework logger interface through the plugin lifecycle and adds support for multiple transports.
Prerequisites@wolfstar/plugin-logger requires Node.js 20 or newer and @wolfstar/http-framework@^3.4.0.
If console output is all you need, @wolfstar/http-framework already provides container.logger; remove the old package without installing the plugin. Install @wolfstar/plugin-logger when you need transport configuration, Sentry integration, or a Consola, Evlog, or Winston backend.

Overview of the Changes

The ILogger methods and level ordering are unchanged: existing trace, debug, info, warn, error, and fatal calls can keep their arguments.

Swapping the Dependency

Keep the plugin in dependencies, not devDependencies: it changes the logger at runtime. The backend integrations are optional peer dependencies, so install only the ones used by your configured transports.

Moving Logger Setup into Client

Replace the manually constructed logger with the logger client option, and read the active instance from the framework container:
The plugin installs its logger during preGenericsInitialization, before the rest of the client initializes. An explicit logger.instance is preserved, so applications with their own ILogger implementation can keep it:

Registering the Plugin

The explicit side-effect import must run before new Client() when the application is built with tsc, uses build.tool: 'none', or does not use the Stars CLI:
With @wolfstar/cli@0.6.0 or newer and a tsdown build, installed @wolfstar/plugin-* packages are discovered from runtime dependencies and their /register entrypoints are activated automatically. In that setup, remove the manual import to avoid registering the plugin twice.

Configuring Transports

Without a transports option, the plugin uses ConsoleTransport. Pass an array to send every log payload to multiple destinations:
Each transport can have its own minimum level; messages below it are skipped for that destination. The Sentry SDK and alternative logger backends are not installed automatically. For example, a Winston transport is configured with the Winston logger instance you already own:
Evlog exposes four levels and Winston’s default npm levels do not include fatal. On those adapters, trace is mapped to debug and fatal to error.

Updating Colour Helper Imports

The old package re-exported colorette helpers. The plugin does not. If the application uses helpers such as red, bold, or stripColor, depend on colorette directly and update the module specifier:

Checklist

  • @wolfstar/logger removed from package.json
  • @wolfstar/plugin-logger added to runtime dependencies when custom transports are needed
  • @wolfstar/http-framework on ^3.4.0 or newer and Node.js 20 or newer
  • Manual new Logger(...) replaced with the logger client option
  • Logger call sites use container.logger
  • /register imported before new Client(), or omitted when the Stars bundler auto-registers plugins
  • Optional backend peer dependencies installed for every configured transport
  • Colour helpers imported directly from colorette