Upgrading Beignet
Beignet packages version together. Upgrade every installed @beignet/*
package, @beignet/cli, and create-beignet to the same release instead of
mixing versions across the framework.
Upgrade workflow
- Read the target release notes and the migration section on this page.
- Update all Beignet packages to the same version and reinstall dependencies.
- Run
bun beignet doctor --strictandbun beignet lint. - Run the app's
typecheck,test, and productionbuildscripts. - Exercise app-owned worker, cron, webhook, outbox, and task entrypoints that are not covered by the web build.
Generated files belong to the app after creation. Do not rerun
create-beignet over an existing app or copy a fresh starter wholesale.
Apply migration steps to the app's current structure and use doctor fixers only
for findings explicitly marked safe.
Unreleased 1.0 stabilization changes
The current stabilization pass removes redundant pre-adoption APIs before the compatibility promise hardens.
CLI commands use one vocabulary
Provider setup now uses the singular provider command group and
capability-first preset names. Replace beignet providers add redis-cache
with beignet provider add cache-redis, and replace providers audit with
provider audit. App creation now accepts the same catalog through
--providers; the former --integrations flag is removed.
Workflow artifact names consistently use feature.name, including the value
passed to make listener --event. The slash form is removed. make feature --with accepts singular addon names such as event, job, and upload.
Resource generation spells out --authorization and --tenant-scoped, and
manual schedule runs use --run-id. Existing-app make, db, task,
schedule, and outbox commands now accept --cwd <dir> consistently.
Runtime port wiring has an explicit name
The app's compile-time port shape remains in ports/index.ts. Rename the
runtime wiring file and value so they cannot be confused with AppPorts:
mv infra/app-ports.ts infra/port-wiring.ts// Before
import { appPorts } from "@/infra/app-ports";
// After
import { initialPorts } from "@/infra/port-wiring";Pass initialPorts to the server's ports option and use it as the base in
test context factories. Custom CLI config replaces
paths.infrastructurePorts with paths.portWiring:
export default defineConfig({
paths: {
ports: "src/ports/index.ts",
portWiring: "src/infra/port-wiring.ts",
},
});There is no compatibility alias for the former path, symbol, or config key.
Provider registration uses factories
Provider packages no longer export shared xProvider instances. Import and
call the matching factory in server/providers.ts:
import { createRedisCacheProvider } from "@beignet/provider-cache-redis";
export const providers = [createRedisCacheProvider()] as const;The same mechanical change applies across first-party providers, for example
pinoLoggerProvider becomes createPinoLoggerProvider() and
stripePaymentsProvider becomes createStripePaymentsProvider().
Provider factories expose stable named types
First-party provider factories now return named provider types such as
RedisCacheProvider, PinoLoggerProvider, and
DrizzlePostgresProvider<TSchema>. Their concrete Zod config schemas are
private implementation details; exported config interfaces still describe the
validated values when an app needs that shape. This does not change provider
registration or InferProviderPorts results.
PostgresConfigSchema and MysqlConfigSchema are no longer exported from the
Drizzle backend subpaths. Apps that imported either schema should own any
app-level environment validation and use PostgresConfig or MysqlConfig for
the corresponding validated shape.
Framework-neutral server imports come from core
Route declarations, route registries, hooks, and framework-neutral server
types come from @beignet/core/server. Runtime adapter packages expose only
their platform-specific APIs:
import {
contractsFromRoutes,
defineRouteGroup,
defineRoutes,
} from "@beignet/core/server";
import { createNextServer, createNextServerLoader } from "@beignet/next";Use @beignet/web for Web Fetch conversion and server adapters. In
particular, replace toNextResponse(...) with toWebResponse(...) imported
from @beignet/web.
Test helpers use one subpath
Move imports from @beignet/core/ports/testing to
@beignet/core/testing. Fixtures, recording adapters, actors, policy helpers,
assertions, factories, seeds, and provider test installation now share that
single public boundary.
Schedule instrumentation uses shared provider targets
The schedule-specific ScheduleInstrumentation type was removed.
createInlineScheduleRunner(...) now accepts a shared provider instrumentation
target; existing record-only sinks remain structurally compatible. You can
also pass the complete context ports object when running a schedule inside an
application boundary:
const runner = createInlineScheduleRunner({
ctx,
instrumentation: ctx.ports,
});The runner now follows the shared provider instrumentation contract,
records events under the schedules watcher, and isolates instrumentation
sink failures. onHookError now reports lifecycle hook failures only.
Route declarations use an app-bound builder
Create the route builders once and import them from feature route files:
// lib/routes.ts
import "@beignet/core/server-only";
import { createRoutes } from "@beignet/core/server";
import type { AppContext } from "@/app-context";
export const { defineRoute, defineRouteGroup } = createRoutes<AppContext>();Replace defineRoute<AppContext>()(...) and
defineRouteGroup<AppContext>()(...) calls with the corresponding app-bound
builder. Add paths.routesBuilder when the file is not lib/routes.ts.
Listener identity is the first argument
Move listener names out of the options object and put the event in options:
defineListener("posts.enqueue-published-email", {
event: PostPublished,
async handle({ payload, ctx }) {},
});Integration names match their role
- Replace
createAuthBetterAuthProvider(...)withcreateBetterAuthProvider(...). - Replace
@beignet/provider-webhooks-githubwith@beignet/webhooks-github. - Replace
@beignet/provider-webhooks-stripewith@beignet/webhooks-stripe.
The webhook packages are route-bound server integrations. They no longer carry
provider audit metadata or appear in beignet provider audit.
Removed aliases
| Removed | Replacement |
|---|---|
contract.pathTemplate | contract.path |
defineFlagRegistry(...) | defineFlags(...) |
ctx.ports.db.db | ctx.ports.db.drizzle |
defineRoute<AppContext>()(...) | app-bound defineRoute(...) from lib/routes.ts |
defineRouteGroup<AppContext>()(...) | app-bound defineRouteGroup(...) from lib/routes.ts |
createAuthBetterAuthProvider(...) | createBetterAuthProvider(...) |
@beignet/provider-webhooks-github | @beignet/webhooks-github |
@beignet/provider-webhooks-stripe | @beignet/webhooks-stripe |
toNextResponse(...) | toWebResponse(...) from @beignet/web |
createInMemoryEventBus(...) | createMemoryEventBus(...) |
createInMemoryEventBusProvider(...) | createMemoryEventBusProvider(...) |
InMemoryEventBus* types | MemoryEventBus* types |
ScheduleInstrumentation | ProviderInstrumentationTarget from @beignet/core/providers |
Getting help from the compiler
Most Beignet migrations are import or option-shape changes. TypeScript should
identify every affected call site. When a removed symbol appears only in a
generated registry or configuration string, beignet doctor --strict and
beignet provider audit cover the registration and provider metadata that
the compiler cannot inspect.
See Stability and releases for the compatibility policy.