UNPKG

defarm-sdk

Version:

DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain

594 lines (508 loc) 13.6 kB
/** * DeFarm SDK Module System * Modular architecture for extensible functionality */ class ModuleSystem { constructor() { this.modules = new Map(); this.hooks = new Map(); this.processors = new Map(); this.validators = new Map(); this.engines = new Map(); this.middleware = []; } /** * Register a module */ register(name, module) { console.log(`📦 Registering module: ${name}`); // Validate module structure this.validateModule(module); // Store module this.modules.set(name, module); // Register module components if (module.hooks) this.registerHooks(name, module.hooks); if (module.processors) this.registerProcessors(name, module.processors); if (module.validators) this.registerValidators(name, module.validators); if (module.engines) this.registerEngines(name, module.engines); if (module.middleware) this.registerMiddleware(name, module.middleware); if (module.commands) this.registerCommands(name, module.commands); // Initialize module if needed if (module.init) { module.init(); } return this; } /** * Load multiple modules */ load(modules) { for (const module of modules) { if (typeof module === 'string') { // Load built-in module by name this.loadBuiltIn(module); } else if (typeof module === 'object') { // Load custom module object this.register(module.name || 'custom', module); } } return this; } /** * Load built-in module */ loadBuiltIn(name) { try { const module = require(`./modules/${name}`); this.register(name, module); } catch (error) { console.warn(`⚠️ Module '${name}' not found or failed to load:`, error.message); } } /** * Validate module structure */ validateModule(module) { if (!module.name) throw new Error('Module must have a name'); if (!module.version) throw new Error('Module must have a version'); // Check for at least one functionality const hasFunctionality = module.hooks || module.processors || module.validators || module.engines || module.middleware || module.commands || module.routes; if (!hasFunctionality) { throw new Error('Module must provide at least one functionality'); } } /** * Register hooks from module */ registerHooks(moduleName, hooks) { for (const [hookName, handler] of Object.entries(hooks)) { if (!this.hooks.has(hookName)) { this.hooks.set(hookName, []); } this.hooks.get(hookName).push({ module: moduleName, handler, priority: handler.priority || 0 }); // Sort by priority this.hooks.get(hookName).sort((a, b) => b.priority - a.priority); } } /** * Register processors from module */ registerProcessors(moduleName, processors) { for (const [type, processor] of Object.entries(processors)) { this.processors.set(type, { module: moduleName, ...processor }); } } /** * Register validators from module */ registerValidators(moduleName, validators) { for (const [field, validator] of Object.entries(validators)) { this.validators.set(field, { module: moduleName, validate: validator }); } } /** * Register engines from module */ registerEngines(moduleName, engines) { for (const [engineName, engine] of Object.entries(engines)) { this.engines.set(engineName, { module: moduleName, engine }); } } /** * Register middleware from module */ registerMiddleware(moduleName, middleware) { for (const mw of middleware) { this.middleware.push({ module: moduleName, handler: mw, priority: mw.priority || 0 }); } // Sort by priority this.middleware.sort((a, b) => b.priority - a.priority); } /** * Register CLI commands from module */ registerCommands(moduleName, commands) { // Commands would be registered with CLI system for (const [cmd, config] of Object.entries(commands)) { console.log(` ➕ Command: ${cmd} from ${moduleName}`); } } /** * Execute hooks */ async executeHook(hookName, data, options = {}) { const hooks = this.hooks.get(hookName); if (!hooks || hooks.length === 0) return data; let result = data; const errors = []; for (const hook of hooks) { try { // Skip if module is disabled if (options.skipModules && options.skipModules.includes(hook.module)) { continue; } result = await hook.handler(result, options); // Allow hooks to stop propagation if (result && result._stopPropagation) { delete result._stopPropagation; break; } } catch (error) { console.error(`Hook error in ${hook.module}:`, error); if (options.failFast) { throw error; } errors.push({ module: hook.module, hook: hookName, error: error.message }); } } if (errors.length > 0 && options.collectErrors) { result._errors = errors; } return result; } /** * Execute middleware chain */ async executeMiddleware(data, context = {}) { let result = data; for (const mw of this.middleware) { try { result = await mw.handler(result, context, (err) => { if (err) throw err; }); } catch (error) { console.error(`Middleware error in ${mw.module}:`, error); throw error; } } return result; } /** * Get processor for type */ getProcessor(type) { return this.processors.get(type); } /** * Get engine by name */ getEngine(name) { const engineInfo = this.engines.get(name); return engineInfo ? engineInfo.engine : null; } /** * Process data through registered processor */ async process(type, data, options = {}) { const processor = this.getProcessor(type); if (!processor) { throw new Error(`No processor found for type: ${type}`); } try { // Run pre-processing hooks const preprocessed = await this.executeHook(`pre:process:${type}`, data, options); // Execute processor const result = await processor.process(preprocessed, options); // Run post-processing hooks return await this.executeHook(`post:process:${type}`, result, options); } catch (error) { console.error(`Processing error for type ${type}:`, error); throw error; } } /** * Validate field using registered validators */ async validate(field, value, context = {}) { const validator = this.validators.get(field); if (!validator) { // No specific validator, use generic validation return { valid: true, field, value }; } try { const result = await validator.validate(value, context); return { valid: result === true || (result && result.valid), field, value, module: validator.module, ...(typeof result === 'object' ? result : {}) }; } catch (error) { return { valid: false, field, value, error: error.message, module: validator.module }; } } /** * Validate multiple fields */ async validateAll(data, context = {}) { const results = { valid: true, fields: {}, errors: [], warnings: [] }; for (const [field, value] of Object.entries(data)) { const validation = await this.validate(field, value, context); results.fields[field] = validation; if (!validation.valid) { results.valid = false; results.errors.push({ field, error: validation.error || 'Validation failed', module: validation.module }); } if (validation.warning) { results.warnings.push({ field, warning: validation.warning, module: validation.module }); } } return results; } /** * List loaded modules */ list() { const moduleList = []; for (const [name, module] of this.modules) { moduleList.push({ name, version: module.version, description: module.description, features: { hooks: module.hooks ? Object.keys(module.hooks).length : 0, processors: module.processors ? Object.keys(module.processors).length : 0, validators: module.validators ? Object.keys(module.validators).length : 0, engines: module.engines ? Object.keys(module.engines).length : 0, middleware: module.middleware ? module.middleware.length : 0, commands: module.commands ? Object.keys(module.commands).length : 0 } }); } return moduleList; } /** * Check if module is loaded */ has(name) { return this.modules.has(name); } /** * Get module configuration */ get(name) { return this.modules.get(name); } /** * Enable module */ enable(name) { const module = this.modules.get(name); if (!module) return false; module.enabled = true; if (module.onEnable) { module.onEnable(); } console.log(`✅ Module '${name}' enabled`); return true; } /** * Disable module */ disable(name) { const module = this.modules.get(name); if (!module) return false; module.enabled = false; if (module.onDisable) { module.onDisable(); } console.log(`🔴 Module '${name}' disabled`); return true; } /** * Unload module */ unload(name) { const module = this.modules.get(name); if (!module) return false; // Clean up module resources if (module.cleanup) { module.cleanup(); } // Remove from registry this.modules.delete(name); // Remove hooks for (const [hookName, hooks] of this.hooks) { const filtered = hooks.filter(h => h.module !== name); if (filtered.length === 0) { this.hooks.delete(hookName); } else { this.hooks.set(hookName, filtered); } } // Remove processors for (const [type, processor] of this.processors) { if (processor.module === name) { this.processors.delete(type); } } // Remove validators for (const [field, validator] of this.validators) { if (validator.module === name) { this.validators.delete(field); } } // Remove engines for (const [engineName, engineInfo] of this.engines) { if (engineInfo.module === name) { this.engines.delete(engineName); } } // Remove middleware this.middleware = this.middleware.filter(mw => mw.module !== name); console.log(`📦 Module '${name}' unloaded`); return true; } /** * Reload module */ async reload(name) { const module = this.modules.get(name); if (!module) return false; // Store module path if it's a built-in const isBuiltIn = module._builtIn; // Unload the module this.unload(name); // Clear require cache if it's a built-in module if (isBuiltIn) { const modulePath = require.resolve(`./modules/${name}`); delete require.cache[modulePath]; } // Reload the module if (isBuiltIn) { this.loadBuiltIn(name); } else { this.register(name, module); } console.log(`🔄 Module '${name}' reloaded`); return true; } } /** * Module builder helper */ class ModuleBuilder { constructor(name) { this.module = { name, version: '1.0.0', description: '', enabled: true, hooks: {}, processors: {}, validators: {}, engines: {}, middleware: [], commands: {}, routes: {} }; } version(v) { this.module.version = v; return this; } description(d) { this.module.description = d; return this; } hook(name, handler, priority = 0) { handler.priority = priority; this.module.hooks[name] = handler; return this; } processor(type, config) { this.module.processors[type] = config; return this; } validator(field, validateFn) { this.module.validators[field] = validateFn; return this; } engine(name, engine) { this.module.engines[name] = engine; return this; } middleware(handler, priority = 0) { handler.priority = priority; this.module.middleware.push(handler); return this; } command(name, config) { this.module.commands[name] = config; return this; } route(method, path, handler) { const key = `${method} ${path}`; this.module.routes[key] = handler; return this; } init(fn) { this.module.init = fn; return this; } cleanup(fn) { this.module.cleanup = fn; return this; } build() { return this.module; } } /** * Create a new module */ function createModule(name) { return new ModuleBuilder(name); } module.exports = { ModuleSystem, ModuleBuilder, createModule };