bb-inspired
Version:
Core library for BB-inspired NestJS backend
201 lines • 8.97 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var PluginManagerMain_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PluginManagerMain = void 0;
const common_1 = require("@nestjs/common");
const core_1 = require("@nestjs/core");
const hook_service_1 = require("./hook.service");
const logger_1 = require("../../utils/logger");
let PluginManagerMain = PluginManagerMain_1 = class PluginManagerMain {
constructor(options = {}, hookService, moduleRef) {
this.options = options;
this.hookService = hookService;
this.moduleRef = moduleRef;
this.logger = new logger_1.AppLogger(PluginManagerMain_1.name);
this.plugins = new Map();
this.enabledPlugins = new Set();
this.moduleContexts = new Map();
}
async onModuleInit() {
this.logger.log('Initializing plugin system');
if (this.options.autoDiscovery && this.options.pluginsDir) {
this.logger.log(`Plugin auto-discovery enabled from directory: ${this.options.pluginsDir}`);
}
if (this.options.customHooks && this.options.customHooks.length > 0) {
for (const hookName of this.options.customHooks) {
this.hookService.registerHookPoint(hookName);
}
this.logger.log(`Registered ${this.options.customHooks.length} custom hooks`);
}
}
async onModuleDestroy() {
this.logger.log('Shutting down plugin system');
for (const pluginName of this.enabledPlugins) {
await this.disable(pluginName);
}
}
async register(plugin) {
var _a;
const { metadata } = plugin;
if (!metadata || !metadata.name || !metadata.version) {
throw new Error('Plugin registration requires valid metadata with name and version');
}
if (this.plugins.has(metadata.name)) {
this.logger.warn(`Plugin ${metadata.name} is already registered. Skipping registration.`);
return;
}
this.plugins.set(metadata.name, plugin);
this.logger.log(`Registered plugin: ${metadata.name} v${metadata.version}`);
const context = {
name: metadata.name,
version: metadata.version,
config: plugin.config || {},
get: (token) => this.moduleRef.get(token, { strict: false }),
logger: {
log: (message) => this.logger.log(message, { context: metadata.name }),
error: (message, trace) => this.logger.error(message, trace, { context: metadata.name }),
warn: (message) => this.logger.warn(message, { context: metadata.name }),
debug: (message) => this.logger.debug(message, { context: metadata.name }),
verbose: (message) => this.logger.verbose(message, { context: metadata.name }),
},
emit: async (eventName, payload) => {
await this.hookService.executeHook(`${metadata.name}:${eventName}`, payload);
}
};
this.moduleContexts.set(metadata.name, context);
if (plugin.hooks) {
for (const [hookName, handler] of Object.entries(plugin.hooks)) {
this.registerHook(hookName, handler);
}
}
if ((_a = plugin.lifecycle) === null || _a === void 0 ? void 0 : _a.onLoad) {
try {
await plugin.lifecycle.onLoad(context);
}
catch (error) {
this.logger.error(`Error initializing plugin ${metadata.name}: ${error.message}`, error.stack);
}
}
if (this.options.loadOnStartup) {
await this.enable(metadata.name);
}
}
async enable(pluginName) {
var _a;
if (!this.plugins.has(pluginName)) {
this.logger.warn(`Cannot enable plugin ${pluginName}: Plugin not registered`);
return false;
}
if (this.enabledPlugins.has(pluginName)) {
return true;
}
const plugin = this.plugins.get(pluginName);
const context = this.moduleContexts.get(pluginName);
if (plugin.metadata.dependencies && plugin.metadata.dependencies.length > 0) {
for (const dependency of plugin.metadata.dependencies) {
if (!this.isEnabled(dependency)) {
try {
const success = await this.enable(dependency);
if (!success) {
this.logger.error(`Cannot enable plugin ${pluginName}: Dependency ${dependency} could not be enabled`);
return false;
}
}
catch (error) {
this.logger.error(`Cannot enable plugin ${pluginName}: Dependency ${dependency} error: ${error.message}`);
return false;
}
}
}
}
if ((_a = plugin.lifecycle) === null || _a === void 0 ? void 0 : _a.onEnable) {
try {
await plugin.lifecycle.onEnable(context);
}
catch (error) {
this.logger.error(`Error enabling plugin ${pluginName}: ${error.message}`, error.stack);
return false;
}
}
this.enabledPlugins.add(pluginName);
this.logger.log(`Enabled plugin: ${pluginName}`);
return true;
}
async disable(pluginName) {
var _a;
if (!this.plugins.has(pluginName)) {
this.logger.warn(`Cannot disable plugin ${pluginName}: Plugin not registered`);
return false;
}
if (!this.enabledPlugins.has(pluginName)) {
return true;
}
const plugin = this.plugins.get(pluginName);
const context = this.moduleContexts.get(pluginName);
for (const [name, p] of this.plugins.entries()) {
if (this.enabledPlugins.has(name) &&
p.metadata.dependencies &&
p.metadata.dependencies.includes(pluginName)) {
try {
const success = await this.disable(name);
if (!success) {
this.logger.error(`Cannot disable plugin ${pluginName}: Dependent plugin ${name} could not be disabled`);
return false;
}
}
catch (error) {
this.logger.error(`Cannot disable plugin ${pluginName}: Error disabling dependent plugin ${name}: ${error.message}`);
return false;
}
}
}
if ((_a = plugin.lifecycle) === null || _a === void 0 ? void 0 : _a.onDisable) {
try {
await plugin.lifecycle.onDisable(context);
}
catch (error) {
this.logger.error(`Error disabling plugin ${pluginName}: ${error.message}`, error.stack);
return false;
}
}
this.enabledPlugins.delete(pluginName);
this.logger.log(`Disabled plugin: ${pluginName}`);
return true;
}
getPlugins() {
return Array.from(this.plugins.values()).map(p => p.metadata);
}
getPlugin(name) {
return this.plugins.has(name) ? this.plugins.get(name).metadata : undefined;
}
isEnabled(name) {
return this.enabledPlugins.has(name);
}
registerHook(hookName, handler) {
this.hookService.registerHandler(hookName, handler);
}
async executeHook(hookName, data) {
return this.hookService.executeHook(hookName, data);
}
};
exports.PluginManagerMain = PluginManagerMain;
exports.PluginManagerMain = PluginManagerMain = PluginManagerMain_1 = __decorate([
(0, common_1.Injectable)(),
__param(0, (0, common_1.Optional)()),
__param(0, (0, common_1.Inject)('PLUGIN_OPTIONS')),
__metadata("design:paramtypes", [Object, hook_service_1.HookService,
core_1.ModuleRef])
], PluginManagerMain);
//# sourceMappingURL=plugin.manager.js.map