super-productivity/docs/ai/plugin-system-improvements.md
Johannes Millan d4d81bf511 feat(plugin-api): create foundational plugin API package
- Add @super-productivity/plugin-api package with TypeScript definitions
- Define core plugin interfaces, types, and manifest structure
- Add plugin hooks system for event-driven architecture
- Create plugin API type definitions and constants
- Add documentation and development guidelines
2025-06-27 18:13:19 +02:00

4.6 KiB

Super Productivity Plugin System - Improvement Plan

Document Version: 2.0
Date: 2025-06-27
Status: DRAFT - Pending Review

Executive Summary

This document outlines a comprehensive plan to improve the Super Productivity plugin system. The improvements focus on security, performance, developer experience, and architectural simplification while maintaining backward compatibility where possible.

Table of Contents

  1. Current State Analysis
  2. Identified Issues
  3. Proposed Solutions
  4. Implementation Roadmap
  5. Risk Assessment
  6. Success Metrics

Current State Analysis

Architecture Overview

The current plugin system consists of:

  • Plugin Service (plugin.service.ts): Main orchestrator
  • Plugin Bridge (plugin-bridge.service.ts): API communication layer
  • Plugin Runner (plugin-runner.ts): Code execution
  • Plugin API (plugin-api.ts): API implementation
  • Plugin Package (@super-productivity/plugin-api): Type definitions

Plugin Types

  1. JavaScript Plugins (plugin.js): Execute in sandboxed environment
  2. Iframe Plugins (index.html): Render UI in sandboxed iframes
  3. Hybrid Plugins: Combine both approaches

Current Capabilities

  • Hook system for app events
  • UI component registration (buttons, menus, shortcuts)
  • Data access (tasks, projects, tags)
  • Persistence layer
  • Node.js execution (Electron only, with permission)

Identified Issues

1. Type System Complexity

Duplicate Types

// plugin-api package
export interface TaskData { ... }

// Internal model
export interface TaskCopy { ... }

// Confusing mappings
taskCopyToTaskData(task: TaskCopy): TaskData
taskDataToPartialTaskCopy(data: Partial<TaskData>): Partial<TaskCopy>

Problems:

  • Maintenance overhead
  • Confusion for developers
  • Potential type mismatches
  • Unnecessary complexity

2. Performance Issues

Loading Strategy

// Current: All plugins loaded on startup
await this._loadBuiltInPlugins();
await this._loadUploadedPlugins();

Problems:

  • No lazy loading
  • Memory overhead for disabled plugins
  • Slow app startup
  • No lifecycle management

3. API Inconsistencies

Message Passing Complexity

// Different patterns for iframe vs plugin.js
// Complex handler registration
// Multiple message types
// Confusing async patterns

Problems:

  • Inconsistent behavior

5. Developer Experience

Current Pain Points

  • Complex boilerplate
  • Limited debugging tools
  • Poor error messages
  • No hot reload
  • Missing development tools

Proposed Solutions

1. Simplified Type System

Unified Types

// Single source of truth in plugin-api package
export interface Task {
  id: string;
  title: string;
  notes?: string;
  // ... unified properties
}

// No more TaskData vs TaskCopy confusion
// Direct use in both plugin and app code

Runtime Validation

// Use Typia for automatic validation
import typia from 'typia';

const validateTask = typia.createValidate<Task>();

// Automatic validation on API boundaries
async updateTask(id: string, updates: unknown): Promise<void> {
  const validated = validateTask(updates);
  if (!validated.success) throw new Error(validated.errors);
  // ... proceed with update
}

3. Performance Optimizations

Lazy Loading

class PluginLoader {
  private loadedPlugins = new Map<string, Plugin>();

  async loadPlugin(id: string): Promise<Plugin> {
    if (this.loadedPlugins.has(id)) {
      return this.loadedPlugins.get(id)!;
    }

    // Load only when needed
    const plugin = await this.fetchAndCompile(id);
    this.loadedPlugins.set(id, plugin);
    return plugin;
  }

  unloadPlugin(id: string): void {
    // Free memory when not needed
    this.loadedPlugins.delete(id);
  }
}

Plugin Lifecycle

interface PluginLifecycle {
  onActivate(): Promise<void>;
  onDeactivate(): Promise<void>;
}

4. Streamlined API

Unified RPC System

// Single API interface for all contexts
class PluginRPC {
  async call<T>(method: string, ...args: any[]): Promise<T> {
    return this.transport.send({ method, args });
  }
}

// Type-safe client
const api = createPluginClient<PluginAPI>();
const tasks = await api.getTasks(); // Fully typed

Type Changes

// OLD
interface TaskCopy { ... }
interface TaskData { ... }

// NEW
interface Task { ... }  // Single unified type