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

@kanian77/choux

v0.2.8

Published

A simple plugin framework for TypeScript.

Readme

Choux Plugin System

Choux is a modular, type-safe plugin system for Node.js/TypeScript applications, built on top of the @kanian77/tject dependency injection framework. It enables plugin lifecycle management and custom hooks, making it easy to extend your application with isolated, reusable modules.

Core Concepts

Plugin

A plugin is a class extending the abstract Plugin base class (src/core/Plugin.ts). Each plugin must define metadata (name, version, optional description and dependencies) and can implement lifecycle methods:

  • onLoad: Called when the plugin is loaded, before dependencies are resolved.
  • onInit: Called after dependencies are resolved and injected.
  • onDestroy: Called when the plugin is being unloaded.

Plugins are implemented as PluginModule classes, which extend the base Module class from @kanian77/tject.

Plugin Loading & Management

  • PluginManager (src/core/PluginManager.ts): Manages the initialization, lifecycle, and shutdown of plugins. It provides methods to load individual plugins, get loaded plugin instances, and properly shutdown plugins.
  • ChouxFacade (src/core/ChouxFacade.ts): A unified facade that combines PluginManager and HookRegistry functionality. This is the recommended way to interact with Choux as it provides a single interface for all plugin and hook operations.
  • ChouxModule (src/core/ChouxModule.ts): The main entry point module that provides the core services (PluginManager, HookRegistry, and ChouxFacade). Must be imported and bootstrapped in your application.

Dependency Injection

All plugins and core services use the DI system from @kanian77/tject. Tokens (see src/lib/types/tokens.ts) are used to register and inject services. Core tokens are defined as Symbols:

  • PLUGIN_MANAGER_TOKEN: For accessing the PluginManager
  • HOOK_REGISTRY_TOKEN: For accessing the HookRegistry
  • CHOUX_FACADE_TOKEN: For accessing the ChouxFacade (recommended)

Hooks & Events

  • HookRegistry (src/core/HookRegistry.ts): Central registry for registering, triggering, and managing hooks/events. Plugins can register handlers for lifecycle or custom hooks, and trigger hooks to communicate with other plugins.
  • Lifecycle Hooks: Standard hooks for plugin load/init/destroy and application start/shutdown. Access them via the LIFECYCLE_HOOKS constant:
    • APPLICATION_START: 'core:application-start'
    • BEFORE_PLUGIN_LOAD: 'core:before-plugin-load'
    • AFTER_PLUGIN_LOAD: 'core:after-plugin-load'
    • BEFORE_PLUGIN_INIT: 'core:before-plugin-init'
    • AFTER_PLUGIN_INIT: 'core:after-plugin-init'
    • BEFORE_PLUGIN_DESTROY: 'core:before-plugin-destroy'
    • AFTER_PLUGIN_DESTROY: 'core:after-plugin-destroy'
    • APPLICATION_SHUTDOWN: 'core:application-shutdown'
  • Custom Hooks: Plugins can define and trigger their own hooks for extensibility.

Decorators & Utilities

  • Hook Decorators: Three decorators work together to enable hook functionality:
    • @EnableHooks: Class-level decorator that must be applied to any class using hook decorators
    • @Hook(hookName): Method decorator for registering standard hook handlers in classes
    • @RequestHook(hookName): Method decorator for registering request hooks that return values (only one handler per hook)
  • Function Utilities (src/lib/functions/): Helper functions for hook registration and plugin management.
  • Types (src/lib/types/): Shared interfaces and type definitions for plugins, hook registry, plugin manager, and metadata.

Usage

Bootstrapping

To use Choux in your application:

  1. Import and bootstrap the ChouxModule:
import { bootstrap, inject } from '@kanian77/tject';
import {
  ChouxModule,
  CHOUX_FACADE_TOKEN,
  type IChouxFacade,
} from '@kanian77/choux';
import { YourPluginModule } from './plugins/your-plugin/module';

// Bootstrap the core module
bootstrap(ChouxModule);

// Get the Choux facade (recommended approach)
const choux = inject<IChouxFacade>(CHOUX_FACADE_TOKEN);

// Initialize your plugins
await choux.initializePlugins([YourPluginModule]);

Creating a Plugin

Create a new plugin by extending the Plugin class and defining it as a PluginModule:

import { Plugin, PluginModule, EnableHooks, Hook } from '@kanian77/choux';
import { Service } from '@kanian77/tject';

// Define a unique symbol token for your plugin
export const YOUR_PLUGIN_TOKEN = Symbol('YOUR_PLUGIN');

@Service({ token: YOUR_PLUGIN_TOKEN })
@EnableHooks
class YourPlugin extends Plugin {
  readonly metadata = {
    name: 'your-plugin',
    version: '1.0.0',
    description: 'Your plugin description',
  };

  async onInit() {
    // Initialize your plugin
  }

  // Example of using hooks
  @Hook('some:event')
  onSomeEvent(data: any) {
    // Handle the event
  }
}

export const YourPluginModule = new PluginModule({
  token: YOUR_PLUGIN_TOKEN,
  providers: [
    {
      provide: YOUR_PLUGIN_TOKEN,
      useClass: YourPlugin,
    },
    // Add your plugin's services here
  ],
});

Accessing Plugin Services

You can inject services provided by plugins using their tokens:

import { inject } from '@kanian77/tject';
import { YOUR_SERVICE_TOKEN } from './tokens';
import { IYourService } from './services';

const yourService = inject<IYourService>(YOUR_SERVICE_TOKEN);
yourService.doSomething();

Using the ChouxFacade

The ChouxFacade provides a unified interface for both plugin management and hook operations:

import { inject } from '@kanian77/tject';
import { CHOUX_FACADE_TOKEN, type ChouxFacade } from '@kanian77/choux';

const choux = inject<ChouxFacade>(CHOUX_FACADE_TOKEN);

// Plugin management
await choux.initializePlugins([PluginModule1, PluginModule2]);
const loadedPlugin = choux.getPlugin('plugin-name'); // Returns { plugin: PluginInstance, metadata: PluginMetadata }
const allPlugins = choux.getAllPlugins();
await choux.shutdown();

// Hook operations - trigger events
await choux.trigger('custom:event', eventData);

// Hook operations - request values
const config = await choux.request<ConfigType>('config:get');

// Register hooks programmatically
choux.register('custom:event', target, handlerFunction);
choux.registerRequest('custom:query', target, queryFunction);

Directory Overview

  • src/core/: Core plugin system (Plugin, PluginManager, HookRegistry, ChouxFacade, ChouxModule)
  • src/decorators/: Hook decorators (@EnableHooks, @Hook, @RequestHook) and registration utilities
  • src/lib/functions/: Helper functions for hooks and plugins
  • src/lib/types/: Shared type definitions and tokens

License

MIT

What's Already Great ✅

  • Core Architecture: Deep integration with @kanian77/tject for robust DI
  • Lifecycle Management: Load, init, destroy with proper ordering
  • Hook System: Both decorators and manual registration
  • Simplicity: Direct plugin loading without file system complexity

Areas for Enhancement 🔧

1. Plugin Configuration System

// src/core/pluginConfig.ts
export interface PluginConfig {
  [pluginName: string]: {
    enabled?: boolean;
    config?: Record<string, any>;
  };
}

// In PluginManager
async initializePlugins(plugins: PluginModule[], config?: PluginConfig): Promise<void>

2. Plugin Validation & Health Checks

// Add to Plugin base class
async healthCheck?(): Promise<{ healthy: boolean; message?: string }>;

// Plugin metadata validation
export function validatePluginMetadata(metadata: PluginMetadata): string[] {
  const errors: string[] = [];
  if (!metadata.name) errors.push('Plugin name is required');
  // ... more validations
  return errors;
}

3. Error Handling & Recovery

// Enhanced error handling in PluginManager
interface PluginLoadError {
  pluginName: string;
  error: Error;
  phase: 'load' | 'init';
}

// Add retry mechanisms for failed plugins
async retryFailedPlugin(pluginName: string): Promise<void>