mycelia-kernel-plugin
v1.6.0
Published
A sophisticated, framework-agnostic plugin system with transaction safety, lifecycle management, and official bindings for React, Vue 3, Svelte, Angular, Qwik, and Solid.js
Maintainers
Readme
Mycelia Plugin System
A sophisticated, framework-agnostic plugin system with transaction safety, lifecycle management, and official bindings for React, Vue 3, Svelte, Angular, Qwik, and Solid.js.
Overview
Mycelia Plugin System is a framework-agnostic, standalone plugin architecture extracted from Mycelia Kernel. It provides:
- Framework-agnostic - Write domain logic once, use it with React, Vue, or any framework. Plugins are completely independent of UI frameworks
- Hook-based composition - Extend systems without modification
- Dependency resolution - Automatic topological sorting
- Transaction safety - Atomic installation with rollback
- Lifecycle management - Built-in initialization and disposal
- Hot reloading - Reload and extend plugins without full teardown
- Facet contracts - Runtime validation of plugin interfaces
- Standalone mode - Works without message system or other dependencies
- Built-in hooks - Ships with
useListenersfor event-driven architectures (see Simple Event System Example), plususeQueueanduseSpeak - Framework bindings - Official bindings for React, Vue 3, Svelte, Angular, Qwik, and Solid.js
Facets are the concrete runtime capabilities produced by hooks and attached to the system.
Framework Integration
The system is designed to be framework-agnostic. Your domain logic lives in Mycelia plugins, which can be used with any framework:
- React - Use
MyceliaProviderand React hooks (useFacet,useListener) - Vue 3 - Use
MyceliaPluginand Vue composables (useFacet,useListener) - Svelte - Use
setMyceliaSystemand Svelte stores (useFacet,useListener) - Angular - Use
MyceliaServiceand RxJS observables (useFacet,useListener) - Qwik - Use
MyceliaProviderand Qwik signals (useFacet,useListener) - Solid.js - Use
MyceliaProviderand Solid.js signals (useFacet,useListener) - Vanilla JS/Node.js - Use the system directly without any framework bindings
See the React Todo App, Vue Todo App, Svelte Todo App, and Solid.js Todo App examples - they all use the exact same plugin code, demonstrating true framework independence.
Quick Start
Using useBase (Recommended)
import { useBase, createHook, Facet } from 'mycelia-kernel-plugin';
// Create a hook
const useDatabase = createHook({
kind: 'database',
version: '1.0.0',
attach: true,
source: import.meta.url,
fn: (ctx, api, subsystem) => {
const config = ctx.config?.database || {};
return new Facet('database', {
attach: true,
source: import.meta.url
})
.add({
async query(sql) {
// Database query implementation
return { rows: [] };
},
async close() {
// Cleanup
}
})
.onInit(async ({ ctx }) => {
// Initialize database connection
})
.onDispose(async () => {
// Close database connection
});
}
});
// Create and use the system with fluent API
const system = await useBase('my-app')
.config('database', { host: 'localhost' })
.use(useDatabase)
.build();
// Or configure multiple facets and hooks at once
const system = await useBase('my-app')
.configMultiple({
database: { host: 'localhost', port: 5432 },
cache: { ttl: 3600 }
})
.useMultiple([useDatabase, useCache])
.build();
// Use the plugin
const db = system.find('database');
await db.query('SELECT * FROM users');Using StandalonePluginSystem Directly
import { StandalonePluginSystem, createHook, Facet } from 'mycelia-kernel-plugin';
// Create a hook (same as above)
const useDatabase = createHook({ /* ... */ });
// Create and use the system
const system = new StandalonePluginSystem('my-app', {
config: {
database: { host: 'localhost' }
}
});
system
.use(useDatabase)
.build();
// Use the plugin
const db = system.find('database');
await db.query('SELECT * FROM users');Simple Event System Example
Create an event-driven system with useBase and useListeners:
import { useBase, useListeners } from 'mycelia-kernel-plugin';
// Create an event system
const eventSystem = await useBase('event-system')
.config('listeners', { registrationPolicy: 'multiple' })
.use(useListeners)
.build();
// Enable listeners
eventSystem.listeners.enableListeners();
// Register event handlers
eventSystem.listeners.on('user:created', (message) => {
console.log('User created:', message.body);
});
eventSystem.listeners.on('user:updated', (message) => {
console.log('User updated:', message.body);
});
// Emit events
eventSystem.listeners.emit('user:created', {
type: 'user:created',
body: { id: 1, name: 'John Doe' }
});
eventSystem.listeners.emit('user:updated', {
type: 'user:updated',
body: { id: 1, name: 'Jane Doe' }
});
// Multiple handlers for the same event
eventSystem.listeners.on('order:placed', (message) => {
console.log('Order notification:', message.body);
});
eventSystem.listeners.on('order:placed', (message) => {
console.log('Order logging:', message.body);
});
// Both handlers will be called
eventSystem.listeners.emit('order:placed', {
type: 'order:placed',
body: { orderId: 123, total: 99.99 }
});
// Cleanup
await eventSystem.dispose();Installation
npm install mycelia-kernel-pluginWhat This System Is Not
This system intentionally does not provide dependency injection containers, service locators, or global mutable state. It focuses on explicit lifecycle management and composable plugin architecture rather than implicit dependency resolution or shared global state.
Features
Hook System
Create composable plugins using the createHook factory:
const useCache = createHook({
kind: 'cache',
required: ['database'], // Dependencies
attach: true,
source: import.meta.url,
fn: (ctx, api, subsystem) => {
const db = subsystem.find('database');
return new Facet('cache', { attach: true })
.add({
async get(key) {
// Cache implementation
}
});
}
});Dependency Resolution
Dependencies are automatically resolved and initialized in the correct order:
system
.use(useDatabase) // Will be initialized first
.use(useCache) // Will be initialized after database
.build();Transaction Safety
If any plugin fails during initialization, all changes are rolled back:
try {
await system
.use(useDatabase)
.use(useCache)
.build();
} catch (error) {
// System is in clean state - all plugins rolled back
}Hot Reloading
Reload the system and add more plugins without full teardown:
// Initial build
await system.use(useDatabase).build();
// Hot reload - add more plugins
await system.reload();
await system.use(useCache).use(useAuth).build();
// All plugins (old + new) are now activeThe reload() method:
- Disposes all facets and resets built state
- Preserves hooks and configuration
- Allows adding more hooks and rebuilding
- Perfect for development and hot-reload scenarios
Note: Persistent external state (e.g., database contents, file system state) is not automatically reverted. The reload() method only manages the plugin system's internal state.
Facet Contracts
Validate plugin interfaces at build time:
import { createFacetContract } from 'mycelia-kernel-plugin';
const databaseContract = createFacetContract({
name: 'database',
requiredMethods: ['query', 'close'],
requiredProperties: ['connection']
});
// Contract is automatically enforced during buildIf a facet doesn't satisfy its contract, build fails with a clear error:
Error: FacetContract 'database': facet is missing required methods: closeArchitecture
StandalonePluginSystem
├── BaseSubsystem (base class)
├── SubsystemBuilder (build orchestrator)
├── FacetManager (plugin registry)
├── FacetContractRegistry (contract validation)
└── DependencyGraphCache (performance optimization)API Reference
Core Classes
StandalonePluginSystem- Main plugin system classBaseSubsystem- Base class for plugin containersSubsystemBuilder- Build orchestratorFacetManager- Plugin registryFacetContractRegistry- Contract validation
Factory Functions
createHook()- Create a plugin hookcreateFacetContract()- Create a facet contractuseBase()- Fluent API builder for StandalonePluginSystem (see useBase Documentation)
Utilities
createLogger()- Create a loggergetDebugFlag()- Extract debug flag from config
Documentation
Comprehensive documentation is available in the docs/ directory:
Quick Links
- useBase Guide - Complete guide to the fluent API builder with all methods
- Instrumentation - Debugging and performance instrumentation
Full Documentation
- Getting Started Guide - Quick start with examples
- Hooks and Facets Overview - Core concepts
- Built-in Hooks - Documentation for
useListeners,useQueue, anduseSpeak - React Bindings - React integration utilities (
MyceliaProvider,useFacet,useListener) - Vue Bindings - Vue 3 integration utilities (
MyceliaPlugin,useFacet,useListener) ⭐ - Svelte Bindings - Svelte integration utilities (
setMyceliaSystem,useFacet,useListener) ⭐ - Angular Bindings - Angular integration utilities (
MyceliaService,useFacet,useListener) ⭐ - Qwik Bindings - Qwik integration utilities (
MyceliaProvider,useFacet,useListener) ⭐ - Solid.js Bindings - Solid.js integration utilities (
MyceliaProvider,useFacet,useListener) ⭐ - Standalone Plugin System - Complete usage guide
- Documentation Index - Full documentation index
Examples
See the examples/ directory for:
- Basic plugin usage
- Plugins with dependencies
- Lifecycle management
- Contract validation
- Hot reloading
- useBase fluent API
Framework Integration Examples
React Todo App – A real-world example showing:
- Domain logic as a Mycelia facet (
useTodoshook) - Event-driven state synchronization (
todos:changedevents) - React bindings (
MyceliaProvider,useFacet,useListener)
- Domain logic as a Mycelia facet (
Vue Todo App ⭐ – A complete Vue 3 example demonstrating:
- Framework-agnostic plugins - Uses the same shared plugin code as the React example
- Event-driven state synchronization (
todos:changedevents) - Vue 3 bindings (
MyceliaPlugin,useFacet,useListener) - Composition API integration with reactive state management
Svelte Todo App ⭐ – A complete Svelte example demonstrating:
- Framework-agnostic plugins - Uses the same shared plugin code as React, Vue, and other examples
- Event-driven state synchronization (
todos:changedevents) - Svelte bindings (
setMyceliaSystem,useFacet,useListener) - Svelte stores for reactive state management
Angular Todo App ⭐ – A complete Angular example demonstrating:
- Framework-agnostic plugins - Uses the same shared plugin code as React, Vue, Svelte, and other examples
- Event-driven state synchronization (
todos:changedevents) - Angular bindings (
MyceliaService,useFacet,useListener) - RxJS observables for reactive state management
Qwik Todo App ⭐ – A complete Qwik example demonstrating:
- Framework-agnostic plugins - Uses the same shared plugin code as all other framework examples
- Event-driven state synchronization (
todos:changedevents) - Qwik bindings (
MyceliaProvider,useFacet,useListener) - Qwik signals for reactive state management
Solid.js Todo App ⭐ – A complete Solid.js example demonstrating:
- Framework-agnostic plugins - Uses the same shared plugin code as React, Vue, Svelte, Angular, and Qwik examples
- Event-driven state synchronization (
todos:changedevents) - Solid.js bindings (
MyceliaProvider,useFacet,useListener) - Signal-based reactivity with automatic updates
All six examples use the exact same Mycelia plugin code from examples/todo-shared/, proving that plugins are truly framework-independent. Write your domain logic once, use it everywhere!
CLI Tool
The package includes a CLI tool for scaffolding hooks, contracts, and projects:
# Create a new hook
npx mycelia-kernel-plugin create hook database
# Create a new contract
npx mycelia-kernel-plugin create contract database
# Initialize a new project (vanilla JS)
npx mycelia-kernel-plugin init my-app
# Initialize a React project with Mycelia bindings
npx mycelia-kernel-plugin init react my-react-app
# Initialize a Vue 3 project with Mycelia bindings
npx mycelia-kernel-plugin init vue my-vue-app
# Initialize a Svelte project with Mycelia bindings
npx mycelia-kernel-plugin init svelte my-svelte-app
# Initialize an Angular project with Mycelia bindings
npx mycelia-kernel-plugin init angular my-angular-app
# Initialize a Qwik project with Mycelia bindings
npx mycelia-kernel-plugin init qwik my-qwik-app
# Initialize a Solid.js project with Mycelia bindings
npx mycelia-kernel-plugin init solid my-solid-appOr install globally:
npm install -g mycelia-kernel-plugin
mycelia-kernel-plugin create hook database
mycelia-kernel-plugin init react my-react-appTesting
npm test
npm run test:watchLicense
MIT License - see LICENSE for details.
Links
- GitHub: https://github.com/lesfleursdelanuitdev/mycelia-kernel-plugin-system
- Main Project: https://github.com/lesfleursdelanuitdev/mycelia-kernel
Made with ❤️ by @lesfleursdelanuitdev
