Creating Plugins
Learn how to create custom Frame-Master plugins and contribute to the ecosystem.
🚀 Quick Start
Generate a plugin boilerplate using the Frame-Master CLI.
# Generate a new pluginframe-master plugin create my-custom-plugin# This creates:# frame-master-plugin-my-custom-plugin/# ├── index.ts # Plugin entry point# ├── package.json # Plugin metadata# ├── README.md # Documentation# └── tsconfig.json # TypeScript config
Plugin Naming Convention
frame-master-plugin-[name] for consistency with the ecosystem.📦 Basic Plugin Structure
Every Frame-Master plugin exports a factory function that returns a FrameMasterPlugin object.
import type { FrameMasterPlugin } from "frame-master/plugin/types";export type MyPluginOptions = {apiKey?: string;debug?: boolean;};export function myPlugin(options: MyPluginOptions = {}): FrameMasterPlugin {const config = {apiKey: options.apiKey || process.env.API_KEY,debug: options.debug ?? false,};return {// Required fieldsname: "my-custom-plugin",version: "1.0.0",// Optional: Execution priority (lower = runs first)priority: 50,// Server lifecycle hooksserverStart: {main: async () => { /* runs on startup */ },dev_main: async () => { /* dev mode only */ },},// Request handling hooksrouter: {before_request: async (master) => { /* init context */ },request: async (master) => { /* handle/intercept */ },after_request: async (master) => { /* modify response */ },html_rewrite: { /* transform HTML */ },},// Build hooksbuild: {buildConfig: async (builder) => ({ /* merge config */ }),beforeBuild: async (config, builder) => { /* pre-build */ },afterBuild: async (config, result, builder) => { /* post-build */ },},// WebSocket supportserverConfig: { routes: { /* ws upgrade routes */ } },websocket: { onOpen, onMessage, onClose },// File watching (dev mode)fileSystemWatchDir: ["src/"],onFileSystemChange: async (event, path) => { /* react */ },// CLI extensioncli: (command) => command.command("my-cmd").action(() => {}),};}export default myPlugin;
Required Fields
namestringrequiredUnique identifier for your plugin. Should match package name for consistency.
versionstringrequiredSemantic version (e.g., '1.0.0'). usually matches package.json.
Hook Reference
For detailed documentation on all available hooks, see the Plugin Hooks Reference.
📋 Plugin Requirements
Specify dependencies and version requirements.
requirement: {// Minimum Frame-Master versionframeMasterVersion: "^2.0.0",// Bun runtime versionbunVersion: ">=1.2.0",// Required pluginsframeMasterPlugins: {"frame-master-plugin-session": "^1.0.0",},}
Validation
Frame-Master validates requirements at startup. Missing dependencies prevent the server from starting.
🔌 WebSocket Support
Add real-time communication to your plugin.
serverConfig: {routes: {"/ws/my-plugin": (req, server) => {return server.upgrade(req, {data: { "my-plugin-ws": true },});},},},websocket: {onOpen: async (ws) => {if (!ws.data["my-plugin-ws"]) return;ws.send(JSON.stringify({ type: "connected" }));},onMessage: async (ws, message) => {if (!ws.data["my-plugin-ws"]) return;const data = JSON.parse(message.toString());// Handle message...},onClose: async (ws) => {if (!ws.data["my-plugin-ws"]) return;// Cleanup...},}
Shared Handlers
WebSocket handlers receive connections from all plugins. Use ws.data to identify your plugin's connections.
💻 CLI Extension
Add custom commands to Frame-Master CLI.
cli: (command) => {return command.command("generate <type>").description("Generate project files").option("-o, --output <dir>", "Output directory", "./").action(async (type, options) => {console.log(`Generating ${type} in ${options.output}`);// Your generation logic});}
Command Access
Commands are available via frame-master extended-cli <command>. Uses Commander.js.
👁️ File Watching
React to file changes in development mode.
// Directories to watchfileSystemWatchDir: ["src/styles/", "config/"],// Handle changesonFileSystemChange: async (eventType, filePath, absolutePath) => {if (filePath.endsWith(".css")) {await rebuildStyles();}if (filePath.includes("config/")) {await reloadConfig();}}
📦 Publishing Your Plugin
Share your plugin with the community.
Package.json Setup
{"name": "frame-master-plugin-my-plugin","version": "1.0.0","description": "Description of what your plugin does","main": "index.ts","type": "module","keywords": ["frame-master", "frame-master-plugin"],"peerDependencies": {"frame-master": "^2.0.0"}}
Publishing Steps
# Update versionnpm version patch # or minor, or major# Publish to npmnpm publish# Tag release on GitHubgit tag v1.0.0 && git push --tags
Plugin Checklist
- ✅ Clear README.md documentation
- ✅ TypeScript type definitions
- ✅ Example configuration
- ✅ Proper versioning (semver)
✨ Best Practices
Tips for creating high-quality plugins.
- Provide sensible defaults for all options
- Export TypeScript types for configuration
- Handle errors gracefully - don't crash the server
- Cache expensive operations
- Document all configuration options
- Use priority values appropriately (auth: 0-10, logging: 80-100)
