@veap/plugins/router exports route-tree contracts and discovery/matching helpers. The plugin contract permits routeTree to be a route node or a function returning one synchronously/asynchronously.
Route tree contract#
A plugin can return the route tree from a plugin-owned app/ directory using discoverRoutes(). Keep the directory scoped to that plugin so route discovery cannot accidentally load another plugin's files.
import type { IPlugin } from "@veap/plugins";
export const plugin: IPlugin = {
manifest: {
id: "reports",
name: "Reports",
version: "1.0.0",
enabled: true,
},
async routeTree() {
const { discoverRoutes } = await import("@veap/plugins/router");
const path = await import("node:path");
const appDir = path.join(process.cwd(), "plugins/reports/src/app");
return discoverRoutes(appDir, (relativePath) =>
import(`../src/app/${relativePath}`),
);
},
};This is a pattern sketch: the loader callback's module path must match your bundler and project layout. Dynamic import paths are subject to bundler constraints; compile and test the actual route discovery in your application before publishing a plugin.
Test the route surface#
At minimum, verify:
- static and dynamic segments resolve to the intended handler;
- nested layouts and error/loading boundaries behave as expected;
- middleware calls
next()correctly and handles thrown errors; - route access checks run on the server;
- plugin disablement removes or blocks the plugin's routes according to the host integration.
The plugin router is not a replacement for Next.js's own App Router. Use the host's documented integration layer and do not assume native Next.js routes are automatically intercepted.