@mv-service/cbm-addon-sdk
v2.0.0
Published
Client SDK für CBM-Addon-Entwickler — Bridge-Protokoll zwischen iframe-App und CBM-Shell
Maintainers
Readme
@mv-service/cbm-addon-sdk
Client SDK fuer CBM-Addon-Entwickler. Kapselt das Bridge-Protokoll (postMessage) zwischen einer iframe-basierten Addon-App und der CBM-Shell.
Breaking Change in 2.0.0 (Issue #4471):
cbmOriginist jetzt eine Pflicht-Option. Siehe Migration von 1.x auf 2.0.
Installation
Das Paket wird öffentlich über die npm-Registry (npmjs.com) verteilt — keine Auth, kein Scope-Redirect nötig.
npm install @mv-service/cbm-addon-sdk
# oder
yarn add @mv-service/cbm-addon-sdk
# oder
pnpm add @mv-service/cbm-addon-sdkQuickstart — Vanilla JS
<!DOCTYPE html>
<html>
<body>
<div id="app">Verbinde mit CBM...</div>
<script type="module">
import { createCbmAddonClient } from '@mv-service/cbm-addon-sdk'
// cbmOrigin = Origin der CBM-Instanz, die dein Addon einbettet (Pflicht, keine Wildcard)
const client = createCbmAddonClient({ cbmOrigin: 'https://cbm.example.com' })
client.onInit((ctx) => {
client.setTitle('Mein Addon')
document.getElementById('app').textContent =
`Hallo ${ctx.userDisplayName} (Tenant: ${ctx.tenantSlug})`
// API-Call mit dem bereitgestellten JWT
fetch(`${ctx.apiBaseUrl}/api/contracts`, {
headers: { Authorization: `Bearer ${ctx.jwt}` },
})
.then((r) => r.json())
.then((data) => console.log('Vertraege:', data))
})
client.onThemeChange(({ theme }) => {
document.documentElement.dataset.theme = theme // 'light' | 'dark'
})
</script>
</body>
</html>Quickstart — TypeScript
import { CbmAddonClient } from '@mv-service/cbm-addon-sdk'
const client = new CbmAddonClient({
cbmOrigin: 'https://cbm.example.com', // Pflicht
autoRefreshBufferSeconds: 120, // Token 2 min vor Ablauf erneuern
})
client
.onInit(({ tenantId, userId, jwt, apiBaseUrl }) => {
client.setTitle('Mein Addon')
client.showToast('Addon geladen', 'success')
// Im Framework-Lifecycle: client.destroy() aufrufen beim Unmount
})
.onThemeChange(({ theme, primaryColor }) => {
document.documentElement.classList.toggle('dark', theme === 'dark')
})
.onTokenRefresh(({ jwt }) => {
console.log('Neuer JWT:', jwt)
})
// Spaeter:
// client.navigate('/customers/123')
// client.destroy()API-Referenz
new CbmAddonClient(options?)
Erstellt die SDK-Instanz und sendet sofort CBM_READY an die CBM-Shell (Handshake-Start).
| Option | Typ | Default | Beschreibung |
|--------|-----|---------|--------------|
| cbmOrigin | string | Pflicht | Origin der einbettenden CBM-Shell, z.B. "https://cbm.example.com" (Schema + Host + ggf. Port, kein Pfad; wird per new URL().origin normalisiert). Ausgehende Nachrichten gehen nur an diese Origin, eingehende werden nur von ihr (und nur vom Eltern-Fenster) akzeptiert. Wildcard "*", leere Werte und Nicht-http(s)-Werte sind nicht erlaubt: der Konstruktor wirft einen TypeError, bevor ein Listener registriert oder CBM_READY gesendet wird. |
| version | string | "1.0" | Bridge-Protokollversion |
| autoRefreshBufferSeconds | number | 120 | Sekunden vor JWT-Ablauf, bei denen automatisch ein Refresh angefordert wird. 0 deaktiviert Auto-Refresh. |
createCbmAddonClient(options) — Factory-Funktion
Identisch zu new CbmAddonClient(options) (options.cbmOrigin ist ebenfalls Pflicht). Bequemer fuer Vanilla-JS-Addons ohne Klassen-Syntax.
Callbacks (chainable)
client.onInit(callback): this
Wird aufgerufen, sobald CBM_INIT von der Shell empfangen wird. Enthaelt den vollen Kontext-Payload.
Falls der Handshake bereits abgeschlossen ist (Late-Registration), wird der Callback sofort synchron aufgerufen.
interface CbmInitPayload {
tenantId: string // UUID des aktiven Tenants
tenantSlug: string // URL-Slug, z.B. "mv-service"
userId: string // UUID des eingeloggten Users
userEmail: string
userDisplayName: string
jwt: string // Kurzlebiger API-Token (15 min, addon-scoped)
jwtExpiresAt: string // ISO-8601-Ablaufzeit
apiBaseUrl: string // z.B. "https://cbm.example.com/api"
addonId: string // UUID der Addon-App
locale: string // Aktives Locale, z.B. "de"
}client.onTokenRefresh(callback): this
Wird aufgerufen, wenn die Shell CBM_TOKEN_REFRESHED sendet (nach einem Refresh-Request).
client.onTokenRefresh(({ jwt, expiresAt }) => {
myHttpClient.setAuthToken(jwt)
})client.onThemeChange(callback): this
Wird aufgerufen, wenn der User das Theme in der CBM-Shell wechselt.
client.onThemeChange(({ theme, primaryColor }) => {
// theme: 'light' | 'dark'
// primaryColor?: string (CSS-Farbwert, optional)
})Getter
| Getter | Typ | Beschreibung |
|--------|-----|--------------|
| client.isReady | boolean | true nach erstem CBM_INIT |
| client.jwt | string \| null | Aktueller JWT (null vor CBM_INIT) |
| client.apiBaseUrl | string \| null | API-Basis-URL (null vor CBM_INIT) |
| client.getInitPayload() | CbmInitPayload \| null | Vollstaendiger Init-Payload |
Shell-Interaktion
client.requestTokenRefresh(): void
Fordert die Shell auf, den JWT zu erneuern. Wird normalerweise automatisch durch den Auto-Refresh-Timer aufgerufen.
client.navigate(path: string): void
Fordert die CBM-Shell auf, zu einem anderen Pfad zu navigieren.
client.navigate('/customers/123')client.showToast(message, variant?): void
Zeigt eine Toast-Benachrichtigung in der CBM-Shell an.
client.showToast('Gespeichert!', 'success') // 'success' | 'error' | 'info'client.setTitle(title: string): void
Setzt den Seitentitel in der CBM-Shell-Kopfzeile.
client.setTitle('Asset-Verwaltung')client.setLoading(loading: boolean): void
Zeigt oder versteckt einen Loading-Overlay in der CBM-Shell.
client.setLoading(true)
await doHeavyWork()
client.setLoading(false)client.destroy(): void
Rauemt Event-Listener und Auto-Refresh-Timer auf. Muss im SPA-Teardown/onUnmount aufgerufen werden.
// React-Beispiel
useEffect(() => {
const client = new CbmAddonClient()
client.onInit(setup)
return () => client.destroy()
}, [])Sicherheitshinweise
- Addons laufen in einem gesandboxten
<iframe>(Sandboxattribute:allow-scripts allow-same-origin allow-forms allow-popups) - Der JWT ist auf die deklarierten
RequiredScopesdes Addons eingeschraenkt (15 min Laufzeit) - Nur registrierte
IframeAllowedOriginsempfangen postMessages von der Shell - Das SDK sendet
postMessageausschliesslich an die konfiguriertecbmOrigin(nie'*') und verwirft eingehende Nachrichten mit andererevent.originoder einer anderen Quelle als dem Eltern-Fenster - Die CBM-Shell prueft in Gegenrichtung
event.origingegen die erlaubten Origins des Addons undevent.sourcegegen das eigene iframe-Fenster und sendet an die Origin der Addon-URL - Die Origin muss zur URL passen, unter der CBM im Browser laeuft (z.B. Prod vs. Staging). Add-ons, die in mehreren Umgebungen laufen, muessen die Origin je Umgebung konfigurieren (z.B. per Build-Variable)
Migration von 1.x auf 2.0
Version 2.0.0 ist ein Major-Release, weil bestehende Aufrufe ohne cbmOrigin nicht mehr funktionieren: der Konstruktor wirft bewusst einen TypeError, statt weiter an '*' zu senden. Eine reine Warnung haette das Sicherheitsproblem (JWT-tragende Nachrichten an beliebige Einbetter, Annahme von Nachrichten beliebiger Absender) fuer ungepflegte Add-ons offen gelassen.
-const client = new CbmAddonClient()
+const client = new CbmAddonClient({ cbmOrigin: 'https://cbm.example.com' })Add-ons, die 1.x im Lockfile behalten, laufen unveraendert weiter (die Shell prueft ihre Origin bereits seit Welle 16), bleiben aber ohne den Schutz auf Add-on-Seite.
Build (fuer Paket-Veroeffentlichung)
npm run build # Erzeugt dist/index.js (ESM), dist/index.cjs (CJS), dist/index.d.ts
npm run dev # Watch-Modus
npm run typecheck