A flexible and extensible TypeScript/JavaScript plugin system supporting dependency management, lifecycle control, service registration, and event communication.
- 🔌 Plugin Lifecycle Management - Complete loading, activation, deactivation, and unloading workflow
- 📦 Dependency Management - Automatic handling of plugin dependencies and loading order
- 🎯 Service Registration - Plugins can register and use shared services
- 📡 Event System - EventEmitter-based inter-plugin communication
- ⚙️ Configuration Management - Dynamic configuration updates and hot reloading
- 🏷️ Tag Classification - Organize and find plugins by tags
- 🔍 Plugin Discovery - Automatic plugin discovery and loading
- 📊 Status Monitoring - Real-time plugin status and statistics monitoring
- 🌐 Browser Support - Complete browser environment support with DOM and storage plugins
- 📱 Cross-Platform - Supports both Node.js and browser environments
The plugin system is designed with a clean separation between core functionality and platform-specific implementations:
src/
├── core/ # Platform-agnostic core functionality
│ ├── types.ts # Core interfaces and types
│ ├── base-plugin.ts # Abstract base plugin class
│ ├── plugin-manager.ts # Core plugin manager
│ ├── plugin-registry.ts # Plugin registration and discovery
│ └── plugin-loader.ts # Plugin loading utilities
├── platforms/
│ ├── node/ # Node.js-specific implementations
│ │ ├── index.ts # Node.js platform entry point
│ │ └── plugins/ # Node.js-specific plugins
│ │ ├── logger-plugin.ts
│ │ └── cache-plugin.ts
│ └── browser/ # Browser-specific implementations
│ ├── index.ts # Browser platform entry point
│ ├── event-emitter.ts # Browser-compatible EventEmitter
│ ├── plugin-manager.ts # Browser plugin manager
│ ├── base-plugin.ts # Browser base plugin class
│ └── plugins/ # Browser-specific plugins
│ ├── dom-plugin.ts
│ └── storage-plugin.ts
└── index.ts # Main entry point (Node.js)
npm install plugin-systemimport { PluginManager, Logger, BasePlugin } from "plugin-system";
// Create a simple logger
class ConsoleLogger implements Logger {
debug(message: string, ...args: any[]): void {
console.debug(`[DEBUG] ${message}`, ...args);
}
info(message: string, ...args: any[]): void {
console.info(`[INFO] ${message}`, ...args);
}
warn(message: string, ...args: any[]): void {
console.warn(`[WARN] ${message}`, ...args);
}
error(message: string, ...args: any[]): void {
console.error(`[ERROR] ${message}`, ...args);
}
}
// Create a simple plugin
class MyPlugin extends BasePlugin {
constructor() {
super({
name: "my-plugin",
version: "1.0.0",
description: "My custom plugin",
author: "Your Name",
tags: ["custom", "example"],
priority: 50,
});
}
protected async onInitialize(): Promise<void> {
// Plugin initialization logic
this.context.logger.info("Plugin initialized");
// Register service
this.context.services.register("myService", {
doSomething: () => console.log("Doing something"),
});
}
protected async onActivate(): Promise<void> {
// Plugin activation logic
this.context.logger.info("Plugin activated");
}
protected async onDeactivate(): Promise<void> {
// Plugin deactivation logic
this.context.logger.info("Plugin deactivated");
}
protected async onDispose(): Promise<void> {
// Plugin cleanup logic
this.context.services.unregister("myService");
this.context.logger.info("Plugin disposed");
}
}
// Use the plugin system
async function main() {
const logger = new ConsoleLogger();
const pluginManager = new PluginManager(logger);
const myPlugin = new MyPlugin();
await pluginManager.registerPlugin(myPlugin, {
customOption: "value",
});
await pluginManager.activatePlugin("my-plugin");
// Use plugin service
const plugin = pluginManager.getPlugin("my-plugin") as any;
const myService = plugin?.context?.services.get("myService");
myService?.doSomething();
}
main().catch(console.error);<!DOCTYPE html>
<html>
<head>
<title>Plugin System Browser Example</title>
</head>
<body>
<script src="dist/browser/plugin-system.umd.js"></script>
<script>
async function init() {
// Create browser plugin system
const pluginSystem = PluginSystem.createBrowserPluginSystem();
// Initialize with default plugins (DOM and Storage)
await pluginSystem.initializeWithDefaults();
await pluginSystem.pluginManager.activateAll();
// Use DOM plugin
const domService = pluginSystem.pluginManager.getService("dom");
const element = domService.createElement(
"div",
{
style: "color: blue; padding: 10px;",
},
"Hello Plugin System!"
);
document.body.appendChild(element);
}
init().catch(console.error);
</script>
</body>
</html>Provides advanced logging management with level control and file output.
import { LoggerPlugin } from "plugin-system";
const loggerPlugin = new LoggerPlugin();
await pluginManager.registerPlugin(loggerPlugin, {
logLevel: "debug", // Log levels: debug, info, warn, error
logFile: "./app.log", // Log file path (optional)
});Provides in-memory caching service with TTL, LRU eviction, and memory management.
import { CachePlugin } from "plugin-system";
const cachePlugin = new CachePlugin();
await pluginManager.registerPlugin(cachePlugin, {
maxSize: 1000, // Maximum cache entries
ttl: 300000, // Default TTL (milliseconds)
});
// Use cache service
const cacheService = pluginManager
.getPlugin("cache")
?.context?.services.get("cache");
cacheService.set("key", "value");
const value = cacheService.get("key");Plugins can declare dependencies, and the system will automatically load them in the correct order:
export class AdvancedPlugin extends BasePlugin {
constructor() {
super({
name: "advanced-plugin",
version: "1.0.0",
dependencies: ["logger", "cache"], // Depends on other plugins
// ...
});
}
protected async onDependencyStatusChange(
dependency: string,
status: PluginStatus
): Promise<void> {
// Handle dependency status changes
this.context.logger.info(`Dependency ${dependency} status changed to: ${status}`);
}
}Plugins can communicate through events:
// Send events
this.emit("custom-event", { data: "some data" });
// Listen for events
this.on("system:log", (data) => {
console.log("Received log event:", data);
});
// Listen for other plugin events
pluginManager.on("plugin:other-plugin:some-event", (data) => {
console.log("Received other plugin event:", data);
});Support for runtime plugin configuration updates:
// Update configuration
await pluginManager.updatePluginConfig('my-plugin', {
newOption: 'new value'
});
// Handle configuration updates in plugin
protected async onConfigUpdate(config: PluginConfig): Promise<void> {
// Handle configuration changes
this.someOption = config.newOption;
}// Batch load all plugins (in dependency order)
await pluginManager.loadAll();
// Batch activate all plugins
await pluginManager.activateAll();
// Batch deactivate all plugins
await pluginManager.deactivateAll();
// Get active plugin list
const activePlugins = pluginManager.getActivePlugins();Use plugin registry for advanced querying and management:
import { PluginRegistry } from "plugin-system";
const registry = new PluginRegistry();
// Find plugins by tag
const logPlugins = registry.getByTag("logging");
// Sort by priority
const sortedPlugins = registry.getByPriority();
// Search plugins
const results = registry.search({
name: "cache",
tags: ["performance"],
author: "System",
});
// Get statistics
const stats = registry.getStats();Automatic plugin discovery and loading:
import {
DefaultPluginLoader,
DefaultPluginDiscovery,
} from "plugin-system";
const loader = new DefaultPluginLoader();
const discovery = new DefaultPluginDiscovery();
// Load plugin from file
const plugin = await loader.loadFromFile("./plugins/my-plugin.js");
// Batch load from directory
const plugins = await loader.loadFromDirectory("./plugins");
// Discover plugins
const descriptors = await discovery.discoverPlugins([
"./plugins",
"./custom-plugins",
]);Main plugin management class responsible for plugin lifecycle management.
registerPlugin(plugin, config?)- Register pluginloadPlugin(name)- Load pluginactivatePlugin(name)- Activate plugindeactivatePlugin(name)- Deactivate pluginunloadPlugin(name)- Unload pluginloadAll()- Batch load all pluginsactivateAll()- Batch activate all pluginsdeactivateAll()- Batch deactivate all pluginsgetPlugin(name)- Get plugin instancegetActivePlugins()- Get active plugin listupdatePluginConfig(name, config)- Update plugin configuration
Base plugin class providing fundamental plugin development functionality.
onInitialize()- Plugin initialization (must implement)onActivate()- Plugin activation (must implement)onDeactivate()- Plugin deactivation (must implement)onDispose()- Plugin cleanup (must implement)onConfigUpdate(config)- Configuration update (optional)onDependencyStatusChange(dependency, status)- Dependency status change (optional)
getConfig(key?)- Get configurationgetService(name)- Get servicegetUtility(name)- Get utilityemit(event, data?)- Send eventon(event, listener)- Listen for eventoff(event, listener)- Remove event listener
Plugin status enumeration:
UNLOADED- Not loadedLOADING- LoadingLOADED- LoadedACTIVE- ActiveERROR- ErrorDISABLED- Disabled
<!DOCTYPE html>
<html>
<head>
<title>Plugin System Browser Example</title>
</head>
<body>
<!-- Load the built plugin system -->
<script src="dist/browser/plugin-system.umd.js"></script>
<script>
async function init() {
// Create browser plugin system
const pluginSystem = PluginSystem.createBrowserPluginSystem();
// Initialize default plugins (DOM and Storage)
await pluginSystem.initializeWithDefaults();
// Activate all plugins
await pluginSystem.pluginManager.activateAll();
// Use DOM plugin
const domService = pluginSystem.pluginManager.getService("dom");
const element = domService.createElement(
"div",
{
style: "color: blue; padding: 10px;",
},
"Hello Plugin System!"
);
document.body.appendChild(element);
// Use storage plugin
const storageService = pluginSystem.pluginManager.getService("storage");
storageService.local.set("greeting", "Hello World!");
console.log(storageService.local.get("greeting"));
}
init().catch(console.error);
</script>
</body>
</html>Provides complete DOM manipulation and event management functionality:
// Get DOM service
const domService = pluginManager.getService("dom");
// Create elements
const button = domService.createElement(
"button",
{
class: "my-button",
id: "test-btn",
},
"Click Me"
);
// Add event listeners
domService.addEventListener(button, "click", () => {
alert("Button clicked!");
});
// Create complex components
const component = domService.createComponent({
tag: "div",
className: "card",
children: [
{ tag: "h3", textContent: "Title" },
{ tag: "p", textContent: "Content" },
],
styles: {
border: "1px solid #ccc",
padding: "10px",
borderRadius: "4px",
},
});
// Observe DOM changes
domService.observeChanges(document.body, (mutations) => {
console.log("DOM changed:", mutations);
});Supports localStorage, sessionStorage, and IndexedDB:
// Get storage service
const storageService = pluginManager.getService("storage");
// localStorage operations
storageService.local.set("user", { name: "John", age: 25 });
const user = storageService.local.get("user");
// sessionStorage operations
storageService.session.set("temp-data", "temporary value");
// IndexedDB operations (async)
await storageService.indexed.set("large-data", {
/* large data */
});
const largeData = await storageService.indexed.get("large-data");
// Get storage statistics
const info = storageService.getInfo();
console.log("Storage usage:", info);# Build browser version
npm run build:browser
# Build production version (minified)
npm run build:browser:devBuilt files are located in the dist/browser/ directory:
plugin-system.umd.js- UMD formatplugin-system.es.js- ES modules format
# Node.js version
npm run build # Compile project
npm run example # Run basic example
npm run example:advanced # Run advanced example
# Browser version
npm run build:browser # Build browser version
npm run build:browser:dev # Build development version
# Development and testing
npm run dev:watch # Development mode (watch file changes)
npm run typecheck # Type checking
npm run lint # Code linting
npm run lint:fix # Fix code style
npm run test # Run tests
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage
npm run test:ui # Run tests with UI
npm run clean # Clean build filesCheck the examples/ directory for complete usage examples, including:
- Plugin manager setup
- Custom plugin creation
- Service usage
- Event handling
- Configuration management
- Batch operations
Run examples:
npm run example
npm run example:advanced- Single Responsibility - Each plugin focuses on one specific functionality
- Loose Coupling - Communicate through services and events, avoid direct dependencies
- Configurable - Provide flexible configuration options
- Error Handling - Handle exceptions properly
- Clearly declare plugin dependencies
- Avoid circular dependencies
- Set reasonable plugin priorities
- Handle dependency status changes
- Lazy load non-critical plugins
- Use cache services reasonably
- Clean up resources promptly
- Monitor memory usage
- Implement complete error handling logic
- Provide meaningful error messages
- Support plugin degradation and recovery
- Log detailed debugging information
Problem: Plugin initialization failed
Solutions:
- Check if plugin dependencies are correctly registered
- Ensure plugin's
onInitializemethod doesn't throw exceptions - Verify plugin configuration is correct
Problem: Circular dependency detected
Solutions:
- Redesign plugin architecture to avoid circular dependencies
- Use event communication instead of direct dependencies
- Consider extracting common dependencies into independent plugins
Problem: Service not found
Solutions:
- Ensure the service-providing plugin is activated
- Check if service registration name is correct
- Verify plugin loading order
Problem: Memory usage continues to grow
Solutions:
- Clean up all resources in plugin's
onDisposemethod - Remove event listeners
- Clear timers and async operations
- Set reasonable size limits when using cache plugins
// Set log level to debug
const logger = new ConsoleLogger();
logger.setLevel("debug");
// Or use built-in logger plugin
await pluginManager.updatePluginConfig("logger", {
logLevel: "debug",
});// Listen for all plugin events
[
"registered",
"loaded",
"activated",
"deactivated",
"unloaded",
"config-changed",
].forEach((event) => {
pluginManager.on(`plugin:${event}`, (data) => {
console.log(`Plugin event [${event}]:`, data);
});
});// Get plugin dependency information
const plugin = pluginManager.getPlugin("my-plugin");
if (plugin) {
console.log("Dependencies:", plugin.metadata.dependencies);
console.log("Status:", plugin.status);
}
// Check all plugin statuses
const allPlugins = pluginManager.getAllPlugins();
for (const [name, plugin] of allPlugins) {
console.log(`${name}: ${plugin.status}`);
}// Only load plugins when needed
if (someCondition) {
await pluginManager.loadPlugin("optional-plugin");
await pluginManager.activatePlugin("optional-plugin");
}// Use batch operations instead of individual calls
await pluginManager.activateAll(); // Instead of multiple activatePlugin calls// Avoid frequent event sending
class MyPlugin extends BasePlugin {
private eventBuffer: any[] = [];
private flushEvents() {
if (this.eventBuffer.length > 0) {
this.emit("batch-events", this.eventBuffer);
this.eventBuffer = [];
}
}
}// Set reasonable limits when using cache plugins
await pluginManager.registerPlugin(cachePlugin, {
maxSize: 1000, // Limit cache entries
ttl: 300000, // Set expiration time
});
// Listen for memory warnings
pluginManager.on("system:memory-warning", () => {
// Clean up non-essential resources
});# Create new plugin directory
mkdir my-plugin
cd my-plugin
# Create plugin file
cat > index.ts << 'EOF'
import { BasePlugin } from 'plugin-system';
export class MyPlugin extends BasePlugin {
constructor() {
super({
name: 'my-plugin',
version: '1.0.0',
description: 'My custom plugin',
author: 'Your Name'
});
}
protected async onInitialize(): Promise<void> {
// Initialization logic
}
protected async onActivate(): Promise<void> {
// Activation logic
}
protected async onDeactivate(): Promise<void> {
// Deactivation logic
}
protected async onDispose(): Promise<void> {
// Cleanup logic
}
}
EOFimport { describe, it, expect, beforeEach, vi } from "vitest";
import { PluginManager } from "plugin-system";
import { MyPlugin } from "./my-plugin";
describe("MyPlugin", () => {
let pluginManager: PluginManager;
let plugin: MyPlugin;
beforeEach(() => {
pluginManager = new PluginManager(mockLogger);
plugin = new MyPlugin();
});
it("should initialize correctly", async () => {
await pluginManager.registerPlugin(plugin);
await pluginManager.loadPlugin("my-plugin");
expect(plugin.status).toBe(PluginStatus.LOADED);
});
});// Recommended production configuration
const pluginManager = new PluginManager(productionLogger);
// Set up error handling
pluginManager.on("plugin:error", (error) => {
// Send to monitoring system
monitoring.reportError(error);
});
// Enable health checks
setInterval(() => {
const health = getSystemHealth();
if (!health.healthy) {
// Trigger alerts
alerting.sendAlert(health);
}
}, 30000);FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
COPY plugins ./plugins
EXPOSE 3000
CMD ["node", "dist/index.js"]# .env file
PLUGIN_LOG_LEVEL=info
PLUGIN_CACHE_SIZE=1000
PLUGIN_DB_HOST=localhost
PLUGIN_DB_PORT=5432- Initial release
- Basic plugin system functionality
- Built-in logger and cache plugins
- Complete TypeScript support
MIT License
Issues and Pull Requests are welcome!
- Fork the project
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
# Clone the project
git clone https://github.com/your-org/plugin-system.git
cd plugin-system
# Install dependencies
npm install
# Run tests
npm test
# Run examples
npm run example
npm run example:advanced
# Build project
npm run build