✨ Highlights
Diese Version ist ein Härtungsdurchlauf für das Plugin- und Template-Ökosystem. Die größte Änderung ist strukturell: Jedes offizielle @docmd/*-Paket führt jetzt einen docmd-Namensraum in seiner package.json, und ein Build-Zeit-Registry-Generator liest diese Namensräume, um die einzige Quelle der Wahrheit zu erzeugen, die der Laufzeit-Loader verwendet. Der Plugin-Auto-Installer ist jetzt robust gegenüber Paketen mit import-only exports-Feldern, und ein neuer docmd doctor-Vorabprüfungs-Befehl erkennt Konfigurations-Drift vor einem Build. Zwei bisher stille Fehler in veröffentlichten Tarballs sind ebenfalls behoben: Die 404-Seite rendert jetzt mit dem Summer-Template und übersetzten Zeichenketten, anstatt auf das Standard-Template mit rohen Übersetzungs-Schlüsseln zurückzufallen.
Keine öffentlichen API-Änderungen. Keine brechenden Konfigurations-Änderungen. Reine Härtung.
🔌 Build-Zeit-Plugin-Registry (einzige Quelle der Wahrheit)
Ein neues Workspace-Skript — scripts/build-plugin-registry.mjs — durchläuft packages/{plugins,templates,engines}/*, liest den package.json#docmd-Namensraum jedes Pakets und erzeugt einen generierten JSON-Katalog (packages/api/registry/plugins.generated.json). Verdrahtet als prebuild-Schritt von @docmd/api, sodass die Registry bei jedem Build neu generiert wird.
Der Laufzeit-Loader (packages/api/src/hooks.ts getPluginRegistry) liest nun aus der generierten Datei, mit zwei Auflösungs-Pfaden (veröffentlichtes Layout <pkg>/registry/... und Monorepo-Dev-Layout <repo>/packages/api/registry/...). Die handgepflegte packages/plugins/installer/registry/plugins.json, die im Installer lebte, wird zur Laufzeit nicht mehr konsultiert.
📦 docmd-Namensraum-Standardisierung
Jedes offizielle @docmd/*-Paket führt jetzt einen docmd-Namensraum in seiner package.json. Der Namensraum ist der Vertrag, den der Registry-Generator liest und den der Loader zur Ladezeit gegen den JS-Deskriptor kreuzprüft. Felder: key (benutzerorientierter Bezeichner), kind (plugin / template / engine), displayName, tagline, capabilities (für Plugins und Templates erforderlich, von der Build-Zeit-Prüfung verwendet) und preview (nur Template).
Engines erhalten dieselbe Form, aber keine capabilities — sie nehmen nicht am Hook-System teil. Der Auto-Installer prüft kind === 'engine' und weigert sich, sie zu installieren.
🩺 docmd doctor — Vorabprüfung
Ein neuer CLI-Unterbefehl für Diagnose. Keine Datei-Schreibvorgänge, keine Build-Nebenwirkungen — rein diagnostisch.
npx @docmd/core doctor [Optionen]
| Option | Beschreibung |
|---|---|
--config <Pfad> |
Pfad zu einer abweichenden docmd.config.json (oder .ts/.js/.mjs). |
--fix |
Fehlende offizielle Plugins oder Templates automatisch installieren. |
--json |
Den Bericht als maschinenlesbares JSON ausgeben. |
Standardmäßig druckt doctor eine menschenlesbare Zusammenfassung mit: installierter @docmd/core-Version, allen konfigurierten Plugins (mit Version und Status ✓ installiert / ⚠ fehlt), dem aktiven Template, den angeforderten Engines (js immer aktiv, rust optional) und einer Liste der Auto-Install-Kandidaten. Mit --fix ruft es den Paket-Manager des Projekts auf, um die Kandidaten zu installieren. Mit --json werden dieselben Daten als einzelnes JSON-Objekt ausgegeben — nützlich für Pre-Commit-Hooks und CI-Gates.
Auch verfügbar als pnpm doctor (was über das Workspace-docmd-Skript im Monorepo läuft).
🛠 Neue pnpm-Skripte im Monorepo
Die package.json des Monorepos erhält eine Reihe neuer pnpm-Skripte, die alle über das bestehende --cwd-Flag auf den Playground zeigen (gleiches Muster wie pnpm dev und pnpm live):
pnpm doctor # → docmd doctor
pnpm validate # → docmd validate
pnpm migrate # → docmd migrate
pnpm gen:deploy # → docmd deploy
pnpm mcp # → docmd mcp
pnpm plugin:add foo # → docmd add foo
pnpm plugin:remove foo # → docmd remove foo
pnpm build:playground # → docmd build (getrennt von `pnpm build`, das den monorepo-weiten Build ist)
🐛 Fehlerbehebungen
- Plugin-Auto-Install: robust gegen
import-onlyexports. Der Auto-Installer in@docmd/apiverwendete zuvorrequire.resolve, um installierte Pakete aufzulösen, wasERR_PACKAGE_PATH_NOT_EXPORTEDfür Pakete wirft, die nur eineimport-Bedingung inexportsausliefern. Der Retry-Pfad verwendet nun direktawait import(name), was dasexports-Feld nativ berücksichtigt. Eine Registry-Reprüfung als Defense-in-Depth wird innerhalb des Retry-Pfads durchgeführt — die Menge der Namen, die automatisch installiert werden können, ist unverändert, nur die Menge derexports-Bedingungen, die sie tragen dürfen, ist nun größer. - Plugin-Loader: Capability-Cache und Manifest-Drift-Prüfung. Ein Pro-Schlüssel-Capability-Set-Cache vermeidet erneutes Durchlaufen der Registry bei jedem Dev-Server-Rebuild. Eine neue Manifest-Drift-Prüfung warnt, wenn das
capabilities-Array des JS-Deskriptors nicht zumcapabilities-Array des Manifests passt — dies schließt den stillen Hook-Drop-Bug, bei dem ein Plugin, das einen Hook implementierte, aber vergaß, die entsprechende Capability im Deskriptor zu deklarieren, seinen Hook stillschweigend fallen ließ. - Bessere Fehlermeldungen „Could not load X after auto-install". Der Catch-Block des Post-Install-Retry zeigt nun
err.code(z. B.ERR_PACKAGE_PATH_NOT_EXPORTED,ERR_MODULE_NOT_FOUND) und die erste Zeile vonerr.message. DerautoInstallPlugin-Catch-Block zeigt auch das zugrunde liegende stderr des Paket-Managers und gibt einen Hinweis für die häufigsten Fälle aus. @docmd/ui:translations/in veröffentlichten Tarball aufnehmen. Der serverseitige Übersetzungs-Loader liest Übersetzungs-JSON-Dateien unter__dirname/../translations/. Daspackage.json#filesenthielt"translations"nicht, sodass npm den Tarball ohne die Übersetzungs-Dateien packte. Die 404-Seite war das auffälligste Symptom: träge durch den Static-File-Fallback der bereitgestellten Site gerendert, war der Übersetzungs-Cache leer und die Schlüssel leakten. Jetzt werden alle 7 Locale-Dateien ausgeliefert.@docmd/template-summer:templates/undassets/in veröffentlichten Tarball aufnehmen. Der Summer-Template-Laufzeit verwendetnew URL('../templates/...', import.meta.url)innerhalb vondist/index.js, was sich zu<package-root>/templates/...auflöst (nicht zu<package-root>/dist/templates/...). Daspackage.json#fileswar nur["dist"], sodass der veröffentlichte Tarball nurdist/enthielt, und der Resolver fiel auf das Standard-Template zurück. Jetzt entspricht der veröffentlichte Tarball dem Monorepo-Dev-Layout und das Summer-Template rendert korrekt.- Dev-Server: führenden Schrägstrich vor
safePath()entfernen. Der CWE-22-Fix aus 0.8.9 (Ersetzen vonfilePath.startsWith(rootAbs)durchsafePath(rootAbs, ...)) hatte eine Regression im Dev-Server: URL-Pfadnamen beginnen immer mit/, undpath.resolve('/abs/root', '/index.html')gibt/index.htmlzurück — was diesafePath-Grenzprüfung immer fehlschlagen ließ und für jede legitime Anfrage403 Forbiddenerzeugte. Fix: Entferne den führenden/aus dem URL-Pfadnamen, bevor er ansafePath()übergeben wird. - Live-Editor, Port-Probe und Docker-Docs: Standardmäßig Loopback. Der
server.listendes Live-Editors und dercheckPortInUse-Probe binden nun an127.0.0.1statt0.0.0.0. LAN-Zugriff ist opt-in überDOCMD_HOST=0.0.0.0oder--host 0.0.0.0, mit TUI-Warnung wenn aktiv. Diedocker/DOCKER.md-Beispiele verwenden nun den Loopback-Standard. - Utils:
scriptLiteralundjsonInjecthärten Inline-Script-Escaping. Beide escapen jetzt korrekt</script,<!--, U+2028 und U+2029 zusätzlich zur bestehenden JSON-sicheren Codierung. Die Härtung ist für nicht-konfligierende Zeichenketten still; Round-Trips durchJSON.parsefunktionieren weiterhin, da das Escape JSON-sichere Sequenzen verwendet.
Changelog
- Registry: Neues
scripts/build-plugin-registry.mjs— erzeugtpackages/api/registry/plugins.generated.jsonaus demdocmd-Namensraum jedes offiziellen Pakets. - Loader:
getPluginRegistryliest die generierte Datei mit zwei Auflösungs-Pfaden; die handgepflegteinstaller/registry/plugins.jsonwird zur Laufzeit nicht mehr konsultiert. - Namensraum:
docmd-Namensraum zu allen 14 offiziellen@docmd/*-Paketen (12 Plugins, 2 Engines) hinzugefügt und auf@docmd/template-summermitkey: "summer"aktualisiert. - Loader: Pro-Schlüssel-Capability-Set-Cache + Manifest-Drift-Prüfung (
checkManifestCapabilityDrift). - Auto-Install: Retry-Pfad verwendet
await import(name)anstelle vonrequire.resolve + import(file://path), mit Defense-in-Depth-Registry-Reprüfung. - Auto-Install-Fehler:
err.code+ erste Zeile vonerr.messagewerden angezeigt; stderr des Paket-Managers + Hinweis werden gedruckt. - CLI:
docmd doctor-Unterbefehl mit--config,--fix,--json-Flags hinzugefügt. Im CLI-Dispatcher registriert; Hilfetext aktualisiert. - CLI: Neue pnpm-Skripte im Monorepo —
doctor,validate,migrate,gen:deploy,mcp,plugin:add,plugin:remove,build:playground. - UI:
packages/ui/package.json#filesenthält jetzttranslations/. - Template:
packages/templates/summer/package.json#filesenthält jetzttemplates/undassets/. - Dev-Server:
safePath()inserveStaticentfernt den führenden/aus URL-Pfadnamen. - Live-Editor:
server.listenundcheckPortInUsebinden an127.0.0.1;DOCMD_HOST=0.0.0.0opt-in. - Docker-Docs: Drei
command: dev --host 0.0.0.0-Beispiele durch den Loopback-Standard ersetzt; “Network Issues”-Fehlerbehebung dokumentiert den Opt-in-Pfad. - Utils:
scriptLiteralundjsonInjectescapen</script,<!--, U+2028, U+2029. - Tests:
packages/utils/test/html-escape.test.jsaktualisiert, um das neue Escape-Verhalten zu prüfen. - Dokumentation: Neuer Abschnitt “ESM Exports — die
default-Bedingung” inbuilding-plugins.mdundbuilding-templates.md; neuer “Entfernung des gebündelten Registers in 0.9.0”-Callout; neuerdocmd doctor-Eintrag inreference/cli-commands.md.