Skip to content

A flexible and extensible TypeScript/JavaScript plugin system supporting dependency management, lifecycle control, service registration, and event communication.

Notifications You must be signed in to change notification settings

lenqwang/plugin-system

Repository files navigation

Plugin System

A flexible and extensible TypeScript/JavaScript plugin system supporting dependency management, lifecycle control, service registration, and event communication.

Features

  • 🔌 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

Architecture

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)

Installation

npm install plugin-system

Quick Start

Node.js Environment

import { 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);

Browser Environment

<!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>

Built-in Plugins

LoggerPlugin - Advanced Logging

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)
});

CachePlugin - Memory Cache

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");

Advanced Features

Dependency Management

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}`);
  }
}

Event Communication

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);
});

Hot Configuration Updates

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 Operations

// 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();

Plugin Registry

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();

Plugin Discovery and Loading

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",
]);

API Reference

PluginManager

Main plugin management class responsible for plugin lifecycle management.

Methods

  • registerPlugin(plugin, config?) - Register plugin
  • loadPlugin(name) - Load plugin
  • activatePlugin(name) - Activate plugin
  • deactivatePlugin(name) - Deactivate plugin
  • unloadPlugin(name) - Unload plugin
  • loadAll() - Batch load all plugins
  • activateAll() - Batch activate all plugins
  • deactivateAll() - Batch deactivate all plugins
  • getPlugin(name) - Get plugin instance
  • getActivePlugins() - Get active plugin list
  • updatePluginConfig(name, config) - Update plugin configuration

BasePlugin

Base plugin class providing fundamental plugin development functionality.

Lifecycle Methods

  • 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)

Utility Methods

  • getConfig(key?) - Get configuration
  • getService(name) - Get service
  • getUtility(name) - Get utility
  • emit(event, data?) - Send event
  • on(event, listener) - Listen for event
  • off(event, listener) - Remove event listener

PluginStatus

Plugin status enumeration:

  • UNLOADED - Not loaded
  • LOADING - Loading
  • LOADED - Loaded
  • ACTIVE - Active
  • ERROR - Error
  • DISABLED - Disabled

Browser Support

Browser Quick Start

<!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>

Browser-Specific Plugins

DOMPlugin - DOM Manipulation Plugin

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);
});

StoragePlugin - Browser Storage Plugin

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);

Browser Build

# Build browser version
npm run build:browser

# Build production version (minified)
npm run build:browser:dev

Built files are located in the dist/browser/ directory:

  • plugin-system.umd.js - UMD format
  • plugin-system.es.js - ES modules format

Development Scripts

# 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 files

Example Projects

Check 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

Best Practices

1. Plugin Design Principles

  • 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

2. Dependency Management

  • Clearly declare plugin dependencies
  • Avoid circular dependencies
  • Set reasonable plugin priorities
  • Handle dependency status changes

3. Performance Optimization

  • Lazy load non-critical plugins
  • Use cache services reasonably
  • Clean up resources promptly
  • Monitor memory usage

4. Error Handling

  • Implement complete error handling logic
  • Provide meaningful error messages
  • Support plugin degradation and recovery
  • Log detailed debugging information

Troubleshooting

Common Issues

1. Plugin Loading Failed

Problem: Plugin initialization failed

Solutions:

  • Check if plugin dependencies are correctly registered
  • Ensure plugin's onInitialize method doesn't throw exceptions
  • Verify plugin configuration is correct

2. Circular Dependency Error

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

3. Service Not Found

Problem: Service not found

Solutions:

  • Ensure the service-providing plugin is activated
  • Check if service registration name is correct
  • Verify plugin loading order

4. Memory Leak

Problem: Memory usage continues to grow

Solutions:

  • Clean up all resources in plugin's onDispose method
  • Remove event listeners
  • Clear timers and async operations
  • Set reasonable size limits when using cache plugins

Debugging Tips

Enable Detailed Logging

// Set log level to debug
const logger = new ConsoleLogger();
logger.setLevel("debug");

// Or use built-in logger plugin
await pluginManager.updatePluginConfig("logger", {
  logLevel: "debug",
});

Monitor Plugin Status

// 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);
  });
});

Check Dependencies

// 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}`);
}

Performance Optimization

1. Lazy Loading

// Only load plugins when needed
if (someCondition) {
  await pluginManager.loadPlugin("optional-plugin");
  await pluginManager.activatePlugin("optional-plugin");
}

2. Batch Operations

// Use batch operations instead of individual calls
await pluginManager.activateAll(); // Instead of multiple activatePlugin calls

3. Event Optimization

// 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 = [];
    }
  }
}

4. Memory Management

// 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
});

Extension Development

Create Plugin Template

# 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
  }
}
EOF

Plugin Test Template

import { 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);
  });
});

Deployment Guide

Production Configuration

// 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);

Docker Deployment

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"]

Environment Variables

# .env file
PLUGIN_LOG_LEVEL=info
PLUGIN_CACHE_SIZE=1000
PLUGIN_DB_HOST=localhost
PLUGIN_DB_PORT=5432

Community Resources

Changelog

v1.0.0

  • Initial release
  • Basic plugin system functionality
  • Built-in logger and cache plugins
  • Complete TypeScript support

License

MIT License

Contributing

Issues and Pull Requests are welcome!

Contributing Guidelines

  1. Fork the project
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Development Setup

# 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

About

A flexible and extensible TypeScript/JavaScript plugin system supporting dependency management, lifecycle control, service registration, and event communication.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published