AvelonDocs

@avelon

next

@avelon/next

@avelon/next is the v1 adapter. It turns a RouteManifest into Next's file router, serves writes as generated server actions, wraps pages around the core kernel, and bridges middleware.ts. Reach for it when the application is a Next app and you want typed routes/web.ts without putting Next types in @avelon/core.

The adapter is a translation layer at three seams: mount, toRequest, and toResponse. Middleware, binding, validation, and exception mapping stay in the kernel (D26).

Installation

bun add @avelon/next

Wire the adapter in avelon.config.ts and generate the router during dev and build.

import { NextAdapter } from '@avelon/next'
import { createKernel, defineConfig } from '@avelon/core'

const adapter = new NextAdapter({ root: process.cwd() })
defineConfig({
  name: 'app',
  adapter,
  drivers: {},
})

await adapter.mount(Route.manifest(), { kernel: createKernel() })

Basic Usage

import { Route } from '@avelon/next'
import { PostController } from '@/app/Http/Controllers/PostController'
import { Post } from '@/app/Models/Post'

Route.get('/', HomeController, 'index').name('home')

Route.middleware('auth').group(() => {
  Route.resource('posts', PostController).bind('post', Post)
})

reeve route:sync (and NextAdapter.mount) wipe app/(web) and rewrite it. Generated pages are four lines and contain no logic:

// Generated by `reeve route:sync`. Do not edit.
import { PostController } from '@/app/Http/Controllers/PostController'
import { page } from '@avelon/next'

export const dynamic = 'force-dynamic'

export default page(PostController, 'show', '/posts/{post}')

Writes become server actions in framework/routing/actions.generated.ts. .api() additionally generates app/(api)/api/.../route.ts so JSON URLs do not collide with pages.

Capabilities

CapabilityValueMeaning
fileSystemRoutingtruemount() writes framework-owned files.
serverActionstrueRoute.post() is served as a generated server action.
streamingtrueThe kernel may return StreamResult.
edgeMiddlewarefalseNext 16 Proxy middleware defaults to Node (D27).

serverActions selects the write transport. It does not gate whether a write route exists.

Request Lifecycle

page() and dispatch() convert native Next inputs with toRequest, call kernel.dispatch, then toResponse. Redirects throw NextRedirect with a NEXT_REDIRECT digest so Next navigation keeps propagating. Validation failures return { kind: 'action', ok: false, errors } for useActionState.

middleware.ts calls middleware(request). Routes that declare auth redirect to /login when no cookies or Authorization header are present; everything else returns { kind: 'next' }.

Method Reference

Method / exportSignatureDescription
NextAdapterclass NextAdapterFile-routing adapter implementing the frozen Adapter contract.
NextAdapter.mount(manifest, options) => Promise<MountResult>Writes the router and binds the kernel.
NextAdapter.attach(manifest, options) => voidBinds kernel and manifest without rewriting generated files.
NextAdapter.registerViews(views) => voidMaps string view tokens to React components for toResponse.
NextAdapter.toRequest(native) => Promise<HttpRequest>Converts page props, FormData, or Request. Unparseable bodies become body: null.
NextAdapter.toResponse(result) => Promise<NextNativeResponse>Converts kernel results. Redirects throw NextRedirect.
nextCapabilitiestypeof nextCapabilitiesLiteral capability object used by CLI and codegen.
bindAdapter`(adapter \undefined) => void`Records the adapter generated helpers dispatch through.
getAdapter() => NextAdapterReturns the bound adapter or throws.
setRuntimeBoot`(boot \undefined) => void`Registers a boot function page and dispatch call when unbound.
ensureAdapter() => Promise<NextAdapter>Returns the bound adapter, booting first when needed.
page(controller, action, uri) => PageComponentNext page wrapper used by generated page.tsx.
dispatch(controller, action, uri, formData) => Promise<NextNativeResponse>Server-action entry used by generated writes.
handleRoute(controller, action, uri, request, params?) => Promise<NextNativeResponse>JSON route-handler entry used by .api() files.
middleware(request, routes?) => Promise<NextNativeResponse>proxy.ts / middleware.ts bridge. Pass generated routes on the Node proxy.
MiddlewareRouteinterfaceMethod, path, and middleware aliases for Edge auth.
setCookie(name, value, options?) => voidQueues a cookie write for the current Next request.
clearCookie(name, options?) => voidQueues a cookie deletion for the current Next request.
PendingCookieinterfaceQueued cookie name, value, and options.
ViewRegistrytypeString view tokens mapped to render functions.
formProps(name, params?) => { action, method, fields }Hidden fields and action export for a named write route.
generateRouter(manifest, root, options) => Promise<readonly string[]>Destructive codegen used by mount.
findMountedRoute(controller, action, uri) => RouteDefinitionResolves a generated helper back to its manifest entry.
Route.get(path, controller, action?) => RouteBuilderRegisters a GET route.
Route.post(path, controller, action?) => RouteBuilderRegisters a POST route.
Route.put(path, controller, action?) => RouteBuilderRegisters a PUT route.
Route.patch(path, controller, action?) => RouteBuilderRegisters a PATCH route.
Route.delete(path, controller, action?) => RouteBuilderRegisters a DELETE route.
Route.resource(name, controller) => ResourceBuilderExpands the seven resource routes.
Route.middleware(...names) => { group }Applies middleware inside a group.
Route.prefix(prefix) => { group }Prefixes routes inside a group.
Route.all() => readonly RouteDefinition[]Returns registrations.
Route.manifest(version?) => RouteManifestBuilds a mountable manifest.
Route.find(name) => RouteDefinitionFinds a named route or throws.
Route.reset() => voidClears registrations.
RouteBuilder.name(name) => thisSets the stable route name.
RouteBuilder.middleware(...names) => thisAppends middleware aliases.
RouteBuilder.bind(param, model) => thisMarks a path parameter for binding.
RouteBuilder.api() => thisAdditionally generates a JSON route handler.
ResourceBuilder.bind(param, model) => thisBinds member parameters.
ResourceBuilder.only(...actions) => thisKeeps named resource actions.
ResourceBuilder.except(...actions) => thisDrops named resource actions.
ResourceBuilder.api() => thisMarks remaining resource routes as .api().
route(name, params?) => stringFills {param} placeholders.
exportName(definition) => stringStable generated export for a write route.
uriToSegments(uri) => stringConverts /posts/{post} to posts/[post].
nativeToRequest(native) => Promise<HttpRequest>Low-level native conversion.
kernelToResponse(result) => Promise<NextNativeResponse>Low-level result conversion.
NextRedirectclass NextRedirect extends ErrorControl-flow throw with a NEXT_REDIRECT digest.
isNextControlFlow(error: unknown) => booleanTrue for Next navigation throws.
PagePropsinterfaceAsync Next page props.
NextNativeRequesttypepage, form, or http native inputs.
NextNativeResponsetypeView, action, redirect, stream, or next.
NextAdapterOptionsinterfaceroot plus optional capability overrides.

Testing

Mount the adapter against a temp directory and call page / dispatch with the same controllers the generator would import. Point the kernel at FakeDatabase when a page loads models.

import { NextAdapter, Route, page } from '@avelon/next'
import { createKernel } from '@avelon/core'

const adapter = new NextAdapter({ root: tempDir })
await adapter.mount(Route.manifest(), { kernel: createKernel() })
const Page = page(PostController, 'index', '/posts')
await Page({})
bun test
bun run typecheck