feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.
New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering
New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# Dokumentation
|
||||
|
||||
Letzte Aktualisierung: 2026-03-09
|
||||
Letzte Aktualisierung: 2026-03-18
|
||||
|
||||
Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert: Tutorials zum Lernen, How-to Guides zum Nachschlagen von Aufgaben, Explanation zum Verstehen und Reference zum Nachschlagen von Fakten.
|
||||
|
||||
@@ -96,7 +96,9 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
|
||||
- `/docs/reference-codex-prompts.md`
|
||||
- Standard-Prompts fuer Skill-basierte Planung und Implementierung.
|
||||
- `/docs/reference-extension-readiness.md`
|
||||
- Verbindliche Vorbedingungen fuer spaetere Module ohne Hook-/Lifecycle-Festlegung.
|
||||
- Freigabe-Checkliste fuer weitere Module auf Basis des bestehenden Modul-Contracts.
|
||||
- `/docs/reference-module-manifest-contract.md`
|
||||
- Verbindlicher Runtime- und Manifest-Contract fuer Module (V1.1).
|
||||
- `/docs/reference-agents-overview.md`
|
||||
- Einstieg in das Rollenmodell und die Artefakte.
|
||||
- `/docs/reference-agents-roles.md`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Entwickler-Checkliste
|
||||
|
||||
Letzte Aktualisierung: 2026-03-09
|
||||
Letzte Aktualisierung: 2026-03-18
|
||||
|
||||
## Ziel
|
||||
|
||||
@@ -41,6 +41,12 @@ Kurze Definition of Done vor Merge.
|
||||
- [ ] Keine inline `<script type="module">`-Blöcke in `pages/**`/`templates/**`; Seitenlogik liegt in `web/js/pages/*`
|
||||
- [ ] Für Seitenmodule ist Config über `<script type="application/json" id="page-config-...">` bereitgestellt
|
||||
- [ ] JS-`src` in `pages/**`/`templates/**` nur über `assetVersion('...js...')`
|
||||
- [ ] Runtime-Komponenten folgen Contract `init(root, config) -> { destroy() }`
|
||||
- [ ] Runtime-Komponenten enthalten keinen eigenen `DOMContentLoaded`-/Sideeffect-Auto-Init
|
||||
- [ ] Listener/Timer/Observer aus Runtime-Komponenten werden in `destroy()` sauber abgebaut
|
||||
- [ ] Runtime-Konfiguration liegt in `page-config-app-components` (default) bzw. `page-config-login-components` (login) und wird ueber `readPageConfig(...)` gelesen
|
||||
- [ ] Host-gebundene Runtime-Komponenten haben explizite `data-app-component`-Hosts im Markup
|
||||
- [ ] Global-Utilities ohne natuerliches Host-Element sind explizit runtime-global registriert (`scope: 'global'`)
|
||||
- [ ] Service-Auflösung in `pages/**`/`templates/**`/`lib/Support/**` nur via `app(Foo::class)`
|
||||
- [ ] Keine `new ...Factory()` in `lib/Service/**` außerhalb `*Factory.php`
|
||||
- [ ] Keine direkten `new ...Service()`, `new ...Gateway()`, `new ...Repository()` in `lib/Service/**` außerhalb `*Factory.php`
|
||||
@@ -62,9 +68,13 @@ Kurze Definition of Done vor Merge.
|
||||
|
||||
## Qualitätschecks
|
||||
|
||||
- [ ] `docker compose exec php vendor/bin/phpunit`
|
||||
- [ ] `docker compose exec php vendor/bin/phpunit` zweimal hintereinander in identischer Umgebung (beide Runs gruen)
|
||||
- [ ] `docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress`
|
||||
- [ ] `docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php`
|
||||
- [ ] bei Modul-/Manifest-/Routen-Aenderungen: `APP_ENABLED_MODULES='' php bin/module-runtime-sync.php`
|
||||
- [ ] bei Modul-/Manifest-/Routen-Aenderungen: `APP_ENABLED_MODULES='addressbook,bookmarks' php bin/module-runtime-sync.php`
|
||||
- [ ] Runtime-Sync erzeugt Step-Summary (`migrate`, `permissions-sync`, `build`, `assets-sync`) und `vendor_warnings_ignored=<n>`
|
||||
- [ ] Keine First-party CLI-Warnings/Notices/Deprecations im Runtime-Sync (Vendor-Warnings sind nur temporaer toleriert und muessen gezaehlt sichtbar sein)
|
||||
- [ ] bei Docs-/Skills-Aenderungen: `docker compose exec php vendor/bin/phpunit tests/Architecture/CodexSkillsContractTest.php`
|
||||
- [ ] bei Docs-/Skills-Aenderungen: `bin/docs-link-check.sh` (scannt standardmaessig ohne `agent-system/runs/**`)
|
||||
- [ ] bei Docs-/Skills-Aenderungen: `bin/codex-skills-sync.sh --check`
|
||||
@@ -82,6 +92,7 @@ Kurze Definition of Done vor Merge.
|
||||
- [ ] betroffene Edit-Seite + Save
|
||||
- [ ] relevante Tenant-Sicht
|
||||
- [ ] ggf. Global Search / API-Flow
|
||||
- [ ] bei optionalen Modulen: Smoke-Test `APP_ENABLED_MODULES` on/off (keine toten Links, keine Notices/Warnungen)
|
||||
|
||||
## Abschluss
|
||||
|
||||
|
||||
@@ -1,41 +1,57 @@
|
||||
# Extension-Readiness (ohne Architektur-Festlegung)
|
||||
# Extension-Readiness (Module-System V1.1)
|
||||
|
||||
Letzte Aktualisierung: 2026-03-09
|
||||
Letzte Aktualisierung: 2026-03-18
|
||||
|
||||
## Ziel
|
||||
|
||||
Verbindliche Vorbedingungen fuer spaetere eigenstaendige Module festhalten, ohne jetzt eine konkrete Extension-Mechanik (Hooks/Lifecycle/Loader) zu standardisieren.
|
||||
Verbindliche Vorbedingungen fuer weitere eigenstaendige Module auf dem bestehenden Modul-System festhalten.
|
||||
|
||||
## Readiness-Checkliste vor neuem Modul
|
||||
## Kanonische Quelle
|
||||
|
||||
Normativer Manifest- und Runtime-Contract: `/docs/reference-module-manifest-contract.md`.
|
||||
|
||||
Diese Readiness-Doku ist bewusst kurz und verweist fuer Felddefinitionen/Guards immer auf den Manifest-Contract.
|
||||
|
||||
## Readiness-Checkliste vor Modul 2+
|
||||
|
||||
1. **Modulgrenzen klarziehen**
|
||||
- Betroffene Schichten benoennen (`pages -> Service -> Repository`).
|
||||
- Keine SQL-/Business-Logik in Templates oder `pages/*.phtml`.
|
||||
2. **Contracts definieren**
|
||||
- Oeffentliche API-/UI-Contracts dokumentieren (Inputs, Outputs, Fehlerfaelle).
|
||||
- Bei API-Aenderungen `docs/openapi.yaml` im selben Merge pflegen.
|
||||
- Keine Modul-spezifischen Hardcodings im Core-Layout/Core-Routing.
|
||||
2. **Manifest-Contract einhalten**
|
||||
- `module.php` validiert gegen den kanonischen Contract (`permissions`, `scheduler_jobs`, `ui_slots`, `routes`).
|
||||
- Route-/Slot-/Layout-Guards laufen fail-fast.
|
||||
3. **Security + Scope absichern**
|
||||
- AuthZ/RBAC serverseitig pruefen.
|
||||
- Tenant-Scope serverseitig pruefen.
|
||||
- POST-Mutationen mit CSRF absichern.
|
||||
4. **Testbarkeit und Gates**
|
||||
4. **Runtime-Hygiene absichern**
|
||||
- `php bin/module-runtime-sync.php` laeuft reproduzierbar mit standardisierter Step-Summary.
|
||||
- Keine First-party CLI-Warnings/Notices/Deprecations; Vendor-Rauschen wird nur gezaehlt/reportet.
|
||||
5. **Testbarkeit und Gates**
|
||||
- Architektur- und Domain-Tests fuer neue Logik ergaenzen.
|
||||
- Relevante Quality Gates (`QG-*`) im Merge reporten.
|
||||
5. **Daten- und Betriebsfaehigkeit**
|
||||
- `phpunit` und `phpstan` als Pflicht-Gates gruen.
|
||||
6. **Daten- und Betriebsfaehigkeit**
|
||||
- Schema-/Datenaenderungen fuer Bestandsinstallationen idempotent in `db/updates/*.sql`.
|
||||
- Logs/Audit ohne unredacted PII/Secrets.
|
||||
|
||||
## Bewusst Out of Scope in diesem Stand
|
||||
## Derzeit Out of Scope
|
||||
|
||||
- Kein standardisierter Extension-Loader.
|
||||
- Kein Hook-/Plugin-Lifecycle.
|
||||
- Keine verbindlichen Modul-Ablageorte ausserhalb bestehender Schichten.
|
||||
- Eigener DB-Server oder Netzwerk-Microservice je Modul.
|
||||
- Externe Drittanbieter-Module mit Legacy-Kompatibilitaet.
|
||||
|
||||
## Entscheidungslog fuer spaetere Standardisierung
|
||||
## Verfuegbare Core-Services fuer Module
|
||||
|
||||
Wenn die Extension-Mechanik spaeter konkretisiert wird, muss die Entscheidung mindestens diese Punkte mitliefern:
|
||||
JS-seitig:
|
||||
|
||||
1. Aktivierung/Deaktivierung und Reihenfolge von Modulen.
|
||||
2. Versionierung und Migrationsstrategie pro Modul.
|
||||
3. Sicherheits- und Tenant-Isolation pro Erweiterung.
|
||||
4. Test- und Rollback-Strategie fuer Modul-Deployments.
|
||||
- Confirm-Dialog (`confirmDialog`)
|
||||
- Async-Flash (`showAsyncFlash`)
|
||||
- DOM-Utilities (`app-dom.js`)
|
||||
- CSRF-Metadaten über `data-csrf-key` / `data-csrf-token`
|
||||
|
||||
PHP-seitig:
|
||||
|
||||
- Guard (`MintyPHP\\Support\\Guard`)
|
||||
- SessionStore (`MintyPHP\\Http\\SessionStoreInterface`)
|
||||
- Flash (`MintyPHP\\Support\\Flash`)
|
||||
- DI-Container (`MintyPHP\\App\\AppContainer`)
|
||||
- Modul-Contracts (`LayoutContextProvider`, `SessionProvider`, `SearchResourceProvider`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Frontend JavaScript
|
||||
|
||||
Letzte Aktualisierung: 2026-03-06
|
||||
Letzte Aktualisierung: 2026-03-18
|
||||
|
||||
## Ordnerstruktur
|
||||
|
||||
@@ -18,7 +18,7 @@ Letzte Aktualisierung: 2026-03-06
|
||||
- setzt früh UI-States (no-js/js, Sidebar-/Contrast-LocalStorage)
|
||||
- `/web/js/app-init.js`
|
||||
- Modul-Entrypoint für Standardseiten
|
||||
- importiert Komponenten zentral
|
||||
- initialisiert Runtime + globale Komponenten zentral
|
||||
- `/web/js/app-login-init.js`
|
||||
- reduzierter Entrypoint für Loginseiten
|
||||
|
||||
@@ -30,6 +30,34 @@ Empfohlenes Muster:
|
||||
2. Früher Guard auf fehlende Elemente
|
||||
3. Keine stillen Fehler verschlucken
|
||||
4. Nur DOM/UI-Verhalten, keine Fachlogik
|
||||
5. Lifecycle-Contract für Runtime-Komponenten: `init(root, config)` gibt API mit `destroy()` zurück
|
||||
6. Runtime-Komponenten dürfen keinen eigenen Auto-Init via `DOMContentLoaded`/Sideeffect enthalten
|
||||
|
||||
## Component Runtime (Restmigration)
|
||||
|
||||
- Zentrale Runtime: `web/js/core/app-component-runtime.js`.
|
||||
- Runtime-Page-Configs:
|
||||
- Standardseiten: `<script type="application/json" id="page-config-app-components">...</script>`
|
||||
- Loginseiten: `<script type="application/json" id="page-config-login-components">...</script>`
|
||||
- Runtime liest Config über `readPageConfig(...)` und mountet registrierte Komponenten pro Entrypoint:
|
||||
- `web/js/app-init.js` (default)
|
||||
- `web/js/app-login-init.js` (login)
|
||||
- Komponenteninterner Contract:
|
||||
- Init-Signatur: `initX(root, config)`
|
||||
- Rueckgabe: `{ destroy() }`
|
||||
- `destroy()` muss alle Listener/Timer/Observer abbauen.
|
||||
- Hybrid Host-Policy:
|
||||
- Host-gebundene UI-Komponenten nutzen explizite `data-app-component`-Hosts im Markup.
|
||||
- Tabs sind host-gebunden und laufen ausschliesslich ueber `data-app-component="tabs"`.
|
||||
- Globale Utilities ohne natuerliches Host-Element werden runtime-global (`scope: 'global'`) registriert, aber mit demselben Lifecycle-Contract.
|
||||
- Root-Strict gilt fuer host-gebundene Komponenten: keine `document.querySelector`-Fallback-Pfade.
|
||||
- Projektseitige Custom-Globals auf `window.*` sind verboten; Komponenten kommunizieren ueber Core-Channels.
|
||||
|
||||
## UI Storage Contract
|
||||
|
||||
- Persistente UI-States laufen fuer Runtime-Komponenten ueber `web/js/core/app-ui-storage.js`.
|
||||
- Storage-Keys sind namespaced (`namespace:version:scope:*`) statt ungeordnet verteilt.
|
||||
- Hard-Cut-Policy: keine Legacy-Key-Fallbacks und kein Legacy-Key-Sync in migrierten Runtime-Komponenten/Core-Modulen.
|
||||
|
||||
Template für neue Komponenten:
|
||||
|
||||
@@ -257,7 +285,7 @@ Für Create/Edit-Detailseiten gilt:
|
||||
|
||||
- Hauptformular über `data-standard-detail-form="1"` explizit opt-in markieren.
|
||||
- Initialisierung läuft zentral über `initStandardDetailPage(...)` aus `web/js/core/app-detail-page-factory.js`.
|
||||
- Auto-Init erfolgt über `web/js/components/app-standard-detail-page.js` (in `app-init.js` eingebunden).
|
||||
- Runtime-Init erfolgt über `web/js/app-init.js` (Komponente `standard-detail-page`).
|
||||
|
||||
Standardverhalten:
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Konventionen (kurz und verbindlich)
|
||||
|
||||
Letzte Aktualisierung: 2026-03-09
|
||||
Letzte Aktualisierung: 2026-03-17
|
||||
|
||||
## 1) Code
|
||||
|
||||
@@ -45,6 +45,8 @@ Letzte Aktualisierung: 2026-03-09
|
||||
- Templates nur für Rendering.
|
||||
- UI-Texte über `t('...')`.
|
||||
- Strukturregeln in `/docs/reference-frontend-css.md` und `/docs/reference-frontend-javascript.md`.
|
||||
- Runtime-Komponenten nur über Lifecycle-Contract (`init(root, config)` + `destroy()`), ohne komponenteneigenen Auto-Init.
|
||||
- Host-gebundene Runtime-Komponenten über `data-app-component` markieren; nur echte Global-Utilities ohne Host als runtime-global registrieren.
|
||||
|
||||
## 6) Mindestchecks vor Merge
|
||||
|
||||
|
||||
99
docs/reference-module-manifest-contract.md
Normal file
99
docs/reference-module-manifest-contract.md
Normal file
@@ -0,0 +1,99 @@
|
||||
# Module Manifest Contract (V1.1)
|
||||
|
||||
Letzte Aktualisierung: 2026-03-18
|
||||
|
||||
## Ziel
|
||||
|
||||
Verbindlicher Contract fuer `modules/*/module.php`, damit Module zur Runtime deterministisch geladen und validiert werden.
|
||||
|
||||
## Status
|
||||
|
||||
Dieses Dokument ist die kanonische (normative) Quelle fuer Manifest-, Runtime- und Guard-Regeln des Modul-Systems.
|
||||
`/docs/reference-extension-readiness.md` referenziert diesen Contract und dupliziert keine Felddefinitionen.
|
||||
|
||||
## Aktivierung (`APP_ENABLED_MODULES`)
|
||||
|
||||
- nicht gesetzt: `config/modules.php` gilt.
|
||||
- gesetzt und leer (`APP_ENABLED_MODULES=''`): keine Module.
|
||||
- gesetzt mit Liste (`APP_ENABLED_MODULES='a,b'`): exakt diese Module.
|
||||
|
||||
## `permissions` Contract
|
||||
|
||||
`permissions` ist eine Objektliste:
|
||||
|
||||
```php
|
||||
'permissions' => [
|
||||
[
|
||||
'key' => 'module.permission',
|
||||
'description' => 'Human readable text',
|
||||
'active' => true, // optional, default true
|
||||
'is_system' => true, // optional, default true
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
- `key` und `description` sind Pflichtfelder.
|
||||
- Runtime-Sync erfolgt ueber `php bin/module-permissions-sync.php`.
|
||||
- `module-runtime-sync` fuehrt diesen Sync automatisch aus.
|
||||
|
||||
## `scheduler_jobs` Contract
|
||||
|
||||
`scheduler_jobs` ist eine Objektliste mit vollem Default-Contract:
|
||||
|
||||
```php
|
||||
'scheduler_jobs' => [
|
||||
[
|
||||
'job_key' => 'module.job',
|
||||
'handler' => App\\Scheduler\\MyJobHandler::class,
|
||||
'label' => 'Job label',
|
||||
'description' => 'Job description',
|
||||
'default_enabled' => true,
|
||||
'default_timezone' => 'Europe/Berlin',
|
||||
'default_schedule_type' => 'daily', // hourly|daily|weekly
|
||||
'default_schedule_interval' => 1,
|
||||
'default_schedule_time' => '02:00', // required for daily/weekly
|
||||
'default_schedule_weekdays_csv' => null, // weekly: e.g. '1,3,5'
|
||||
'default_catchup_once' => true,
|
||||
'allowed_schedule_types' => ['daily', 'weekly'],
|
||||
],
|
||||
],
|
||||
```
|
||||
|
||||
- Manifest ist Source of Truth fuer Job-Metadaten und Defaults.
|
||||
- Handler werden zur Laufzeit container-resolvable fuer `execute(...)` geladen.
|
||||
|
||||
## `ui_slots` Contract (V1.2)
|
||||
|
||||
Neue generische Slot-Typen:
|
||||
|
||||
- `topbar.right_item`: `{ key, template, permission?, order }`
|
||||
- `layout.body_end_template`: `{ key, template, permission?, order }`
|
||||
- `layout.head_style`: `{ key, path, permission?, order }`
|
||||
- `runtime.component`: `{ key, script, export?, selector?, scope?, config_path?, default_config?, phase?, permission?, order }`
|
||||
|
||||
`runtime.component.phase` ist `early|default|late` (Default: `late`).
|
||||
|
||||
Abgrenzung CSS:
|
||||
|
||||
- `layout.head_style` = global auf allen Seiten.
|
||||
- `asset_groups` = seiten-/kontextbezogen ueber `style_groups`.
|
||||
|
||||
## Guard-Regeln
|
||||
|
||||
- Route-Kollisionen sind fail-fast:
|
||||
- Modul vs Modul auf `target` und `path`.
|
||||
- Modul vs Core auf `path` beim Router-Boot.
|
||||
- Layout-Provider duerfen reservierte Core-Keys nicht ueberschreiben (`moduleSlots`, `csrfKey`, `currentTenant`, ...).
|
||||
- Layout-Provider-Top-Level-Keys muessen namespaced sein (`<module_id>.<key>`).
|
||||
|
||||
## Standard-Runbook
|
||||
|
||||
`php bin/module-runtime-sync.php` fuehrt in fester Reihenfolge aus:
|
||||
|
||||
1. `module-migrate`
|
||||
2. `module-permissions-sync`
|
||||
3. `module-build`
|
||||
4. `module-assets-sync`
|
||||
|
||||
Der Output enthaelt eine deterministische Step-Summary (`migrate`, `permissions-sync`, `build`, `assets-sync`) inklusive `vendor_warnings_ignored=<n>`.
|
||||
First-party Warnings/Notices/Deprecations gelten als Fehler (fail-fast).
|
||||
Reference in New Issue
Block a user