Crear Plugins y Drivers
Iskra se extiende a través de tres contratos: Drivers (recursos con ciclo de vida), Plugins (extensiones livianas que corren al instalar) y Features (módulos del Web Kit con resolución de dependencias). Esta guía muestra cómo crear cada uno.
Escribir un Driver
Sección titulada «Escribir un Driver»Implementá la interfaz Driver. La App llama init() y luego start() durante app.start(), y stop() durante app.stop().
import type { Driver, App } from '@iskra-bun/core';
export class MetricsDriver implements Driver { name = 'MetricsDriver'; #app!: App; #timer?: ReturnType<typeof setInterval>;
async init(app: App) { // Keep a reference; register services in the DI container. this.#app = app; app.context.set('metrics', this); }
async start() { this.#timer = setInterval(() => { this.#app.emit('metrics:tick', { at: Date.now() }); }, 5000); this.#app.logger.info('MetricsDriver started'); }
async stop() { if (this.#timer) clearInterval(this.#timer); this.#app.logger.info('MetricsDriver stopped'); }}Registralo:
app.register(new MetricsDriver());Declara lo que el driver deja en la app, asi app.context.get('metrics') y app.on('metrics:tick', …) quedan tipados para todos los que lo usen (los kits hacen lo mismo):
declare module '@iskra-bun/core' { interface AppContextRegistry { metrics: MetricsDriver; } interface AppEvents { 'metrics:tick': { at: number }; }}Recomendaciones
Sección titulada «Recomendaciones»initno adquiere nada pesado — guardá la referencia a laAppy registrá entradas de DI. No abras sockets ni conexiones acá.startabre recursos — conexiones, servidores, intervalos.stopes el espejo destart— liberá todo, de forma idempotente.- Tirá errores tipados — envolvé las fallas en
DriverErrorconcauseycontextpara que el ciclo de vida las reporte con claridad.
Escribir un Plugin
Sección titulada «Escribir un Plugin»Los plugins no tienen ciclo de vida; install(app) corre de inmediato cuando llamás app.use(plugin).
import type { Plugin, App } from '@iskra-bun/core';
export const requestIdPlugin: Plugin = { name: 'requestId', install(app: App) { app.context.set('genId', () => crypto.randomUUID()); app.logger.debug('requestId plugin installed'); },};app.use(requestIdPlugin);Usá un Plugin cuando no haya un recurso que abrir o cerrar — solo comportamiento o helpers que adjuntar.
Escribir una Feature de Web Kit
Sección titulada «Escribir una Feature de Web Kit»Las features son módulos del Kernel web. Declaran dependencias y el Kernel las resuelve con un sort topológico.
import type { Feature, Kernel } from '@iskra-bun/web-kit';import type { Hono } from 'hono';
export class GreetingFeature implements Feature { name = 'greeting'; dependencies = ['error-handler'];
async initialize(kernel: Kernel) { // Set up shared state, read config from the kernel. }
routes(app: Hono) { app.get('/greet/:name', (c) => c.json({ hello: c.req.param('name') })); }
async shutdown() { // Optional cleanup. }}Agregala a un WebPlugin:
import { WebPlugin } from '@iskra-bun/web-kit';
app.register(new WebPlugin({ port: 3000, features: [new GreetingFeature()] }));dependencies vs peerDependencies
Sección titulada «dependencies vs peerDependencies»dependencies— nombres de features que deben estar registradas. Se inicializan primero, así que su middleware corre antes que el tuyo. Si falta alguna, es un error.peerDependencies— nombres de paquetes npm que la feature necesita. Si falta alguno solo se registra un warning, y no cambian el orden de inicialización.
Las features sin dependencias entre sí corren su middleware en el orden en que se registraron.
Reglas que aplica el Kernel
Sección titulada «Reglas que aplica el Kernel»- Middleware en
initialize(), rutas enroutes(). Hono solo corre el middleware registrado antes de una ruta. El Kernel registra primero el middleware de todas las features y después las rutas de todas, así CSRF, rate limiting, auth y CORS aplican a todas las rutas. Una feature que agrega una ruta eninitialize()se saltearía el middleware de las features inicializadas después, así queinitialize()falla. - Nombres únicos. Registrar una feature con un nombre ya usado lanza un error. Antes reemplazaba a la primera en silencio: un helper llamado
csrfeliminaba la verificación CSRF.RateLimitFeatureacepta unnamepara un segundo limitador. - Rutas después de
initialize(). Con unKernelsuelto, llamá aawait kernel.initialize()antes dekernel.getApp().get(...): una ruta agregada antes se saltearía las cabeceras de seguridad y el middleware de todas las features, así queinitialize()lanza un error. ConWebPlugin, pasá tus rutas comorouter.
Un Driver, Plugin o Feature corre con acceso total a la app: su configuración y secretos, app.context (donde set() reemplaza una clave existente como db) y todos los eventos del bus. Revisalo como a cualquier otra dependencia.
Empaquetar para reutilizar
Sección titulada «Empaquetar para reutilizar»Empezá desde el template plugin-starter. Exportá tu Driver/Plugin desde el entry point del paquete y declará @iskra-bun/core como peer dependency para que quien lo consuma controle la versión. Usá el rango con caret de la versión de core contra la que compilás (el template usa workspace:*, que solo se resuelve dentro de este monorepo):
{ "peerDependencies": { "@iskra-bun/core": "^0.1.1" }}Mirá Arquitectura para ver cómo encajan estas piezas en el modelo por capas.