xPulse
🇩🇪 DE

@xpulse/app – Service Loader

Status: ACCEPTED · März 2026 Ersetzt: hartcodiertes bootstrap/services.js


Problem

bootstrap/services.js ist eine hartcodierte Liste von tryInit()-Aufrufen in fester Reihenfolge. Jedes neue @xpulse/*-Package erfordert eine manuelle Änderung in @xpulse/app. Das widerspricht dem Auto-Discovery-Prinzip, das der Rest des Frameworks befolgt.


Lösung: Auto-Discovery Service Loader

Der Service Loader liest die xpulse.json jedes installierten @xpulse/*-Packages, baut daraus einen Abhängigkeitsgraph, sortiert ihn topologisch und ruft das init() jedes Packages in der richtigen Reihenfolge auf.

Kein manuelles Registrieren. Ein Package installieren reicht.


Service-Metadata in `xpulse.json`

Jedes @xpulse/*-Package deklariert sich in seiner eigenen xpulse.json:

{
"name": "xpulse-router",
"type": "component",
"service": {
"xpulse-router": {
"load": true,
"load-after": ["xpulse-http"]
}
}
}

Regeln:


Abhängigkeitsgraph (Referenz)

xpulse-logger
xpulse-http
└── (keine)
xpulse-router
└── xpulse-http
xpulse-template
└── (keine)
xpulse-theme
└── xpulse-router, xpulse-config
xpulse-controller
└── xpulse-router, xpulse-template
xpulse-session
└── xpulse-router, xpulse-crypto
xpulse-debug
└── nicht Teil des Service Loaders — siehe unten
xpulse-doc
└── xpulse-router

App-eigene Overrides

Die xpulse.json der Applikation kann jeden Service-Eintrag überschreiben. Der Loader führt einen Deep-Merge durch: App-Config gewinnt über Package-Defaults.

Service deaktivieren:

{
"service": {
"xpulse-theme": { "load": false }
}
}

App-lokalen Service registrieren (aus src/services/):

{
"service": {
"my-auth": { "load": true, "load-after": ["xpulse-router", "xpulse-session"] }
}
}

App-lokale Services werden aus src/services/*/xpulse.json discovert und vor dem App-Override gemergt.


Merge-Reihenfolge

1. node_modules/@xpulse/*/xpulse.json ← Package-Defaults
2. src/services/*/xpulse.json ← App-lokale Services
3. xpulse.json { "service": { … } } ← App-Overrides (letzter gewinnt)

Da jedes Package nur seinen eigenen Namen als Key deklariert, gibt es in Schritt 1 keine Konflikte. Schritte 2 und 3 können alles aus Schritt 1 überschreiben.


Loader-Algorithmus

1. readdir(node_modules/@xpulse/*)
2. Pro Package: xpulse.json lesen → service-Section extrahieren
3. readdir(src/services/*) → gleiche Logik
4. App-eigene xpulse.json service-Section darüber mergen
5. Filtern: load !== false
6. Topologischer Sort nach load-after[]
7. Pro Service (in Reihenfolge):
- import('@xpulse/<name>') bzw. import('src/services/<name>')
- mod.default.init() aufrufen falls vorhanden
- app:service:ready / app:service:error / app:service:skipped emittieren

Eine zirkuläre Abhängigkeit wirft einen Fehler mit einer klaren Meldung, die den Zyklus nennt. Eine fehlende Abhängigkeit (in load-after gelistet, aber load: false oder nicht installiert) emittiert app:service:skipped und macht weiter — resilient by default.


CLI-Verhalten

Beim Start via npx xpulse <command> wird app.init() ohne app.start() aufgerufen. Der gesamte Service-Stack bootet — identisch zu einem normalen App-Start — aber der HTTP-Server beginnt nicht, auf einem Port zu lauschen.

Das ist das bestehende Verhalten und ändert sich mit dem neuen Loader nicht.


Events

Event Payload Wann
app:service:ready { service } Service erfolgreich initialisiert
app:service:skipped { service, reason } Nicht installiert oder load: false
app:service:error { service, error } Init hat geworfen, Loader macht weiter

@xpulse/debug — Sonderstellung

@xpulse/debug ist nicht Teil des Service Loaders. Es hat einen eigenen dedizierten Bootstrap-Schritt der vor dem Loader läuft, damit jeder nachfolgende Service beim Init bereits debuggbar ist.

Konfiguriert wie gehabt via xpulse.json:

{ "debug": { "enabled": true } }

Die service-Section von @xpulse/debug/xpulse.json hat keinen load-Eintrag. Der Loader ignoriert es vollständig.

Bootstrap-Ablauf in app.init():

1. config.load() ← immer
2. debug.bootstrapDebug() ← nur wenn debug.enabled — vor dem Loader
3. Service Loader ← alle anderen @xpulse/*-Packages

Implementierungshinweise

de/service-loader.md 2026-04-10