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