Plugin Hooks Reference
Complete reference guide for all available hooks in Frame-Master plugins.
🚀 Server Lifecycle Hooks
Hooks that execute during server initialization and startup.
serverStart.main
serverStart.main() => Promise<void>Executes on the main thread when the server starts. Runs in both development and production modes.
export function myPlugin(): FrameMasterPlugin {return {name: "my-plugin",serverStart: {main: async () => {// Initialize database connectionsawait db.connect();// Load configurationconst config = await loadConfig();// Set up global stateglobal.appConfig = config;console.log("Plugin initialized");},},};}
serverStart.dev_main
serverStart.dev_main() => Promise<void>Executes only in development mode. Use for dev-specific initialization like file watchers or debug tools.
serverStart: {dev_main: async () => {// Enable debug loggingenableDebugMode();// Start file watcherwatchForChanges();// Initialize hot reloadsetupHotReload();console.log("Dev mode initialized");},}
🔀 Router Hooks
Hooks for intercepting and modifying HTTP requests and responses.
router.before_request
router.before_request(master: RequestManager) => Promise<void>Called before request processing begins. Use to initialize context or set global values.
Available Methods:
master.setContext(data)- Set request-specific context datamaster.setGlobalValues(values)- Inject global values accessible in client codemaster.request- Access the incoming Request object
router: {before_request: async (master) => {// Initialize contextmaster.setContext({requestId: crypto.randomUUID(),startTime: Date.now(),user: null,});// Inject global values (accessible as globalThis.__API_URL__)master.setGlobalValues({__API_URL__: process.env.API_URL,__VERSION__: "1.0.0",});},}
router.request
router.request(master: RequestManager) => Promise<void>Called during request processing. Can intercept and handle requests or let them pass through.
Available Methods:
master.request- The incoming Request objectmaster.setResponse(body, options)- Set the response body and optionsmaster.sendNow()- Send response immediately, skipping other pluginsmaster.getContext()- Get request context data
router: {request: async (master) => {const url = new URL(master.request.url);// Handle API routesif (url.pathname.startsWith("/api/")) {const data = await handleApiRequest(master.request);master.setResponse(JSON.stringify(data), {status: 200,header: {"Content-Type": "application/json","X-Custom-Header": "value",},}).sendNow(); // Skip remaining pluginsreturn;}// Let other plugins handle it},}
sendNow() Behavior
Calling sendNow() immediately sends the response and prevents subsequent request hooks from executing. Only use this when you want to bypass the normal request flow.
router.after_request
router.after_request(master: RequestManager) => Promise<void>Called after request processing. Use to modify response headers or perform cleanup.
Available Methods:
master.response- The Response object (may be null)master.request- The original Request objectmaster.getContext()- Get request context data
router: {after_request: async (master) => {const response = master.response;if (!response) return;// Add security headersresponse.headers.set("X-Frame-Options", "DENY");response.headers.set("X-Content-Type-Options", "nosniff");response.headers.set("X-XSS-Protection", "1; mode=block");// Add timing informationconst context = master.getContext();const duration = Date.now() - context.startTime;response.headers.set("X-Response-Time", `${duration}ms`);// Log requestconsole.log(`[${master.request.method}] ${master.request.url} - ${duration}ms`);},}
router.html_rewrite
router.html_rewrite{ initContext, rewrite, after }Transform HTML responses using Bun's HTMLRewriter API.
router: {html_rewrite: {// Initialize context for HTML rewritinginitContext: (req) => {return {injectAnalytics: process.env.NODE_ENV === "production",theme: req.headers.get("x-theme") || "dark",userId: req.headers.get("x-user-id"),};},// Rewrite HTML elementsrewrite: async (reWriter, master, context) => {// Inject scripts in headreWriter.on("head", {element(element) {if (context.injectAnalytics) {element.append('<script src="/analytics.js"></script>',{ html: true });}},});// Modify body attributesreWriter.on("body", {element(element) {element.setAttribute("data-theme", context.theme);if (context.userId) {element.setAttribute("data-user", context.userId);}},});// Transform specific elementsreWriter.on("img", {element(element) {const src = element.getAttribute("src");if (src && !src.startsWith("http")) {element.setAttribute("loading", "lazy");}},});},// Final processing after HTML rewriteafter: async (HTML, master, context) => {// Additional HTML transformationsconsole.log("HTML processing complete");// You can modify HTML string here if needed// return modifiedHTML;},},}
HTMLRewriter API
The reWriter parameter is Bun's HTMLRewriter instance. You can use all standard HTMLRewriter methods to transform HTML.
👁️ File System Hooks
Hooks for reacting to file system changes in development mode.
fileSystemWatchDir
fileSystemWatchDirstring[]Array of directory paths to watch for changes. Only active in development mode.
export function myPlugin(): FrameMasterPlugin {return {name: "my-plugin",// Specify directories to watchfileSystemWatchDir: ["src/","public/styles/","config/",],// ...};}
onFileSystemChange
onFileSystemChange(eventType: string, filePath: string, absolutePath: string) => Promise<void>Called when a file in watched directories changes. Only active in development mode.
Parameters:
eventType- Type of change ("change", "rename", etc.)filePath- Relative path to the changed fileabsolutePath- Absolute path to the changed file
onFileSystemChange: async (eventType, filePath, absolutePath) => {console.log(`File ${eventType}: ${filePath}`);// Rebuild CSS on style changesif (filePath.endsWith(".css")) {await rebuildStyles();console.log("Styles rebuilt");}// Clear cache on component changesif (filePath.includes("/components/")) {clearComponentCache();console.log("Component cache cleared");}// Reload config on config changesif (filePath.includes("config/")) {await reloadConfig();console.log("Config reloaded");}}
⚙️ Plugin Configuration
Configure plugin metadata, priority, and requirements.
name
namestringrequiredUnique identifier for the plugin. Used in logging and error messages.
export function myPlugin(): FrameMasterPlugin {return {name: "my-custom-plugin",// ...};}
priority
prioritynumberExecution priority. Lower numbers run first. Default is 50.
Default: 50
// Auth plugin - runs firstexport function authPlugin(): FrameMasterPlugin {return {name: "auth-plugin",priority: 0,// ...};}// Logging plugin - runs lastexport function loggingPlugin(): FrameMasterPlugin {return {name: "logging-plugin",priority: 100,// ...};}
requirement
requirement{ frameMasterVersion?, bunVersion?, frameMasterPlugins? }Specify version requirements for Frame-Master, Bun, and other plugins.
requirement: {// Require Frame-Master versionframeMasterVersion: "^1.0.0",// Require Bun runtime versionbunVersion: ">=1.2.0",// Require other pluginsframeMasterPlugins: {"frame-master-plugin-react-ssr": "^1.0.0","my-database-plugin": "^2.0.0",},}
🔧 Advanced Features
Advanced plugin capabilities for custom functionality.
directives
directivesArray<{ name: string, regex: RegExp }>Define custom directives for special file handling.
directives: [{name: "use-server",regex: /^(?:\s*(?:\/\/.*?\n|\s)*)?['""]use[-\s]server['""];?\s*(?:\/\/.*)?(?:\r?\n|$)/m,},{name: "use-client",regex: /^(?:\s*(?:\/\/.*?\n|\s)*)?['""]use[-\s]client['""];?\s*(?:\/\/.*)?(?:\r?\n|$)/m,},{name: "use-cache",regex: /^(?:\s*(?:\/\/.*?\n|\s)*)?['""]use[-\s]cache['""];?\s*(?:\/\/.*)?(?:\r?\n|$)/m,},]
runtimePlugins
runtimePluginsBunPlugin[]Bun runtime plugins for custom module resolution and transformation.
import type { BunPlugin } from "bun";const customLoader: BunPlugin = {name: "custom-loader",setup(build) {// Handle .custom filesbuild.onLoad({ filter: /\.custom$/ }, async (args) => {const contents = await Bun.file(args.path).text();return {contents: transformCustomFile(contents),loader: "js",};});// Resolve custom importsbuild.onResolve({ filter: /^@custom\/.*/ }, (args) => {return {path: resolveCustomPath(args.path),namespace: "custom",};});},};export function myPlugin(): FrameMasterPlugin {return {name: "my-plugin",runtimePlugins: [customLoader],// ...};}
