Erweiterbare Registrar-Methoden
Core Features Konzept
Core Features werden vom Framework mitgeliefert, folgen aber der gleichen defineFeature() API wie App Features. Der einzige Unterschied: sie leben in packages/framework/src/features/ und andere Features koennen requires: ["jobs"] darauf setzen.
packages/framework/ src/ features/ ← Core Features (vom Framework) config/ ← Tenant/User Config, UI Sektionen files/ ← File Storage, Upload/Download jobs/ ← Job Queue, Worker, UI, Logs engine/ db/ ...
features/ ← App Features (vom User) admin-users/ user-profile/Features deklarieren Abhaengigkeiten:
export default defineFeature("adminUsers", (r) => { r.requires("jobs"); // Framework validiert beim Boot // ...});Das Problem
// Jemand will Custom Fields — muesste "customFields: true" in den EntityDefinition-Typ einfuegen// Jemand will Tags — muesste "taggable: true" einfuegen// Jemand will Audit — muesste "audited: true" einfuegen// → Framework-Core wird zum Bottleneck, Package-Nutzer koennen nicht erweiternLoesung: r.extendsRegistrar()
Features erweitern Schema, Pipeline und UI ueber 5 Ebenen:
r.extendsRegistrar("customFields", { // 1. Registrierung: wird aufgerufen wenn ein Feature r.customFields("property") nutzt onRegister: (entityName: string, options?: ExtensionOptions) => { ... },
// 2. Schema-Erweiterung: fuegt Spalten zur Entity hinzu extendSchema: (entityName: string) => ({ customFields: { type: "jsonb", default: {} }, }),
// 3. Pipeline-Hooks: CRUD-Verhalten erweitern hooks: { preSave: (changes, ctx) => { /* Custom Fields validieren */ }, postRead: (data, ctx) => { /* Custom Fields zum Response hinzufuegen */ }, },
// 4. Search-Erweiterung: zusaetzliche Felder indexieren extendSearch: (entityName: string, ctx) => { /* Searchable Custom Fields zum Meilisearch Index hinzufuegen */ },
// 5. UI-Erweiterung: Formular und Liste erweitern uiExtension: { editSection: "customFieldsSection", listColumns: "customFieldColumns", filters: "customFieldFilters", },});Die 5 Erweiterungs-Ebenen
| Ebene | Was | Beispiel Custom Fields | Beispiel Tags |
|---|---|---|---|
| onRegister | Registrierung | Entity in FieldDefinitions tracken | Entity in Pivot-Table tracken |
| extendSchema | DB-Spalten hinzufuegen | JSONB customFields Spalte | - (eigene Pivot-Table) |
| hooks | CRUD-Pipeline erweitern | Validierung + Read/Write | Response + Filter erweitern |
| extendSearch | Search-Index erweitern | Dynamische Felder indexieren | Tags-Array indexieren |
| uiExtension | UI-Komponenten einklinken | Formular-Sektion + Spalten | Chips + Picker + Filter |
Nicht jede Erweiterung braucht alle 5 Ebenen.
Nutzung
defineFeature("fleet", (r) => { r.requires("customFields", "tags", "config");
r.entity("property", { fields: { name: { type: "text" }, photos: { type: "images", maxSize: "5mb", accept: ["jpg", "png"], maxCount: 50 }, }, });
r.customFields("property"); // Vom Custom Fields Feature r.tags("property"); // Vom Tags-Feature r.config({ keys: { ... } }); // Vom Config-Feature});Eigene Erweiterungen
defineFeature("comments", (r) => { r.entity("comment", { fields: { ... } });
r.extendsRegistrar("commentable", { onRegister: (entityName) => { /* Track entity */ }, hooks: { postRead: (data, ctx) => { /* Comments Count mitlesen */ }, }, uiExtension: { editSection: "commentsSection", }, });});
defineFeature("tickets", (r) => { r.requires("comments"); r.entity("ticket", { fields: { ... } }); r.commentable("ticket");});Was im Framework-Core bleibt (nicht erweiterbar)
r.entity()— Feld-Typen inkl.file,image,files,images,encryptedr.writeHandler(),r.queryHandler(),r.crud()r.relation(),r.hook(),r.translations()r.requires(),r.screen(),r.nav()r.extendsRegistrar()— das Meta-Feature das Erweiterungen ermoeglicht
Was ueber Registrar-Erweiterungen kommt
r.config()— vom Config-Featurer.job(),r.scheduledJob()— vom Jobs-Featurer.customFields()— vom Custom Fields Featurer.tags()— vom Tags-Featurer.audited()— spaeter vom Audit-Featurer.importable()— spaeter vom Import-Feature