Les plugins ont besoin de configuration — clés API, feature flags, préférences d’affichage. EmDash propose deux mécanismes : un schéma de paramètres pour les options configurables dans l’admin et un store KV pour l’accès programmatique.
Schéma de paramètres
Déclarez un schéma de paramètres dans admin.settingsSchema pour générer automatiquement une interface d’administration :
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 génère un formulaire de paramètres dans la section admin du plugin. Les utilisateurs modifient les réglages sans toucher au code.
Types de champs
String
Saisie de texte pour des chaînes sur une ou plusieurs lignes.
siteTitle: {
type: "string",
label: "Site Title",
description: "Optional help text",
default: "My Site",
multiline: false // Set true for textarea
}
Number
Saisie numérique avec contraintes min/max optionnelles.
maxItems: {
type: "number",
label: "Maximum Items",
default: 100,
min: 1,
max: 1000
}
Boolean
Interrupteur pour les valeurs vrai/faux.
enabled: {
type: "boolean",
label: "Enabled",
description: "Turn this feature on or off",
default: true
}
Select
Liste déroulante pour des options prédéfinies.
theme: {
type: "select",
label: "Theme",
options: [
{ value: "light", label: "Light" },
{ value: "dark", label: "Dark" },
{ value: "auto", label: "System" }
],
default: "auto"
}
Secret
Champ chiffré pour des valeurs sensibles comme les clés API. Jamais renvoyé au client après enregistrement.
apiKey: {
type: "secret",
label: "API Key",
description: "Stored encrypted"
}
Accéder aux paramètres
Lisez les paramètres dans les hooks et les routes via 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;
}
Les paramètres sont stockés avec le préfixe settings: par convention. Cela distingue les valeurs configurables par l’utilisateur de l’état interne du plugin.
API du store KV
Le store KV (ctx.kv) est un stockage clé-valeur à usage général pour les données du 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 }>>;
}
Lire des valeurs
// 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");
Écrire des valeurs
// 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,
});
Lister des valeurs
// 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();
Supprimer des valeurs
const deleted = await ctx.kv.delete("state:tempData");
// Returns true if key existed
Conventions de nommage des clés
Utilisez des préfixes pour organiser les données KV :
| Préfixe | Rôle | Exemple |
|---|---|---|
settings: | Préférences configurables par l’utilisateur | settings:apiKey |
state: | État interne du plugin | state:lastSync |
cache: | Données mises en cache | 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);
Paramètres vs Storage vs KV
Choisissez le bon mécanisme de stockage :
| Cas d’usage | Mécanisme |
|---|---|
| Préférences modifiables dans l’admin | admin.settingsSchema + ctx.kv avec settings: |
| État interne du plugin | ctx.kv avec state: |
| Collections de documents | ctx.storage |
Les paramètres sont des valeurs configurables par l’utilisateur — ce qu’un administrateur pourrait modifier. Ils bénéficient d’une interface générée automatiquement.
Le KV sert à l’état interne : horodatages, curseurs de synchronisation, calculs mis en cache. Pas d’interface, uniquement du code.
Storage sert aux collections de documents avec requêtes indexées — soumissions de formulaires, journaux d’audit, etc.
Charger les paramètres dans les routes
Les routes API peuvent exposer les paramètres aux composants de l’interface d’administration :
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 };
}
}
}
Valeurs par défaut
Les paramètres issus de settingsSchema ne sont pas persistés automatiquement. Ce sont des valeurs par défaut dans l’interface d’administration. Votre code doit gérer les valeurs manquantes :
"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;
// ...
}
Vous pouvez aussi persister les valeurs par défaut dans 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);
}
}
Implémentation du stockage
Les valeurs KV sont stockées dans la table _options avec des clés préfixées par le plugin :
INSERT INTO _options (name, value) VALUES
('plugin:seo:settings:siteTitle', '"My Site"'),
('plugin:seo:settings:maxTitleLength', '60');
Le préfixe plugin:seo: est ajouté automatiquement. Votre code utilise settings:siteTitle, et EmDash stocke plugin:seo:settings:siteTitle.
Cela garantit que les plugins n’écrasent pas accidentellement les données des autres.