Skip to main content

Plugin SDK API Reference

Complete API reference for @framework-m/plugin-sdk and @framework-m/vite-plugin.

@framework-m/plugin-sdk

Types

FrameworkMPlugin

The main plugin contract. Every plugin must export an object matching this interface.

interface FrameworkMPlugin {
/** Unique plugin name */
name: string;
/** Semantic version */
version: string;
/** Menu items to register in the sidebar */
menu?: MenuItem[];
/** Route definitions for lazy-loaded pages */
routes?: RouteDefinition[];
/** Service factories for dependency injection */
services?: Record<string, ServiceFactory>;
}

Navigation items displayed in the sidebar.

interface MenuItem {
/** Unique identifier (e.g., "wms.dashboard") */
name: string;
/** Display label */
label: string;
/** Navigation route path */
route?: string;
/** Icon identifier */
icon?: string;
/** Top-level module group (e.g., "WMS") */
module?: string;
/** Category within module (e.g., "Operations") */
category?: string;
/** Sort order (lower numbers appear first) */
order?: number;
/** Nested children (auto-populated by registry) */
children?: MenuItem[];
}

RouteDefinition

Route configuration for plugin pages.

interface RouteDefinition {
/** URL path (e.g., "/wms/dashboard") */
path: string;
/** Component or lazy loader */
element: React.ComponentType | (() => Promise<{ default: React.ComponentType }>);
}

ServiceFactory

Factory function for dependency injection services.

type ServiceFactory<T = unknown> = () => T | Promise<T>;

Classes

PluginRegistry

Central registry for managing plugins, menus, and routes.

class PluginRegistry {
/** Register a plugin. Validates and stores plugin data. */
register(plugin: FrameworkMPlugin): Promise<void>;

/** Get a registered plugin by name. Returns undefined if not found. */
getPlugin(name: string): FrameworkMPlugin | undefined;

/** Get all registered plugins. */
getPlugins(): FrameworkMPlugin[];

/** Get merged menu tree from all plugins. Organized by module → category → items. */
getMenu(): MenuItem[];

/** Get all routes from all plugins, flattened. */
getRoutes(): RouteDefinition[];

/** Access the service container. */
getServiceContainer(): ServiceContainer;
}

ServiceContainer

Lazy singleton dependency injection container.

class ServiceContainer {
/** Register a service factory. */
register<T>(name: string, factory: ServiceFactory<T>): void;

/** Get a service instance. Factory called once (lazy singleton). */
get<T>(name: string): T | Promise<T>;

/** Check if a service is registered. */
has(name: string): boolean;

/** Get all registered service names. */
getAll(): string[];
}

React Context

PluginRegistryProvider

React context provider that makes the registry available to hooks.

import { PluginRegistryProvider, PluginRegistry } from "@framework-m/plugin-sdk";

const registry = new PluginRegistry();
await registry.register(myPlugin);

<PluginRegistryProvider registry={registry}>
<App />
</PluginRegistryProvider>

Hooks

usePluginMenu(): MenuItem[]

Returns the merged menu tree from all registered plugins.

import { usePluginMenu } from "@framework-m/plugin-sdk";

function Sidebar() {
const menu = usePluginMenu();
// menu: MenuItem[] — tree of module → category → items
return menu.map(module => <MenuGroup key={module.name} {...module} />);
}

Throws: Error if used outside PluginRegistryProvider.

usePlugin(name: string): FrameworkMPlugin | undefined

Returns a specific registered plugin by name.

import { usePlugin } from "@framework-m/plugin-sdk";

function PluginInfo({ name }: { name: string }) {
const plugin = usePlugin(name);
if (!plugin) return <span>Plugin not found</span>;
return <span>{plugin.name} v{plugin.version}</span>;
}

Throws: Error if used outside PluginRegistryProvider.

useService<T>(name: string): { service: T | null; loading: boolean; error: Error | null }

Retrieves a service from the DI container with loading/error states.

import { useService } from "@framework-m/plugin-sdk";

function StockLevel() {
const { service, loading, error } = useService<InventoryService>("inventoryService");

if (loading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <div>{service.getStockLevel()}</div>;
}

Throws: Error if used outside PluginRegistryProvider.


@framework-m/vite-plugin

frameworkMPlugin(options?): VitePlugin

Vite plugin that auto-discovers workspace plugins at build time.

import { frameworkMPlugin } from "@framework-m/vite-plugin";

export default defineConfig({
plugins: [react(), frameworkMPlugin()],
});

What it does:

  1. Build start: Scans workspace for package.json files with "framework-m" metadata
  2. Virtual module: Generates virtual:framework-m-plugins with import/export statements
  3. HMR: Watches plugin config files for hot reload during development

Virtual module output (auto-generated):

// virtual:framework-m-plugins
import plugin_0 from "@wms/frontend/src/plugin.config.ts";
import plugin_1 from "@personnel/frontend/src/plugin.config.ts";
export default [plugin_0, plugin_1];

scanWorkspacePlugins(root?): Promise<PluginInfo[]>

Scans the workspace for packages with "framework-m" metadata.

import { scanWorkspacePlugins } from "@framework-m/vite-plugin";

const plugins = await scanWorkspacePlugins("/path/to/workspace");
// [{ name: "@wms/frontend", configPath: "./src/plugin.config.ts" }, ...]

generateVirtualModule(plugins): string

Generates the virtual module source code for discovered plugins.

import { generateVirtualModule } from "@framework-m/vite-plugin";

const code = generateVirtualModule(plugins);
// "import plugin_0 from '...'; export default [plugin_0];"