Add usage guide
This commit is contained in:
249
USAGE.md
Normal file
249
USAGE.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# @paarrot/plugin-manager
|
||||
|
||||
Framework-agnostic, event-client-agnostic plugin system.
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install git+http://synbox.ruv.wtf:8418/litruv/plugin-manager.git
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Core Concepts
|
||||
|
||||
| Class / Function | Purpose |
|
||||
|---|---|
|
||||
| `PluginRegistry` | Central store — manages plugins, commands, settings, themes, etc. |
|
||||
| `createPluginContext` | Factory that wires a `PluginContext` to a registry instance. |
|
||||
| `PluginContext` | The object passed to every plugin's `onLoad`. |
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Create the registry
|
||||
|
||||
```ts
|
||||
import { PluginRegistry } from '@paarrot/plugin-manager';
|
||||
|
||||
export const registry = new PluginRegistry({
|
||||
// Use localStorage in the browser, or MemoryStorage for Node/tests
|
||||
storage: localStorage,
|
||||
|
||||
// Called when a plugin registers a theme — inject the CSS however you want
|
||||
onThemeRegistered: (themeId, className, css) => {
|
||||
const el = document.createElement('style');
|
||||
el.id = `theme-${themeId}`;
|
||||
el.textContent = css;
|
||||
document.head.appendChild(el);
|
||||
},
|
||||
|
||||
onThemeUnregistered: (themeId) => {
|
||||
document.getElementById(`theme-${themeId}`)?.remove();
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Load a plugin
|
||||
|
||||
```ts
|
||||
import { createPluginContext } from '@paarrot/plugin-manager';
|
||||
|
||||
// Any object with on()/off() works as the event client —
|
||||
// Node EventEmitter, matrix-js-sdk MatrixClient, socket.io, etc.
|
||||
const context = createPluginContext(
|
||||
{
|
||||
pluginId: 'my-plugin',
|
||||
eventClient: myEmitter,
|
||||
onNotify: (opts) => console.log(`[${opts.title}]`, opts.body),
|
||||
},
|
||||
registry
|
||||
);
|
||||
|
||||
registry.registerPlugin('my-plugin', plugin, context);
|
||||
await plugin.onLoad(context);
|
||||
```
|
||||
|
||||
### 3. Unload / cleanup
|
||||
|
||||
```ts
|
||||
await registry.unregisterPlugin('my-plugin');
|
||||
|
||||
// Or tear everything down at once (e.g. on page unmount):
|
||||
registry.clear();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Writing a Plugin
|
||||
|
||||
```js
|
||||
// my-plugin/index.js
|
||||
module.exports = {
|
||||
name: 'My Plugin',
|
||||
version: '1.0.0',
|
||||
|
||||
onLoad(ctx) {
|
||||
// Register a slash command
|
||||
ctx.commands.register({
|
||||
name: 'hello',
|
||||
description: 'Say hello',
|
||||
run: (args, ctx) => {
|
||||
ctx.log('Hello!');
|
||||
return 'Hello, world!';
|
||||
},
|
||||
});
|
||||
|
||||
// Define persistent settings
|
||||
ctx.settings.define({
|
||||
enabled: { type: 'boolean', label: 'Enable feature', default: true },
|
||||
prefix: { type: 'string', label: 'Prefix', default: '!' },
|
||||
});
|
||||
|
||||
// Listen to events from the event client
|
||||
ctx.events.on('Room.timeline', (event) => {
|
||||
ctx.log('Received event:', event.getType());
|
||||
});
|
||||
|
||||
// Intercept outgoing messages
|
||||
ctx.messages.onBeforeSend((msg) => {
|
||||
if (msg.content.startsWith('brb')) {
|
||||
msg.content = msg.content.replace('brb', 'be right back');
|
||||
}
|
||||
});
|
||||
|
||||
// Send a notification on load
|
||||
ctx.notify({ title: 'My Plugin', body: 'Loaded!', type: 'success' });
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
// cleanup if needed
|
||||
},
|
||||
|
||||
// Other plugins can call ctx.require('my-plugin').greet()
|
||||
exports: {
|
||||
greet: (name) => `Hello, ${name}!`,
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PluginContext API Reference
|
||||
|
||||
### `commands`
|
||||
```ts
|
||||
ctx.commands.register(command: PluginCommand): void
|
||||
ctx.commands.unregister(name: string): void
|
||||
ctx.commands.execute(name: string, args: Record<string, unknown>): Promise<string | void>
|
||||
```
|
||||
|
||||
### `messages`
|
||||
```ts
|
||||
ctx.messages.onBeforeSend(interceptor: MessageInterceptor): void
|
||||
ctx.messages.onReceive(interceptor: MessageInterceptor): void
|
||||
```
|
||||
|
||||
### `settings`
|
||||
```ts
|
||||
ctx.settings.define(schema: SettingsSchema): void
|
||||
ctx.settings.get<T>(key: string): T | undefined
|
||||
ctx.settings.set(key: string, value: unknown): void
|
||||
```
|
||||
Settings are persisted to the storage adapter automatically.
|
||||
|
||||
### `themes`
|
||||
```ts
|
||||
ctx.themes.register(theme: PluginTheme): void
|
||||
ctx.themes.unregister(themeId: string): void
|
||||
```
|
||||
|
||||
### `events`
|
||||
```ts
|
||||
ctx.events.on(eventType: string, handler: (...args: any[]) => void): void
|
||||
ctx.events.off(eventType: string, handler: (...args: any[]) => void): void
|
||||
```
|
||||
Wires to whatever `eventClient` you pass into `createPluginContext`. All handlers are cleaned up automatically on `unregisterPlugin`.
|
||||
|
||||
### `timers`
|
||||
```ts
|
||||
ctx.timers.setInterval(callback, ms): number
|
||||
ctx.timers.setTimeout(callback, ms): number
|
||||
ctx.timers.clearInterval(id): void
|
||||
ctx.timers.clearTimeout(id): void
|
||||
```
|
||||
All timers are cleared automatically on `unregisterPlugin`.
|
||||
|
||||
### `ui`
|
||||
```ts
|
||||
ctx.ui.registerRenderer(type: string, renderer: CustomRenderer): void
|
||||
ctx.ui.unregisterRenderer(type: string): void
|
||||
```
|
||||
|
||||
### `notify`
|
||||
```ts
|
||||
ctx.notify('Simple message')
|
||||
ctx.notify({ title: 'Title', body: 'Body', type: 'success' | 'error' | 'warning' | 'info' })
|
||||
```
|
||||
Delegates to the `onNotify` callback you provide to `createPluginContext`.
|
||||
|
||||
### `require`
|
||||
```ts
|
||||
const api = ctx.require('other-plugin-id');
|
||||
```
|
||||
Returns `exports` from another loaded plugin.
|
||||
|
||||
### Logging
|
||||
```ts
|
||||
ctx.log(...args)
|
||||
ctx.warn(...args)
|
||||
ctx.error(...args)
|
||||
```
|
||||
Forwarded to `console` and stored in the registry log buffer (accessible via `registry.getLogs(pluginId)`).
|
||||
|
||||
---
|
||||
|
||||
## Registry Methods
|
||||
|
||||
```ts
|
||||
registry.processBeforeSend(msg: MessageContext): Promise<MessageContext>
|
||||
registry.processReceive(msg: MessageContext): Promise<MessageContext>
|
||||
registry.getCommands(): Array<{ name, pluginId, command }>
|
||||
registry.getRenderer(type: string): CustomRenderer | undefined
|
||||
registry.getPluginThemes(): Array<{ id, name, kind, className }>
|
||||
registry.getLogs(pluginId?: string): PluginLogEntry[]
|
||||
registry.clearLogs(pluginId?: string): void
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Storage Adapter
|
||||
|
||||
The default is `MemoryStorage` (in-memory, not persisted). Pass any object implementing:
|
||||
|
||||
```ts
|
||||
interface IPluginStorage {
|
||||
getItem(key: string): string | null;
|
||||
setItem(key: string, value: string): void;
|
||||
}
|
||||
```
|
||||
|
||||
`localStorage` works directly in browsers.
|
||||
|
||||
---
|
||||
|
||||
## Event Client Adapter
|
||||
|
||||
Pass any object implementing:
|
||||
|
||||
```ts
|
||||
interface IPluginEventClient {
|
||||
on(event: string, handler: (...args: any[]) => void): void;
|
||||
off(event: string, handler: (...args: any[]) => void): void;
|
||||
}
|
||||
```
|
||||
|
||||
Compatible with: Node.js `EventEmitter`, `matrix-js-sdk` `MatrixClient`, `socket.io` sockets, and similar.
|
||||
Reference in New Issue
Block a user