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

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>;
}
interface NavigationManifest {
app_id: string;
label: string;
icon: string;
resources: 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:

  1. Workspace Scanning: Automatically traverses the workspace to find package.json files containing "framework-m": { "plugin": "..." }.
  2. Selective Bundling:
    • If the local package.json identifies 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.
  3. Virtual Modules: Generates virtual:framework-m-plugins to aggregate discovered plugins.
  4. MFE Configuration: Injects FRAMEWORK_M_SERVICE_NAME via define for 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];