Spraxium logoSpraxium

Crear un plugin

Usa definePlugin para exponer una factory tipada para tu propio paquete de plugin de Spraxium. Aprende a definir la interfaz de configuración, crear la factory, registrarla y leer la configuración en runtime.

Qué hace una factory de plugin

Una factory de plugin es una función que acepta un objeto de configuración y devuelve una entrada { namespace, config } para el array plugins. Usar el helper definePlugin de @spraxium/core crea esta factory con inferencia completa de TypeScript: el string de namespace queda bloqueado como tipo literal, la carga de configuración se valida en tiempo de compilación y ConfigStore.getPluginConfig infiere automáticamente el tipo de retorno cuando pasas la factory como clave.

Escribir una factory significa que los consumidores nunca tocan strings de namespace crudos. La misma referencia, la función factory exportada, sirve tanto como entrada de registro en spraxium.config.ts como clave de búsqueda dentro de tu código de servicio. Si el namespace cambia alguna vez, solo hace falta actualizar el archivo de definición de la factory.

Define la interfaz de configuración

Crea una interfaz de TypeScript que describa cada opción que tu plugin necesita en runtime. Mantenla plana y centrada en lo que el plugin consume realmente. Agrega comentarios JSDoc para que tu editor muestre tooltips cuando los consumidores configuren el plugin.

src/interfaces/my-plugin-config.interface.ts
export interface MyPluginConfig {
  enabled: boolean;
  apiKey: string;
  retries?: number;
}

Crea la factory con definePlugin

Llama a definePlugin con el literal string del namespace como primer parámetro de tipo y la interfaz de configuración como segundo. Exporta la factory resultante para que los consumidores puedan importarla en su spraxium.config.ts. El namespace debe ser único entre todos los plugins registrados en el mismo proyecto.

src/my-plugin.config.ts
import { definePlugin } from '@spraxium/core';
import type { MyPluginConfig } from './interfaces/my-plugin-config.interface';

export const defineMyPlugin = definePlugin<'my-plugin', MyPluginConfig>(
  'my-plugin',
);

Registra en spraxium.config.ts

Importa la factory y llámala dentro del array plugins. TypeScript impone la forma del objeto de configuración y rechaza claves inválidas o tipos de valor incorrectos en tiempo de compilación, antes de ejecutar cualquier código.

spraxium.config.ts
import { defineConfig } from '@spraxium/core';
import { defineMyPlugin } from './src/my-plugin.config';

export default defineConfig({
  plugins: [
    defineMyPlugin({
      enabled: true,
      apiKey: process.env.MY_PLUGIN_API_KEY ?? '',
      retries: 3,
    }),
  ],
});

Lee la configuración en runtime

Dentro de tu servicio de plugin, llama a ConfigStore.getPluginConfig con la factory como argumento. El tipo de retorno se infiere automáticamente como MyPluginConfig | undefined. Si el plugin no fue registrado, el método devuelve undefined, así que siempre deberías proteger ese caso antes de acceder a sus campos.

src/my-plugin.service.ts
import { Injectable } from '@spraxium/common';
import { ConfigStore } from '@spraxium/core';
import { defineMyPlugin } from './my-plugin.config';

@Injectable()
export class MyPluginService {
  private readonly config = ConfigStore.getPluginConfig(defineMyPlugin);

  initialize(): void {
    if (!this.config?.enabled) return;
    // Use this.config.apiKey, this.config.retries, etc.
  }
}

El namespace debe ser único

Si dos plugins comparten el mismo string de namespace, la segunda entrada en el array plugins ocultará a la primera. Ambas estarán presentes en el array, pero getPluginConfig solo devolverá la última con ese namespace. Elige namespaces que reflejen el nombre del paquete para evitar colisiones.

Leer configuración fuera de un servicio

ConfigStore.getPluginConfig es un método estático que puede llamarse en cualquier lugar después de cargar la configuración. Esto incluye guards, interceptors y código a nivel de módulo. La función factory es el único argumento necesario; no se requieren búsquedas por string ni casts manuales de tipos.

src/guards/plugin-enabled.guard.ts
import { Guard } from '@spraxium/common';
import type { ExecutionContext, SpraxiumGuard } from '@spraxium/common';
import { ConfigStore } from '@spraxium/core';
import { defineMyPlugin } from '../my-plugin.config';

@Guard()
export class PluginEnabledGuard implements SpraxiumGuard {
  canActivate(_ctx: ExecutionContext): boolean {
    const config = ConfigStore.getPluginConfig(defineMyPlugin);
    return config?.enabled === true;
  }
}

Organizar el archivo de la factory

En paquetes publicados, la factory y la interfaz de configuración normalmente se ubican juntas en la raíz del paquete y se reexportan desde el índice del paquete. De esta manera, los consumidores importan todo lo que necesitan en una sola línea.

Ejemplo de estructura de un paquete de plugin
index.ts# Reexporta defineMyPlugin y MyPluginConfig
my-plugin.config.ts# Factory creada con definePlugin
my-plugin-config.interface.ts# Interfaz de configuración
my-plugin.service.ts# Servicio que lee desde ConfigStore