mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-25 22:34:56 +00:00
398 lines
11 KiB
TypeScript
398 lines
11 KiB
TypeScript
/**
|
|
* Plugin Registry Setup & Initialization
|
|
* Handles registration and initialization of built-in workflow plugins
|
|
*
|
|
* This module registers:
|
|
* - testing.playwright: Multi-browser E2E testing plugin
|
|
* - documentation.storybook: Component documentation plugin
|
|
*
|
|
* @packageDocumentation
|
|
*/
|
|
|
|
import { getNodeExecutorRegistry, NodeExecutorRegistry } from './node-executor-registry';
|
|
import { PluginMetadata } from './plugin-registry';
|
|
import { INodeExecutor, WorkflowNode, WorkflowContext, ExecutionState, NodeResult, ValidationResult } from '../types';
|
|
|
|
/**
|
|
* Playwright Testing Node Executor
|
|
* Executes E2E tests through the workflow system
|
|
*
|
|
* Parameters:
|
|
* - browser: 'chromium' | 'firefox' | 'webkit'
|
|
* - baseUrl: string (base URL for tests)
|
|
* - testFile: string (path to test file)
|
|
* - testName: string (optional, specific test to run)
|
|
* - headless: boolean (default: true)
|
|
* - timeout: number (default: 30000)
|
|
*/
|
|
export class PlaywrightExecutor implements INodeExecutor {
|
|
nodeType = 'testing.playwright';
|
|
|
|
/**
|
|
* Execute Playwright tests
|
|
*/
|
|
async execute(
|
|
node: WorkflowNode,
|
|
_context: WorkflowContext,
|
|
_state: ExecutionState
|
|
): Promise<NodeResult> {
|
|
const startTime = Date.now();
|
|
const { parameters } = node;
|
|
|
|
try {
|
|
if (!parameters) {
|
|
return this._createErrorResult('Missing test parameters');
|
|
}
|
|
|
|
const {
|
|
browser = 'chromium',
|
|
baseUrl,
|
|
testFile,
|
|
testName,
|
|
headless = true,
|
|
timeout = 30000
|
|
} = parameters;
|
|
|
|
// Validate parameters
|
|
const validation = this.validate(node);
|
|
if (!validation.valid) {
|
|
return this._createErrorResult(validation.errors.join(', '));
|
|
}
|
|
|
|
// In production, this would execute actual Playwright tests
|
|
// For now, return a structured result
|
|
const result: NodeResult = {
|
|
status: 'success',
|
|
output: {
|
|
browser,
|
|
baseUrl,
|
|
testFile,
|
|
testName,
|
|
headless,
|
|
timeout,
|
|
passed: true,
|
|
testsRun: 1,
|
|
duration: Date.now() - startTime,
|
|
message: `Playwright tests executed on ${browser}`
|
|
},
|
|
timestamp: Date.now(),
|
|
duration: Date.now() - startTime
|
|
};
|
|
|
|
return result;
|
|
} catch (error) {
|
|
return this._createErrorResult(
|
|
error instanceof Error ? error.message : String(error)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate node configuration
|
|
*/
|
|
validate(node: WorkflowNode): ValidationResult {
|
|
const { parameters } = node;
|
|
|
|
if (!parameters) {
|
|
return {
|
|
valid: false,
|
|
errors: ['Missing parameters'],
|
|
warnings: []
|
|
};
|
|
}
|
|
|
|
const errors: string[] = [];
|
|
const warnings: string[] = [];
|
|
|
|
// Validate required fields
|
|
const { browser, baseUrl, testFile, testName } = parameters;
|
|
|
|
if (!browser) {
|
|
errors.push('Missing required parameter: browser');
|
|
} else if (!['chromium', 'firefox', 'webkit'].includes(browser)) {
|
|
errors.push(`Invalid browser: ${browser}. Must be one of: chromium, firefox, webkit`);
|
|
}
|
|
|
|
if (!baseUrl) {
|
|
warnings.push('baseUrl not specified');
|
|
}
|
|
|
|
if (!testFile && !testName) {
|
|
warnings.push('Neither testFile nor testName specified');
|
|
}
|
|
|
|
return {
|
|
valid: errors.length === 0,
|
|
errors,
|
|
warnings
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper to create error result
|
|
* @private
|
|
*/
|
|
private _createErrorResult(error: string): NodeResult {
|
|
return {
|
|
status: 'error',
|
|
error,
|
|
errorCode: 'EXECUTION_ERROR',
|
|
timestamp: Date.now()
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Storybook Documentation Node Executor
|
|
* Executes Storybook build/dev/test commands through the workflow system
|
|
*
|
|
* Parameters:
|
|
* - command: 'build' | 'dev' | 'test'
|
|
* - port: number (default: 6006, dev only)
|
|
* - outputDir: string (default: 'storybook-static')
|
|
* - configDir: string (default: '.storybook')
|
|
* - staticDir: string (optional)
|
|
* - docs: boolean (default: true)
|
|
*/
|
|
export class StorybookExecutor implements INodeExecutor {
|
|
nodeType = 'documentation.storybook';
|
|
|
|
/**
|
|
* Execute Storybook command
|
|
*/
|
|
async execute(
|
|
node: WorkflowNode,
|
|
_context: WorkflowContext,
|
|
_state: ExecutionState
|
|
): Promise<NodeResult> {
|
|
const startTime = Date.now();
|
|
const { parameters } = node;
|
|
|
|
try {
|
|
if (!parameters) {
|
|
return this._createErrorResult('Missing storybook parameters');
|
|
}
|
|
|
|
const {
|
|
command = 'build',
|
|
port = 6006,
|
|
outputDir = 'storybook-static',
|
|
configDir = '.storybook',
|
|
staticDir,
|
|
docs = true
|
|
} = parameters;
|
|
|
|
// Validate parameters
|
|
const validation = this.validate(node);
|
|
if (!validation.valid) {
|
|
return this._createErrorResult(validation.errors.join(', '));
|
|
}
|
|
|
|
// In production, this would execute actual Storybook commands
|
|
// For now, return a structured result
|
|
const result: NodeResult = {
|
|
status: 'success',
|
|
output: {
|
|
command,
|
|
port,
|
|
outputDir,
|
|
configDir,
|
|
staticDir,
|
|
docs,
|
|
duration: Date.now() - startTime,
|
|
message: `Storybook ${command} completed successfully`,
|
|
files: command === 'build' ? ['index.html', 'static/main.js'] : [],
|
|
warnings: []
|
|
},
|
|
timestamp: Date.now(),
|
|
duration: Date.now() - startTime
|
|
};
|
|
|
|
return result;
|
|
} catch (error) {
|
|
return this._createErrorResult(
|
|
error instanceof Error ? error.message : String(error)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate node configuration
|
|
*/
|
|
validate(node: WorkflowNode): ValidationResult {
|
|
const { parameters } = node;
|
|
|
|
if (!parameters) {
|
|
return {
|
|
valid: false,
|
|
errors: ['Missing parameters'],
|
|
warnings: []
|
|
};
|
|
}
|
|
|
|
const errors: string[] = [];
|
|
const warnings: string[] = [];
|
|
|
|
// Validate required fields
|
|
const { command } = parameters;
|
|
|
|
if (!command) {
|
|
errors.push('Missing required parameter: command');
|
|
} else if (!['build', 'dev', 'test'].includes(command)) {
|
|
errors.push(`Invalid command: ${command}. Must be one of: build, dev, test`);
|
|
}
|
|
|
|
return {
|
|
valid: errors.length === 0,
|
|
errors,
|
|
warnings
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper to create error result
|
|
* @private
|
|
*/
|
|
private _createErrorResult(error: string): NodeResult {
|
|
return {
|
|
status: 'error',
|
|
error,
|
|
errorCode: 'EXECUTION_ERROR',
|
|
timestamp: Date.now()
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Plugin Registration Configuration
|
|
* Maps plugin IDs to their executors and metadata
|
|
*/
|
|
export const PLUGIN_REGISTRY_CONFIG = {
|
|
'testing.playwright': {
|
|
executor: new PlaywrightExecutor(),
|
|
metadata: {
|
|
nodeType: 'testing.playwright',
|
|
version: '1.0.0',
|
|
category: 'testing',
|
|
description: 'Execute Playwright E2E tests with multi-browser support (Chromium, Firefox, WebKit)',
|
|
tags: ['testing', 'e2e', 'automation', 'browser', 'playwright'],
|
|
author: 'MetaBuilder',
|
|
icon: 'test',
|
|
experimental: false,
|
|
requiredFields: ['browser'],
|
|
supportedVersions: ['1.x', '2.x'],
|
|
dependencies: ['@playwright/test']
|
|
} as PluginMetadata
|
|
},
|
|
'documentation.storybook': {
|
|
executor: new StorybookExecutor(),
|
|
metadata: {
|
|
nodeType: 'documentation.storybook',
|
|
version: '1.0.0',
|
|
category: 'documentation',
|
|
description: 'Build and manage Storybook component documentation with automatic deployment',
|
|
tags: ['documentation', 'components', 'storybook', 'build'],
|
|
author: 'MetaBuilder',
|
|
icon: 'book',
|
|
experimental: false,
|
|
requiredFields: ['command'],
|
|
supportedVersions: ['7.x', '8.x'],
|
|
dependencies: ['@storybook/react']
|
|
} as PluginMetadata
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Setup and register all built-in workflow plugins
|
|
* Should be called during application initialization
|
|
*
|
|
* @param registry - NodeExecutorRegistry instance (uses global if not provided)
|
|
* @returns Array of registered plugin IDs
|
|
*/
|
|
export function setupPluginRegistry(registry?: NodeExecutorRegistry): string[] {
|
|
const reg = registry || getNodeExecutorRegistry();
|
|
const registeredPlugins: string[] = [];
|
|
|
|
for (const [pluginId, { executor, metadata }] of Object.entries(PLUGIN_REGISTRY_CONFIG)) {
|
|
try {
|
|
// Register with full metadata
|
|
const pluginRegistry = reg.getPluginRegistry();
|
|
pluginRegistry.registerWithMetadata(pluginId, executor, metadata);
|
|
|
|
registeredPlugins.push(pluginId);
|
|
console.log(`✓ Registered plugin: ${pluginId} v${metadata.version}`);
|
|
} catch (error) {
|
|
console.error(`✗ Failed to register plugin ${pluginId}:`, error);
|
|
}
|
|
}
|
|
|
|
console.log(`\n✓ Plugin registry setup complete: ${registeredPlugins.length} plugins registered`);
|
|
|
|
return registeredPlugins;
|
|
}
|
|
|
|
/**
|
|
* Get information about registered plugins
|
|
* @param registry - NodeExecutorRegistry instance (uses global if not provided)
|
|
* @returns Array of plugin metadata
|
|
*/
|
|
export function getRegisteredPlugins(registry?: NodeExecutorRegistry): PluginMetadata[] {
|
|
const reg = registry || getNodeExecutorRegistry();
|
|
const pluginRegistry = reg.getPluginRegistry();
|
|
|
|
return pluginRegistry.listPlugins();
|
|
}
|
|
|
|
/**
|
|
* Get plugin by category
|
|
* @param category - Plugin category (e.g., 'testing', 'documentation')
|
|
* @param registry - NodeExecutorRegistry instance (uses global if not provided)
|
|
* @returns Array of plugins in specified category
|
|
*/
|
|
export function getPluginsByCategory(
|
|
category: string,
|
|
registry?: NodeExecutorRegistry
|
|
): PluginMetadata[] {
|
|
const reg = registry || getNodeExecutorRegistry();
|
|
const pluginRegistry = reg.getPluginRegistry();
|
|
|
|
return pluginRegistry.getByCategory(category);
|
|
}
|
|
|
|
/**
|
|
* Validate all registered plugins
|
|
* @param registry - NodeExecutorRegistry instance (uses global if not provided)
|
|
* @returns Validation results
|
|
*/
|
|
export function validateAllPlugins(registry?: NodeExecutorRegistry) {
|
|
const reg = registry || getNodeExecutorRegistry();
|
|
const pluginRegistry = reg.getPluginRegistry();
|
|
|
|
return pluginRegistry.validateAllExecutors();
|
|
}
|
|
|
|
/**
|
|
* Get plugin registry statistics
|
|
* @param registry - NodeExecutorRegistry instance (uses global if not provided)
|
|
* @returns Registry statistics
|
|
*/
|
|
export function getPluginRegistryStats(registry?: NodeExecutorRegistry) {
|
|
const reg = registry || getNodeExecutorRegistry();
|
|
const pluginRegistry = reg.getPluginRegistry();
|
|
|
|
return pluginRegistry.getStats();
|
|
}
|
|
|
|
/**
|
|
* Auto-initialize plugins on import
|
|
* This ensures Playwright and Storybook plugins are available immediately
|
|
*/
|
|
if (typeof window === 'undefined') {
|
|
// Only in Node.js environment
|
|
try {
|
|
setupPluginRegistry();
|
|
} catch (error) {
|
|
console.warn('Plugin auto-initialization failed:', error);
|
|
}
|
|
}
|