- Architecture: Federated Plugin with Module Federation
- Last Updated: January 2025
- Maturity: Active Development
- Live Demo: https://toplocs.github.io/link-plugin/
# Install dependencies
pnpm install
# Start development server
pnpm dev
# Build for production
pnpm build
# Preview built plugin
pnpm previewDevelopment URLs:
- Dev Environment: http://localhost:4173 (main development interface)
- Plugin Server: http://localhost:3006 (federated plugin with hot-reload)
- Plugin URL: http://localhost:3006/plugin.js (federation endpoint)
pnpm dev- Start development server with hot-reloadpnpm build- Build plugin for productionpnpm preview- Preview built plugin locallypnpm test- Run testspnpm type-check- Run TypeScript type checkingpnpm lint- Run ESLint and fix issues
The Link Plugin enables users to share and organize links within TopLocs spheres. It features a modern decoupled architecture with separate development and plugin environments, using Module Federa### Core Plugin Registration
src/config.ts: Plugin configuration using the SDK's fluent APIsrc/gun.ts: Gun.js instance configurationsrc/plugin/index.ts: Main plugin entry point using SDK directly
dev/composables/usePluginDev.ts: Development composable using SDK directlydev/App.vue: Development interface with plugin preview and registration controls
- Direct SDK Usage: No wrapper services or composables, uses SDK directly
- Simplified Structure: Removed redundant types and wrapper servicesamic loading and configurable plugin registration.
The plugin URL is computed based on the environment:
// Development: http://localhost:3005/assets/plugin.js
// Production: configurable via VITE_PLUGIN_URL environment variableConfigure the plugin URL using environment variables:
# Development (default)
VITE_DEV=true VITE_PORT=3005 VITE_HOST=localhost
# Custom development port
VITE_DEV=true VITE_PORT=3005 VITE_HOST=localhost
# Production deployment
VITE_PLUGIN_URL=https://your-domain.com/plugins/link-plugin/plugin.jsThe dev environment (/dev) provides:
- Registration Status Display: Shows current plugin registration info
- Auto-Registration: Automatically registers the plugin if not found
- Plugin Discovery: Lists all registered plugins for debugging
- Manual Controls: Buttons to refresh or re-register the plugin
- Component Testing: Live preview of federated components
This plugin automatically deploys to GitHub Pages via GitHub Actions:
- Live Demo: https://toplocs.github.io/link-plugin/
- Deployment: Automatic on push to main branch
- Build Output: Only the
distfolder (static JS files) is deployed
# Build for production
pnpm build
# Deploy dist/ folder to your hosting service
# The plugin will be available at your-domain.com/plugin.jslink-plugin/
โโโ dev/ # ๐ง Development Environment
โ โโโ index.html # Dev HTML entry point
โ โโโ main.ts # Dev bootstrap (no plugin logic)
โ โโโ App.vue # Dev showcase with federation loading
โโโ src/ # ๐งฉ Plugin Source Code
โ โโโ plugin/ # Federation entry points
โ โ โโโ index.ts # Plugin registration
โ โ โโโ Sidebar.ts # Sidebar component export
โ โ โโโ Settings.ts # Settings component export
โ โโโ views/ # Plugin components
โ โโโ main.ts # Plugin standalone entry
โ โโโ ... # Core plugin code
โโโ vite.config.ts # Federation configuration
- Frontend: Vue 3, TypeScript, Tailwind CSS
- P2P Data: Gun.js (distributed graph database)
- Federation: Module Federation for dynamic plugin loading
- Build: Vite with federation plugin
- Package Manager: pnpm
Start the development environment that loads the plugin dynamically:
# Install dependencies
pnpm install
# Start development with federation
pnpm devThis starts the Vite dev server on http://localhost:3005 with:
- Development showcase environment using PluginComponent
- Plugin registration monitoring and controls
- Component federation testing via the plugin server
- Live plugin status information
Two-Server Development Setup:
- Dev Environment:
http://localhost:3005- Serves the development interface - Plugin Server:
http://localhost:3006/assets/plugin.js- Serves the built federated plugin
The development environment uses PluginComponent to dynamically load the federated plugin from the plugin server, simulating how it would work in production.
-
Start Development:
pnpm dev
This starts the development server with hot-reload
-
Edit Plugin Code:
- Modify files in
src/ - Changes hot-reload automatically
- View updates in the browser
- Modify files in
-
Test Plugin:
- Open
http://localhost:4173in your browser - Test plugin components in the development environment
- Verify plugin loading and functionality
- Open
-
Build and Preview:
pnpm build pnpm preview
Test the built plugin locally before deployment
The plugin exposes these federated modules:
// vite.config.ts federation setup
exposes: {
'./PluginConfig': './src/index.ts', // Plugin configuration
'./InfoSidebar': './src/views/info/Sidebar.vue', // Info sidebar component
'./SettingsContent': './src/views/settings/Content.vue', // Settings content component
}The dev environment loads plugins dynamically:
// dev/components/PluginEnvironment.vue - Federation loading with fallback
try {
// Try to load as federated modules
const sidebarModule = await import('link-plugin/InfoSidebar');
const settingsModule = await import('link-plugin/SettingsContent');
SidebarComponent.value = sidebarModule.default;
SettingsComponent.value = settingsModule.default;
} catch (error) {
// Fallback to local components for development
const { default: SidebarView } = await import('../src/views/info/Sidebar.vue');
const { default: SettingsView } = await import('../src/views/settings/Content.vue');
SidebarComponent.value = SidebarView;
SettingsComponent.value = SettingsView;
}- File:
src/plugin/index.ts - Purpose: Main plugin registration and federation exports
- Build: Configured in
vite.config.tsโbuild.lib.entry - Federation: Exposes components via
./PluginConfig,./InfoSidebar,./SettingsContent
- File:
dev/main.ts - Purpose: Development environment for testing and debugging
- HTML:
dev/index.html - Server: Configured in
vite.config.tsโroot: './dev'(dev mode)
# Start development environment
pnpm dev
# Edit plugin components in src/
# Changes auto-reload in dev environment
# Test components in browser
# Check browser console for any errors# Build plugin for integration
pnpm build
# Preview built plugin
pnpm preview
# Test plugin federation loading# Build plugin for production
pnpm build
# Plugin assets output to dist/
# Deploy via GitHub Actions or manual deploymentdist/
โโโ assets/
โ โโโ plugin.js # Federation entry point
โ โโโ Sidebar-[hash].js # Sidebar component
โ โโโ Settings-[hash].js # Settings component
โ โโโ ... # Other assets
โโโ index.html # Plugin standalone page
# Build for production
pnpm build
# Upload dist/ to CDN
# Update plugin URL in host application# Build for production
pnpm build
# Deploy to static hosting (Netlify, Vercel, etc.)
# Use dist/ as deployment directory// Host application loads plugin
const pluginUrl = 'https://your-cdn.com/link-plugin/assets/plugin.js';
// Dynamic import
const linkPlugin = await import(pluginUrl);// Links stored in Gun.js distributed graph
gun.get('links').get(sphereId).get(linkId)
// Link categories
gun.get('links').get(sphereId).get('categories')
// User link collections
gun.user().get('links').get(collectionId)
// Real-time link updates
gun.get('links').get(sphereId).on(data => {
// Update link list
})// src/plugin/index.ts - Automatic registration
const registerPlugin = () => {
const chain = gun.get('link_plugin');
chain.once(data => {
if (!data) {
const node = chain.put({
id: 'link_plugin',
name: 'Link',
url: 'http://localhost:3005/assets/plugin.js',
});
const slots = gun.get('link_plugin/slots');
slots.set({ slot: 'InfoView', component: 'Sidebar' });
slots.set({ slot: 'Settings', component: 'Settings' });
node.get('slots').put(slots);
gun.get('plugins').set(node);
}
});
};- Development environment (
dev/) is purely for showcasing and testing - Plugin code (
src/) contains only business logic and components - No mixing of dev tooling with plugin functionality
- Dynamic loading of plugin components
- Shared dependencies (Vue, TailwindCSS) for optimal bundle size
- Runtime integration with host applications
- Rapid iteration with hot reload in dev environment
- Isolated testing of plugin components
- Multiple deployment targets (standalone, federated, embedded)
- Optimized builds with proper code splitting
- CDN-friendly static assets
- Version management through federation
// Check browser console for federation errors
// Verify plugin URL is accessible
// Check CORS configuration for cross-origin loading# Clear pnpm cache
pnpm store prune
# Remove node_modules and reinstall
rm -rf node_modules
pnpm install
# Check port conflicts (default: 3005)# Type check
pnpm type-check
# Lint code
pnpm lint
# Clean build
rm -rf dist && pnpm build- Client-side link preview generation
- Distributed link metadata cache
- IPFS integration for preview images
- Offline-first link management
- Plugin hot-swapping in host applications
- Version compatibility checks
- Automatic plugin updates
- Plugin dependency management
# Clone repository
git clone <repository-url>
cd link-plugin
# Install dependencies
pnpm install
# Start development
pnpm dev- Keep dev and plugin code separate - don't mix development tooling with plugin logic
- Test federation loading - ensure components load properly as federated modules
- Follow TypeScript patterns - maintain type safety throughout
- Update documentation - keep README and comments current
- Run tests and linting - use
pnpm testandpnpm lintbefore committing
- Enhanced link preview generation
- Better categorization and tagging
- Improved collaborative features
- Performance optimizations
- Mobile-responsive design improvements
MIT License - See the main TopLocs project for details.
src/composables/usePluginRegistration.ts: Production-ready plugin registration functionalitysrc/services/PluginManager.ts: Core registration service handling Gun.js interactionssrc/config/plugin.ts: Environment-aware configuration and URL computation
dev/composables/usePluginDev.ts: Development-specific debugging and management featuresdev/App.vue: Development interface with plugin preview and registration controls
- Production composable (
usePluginRegistration): Lightweight, essential registration functions only - Development composable (
usePluginDev): Full debugging features, reactive state, UI integration
src/config.ts: Plugin configuration using SDK's fluent APIsrc/gun.ts: Gun.js instance for decentralized storagesrc/plugin/index.ts: Main plugin entry point
src/plugin-sdk/: Complete SDK for plugin developmenttypes.ts- Core type definitionsBasePluginManager.ts- Abstract plugin managerGunPluginManager.ts- Gun.js implementationenvironment.ts- Environment utilitiescomposables.ts- Vue composablesutils.ts- Development utilities
dev/composables/usePluginDev.ts: Dev composable using SDKdev/App.vue: Development interface
- โ Removed: Custom types (use SDK types)
- โ Removed: Wrapper services (use SDK directly)
- โ Removed: Wrapper composables (use SDK directly)
- โ Direct SDK Usage: Cleaner, less boilerplate
- โ Reusable SDK: Can be extracted for other plugins
This project includes a complete TopLocs Plugin SDK that can be reused across multiple plugins:
src/plugin-sdk/types.ts: Core plugin type definitionssrc/plugin-sdk/BasePluginManager.ts: Abstract plugin manager base classsrc/plugin-sdk/GunPluginManager.ts: Gun.js implementationsrc/plugin-sdk/environment.ts: Environment detection utilitiessrc/plugin-sdk/composables.ts: Vue composables for plugin developmentsrc/plugin-sdk/utils.ts: Plugin development utilitiessrc/plugin-sdk/index.ts: Main SDK exports
- โ Standardized Types: Common interfaces across all plugins
- โ Reusable Managers: Abstract base class with concrete implementations
- โ Environment Helpers: URL generation and validation utilities
- โ Vue Integration: Ready-to-use composables for registration
- โ Development Tools: Debugging and validation utilities
- โ Configuration Builder: Fluent API for plugin setup
import {
createPluginConfig,
GunPluginManager,
usePluginRegistration
} from './plugin-sdk';
// Create plugin configuration
const config = createPluginConfig()
.setId('my_plugin')
.setName('My Plugin')
.addSlot('Settings', 'MyComponent')
.build();
// Create manager and register
const manager = new GunPluginManager(gun, config);
const { ensureRegistration } = usePluginRegistration(manager);See src/plugin-sdk/README.md for complete SDK documentation.