@sucoza/plugin-core
v0.1.9
Published
Core infrastructure for TanStack DevTools plugins
Maintainers
Readme
@sucoza/plugin-core
Core infrastructure and base classes for building TanStack DevTools plugins. This package provides the essential building blocks for creating powerful, consistent DevTools plugins.
Features
- 🏗️ Plugin Architecture - Standardized plugin lifecycle and architecture
- 🔄 Event System - Robust event-driven communication framework
- 📊 State Management - Built-in state management with React integration
- 🎣 React Hooks - Pre-built hooks for common plugin functionality
- 🔌 Hot Reload Support - Development-friendly hot reloading
- 🛡️ Error Boundaries - Built-in error handling and recovery
- 📝 TypeScript First - Full type safety for plugin development
Installation
npm install @sucoza/plugin-core
# or
yarn add @sucoza/plugin-core
# or
pnpm add @sucoza/plugin-coreQuick Start
Creating a Basic Plugin
import { BaseDevToolsPlugin, usePluginState } from '@sucoza/plugin-core';
interface MyPluginState {
items: string[];
selectedItem: string | null;
}
class MyDevToolsPlugin extends BaseDevToolsPlugin<MyPluginState> {
constructor() {
super({
name: 'My DevTools Plugin',
version: '1.0.0',
namespace: 'my-plugin'
});
}
protected getInitialState(): MyPluginState {
return {
items: [],
selectedItem: null
};
}
protected onActivate(): void {
// Plugin activation logic
this.startMonitoring();
}
protected onDeactivate(): void {
// Plugin deactivation logic
this.stopMonitoring();
}
private startMonitoring(): void {
// Start collecting data
this.updateState({
items: ['item1', 'item2', 'item3']
});
}
private stopMonitoring(): void {
// Stop collecting data
}
}
export const myPlugin = new MyDevToolsPlugin();Plugin Component
import React from 'react';
import { usePluginState, PluginPanel } from '@sucoza/plugin-core';
import { myPlugin } from './my-plugin';
export function MyPluginPanel() {
const { state, actions } = usePluginState(myPlugin);
return (
<PluginPanel
title="My Plugin"
plugin={myPlugin}
>
<div>
<h3>Items ({state.items.length})</h3>
<ul>
{state.items.map((item, index) => (
<li
key={index}
onClick={() => actions.updateState({ selectedItem: item })}
style={{
backgroundColor: state.selectedItem === item ? '#e3f2fd' : 'transparent'
}}
>
{item}
</li>
))}
</ul>
</div>
</PluginPanel>
);
}Core Concepts
Plugin Lifecycle
import { BaseDevToolsPlugin, PluginLifecycle } from '@sucoza/plugin-core';
class MyPlugin extends BaseDevToolsPlugin implements PluginLifecycle {
// Called when plugin is first loaded
async onInitialize(): Promise<void> {
console.log('Plugin initialized');
}
// Called when plugin becomes active
protected onActivate(): void {
console.log('Plugin activated');
}
// Called when plugin becomes inactive
protected onDeactivate(): void {
console.log('Plugin deactivated');
}
// Called when plugin is being destroyed
async onDestroy(): Promise<void> {
console.log('Plugin destroyed');
}
}Event System
import { EventEmitter, PluginEvent } from '@sucoza/plugin-core';
// Emit custom events
myPlugin.emit('data-updated', {
timestamp: Date.now(),
items: newItems
});
// Listen to events
myPlugin.on('data-updated', (event: PluginEvent<{ items: string[] }>) => {
console.log('Data updated:', event.data.items);
});
// One-time event listener
myPlugin.once('initialized', () => {
console.log('Plugin ready!');
});State Management
import { usePluginState, StateAction } from '@sucoza/plugin-core';
function MyComponent() {
const {
state,
actions,
isLoading,
error,
reset
} = usePluginState(myPlugin);
// Update state
const handleAddItem = (newItem: string) => {
actions.updateState({
items: [...state.items, newItem]
});
};
// Batch state updates
const handleBatchUpdate = () => {
actions.batchUpdate([
{ items: [...state.items, 'new1'] },
{ selectedItem: 'new1' }
]);
};
return (
<div>
{isLoading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
<button onClick={() => handleAddItem('new item')}>
Add Item
</button>
<button onClick={() => reset()}>
Reset State
</button>
</div>
);
}API Reference
BaseDevToolsPlugin
Core abstract class for all DevTools plugins.
Constructor Options
interface PluginOptions {
name: string;
version: string;
namespace: string;
enabled?: boolean;
debug?: boolean;
autoActivate?: boolean;
}Methods
| Method | Description |
|--------|-------------|
| activate() | Activates the plugin |
| deactivate() | Deactivates the plugin |
| isActive() | Returns plugin activation status |
| getState() | Returns current plugin state |
| updateState(update) | Updates plugin state |
| emit(event, data) | Emits a plugin event |
| on(event, handler) | Subscribes to plugin events |
| off(event, handler) | Unsubscribes from events |
| destroy() | Destroys the plugin |
Abstract Methods
| Method | Description |
|--------|-------------|
| getInitialState() | Returns initial plugin state |
| onActivate() | Called when plugin activates |
| onDeactivate() | Called when plugin deactivates |
Hooks
usePluginState(plugin: BaseDevToolsPlugin)
React hook for accessing plugin state and actions.
Returns:
{
state: T; // Current plugin state
actions: {
updateState: (update: Partial<T>) => void;
batchUpdate: (updates: Partial<T>[]) => void;
reset: () => void;
};
isLoading: boolean; // Plugin loading status
error: string | null; // Current error state
isActive: boolean; // Plugin activation status
reset: () => void; // Reset to initial state
}usePluginEvents(plugin: BaseDevToolsPlugin)
Hook for subscribing to plugin events.
const events = usePluginEvents(myPlugin, {
'data-updated': (data) => console.log('Data:', data),
'error': (error) => console.error('Error:', error)
});usePluginLifecycle(plugin: BaseDevToolsPlugin)
Hook for managing plugin lifecycle.
const {
activate,
deactivate,
isActive,
isInitialized
} = usePluginLifecycle(myPlugin);Components
PluginPanel
Standard panel component for plugin UI.
<PluginPanel
title="My Plugin"
plugin={myPlugin}
toolbar={<MyToolbar />}
onClose={() => console.log('Panel closed')}
>
<MyPluginContent />
</PluginPanel>PluginProvider
Context provider for plugin state management.
<PluginProvider plugin={myPlugin}>
<MyPluginComponents />
</PluginProvider>ErrorBoundary
Error boundary component with plugin integration.
<ErrorBoundary
plugin={myPlugin}
fallback={<div>Plugin Error</div>}
>
<MyPluginContent />
</ErrorBoundary>Event Types
// Plugin lifecycle events
interface PluginLifecycleEvents {
'plugin:initialized': { plugin: BaseDevToolsPlugin };
'plugin:activated': { plugin: BaseDevToolsPlugin };
'plugin:deactivated': { plugin: BaseDevToolsPlugin };
'plugin:destroyed': { plugin: BaseDevToolsPlugin };
'plugin:error': { plugin: BaseDevToolsPlugin; error: Error };
}
// State management events
interface PluginStateEvents {
'state:updated': { state: any; previousState: any };
'state:reset': { state: any };
'state:error': { error: Error };
}Advanced Usage
Custom Plugin Registry
import { PluginRegistry } from '@sucoza/plugin-core';
const registry = new PluginRegistry();
// Register plugins
registry.register(myPlugin);
registry.register(anotherPlugin);
// Get all plugins
const allPlugins = registry.getAll();
// Get plugin by namespace
const plugin = registry.get('my-plugin');
// Check if plugin exists
const exists = registry.has('my-plugin');Plugin Communication
// Plugin A emits data
pluginA.emit('data-available', { data: myData });
// Plugin B listens for data
pluginB.on('plugin:data-available', (event) => {
if (event.source === 'pluginA') {
// Handle data from Plugin A
console.log('Received data:', event.data);
}
});Middleware System
import { PluginMiddleware } from '@sucoza/plugin-core';
const loggingMiddleware: PluginMiddleware = (plugin, next) => {
return (action) => {
console.log('Action:', action.type);
const result = next(action);
console.log('State after action:', plugin.getState());
return result;
};
};
myPlugin.use(loggingMiddleware);Examples
Check out the examples/ directory for complete plugin implementations and usage patterns.
Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/plugin-improvement) - Commit your changes (
git commit -m 'Add plugin feature') - Push to the branch (
git push origin feature/plugin-improvement) - Open a Pull Request
License
MIT © tyevco
Part of the @sucoza TanStack DevTools ecosystem.
Related Packages
- @sucoza/devtools-common - Common utilities and types
- @sucoza/shared-components - Shared UI components
- @sucoza/devtools-importer - Simplified plugin importing
