Plugin SDK API Reference
Complete API reference for
@framework-m/plugin-sdkand@framework-m/vite-plugin.
@framework-m/plugin-sdk
Types
FrameworkMPlugin
interface FrameworkMPlugin {
name: string;
version: string;
minSdkVersion?: string;
peerPlugins?: string[];
manifests?: NavigationManifest[];
routes?: RouteDefinition[];
services?: Record<string, ServiceFactory>;
providers?: Provider[];
doctypes?: DocTypeExtension[];
widgets?: Widget[];
pages?: GlobalPageExtension[];
onInit?: () => Promise<void> | void;
onDestroy?: () => Promise<void> | void;
traversals?: Record<string, (...args: unknown[]) => unknown>;
}
NavigationManifest
interface NavigationManifest {
app_id: string;
label: string;
icon: string;
resources: NavigationNode[];
}
NavigationNode
interface NavigationNode {
name: string;
label: string;
type?: "DocType" | "Page" | "Report" | "Kiosk" | "Link";
route: string;
icon?: string;
feature_id?: string;
visibility_policy?: VisibilityPolicy;
badge?: string;
hardware_intent?: string;
interaction_mode?: string;
workspace_route?: string;
children?: NavigationNode[];
hidden?: boolean;
permissions?: string[];
}
VisibilityPolicy
interface VisibilityPolicy {
roles?: string[];
attributes?: Record<string, unknown>;
feature_flag?: string;
}
RouteDefinition
interface RouteDefinition {
path: string;
element:
| LazyExoticComponent<ComponentType>
| (() => Promise<{ default: ComponentType }>);
}
ServiceFactory
type ServiceFactory<T = unknown> = () => T | Promise<T>;
PermissionChecker
type PermissionChecker = (permissions: string[]) => boolean | Promise<boolean>;
Provider
interface Provider {
component: () => Promise<{ default: ComponentType }>;
props?: Record<string, unknown>;
}
DocTypeExtension
interface DocTypeExtension {
doctype: string;
fields?: Record<string, unknown>;
actions?: Array<Record<string, unknown>>;
components?: Record<string, ComponentType>;
}
Widget
interface Widget {
id: string;
title: string;
component: () => Promise<{ default: ComponentType }>;
size?: "small" | "medium" | "large";
permissions?: string[];
}
GlobalPageExtension
interface GlobalPageExtension {
name: string;
component: ComponentType;
}
Classes
PluginRegistry
class PluginRegistry {
/** Access the global registry singleton */
static get_instance(): PluginRegistry;
/** Register a local plugin configuration */
register(plugin: FrameworkMPlugin): Promise<void>;
/** Unregister a plugin by name */
unregister(pluginName: string): Promise<boolean>;
/** Map a DocType/Resource to a Service owner for discovery */
registerResourceOwner(resource: string, owner: string): void;
/** Bulk registration of resource owners (useful for tests/static mode) */
preRegisterOwners(owners: Record<string, string>): void;
/**
* Resolves a relative resource name into a versioned, namespaced API URL.
*
* Logic:
* 1. Check exact match in _resourceOwners.
* 2. Check fuzzy match (suffix match) in _resourceOwners.
* 3. Construct URL: `/api/{service}/{version}/{resource}`.
*/
resolveApiUrl(resource: string, version?: string): string;
/**
* Decomposes an API path into its constituent parts.
* Format: `/api/{service}/{version}/{resource}` or `/api/{version}/{resource}`.
*/
parseApiPath(path: string): { service?: string; version: string; resource: string };
getApps(): NavigationManifest[];
getRoutes(): RouteDefinition[];
getPlugin(name: string): FrameworkMPlugin | undefined;
getServiceContainer(): ServiceContainer;
reset(): void; // Reset registry state for test isolation
}
ServiceContainer
class ServiceContainer {
register<T>(name: string, factory: ServiceFactory<T>): void;
get<T = unknown>(name: string): Promise<T>;
has(name: string): boolean;
getAll(): string[];
clear(): void;
}
React Context and Hooks
PluginRegistryProvider
import {
PluginRegistryProvider,
PluginRegistry,
} from "@framework-m/plugin-sdk";
const registry = new PluginRegistry();
await registry.register(wmsPlugin);
<PluginRegistryProvider registry={registry}>
<App />
</PluginRegistryProvider>;
usePluginApps()
import { usePluginApps } from "@framework-m/plugin-sdk";
function Sidebar() {
const apps = usePluginApps();
return apps.map(app => (
<section key={app.app_id}>
<h3>{app.label}</h3>
{app.resources.map(node => (
<div key={node.name}>{node.label}</div>
))}
</section>
));
}
usePlugin(name)
import { usePlugin } from "@framework-m/plugin-sdk";
function PluginInfo() {
const wms = usePlugin("wms");
return wms ? (
<span>
{wms.name} v{wms.version}
</span>
) : null;
}
useService(name)
import { useService } from "@framework-m/plugin-sdk";
function StockLevel() {
const { service, isLoading, error } = useService("inventoryService");
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage error={error} />;
return <div>{service.getStockLevel()}</div>;
}
useWidgets()
import { useWidgets } from "@framework-m/plugin-sdk";
function Dashboard() {
const widgets = useWidgets();
return widgets.map(widget => <WidgetHost key={widget.id} widget={widget} />);
}
@framework-m/vite-plugin
frameworkMPlugin(options?)
import { frameworkMPlugin } from "@framework-m/vite-plugin";
export default defineConfig({
plugins: [react(), frameworkMPlugin()],
});
What it does:
- Workspace Scanning: Automatically traverses the workspace to find
package.jsonfiles containing"framework-m": { "plugin": "..." }. - Selective Bundling:
- If the local
package.jsonidentifies as a Plugin, it only bundles the local plugin code. - If the local package is a Shell App, it bundles all discovered plugins from the workspace.
- If the local
- Virtual Modules: Generates
virtual:framework-m-pluginsto aggregate discovered plugins. - MFE Configuration: Injects
FRAMEWORK_M_SERVICE_NAMEviadefinefor runtime identity.
Virtual Module Output
// virtual:framework-m-plugins
import plugin_0 from "/path/to/apps/wms/frontend/src/plugin.config.ts";
import plugin_1 from "/path/to/apps/hr/frontend/src/plugin.config.ts";
export default [plugin_0, plugin_1];