npm package: @kanian77/choux
Description: A simple plugin framework for TypeScript.
Author: Patrick Assoa Adou
Latest version: 0.2.8

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

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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**

```typescript
// 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**

```typescript
// 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**

```typescript
// 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>
```