133 lines
5.7 KiB
JavaScript
133 lines
5.7 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.loadPlugins = loadPlugins;
|
|
const path_1 = __importDefault(require("path"));
|
|
const fs_1 = __importDefault(require("fs"));
|
|
const logger_1 = require("../config/logger");
|
|
const PLUGINS_DIR = process.env.NODE_ENV === 'production'
|
|
? '/app/plugins'
|
|
: path_1.default.resolve(__dirname, '../../../plugins');
|
|
/**
|
|
* Ordena plugins por dependências usando topological sort (Kahn's algorithm).
|
|
* Plugins sem dependências vêm primeiro; core plugins antes dos business plugins.
|
|
*/
|
|
function topoSort(plugins) {
|
|
const byName = new Map(plugins.map((p) => [p.manifest.name, p]));
|
|
const inDegree = new Map();
|
|
const dependents = new Map();
|
|
for (const p of plugins) {
|
|
inDegree.set(p.manifest.name, 0);
|
|
dependents.set(p.manifest.name, []);
|
|
}
|
|
for (const p of plugins) {
|
|
for (const dep of p.manifest.dependencies) {
|
|
if (!byName.has(dep)) {
|
|
logger_1.logger.warn({ plugin: p.manifest.name, dep }, '[PluginLoader] Dependência não encontrada');
|
|
continue;
|
|
}
|
|
dependents.get(dep).push(p.manifest.name);
|
|
inDegree.set(p.manifest.name, (inDegree.get(p.manifest.name) ?? 0) + 1);
|
|
}
|
|
}
|
|
const queue = plugins
|
|
.filter((p) => inDegree.get(p.manifest.name) === 0)
|
|
.sort((a, b) => (a.manifest.category === 'core' ? -1 : 1) - (b.manifest.category === 'core' ? -1 : 1));
|
|
const sorted = [];
|
|
while (queue.length > 0) {
|
|
const current = queue.shift();
|
|
sorted.push(current);
|
|
for (const depName of dependents.get(current.manifest.name) ?? []) {
|
|
const newDegree = (inDegree.get(depName) ?? 1) - 1;
|
|
inDegree.set(depName, newDegree);
|
|
if (newDegree === 0) {
|
|
queue.push(byName.get(depName));
|
|
}
|
|
}
|
|
}
|
|
if (sorted.length !== plugins.length) {
|
|
logger_1.logger.error('[PluginLoader] Dependência circular detectada — carregando na ordem original');
|
|
return plugins;
|
|
}
|
|
return sorted;
|
|
}
|
|
/**
|
|
* Escaneia o diretório /plugins, lê cada manifest.json e importa index.ts/js.
|
|
* Retorna plugins ordenados por dependência.
|
|
*/
|
|
async function loadPlugins() {
|
|
if (!fs_1.default.existsSync(PLUGINS_DIR)) {
|
|
logger_1.logger.warn({ dir: PLUGINS_DIR }, '[PluginLoader] Diretório plugins/ não encontrado');
|
|
return [];
|
|
}
|
|
const entries = fs_1.default.readdirSync(PLUGINS_DIR, { withFileTypes: true });
|
|
const pluginDirs = entries.filter((e) => e.isDirectory()).map((e) => e.name);
|
|
const loaded = [];
|
|
for (const dirName of pluginDirs) {
|
|
const pluginDir = path_1.default.join(PLUGINS_DIR, dirName);
|
|
const manifestPath = path_1.default.join(pluginDir, 'manifest.json');
|
|
const indexTs = path_1.default.join(pluginDir, 'index.ts');
|
|
const indexJs = path_1.default.join(pluginDir, 'index.js');
|
|
if (!fs_1.default.existsSync(manifestPath)) {
|
|
logger_1.logger.warn({ dir: dirName }, '[PluginLoader] manifest.json não encontrado — pulando');
|
|
continue;
|
|
}
|
|
// Prefere .js (produção compilada); fallback para .ts (tsx watch em dev)
|
|
const entryPoint = fs_1.default.existsSync(indexJs) ? indexJs : fs_1.default.existsSync(indexTs) ? indexTs : null;
|
|
if (!entryPoint) {
|
|
logger_1.logger.warn({ dir: dirName }, '[PluginLoader] index.ts/js não encontrado — pulando');
|
|
continue;
|
|
}
|
|
try {
|
|
const manifest = JSON.parse(fs_1.default.readFileSync(manifestPath, 'utf-8'));
|
|
const mod = await Promise.resolve(`${entryPoint}`).then(s => __importStar(require(s)));
|
|
const instance = mod.default ?? mod;
|
|
if (typeof instance.activate !== 'function') {
|
|
logger_1.logger.warn({ dir: dirName }, '[PluginLoader] Plugin sem método activate() — pulando');
|
|
continue;
|
|
}
|
|
loaded.push({ instance, manifest, dir: pluginDir });
|
|
logger_1.logger.info({ name: manifest.name, version: manifest.version }, '[PluginLoader] Plugin carregado');
|
|
}
|
|
catch (err) {
|
|
logger_1.logger.error({ err, dir: dirName }, '[PluginLoader] Falha ao carregar plugin');
|
|
}
|
|
}
|
|
return topoSort(loaded);
|
|
}
|