Construire un plugin
Ce guide vous guide dans la création d'un plugin bext à partir de zéro. Nous allons construire un plugin middleware "mode maintenance" qui renvoie une page 503 lorsqu'un indicateur est défini, en utilisant le bac à sable QuickJS.
Échafauder le projet#
bext plugin init maintenance-mode
cd maintenance-mode
Cela crée la structure suivante :
maintenance-mode/
plugin.toml # Plugin manifest
src/
index.js # Plugin entry point
test/
plugin.test.js # Test harness
Le manifeste : plugin.toml#
Chaque plugin nécessite un plugin.toml qui déclare son identité et ses capacités :
[manifest]
name = "maintenance-mode"
version = "0.1.0"
description = "Return a 503 maintenance page when enabled via config"
capabilities = ["middleware"]
[sandbox]
type = "quickjs"
max_memory_mb = 16
max_time_secs = 5
storage_quota_kb = 256
[permissions]
allowed_urls = []
max_fetch_per_minute = 0
Champs :
manifest.name— identifiant unique, en minuscules avec tirets.manifest.capabilities— un ou plusieurs des éléments suivants :middleware,transform,lifecycle,cache_backend.sandbox.type—quickjs,wasmounsjail.sandbox.max_memory_mb— limite de mémoire pour le bac à sable (par défaut : 64 Mo).sandbox.max_time_secs— délai d'expiration de l'horloge murale par appel de crochet (par défaut : 10 s).permissions.allowed_urls— modèles globaux pour HTTP sortant. Vide signifie aucun accès HTTP.
Fonctions de crochet d’écriture#
Ouvrez src/index.js. Le plugin exporte les fonctions de hook de cycle de vie :
// src/index.js
// Called once when the server starts
export function onServerStart(config) {
bext.storage.set("enabled", config.enabled ? "true" : "false");
console.log(`Maintenance mode: ${config.enabled ? "ON" : "OFF"}`);
}
// Called on every incoming request (middleware capability)
export function onRequest(ctx) {
const enabled = bext.storage.get("enabled");
if (enabled !== "true") {
return { action: "continue" };
}
// Allow health checks through
if (ctx.path === "/healthz" || ctx.path === "/readyz") {
return { action: "continue" };
}
return {
action: "respond",
status: 503,
headers: [
["Content-Type", "text/html"],
["Retry-After", "300"],
],
body: `<!DOCTYPE html>
<html><head><title>Maintenance</title></head>
<body><h1>We'll be back shortly</h1>
<p>Scheduled maintenance in progress.</p>
</body></html>`,
};
}
// Called after the response is sent (async, non-blocking)
export function onRequestComplete(event) {
if (event.status === 503) {
bext.metric("maintenance_blocked", 1, [
["path", event.path],
]);
}
}
// Called during graceful shutdown
export function cleanup() {
console.log("Maintenance mode plugin shutting down");
}
API disponibles#
Dans le sandbox QuickJS, votre plugin a accès à :
| API | Description |
|---|---|
bext.storage.get(key) |
Lire à partir du magasin KV limité à chaque plugin |
bext.storage.set(key, value) |
Écrire dans le magasin KV (quota appliqué) |
bext.storage.delete(key) |
Supprimer une entrée KV |
bext.fetch(url, options) |
Client HTTP à débit limité et liste autorisée d'URL |
bext.config |
Objet de configuration du plugin en lecture seule |
bext.metric(name, value, tags) |
Émettre une métrique au pipeline d'observabilité |
console.log/warn/error/info/debug |
Journalisation structurée |
Configuration du plugin#
Dans le bext.config.toml de votre application, ajoutez la section plugin :
[plugins.maintenance-mode]
sandbox = "quickjs"
path = "./plugins/maintenance-mode/src/index.js"
[plugins.maintenance-mode.config]
enabled = false
Tester localement#
Utilisez le programme d'exécution de test du plugin intégré :
bext plugin test
Cela charge votre plugin dans un contexte QuickJS isolé et exécute les tests dans test/plugin.test.js :
// test/plugin.test.js
import { describe, it, expect } from "bext:test";
describe("maintenance-mode", () => {
it("returns 503 when enabled", () => {
plugin.call("onServerStart", { enabled: true });
const result = plugin.call("onRequest", {
path: "/dashboard",
method: "GET",
});
expect(result.action).toBe("respond");
expect(result.status).toBe(503);
});
it("passes through when disabled", () => {
plugin.call("onServerStart", { enabled: false });
const result = plugin.call("onRequest", {
path: "/dashboard",
method: "GET",
});
expect(result.action).toBe("continue");
});
it("always allows health checks", () => {
plugin.call("onServerStart", { enabled: true });
const result = plugin.call("onRequest", {
path: "/healthz",
method: "GET",
});
expect(result.action).toBe("continue");
});
});
Débogage#
Activez la journalisation détaillée du plug-in avec la variable d'environnement BEXT_PLUGIN_LOG :
BEXT_PLUGIN_LOG=debug bext dev
Ceci imprime toutes les sorties console.* de votre plugin, ainsi que l'utilisation des ressources du bac à sable (mémoire, nombre de récupérations, octets de stockage).
Pour inspecter le contenu du magasin KV au moment de l'exécution :
bext plugin storage list maintenance-mode
bext plugin storage get maintenance-mode enabled
Un permissions.allowed_urls = [] vide signifie que le plugin n'a aucun accès HTTP sortant. Ajoutez un modèle global pour activer les appels bext.fetch, ou laissez-le vide pour les plugins qui lisent uniquement bext.storage ou émettent des métriques.
Prochaines étapes#
- Plugins QuickJS — référence API complète pour le bac à sable JS
- Plugins WASM — créez des plugins hautes performances dans Rust
- Édition — partagez votre plugin sur les plugins. bext.dev