Was ein Plugin ist
TrakState bringt kein festes Datenmodell mit, und es bringt auch keine feste Funktionsliste mit. Was das Produkt kann, entsteht zu einem großen Teil aus Plugins — die eigenen Pakete des Herstellers laufen über dieselbe Naht wie Ihre.
Ein Plugin ist ein signiertes ZIP-Archiv mit einer manifest.json in der Wurzel. Darin
stecken bis zu drei Dinge:
| Teil | Was es ist | Wann Sie ihn brauchen |
|---|---|---|
| Oberfläche | Ein Module-Federation-Remote, das die Schale zur Laufzeit lädt | Nur wenn Ihr Plugin eigene Seiten hat |
| Backend | Code auf dem Server — ein TypeScript-Modul oder ein eigener Prozess | Nur wenn serverseitig etwas passieren soll |
| Deklarationen | Contributions: Routen, Befehle, Einstellungen, Seitentypen, Konnektoren | Fast immer |
Ein Konnektor-Plugin ist reines Backend. Es liefert keine Oberfläche und kein Stylesheet: das Einstellungsformular entsteht aus dem, was der Konnektor-Typ deklariert. Wenn Sie für einen Konnektor Oberfläche schreiben, ist etwas schiefgelaufen.
Der Vertrag heißt @prodlog/sdk
Abschnitt betitelt „Der Vertrag heißt @prodlog/sdk“Ein Plugin wird gegen dieses Paket gebaut und gegen nichts anderes — nie gegen den Quelltext des Hosts. Was dort exportiert ist, ist das, was der Host weiter zu erfüllen verspricht. Was nicht exportiert ist, gehört nicht zum Vertrag, so praktisch es von außen aussieht.
import { parseManifest, type PluginManifest, type Plugin, type PluginHost } from '@prodlog/sdk';Das SDK trägt vier Gruppen von Dingen:
- Manifest — Schema, Typen und die Prüffunktionen
parseManifest/parseUploadedManifest. - Laufzeit —
Plugin,PluginHost,CapabilityDefinition,InvocationContext,CapabilityConflictError. - Oberfläche —
ShellContext,Surface,ScopedStore,PageInstanceund der Seitentyp-Vertrag. - Konnektoren und Konfiguration —
ConnectorType,connectorContribution, das Konfigurationsdokument eines Templates.
Was ein Plugin nie bekommt
Abschnitt betitelt „Was ein Plugin nie bekommt“Das ist keine Höflichkeit, sondern die tragende Grenze des Systems.
- Keine Datenbankverbindung. Kein Handle, keine Zugangsdaten. Jede Abfrage läuft über die vermittelte Host-Oberfläche und damit unter dem Mandantenkontext des Aufrufs (Row Level Security).
- Kein Zugriff auf fremde Plugin-Daten. Tabellen sind auf den Plugin-Namensraum festgenagelt, und der Host prüft das vor dem Laden.
- Kein Dateisystem außerhalb des eigenen Arbeitsverzeichnisses.
- Keine Umgebungsvariablen außer einer kurzen Erlaubnisliste (
TZ,LANG,LC_ALL,NODE_ENV).DATABASE_URLund alles Geheime sind strukturell nicht erreichbar. - Kein Mandantenwechsel. Auf dem Aufrufkontext gibt es überhaupt kein Feld, mit dem ein Plugin einen anderen Mandanten benennen könnte.
Wenn Ihnen etwas fehlt, ist das ein Gespräch über den Vertrag — kein Loch, um das man herumbaut.
Zwei Rechte-Achsen, und sie sind nicht dasselbe
Abschnitt betitelt „Zwei Rechte-Achsen, und sie sind nicht dasselbe“Diese Verwechslung ist der häufigste Fehler beim ersten Paket.
permissionssagt, was Ihr Code auf den Host-Schnittstellen erreichen darf:data:read,data:write,plugin:manage,capability:invoke,audit:read,net:request,net:socket. Ein Betreiber liest diese Liste auf dem Installationsdialog.rbacPermissionssagt, was ein Mensch in Ihrem eigenen Namensraum tun darf, zum Beispielacme.tcp:configure.
Beide Riegel müssen halten. Ein fehlendes data:write endet in permission_denied, auch
wenn die Rolle des Benutzers alles erlaubt — und umgekehrt.
Die Version ist ein Bereich, kein Wunsch
Abschnitt betitelt „Die Version ist ein Bereich, kein Wunsch“SDK-Version und Host-Version bewegen sich im Gleichschritt. Ihr Manifest nennt den Bereich, gegen den Sie gebaut haben:
"sdk": "^0.9.0"Ein Host stellt seine eigene Vertragsversion bereit plus jede ältere, die er noch
vollständig erfüllt. Der Host 0.9.0 stellt damit 0.9.0, 0.8.0, 0.7.0 und 0.6.0
bereit; ein Paket auf ^0.6.0 läuft dort unverändert weiter. Das ist eine gepflegte
Tabelle, keine Regel über 0.x-Versionen: 0.6.0 war selbst brechend. Lesen Sie, was sich
geändert hat, bevor Sie eine Minor-Version überspringen.
Passt der Bereich nicht, wird das Paket mit sdk_incompatible abgelehnt und lädt nie.
Der Weg von der leeren Mappe zum installierten Paket
Abschnitt betitelt „Der Weg von der leeren Mappe zum installierten Paket“- Manifest schreiben und mit
parseManifestgegen den eigenen Übersetzer prüfen. - Backend-Art wählen — in-process oder Sidecar.
- Bauen und packen — deterministisch, sonst wandert der Digest bei jedem Bau.
- Signieren — Ed25519 über die Archivbytes.
- Installieren — hochladen oder über den Katalog des Betreibers.
- Einschalten — Installieren ist nicht Einschalten. Erst beim Einschalten startet ein Backend.
Jeder dieser Schritte hat weiter unten in diesem Kapitel ein eigenes Thema.
Ein vollständiges Beispiel im Original
Abschnitt betitelt „Ein vollständiges Beispiel im Original“Im Produkt-Arbeitsbereich liegen vier echte Pakete, und sie decken zusammen jede Bauform ab:
| Paket | Wofür es das Muster ist |
|---|---|
plugins/tcp |
Sidecar-Konnektor: Python-Backend, gehaltener Socket, Konnektor-Typ als Daten, eigenes Pack-Skript |
plugins/tenant-user |
Elf Capabilities, zwei Rechte-Achsen, ein System-Plugin ohne eigene Tabellen |
plugins/board |
Seitentyp, in-process-Backend, eigene Tabellen aus der Manifest-Beschreibung |
Eine ehrliche Grenze von heute
Abschnitt betitelt „Eine ehrliche Grenze von heute“@prodlog/sdk liegt noch nicht in einer öffentlichen Registry. npm install @prodlog/sdk
funktioniert von außen nicht; heute wird ein Plugin im Arbeitsbereich des Produkts gebaut.
Diese Anleitung wandert mit, sobald sich das ändert.