Ajustes del plugin

En esta página

Los plugins necesitan configuración: claves API, feature flags, preferencias de visualización. EmDash ofrece dos mecanismos: un esquema de ajustes para opciones configurables en el admin y un almacén KV para acceso programático.

Esquema de ajustes

Declara un esquema de ajustes en admin.settingsSchema para generar automáticamente la UI de administración:

import { definePlugin } from "emdash";

export default definePlugin({
	id: "seo",
	version: "1.0.0",

	admin: {
		settingsSchema: {
			siteTitle: {
				type: "string",
				label: "Site Title",
				description: "Used in title tags and meta",
				default: "",
			},
			maxTitleLength: {
				type: "number",
				label: "Max Title Length",
				description: "Characters before truncation",
				default: 60,
				min: 30,
				max: 100,
			},
			generateSitemap: {
				type: "boolean",
				label: "Generate Sitemap",
				description: "Automatically generate sitemap.xml",
				default: true,
			},
			defaultRobots: {
				type: "select",
				label: "Default Robots",
				options: [
					{ value: "index,follow", label: "Index & Follow" },
					{ value: "noindex,follow", label: "No Index, Follow" },
					{ value: "noindex,nofollow", label: "No Index, No Follow" },
				],
				default: "index,follow",
			},
			apiKey: {
				type: "secret",
				label: "API Key",
				description: "Encrypted at rest",
			},
		},
	},
});

EmDash genera un formulario de ajustes en la sección de administración del plugin. Los usuarios editan la configuración sin tocar código.

Tipos de campo

String

Entrada de texto para cadenas de una o varias líneas.

siteTitle: {
  type: "string",
  label: "Site Title",
  description: "Optional help text",
  default: "My Site",
  multiline: false  // Set true for textarea
}

Number

Entrada numérica con límites opcionales min/max.

maxItems: {
  type: "number",
  label: "Maximum Items",
  default: 100,
  min: 1,
  max: 1000
}

Boolean

Interruptor para valores verdadero/falso.

enabled: {
  type: "boolean",
  label: "Enabled",
  description: "Turn this feature on or off",
  default: true
}

Select

Lista desplegable con opciones predefinidas.

theme: {
  type: "select",
  label: "Theme",
  options: [
    { value: "light", label: "Light" },
    { value: "dark", label: "Dark" },
    { value: "auto", label: "System" }
  ],
  default: "auto"
}

Secret

Campo cifrado para valores sensibles como claves API. Nunca se envía al cliente tras guardar.

apiKey: {
  type: "secret",
  label: "API Key",
  description: "Stored encrypted"
}

Acceder a los ajustes

Lee los ajustes en hooks y rutas mediante ctx.kv:

"content:beforeSave": async (event, ctx) => {
  // Read a setting
  const maxLength = await ctx.kv.get<number>("settings:maxTitleLength");
  const apiKey = await ctx.kv.get<string>("settings:apiKey");

  // Use defaults if not set
  const limit = maxLength ?? 60;

  ctx.log.info("Using max length", { limit });
  return event.content;
}

Los ajustes se almacenan por convención con el prefijo settings:. Así se distinguen los valores configurables por el usuario del estado interno del plugin.

API del almacén KV

El almacén KV (ctx.kv) es un almacén clave-valor de propósito general para datos del plugin:

interface KVAccess {
	get<T>(key: string): Promise<T | null>;
	set(key: string, value: unknown): Promise<void>;
	delete(key: string): Promise<boolean>;
	list(prefix?: string): Promise<Array<{ key: string; value: unknown }>>;
}

Leer valores

// Get a single value
const enabled = await ctx.kv.get<boolean>("settings:enabled");

// Get with type
const config = await ctx.kv.get<{ url: string; timeout: number }>("state:config");

Escribir valores

// Set a value
await ctx.kv.set("settings:lastSync", new Date().toISOString());

// Set complex values
await ctx.kv.set("state:cache", {
	data: items,
	expiry: Date.now() + 3600000,
});

Listar valores

// List all settings
const settings = await ctx.kv.list("settings:");
// Returns: [{ key: "settings:enabled", value: true }, ...]

// List all plugin keys
const all = await ctx.kv.list();

Eliminar valores

const deleted = await ctx.kv.delete("state:tempData");
// Returns true if key existed

Convenciones de nombres de claves

Usa prefijos para organizar los datos KV:

PrefijoPropósitoEjemplo
settings:Preferencias configurables por el usuariosettings:apiKey
state:Estado interno del pluginstate:lastSync
cache:Datos en cachécache:results
// Good: clear prefixes
await ctx.kv.set("settings:webhookUrl", url);
await ctx.kv.set("state:lastRun", timestamp);
await ctx.kv.set("cache:feed", feedData);

// Avoid: no prefix, unclear purpose
await ctx.kv.set("url", url);

Ajustes frente a Storage frente a KV

Elige el mecanismo de almacenamiento adecuado:

Caso de usoMecanismo
Preferencias editables en el adminadmin.settingsSchema + ctx.kv con settings:
Estado interno del pluginctx.kv con state:
Colecciones de documentosctx.storage

Los ajustes son valores configurables por el usuario: cosas que un administrador podría cambiar. Obtienen una UI generada automáticamente.

El KV es para estado interno como marcas de tiempo, cursores de sincronización o cálculos en caché. Sin UI, solo código.

Storage es para colecciones de documentos con consultas indexadas: envíos de formularios, registros de auditoría, etc.

Cargar ajustes en rutas

Las rutas API pueden exponer ajustes a componentes de la UI de administración:

routes: {
  settings: {
    handler: async (ctx) => {
      const settings = await ctx.kv.list("settings:");
      const result: Record<string, unknown> = {};

      for (const entry of settings) {
        const key = entry.key.replace("settings:", "");
        result[key] = entry.value;
      }

      return result;
    }
  },

  "settings/save": {
    handler: async (ctx) => {
      const input = ctx.input as Record<string, unknown>;

      for (const [key, value] of Object.entries(input)) {
        if (value !== undefined) {
          await ctx.kv.set(`settings:${key}`, value);
        }
      }

      return { success: true };
    }
  }
}

Valores por defecto

Los ajustes de settingsSchema no se persisten automáticamente. Son valores por defecto en la UI de administración. Tu código debe manejar valores ausentes:

"content:afterSave": async (event, ctx) => {
  // Always provide a fallback
  const enabled = await ctx.kv.get<boolean>("settings:enabled") ?? true;
  const maxItems = await ctx.kv.get<number>("settings:maxItems") ?? 100;

  if (!enabled) return;
  // ...
}

Alternativamente, persiste los valores por defecto en plugin:install:

hooks: {
  "plugin:install": async (_event, ctx) => {
    // Persist schema defaults
    await ctx.kv.set("settings:enabled", true);
    await ctx.kv.set("settings:maxItems", 100);
  }
}

Implementación del almacenamiento

Los valores KV se guardan en la tabla _options con claves con espacio de nombres del plugin:

INSERT INTO _options (name, value) VALUES
  ('plugin:seo:settings:siteTitle', '"My Site"'),
  ('plugin:seo:settings:maxTitleLength', '60');

El prefijo plugin:seo: se añade automáticamente. Tu código usa settings:siteTitle, y EmDash lo almacena como plugin:seo:settings:siteTitle.

Así los plugins no sobrescriben accidentalmente los datos unos de otros.