Die technische Innensicht: wie das Projekt aufgebaut ist, welche Entscheidungen warum so
getroffen wurden und wo die Grenzen liegen. Für Betrieb und Bedienung siehe
README.md.
npm-Workspaces mit drei Paketen. Die Typen liegen bewusst dazwischen: Server und
Oberfläche teilen sich einen Vertrag, den der Übersetzer prüft — ändert sich ein Feld,
bricht die Übersetzung auf beiden Seiten.
| Paket | Rolle |
|---|---|
bibliothek-types |
Gemeinsame Typen. Kennt weder Server noch Browser |
bibliothek-server |
Express, SQLite, Bezugsquellen, Zugriffsgrenze |
bibliothek-client |
Oberfläche aus @dh-software/dh-components (Lit) |
TypeScript durchgehend im strict-Modus.
Der übernommene Prototyp war Flask. Der Umbau auf Node hatte zwei Gründe: Die Oberfläche
sollte auf dem hauseigenen Komponenten-Satz aufsetzen, und der Auslieferungsstand sollte
eine einzelne Programmdatei ohne installierte Laufzeitumgebung sein. Beides zieht Node
nach sich.
Ab Node 22 bringt die Laufzeitumgebung SQLite mit (node:sqlite). Damit entfällt eine
native Abhängigkeit, die für jede Node-Version neu übersetzt werden müsste — genau daran
war ein erster Versuch mit better-sqlite3 gescheitert. Journalmodus WAL, damit Lesen
während eines laufenden Abgleichs nicht blockiert.
Das Herzstück. Jede Art, an eine Datei zu kommen, ist eine Klasse hinter derselben
Schnittstelle:
interface Bezugsquelle {
gleicheAb(auftrag: Abgleichauftrag): Promise<QuellenAbgleich>;
}
| Typ | Vorgehen |
|---|---|
direkt |
HTTP-Abruf eines festen Links |
github |
Neuestes Release über die GitHub-Schnittstelle, passendes Asset wählen |
winget |
winget download, danach winget show für das Veröffentlichungsdatum |
svn |
svn export einer Datei |
git |
Repository klonen bzw. aktualisieren, bauen, Ergebnis übernehmen |
manuell |
Kein Abruf — die Datei wird einmalig hinterlegt und bleibt liegen |
Eine neue Quelle anzubinden heißt: eine Klasse schreiben und in der Übersicht eintragen.
Der Rest — Abgleich, Prüfung, Speicherung, Anzeige — bleibt unberührt.
Der teuerste Fehler wäre, eine funktionierende Datei gegen Müll zu tauschen, weil eine
Quelle stillschweigend etwas anderes liefert als erwartet. Deshalb schreibt keine Quelle
direkt in den Zielordner:
<zielordner>.neuinhaltBeanstanden() prüft: leer? HTML statt Programmdatei (Signatur undDer Eintrag meldet dann das Problem, behält aber seine bisherige Datei. Eine tote Quelle
kostet Aktualität, nie den Download.
Bei git beschreibt das Repository seinen eigenen Bau in software-bibliothek.json:
{
"buildCommand": "…",
"artifact": "dist/MeinWerkzeug.exe",
"secrets": [{ "name": "…", "description": "…" }]
}
Damit muss die Bibliothek nichts über einzelne Werkzeuge wissen — ein neues anzubinden
erfordert keine Änderung an ihr. Gebaut wird nur, wenn sich der Commit geändert hat.
Zeitgrenze 30 Minuten, weil Bauvorgänge mit vielen Abhängigkeiten lange dauern.
Die Kennung für den Zugriff kommt aus den Einstellungen (GIT_SSH_COMMAND), nicht aus der
URL. So bleibt der Katalogeintrag frei von Kontoangaben, und ein Kontowechsel ist eine
einzige Änderung statt einer pro Eintrag.
Werte liegen getrennt vom Katalog in secrets/<id>.json und werden beim Bau als
Umgebungsvariablen gesetzt — sie berühren die Platte der Arbeitskopie nie. Das Verfahren
im Ganzen beschreibt der Skill build-secrets im Repository dh-claude-rules.
Geschützt sind sie durch Dateiberechtigungen (icacls, beschränkt auf SYSTEM,
Administratoren und das Dienstkonto, bei jedem Start neu gesetzt), durch die
Zugriffsgrenze und dadurch, dass sie in keiner aus dem Netzwerk lesbaren Schnittstelle
auftauchen. Eine Verschlüsselung wäre unter Windows nur über die Datenschutz-Schnittstelle
des Betriebssystems sinnvoll; die ist aus der Laufzeitumgebung nicht ohne Zusatzpaket
erreichbar.
Kein Anmeldeverfahren, sondern eine Grenze an der Netzwerkschnittstelle: Eine Middleware
prüft, ob die Anfrage von localhost kommt. Verwaltende Routen tragen sie, lesende nicht.
Die Oberfläche fragt beim Start /api/umgebung und blendet aus, was ohnehin abgewiesen
würde. Das ist Bequemlichkeit, keine Absicherung — die liegt beim Server.
Das passt zum Auftrag („Vollzugriff nur am Server") und kommt ohne Benutzerverwaltung aus.
Der Preis: Wer am Server sitzt, darf alles. Rollen und Rechte sind im Ticket bewusst
zurückgestellt.
Aufgebaut aus @dh-software/dh-components, damit das Werkzeug aussieht wie der Rest des
Hauses. Zwei Besonderheiten:
Light DOM statt Shadow DOM. Die Komponenten werden über createRenderRoot() ins Light
DOM gerendert, sonst greifen die globalen ::part()-Regeln des Themes nicht.
Themewechsel zur Laufzeit. Zwei <style>-Elemente mit den Farbdefinitionen für hell
und dunkel, umgeschaltet über .disabled. Drei Modi: System, hell, dunkel. Nach dem
Vorbild des Meeting-Transkriptors, damit sich die Werkzeuge gleich anfühlen.
Der Katalog ist eine kompakte Liste statt Karten — er soll dreistellige Zahlen an
Einträgen aushalten. Reiter trennen lizenzfrei, lizenzpflichtig und intern; die
Mehrfachauswahl wirkt über Reitergrenzen hinweg und lädt als ZIP.
Zusatzinhalte (Sprachpakete, Erweiterungen) hängen über elternId an ihrem Werkzeug und
stehen eingerückt darunter, ohne eigene Trennlinie — Werkzeug und Zubehör lesen sich als
ein Block.
npm run build:exe in vier Schritten:
postject hineinschreibenWarum Einzelressourcen und nicht ein eingebettetes Bündel: Ein früher Versuch hat das
JavaScript-Bündel als Zeichenkette in die Server-Datei eingebettet. Die eingebettete
Schriftart enthält Bytefolgen, die als UTF-8 ungültig sind — sie wurden zu U+FFFD und das
Bündel damit unbrauchbar. Die Seite blieb weiß, ohne Fehlermeldung. Aufgefallen ist es
erst beim Vergleich der ausgelieferten Bytes mit der Datei auf der Platte. Übersea.getRawAsset() kommen die Ressourcen unverändert an.
Warum esbuild und nicht webpack: webpack scheitert an einer Abhängigkeit in der
Server-Kette, deren tsconfig.json per extends auf ein nicht aufgelöstes Paket zeigt.
esbuild kümmert das nicht.
Der ZIP-Erzeuger für Mehrfach-Downloads ist von Hand geschrieben (nur speichernd, ohne
Kompression — Installer sind ohnehin komprimiert). Das spart eine Abhängigkeit in einem
Bündel, das ohne Fremdcode auskommen soll.
Eine Tabelle software und eine Tabelle für das Zugriffsprotokoll. Neue Spalten kommen
über eine Liste hinzu, die beim Start mit dem tatsächlichen Schema abgeglichen wird —
fehlende werden ergänzt. Damit übersteht eine bestehende Installation ein Update ohne
Handarbeit.
Ordnernamen im Datenverzeichnis haben denselben Weg: Eine Umbenennungsliste beim Start
zieht ältere Installationen nach.
Zur Version: Angezeigt wird das Veröffentlichungsdatum des Anbieters, nicht der
Abrufzeitpunkt. Nur wenn eine Quelle keins liefert, wird ersatzweise der Abrufzeitpunkt
genannt und als solcher benannt — die Frage „ist das aktuell?" beantwortet das Datum des
Anbieters, unser Abrufzeitpunkt sagt darüber nichts.
showDirectoryPicker, crypto.subtle) stehen an der Netzwerkadresse nichtlocalhost vorhanden, an der IP nicht.