npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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 useListeners for event-driven architectures (see Simple Event System Example), plus useQueue and useSpeak
  • 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 MyceliaProvider and React hooks (useFacet, useListener)
  • Vue 3 - Use MyceliaPlugin and Vue composables (useFacet, useListener)
  • Svelte - Use setMyceliaSystem and Svelte stores (useFacet, useListener)
  • Angular - Use MyceliaService and RxJS observables (useFacet, useListener)
  • Qwik - Use MyceliaProvider and Qwik signals (useFacet, useListener)
  • Solid.js - Use MyceliaProvider and 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-plugin

What 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 active

The 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 build

If a facet doesn't satisfy its contract, build fails with a clear error:

Error: FacetContract 'database': facet is missing required methods: close

Architecture

StandalonePluginSystem
  ├── BaseSubsystem (base class)
  ├── SubsystemBuilder (build orchestrator)
  ├── FacetManager (plugin registry)
  ├── FacetContractRegistry (contract validation)
  └── DependencyGraphCache (performance optimization)

API Reference

Core Classes

  • StandalonePluginSystem - Main plugin system class
  • BaseSubsystem - Base class for plugin containers
  • SubsystemBuilder - Build orchestrator
  • FacetManager - Plugin registry
  • FacetContractRegistry - Contract validation

Factory Functions

  • createHook() - Create a plugin hook
  • createFacetContract() - Create a facet contract
  • useBase() - Fluent API builder for StandalonePluginSystem (see useBase Documentation)

Utilities

  • createLogger() - Create a logger
  • getDebugFlag() - Extract debug flag from config

Documentation

Comprehensive documentation is available in the docs/ directory:

Quick Links

Full Documentation

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 (useTodos hook)
    • Event-driven state synchronization (todos:changed events)
    • React bindings (MyceliaProvider, useFacet, useListener)
  • 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:changed events)
    • 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:changed events)
    • 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:changed events)
    • 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:changed events)
    • 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:changed events)
    • 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-app

Or install globally:

npm install -g mycelia-kernel-plugin
mycelia-kernel-plugin create hook database
mycelia-kernel-plugin init react my-react-app

Testing

npm test
npm run test:watch

License

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