v2.5Erweitert

Plugin-Entwicklung

Die Plugin-Architektur erlaubt es, Velisch-Anwendungen zur Laufzeit zu erweitern, ohne den Kern neu zu kompilieren. Plugins sind kompilierte .vplugin-Dateien.

Architektur eines Plugins

Ein Plugin muss das Plugin-Trait implementieren:

// Datei: my_plugin.velin

struct MyPlugin {
    config: Map<string, any>
}

impl Plugin for MyPlugin {
    // Wird beim Laden aufgerufen
    fn on_load(ctx: PluginContext) {
        log.info("Plugin wird initialisiert...");
        
        // Hooks registrieren
        ctx.register_hook("on_request", |req| {
            log.info("Request empfangen: " + req.path);
        });
        
        // Eigene Befehle hinzufügen
        ctx.register_command("hello", || log.info("Hallo vom Plugin!"));
    }
    
    // Wird beim Entladen (oder Shutdown) aufgerufen
    fn on_unload() {
        log.info("Plugin wird gestoppt.");
    }
}

// Export der Factory-Funktion
pub fn create(config: Map<string, any>): Plugin {
    return MyPlugin { config: config };
}

Lifecycle Hooks

Plugins können sich in verschiedene Phasen der Anwendung einklinken:

on_load

Initialisierung beim Laden

on_config_loaded

Nach dem Laden der Konfiguration

on_server_start

Bevor der HTTP-Server startet

on_request

Middleware-ähnlicher Hook für jeden Request

on_error

Globaler Error-Handler

on_unload

Cleanup beim Entladen

Beispiel: Ein Metrik-Plugin

Ein praktisches Beispiel, das Request-Metriken sammelt und an Prometheus exportiert:

// metrics_plugin.velin

struct MetricsPlugin {
    requestCount: number,
    requestDurations: List<number>,
}

impl Plugin for MetricsPlugin {
    fn on_load(ctx: PluginContext) {
        self.requestCount = 0;
        self.requestDurations = [];
        
        // Request-Hook für Metriken
        ctx.register_hook("on_request", |req| {
            let start = datetime.now();
            self.requestCount += 1;
            
            // Nach der Response die Dauer messen
            ctx.after_response(|| {
                let duration = datetime.now() - start;
                self.requestDurations.push(duration);
            });
        });
        
        // Prometheus-Endpunkt registrieren
        ctx.register_route("GET", "/metrics", || {
            let avgDuration = self.requestDurations.avg();
            
            return `
# HELP http_requests_total Total HTTP requests
# TYPE http_requests_total counter
http_requests_total ${self.requestCount}

# HELP http_request_duration_avg Average request duration
# TYPE http_request_duration_avg gauge
http_request_duration_avg ${avgDuration}
            `;
        });
        
        log.info("Metrics Plugin aktiviert auf /metrics");
    }
    
    fn on_unload() {
        log.info("Metrics Plugin deaktiviert");
    }
}

pub fn create(config: Map<string, any>): Plugin {
    return MetricsPlugin {};
}

Plugin laden und konfigurieren

Plugins werden in der velin.toml aktiviert:

[plugins]
# Aus der Registry
metrics = { version = "1.0.0" }

# Lokales Plugin
my-custom = { path = "./plugins/my_plugin.vplugin" }

# Mit Konfiguration
auth-provider = { 
    version = "2.1.0",
    config = { provider = "oauth2", clientId = "abc123" }
}

Plugin Context API

// Verfügbare Methoden im PluginContext

impl Plugin for MyPlugin {
    fn on_load(ctx: PluginContext) {
        // Hooks registrieren
        ctx.register_hook("on_request", |req| { ... });
        ctx.register_hook("on_error", |err| { ... });
        
        // Eigene CLI-Befehle
        ctx.register_command("my-cmd", || { ... });
        
        // HTTP-Routen hinzufügen
        ctx.register_route("GET", "/my-endpoint", || { ... });
        ctx.register_route("POST", "/my-endpoint", |body| { ... });
        
        // Konfiguration lesen
        let apiKey = ctx.config.get("apiKey");
        
        // Auf andere Services zugreifen
        let db = ctx.get_service("database");
        let cache = ctx.get_service("cache");
        
        // Events emittieren
        ctx.emit("my_plugin:initialized", { timestamp: datetime.now() });
        
        // Auf Events lauschen
        ctx.on("user:created", |user| {
            log.info("Neuer User: " + user.email);
        });
    }
}

Plugin bauen und veröffentlichen

# Plugin kompilieren
velin build --plugin my_plugin.velin

# Output: my_plugin.vplugin

# Lokales Testen
velin run --plugin ./my_plugin.vplugin

# In Registry veröffentlichen
velin publish --plugin

Best Practices

Cleanup in on_unload

Ressourcen freigeben, Connections schließen

Fehler abfangen

Plugin-Fehler dürfen Host nicht crashen

Konfiguration validieren

Fehlende Config früh erkennen

Versionierung

Semantic Versioning für Kompatibilität