defarm-sdk
Version:
DeFarm SDK - On-premise blockchain data processing and tokenization engine for agriculture supply chain
594 lines (508 loc) • 13.6 kB
JavaScript
/**
* 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
};