Sistema de plugins
Los plugins extienden Keirost sin tocar el core: campos y tablas nuevas, lógica de negocio en hooks, endpoints HTTP, módulos de UI, widgets de dashboard, tools de IA y temas. Se activan por empresa (tenant).
Qué puede hacer un plugin
Sección titulada «Qué puede hacer un plugin»| Capacidad | API del contexto | Referencia |
|---|---|---|
| Añadir campos a tablas del ERP | migration.addCustomField |
Migraciones |
| Crear tablas propias | migration.createTable |
Migraciones |
| Reaccionar a eventos (facturas, pagos, envíos…) | hooks.register / documents.on* |
Catálogo de hooks |
| Exponer endpoints HTTP | app.get / app.post |
PluginContext |
| Lógica de negocio transaccional | factuApi |
FactuAPI |
| Consultar la BD directamente | db.public / db.forTenant |
PluginContext |
| Módulos y páginas en el ERP | manifest.json → ui |
Manifest y UI |
| Widgets en el dashboard | ui.dashboardWidgets / widgets.registerDashboard |
Manifest y UI |
| Tools para el chat de IA | aiTools.register |
PluginContext |
| Temas visuales | ui.themes |
Manifest y UI |
Anatomía de un plugin
Sección titulada «Anatomía de un plugin»Directoriomi-plugin/
- index.ts exporta
init()— migraciones, hooks, endpoints - manifest.json metadatos y UI declarativa
Directorioui/
- Page.tsx componentes React (opcional)
- package.json con
@openfactu/plugin-sdk
- index.ts exporta
El punto de entrada es una función init que recibe el PluginContext:
import type { PluginContext } from '@openfactu/plugin-sdk';
export const init = async (context: PluginContext) => { // context: { app, migration, hooks, documents, factuApi, db, widgets, aiTools }};
// type PluginInit = (context: PluginContext) => void | Promise<void>;Ciclo de vida
Sección titulada «Ciclo de vida»- Carga — al arrancar, el servidor recorre
plugins/y ejecuta elinit()de cada plugin (las migraciones son idempotentes). - Activación por tenant — cada empresa activa o desactiva el plugin de forma independiente; con el plugin desactivado sus hooks no se ejecutan y su UI no se carga. Ver Activación por empresa.
- Hot reload — en desarrollo, al guardar un archivo se limpian los hooks, se re-ejecuta
init()y el frontend se actualiza por WebSocket sin refrescar. Ver Desarrollo remoto.
Los tres vocabularios de documento
Sección titulada «Los tres vocabularios de documento»El mismo documento se nombra de tres formas según la API — es la principal fuente de confusión al escribir plugins:
| Documento | DocType( factuApi.create) |
DocumentType(eventos de hook) |
CoreTableName(tablas, documents.on*) |
|---|---|---|---|
| Factura de venta | SINV |
salesInvoice |
SalesInvoice |
| Factura de compra | PINV |
purchaseInvoice |
PurchaseInvoice |
| Pedido de venta | SO |
salesOrder |
SalesOrder |
| Pedido de compra | PO |
purchaseOrder |
PurchaseOrder |
| Albarán de venta | SDN |
salesDeliveryNote |
SalesDeliveryNote |
| Albarán de compra | PDN |
purchaseDeliveryNote |
PurchaseDeliveryNote |
npm install --save-dev @openfactu/plugin-sdk@openfactu/plugin-sdk (v0.4.0) es type-only: exporta los tipos (PluginContext, HookContext, PluginManifest, HookEvent, CoreTableName…) pero ninguna implementación — el runtime lo inyecta el servidor al cargar el plugin. Peer dependencies: react ^19, react-dom ^19, lucide-react >=0.400.
API de gestión
Sección titulada «API de gestión»GET /api/plugins/availablePOST /api/plugins/:pluginId/activatePOST /api/plugins/:pluginId/deactivatePOST /api/plugins/:pluginId/reloadPOST /api/plugins/uploadPOST /api/plugins/:pluginId/pushPor dónde empezar
Sección titulada «Por dónde empezar»Tutorial: tu primer pluginConstruye un plugin completo paso a paso — campos, tablas, hooks, UI, widget y AI tool.
Referencia del PluginContextLos 8 campos del contexto que recibe init().
Desarrollo remotoDesarrolla contra cualquier servidor con push, watch y hot reload.
MarketplaceInstala plugins de la comunidad y publica los tuyos.
Claritas: componentes de UILos 77 componentes con los que se construye la interfaz de un plugin, para que se vea igual que el core.