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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@sucoza/plugin-core

v0.1.9

Published

Core infrastructure for TanStack DevTools plugins

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-core

Quick 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

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/plugin-improvement)
  3. Commit your changes (git commit -m 'Add plugin feature')
  4. Push to the branch (git push origin feature/plugin-improvement)
  5. Open a Pull Request

License

MIT © tyevco


Part of the @sucoza TanStack DevTools ecosystem.

Related Packages