@dualmark/vercel
v0.10.0
Published
Vercel Edge Middleware adapter for the Dualmark AEO framework. Wraps any upstream handler and transparently serves markdown to AI bots at the edge.
Downloads
202
Maintainers
Readme
@dualmark/vercel
Vercel Edge Middleware adapter for the Dualmark AEO framework. Wraps any upstream handler and transparently serves markdown to AI bots at the edge — no changes to your existing site.
Install
bun add @dualmark/vercel @dualmark/coreUsage
// proxy.ts
import { NextResponse } from "next/server";
import { createAEOMiddleware } from "@dualmark/vercel";
const middleware = createAEOMiddleware({
upstream: async () => NextResponse.next(),
fetchAsset: async (url, init) => fetch(url.toString(), init),
trailingSlash: "never",
enableLinkHeader: true,
hooks: {
onAIRequest: (info) => console.log(`${info.botName} hit ${info.pathname}`),
onMiss: (info) => console.warn(`miss: ${info.pathname}`),
},
});
export default middleware;
export const config = {
matcher: [
{
source: "/((?!_next/|favicon.ico).*)",
missing: [{ type: "header", key: "next-router-prefetch" }],
},
],
};How it works on Vercel
Vercel Edge Middleware re-triggers for same-origin fetch() calls. To prevent infinite loops, the adapter adds an x-dualmark-subrequest header to internal fetchAsset calls and short-circuits when it detects a subrequest — returning NextResponse.next() so Vercel serves the static file directly.
upstreamshould returnNextResponse.next()for browser requests. The adapter injects Link headers directly on the response object.fetchAssetshould forward the optionalRequestInittofetch()so the subrequest header is included.
Options
| Option | Type | Default | Description |
| ------------------ | ----------------------------------- | --------- | ----------------------------------------------------------- |
| upstream | (req) => Response | — | Handler for non-bot requests (return NextResponse.next()) |
| fetchAsset | (url, init?) => Response | — | Fetch a .md file by URL (forward init to fetch) |
| trailingSlash | "never" \| "always" \| "preserve" | "never" | Trailing slash mode |
| enableLinkHeader | boolean | true | Inject Link rel=alternate on HTML responses |
| hooks | { onAIRequest?, onMiss? } | — | Lifecycle callbacks for AI request events |
| redirects | { internal?, external? } | — | Redirect rules for AI bots |
| skip | { prefixes?, extensions? } | — | Paths to skip entirely |
| headers | { cacheControl? } | — | Custom response headers |
What it does
- Subrequest detection to prevent
fetch()infinite loops on Vercel - Trailing-slash enforcement (configurable:
never,always,preserve) - AI bot detection via UA
- Content negotiation via Accept header
- Serves pre-built
.mdviafetchAssetwith full AEO headers - Internal redirects: routes to target's
.md - External redirects: returns markdown notice
- 406 when neither HTML nor markdown is acceptable
- Link header injection on upstream
NextResponse.next()for HTML responses - Analytics hooks (onAIRequest / onMiss)
License
Apache 2.0
