# V2 Plattform-Bibliotheken Helfer-Libs für V2-Module, sparen Boilerplate. ESM, keine Build-Step, keine Dependencies. ## Verfügbar | Lib | Zweck | |---|---| | [telemetry-client.js](telemetry-client.js) | Telemetry, Profil, runtime-config, State, Result. Spec-Vollständig. | ## TelemetryClient — Quickstart ```js import { TelemetryClient } from '../../../v2-platform/lib/telemetry-client.js'; // 1. Instanz const tc = new TelemetryClient({ moduleSlug: 'hallo-welt', moduleVersion: '1.0.0', specVersion: '1.0', }); // 2. Init (holt runtime-config + profile, startet Heartbeat + Stuck-Detection) await tc.init(); // 3. Profil + Session sind jetzt verfügbar console.log(tc.profile); // { displayName, avatarUrl, easyLanguage, nachteilsausgleich, levelUnlocks, ... } console.log(tc.session); // { mode, difficulty, lang, classId, studentId, isMock, ... } // 4. Phase wechseln (Lehrperson-Dashboard sieht das live) tc.setPhase('welcome', 'Begrüßung'); // 5. Milestone emittieren tc.milestone('welcome-shown', 'Begrüßung gesehen', /* scoreDelta */ 1); // 6. Score aktualisieren (fließt in nächste Heartbeats) tc.updateScore(42, 100); // 7. Resume-State speichern (regelmäßig oder bei Milestones) await tc.saveState({ ...modulInternerState }, 'Tag 3, Level 2'); // 8. Modul abschließen (sendet Result-POST + cleanup) await tc.complete({ completed: true, lehrplanCoverage: { 'AT-GW-1.4.2': 'full' }, freeTextAnswers: [], moduleSpecificData: { /* modul-eigene Kennzahlen */ }, }); // 9. Bei manuellem Abbruch (z.B. ✕-Klick) tc.exit('user-cancelled'); ``` ## Was die Lib für dich erledigt - **URL-Param-Parsing** — `session_token`, `mode`, `difficulty`, `lang`, `class_id`, `student_id`, `mock=1` - **Runtime-Config** — Heartbeat-Frequenz wird auf Plattform-Grenzen geclampt (Default 10–30s) - **Heartbeat-Loop** — alle X Sekunden automatisch, pausiert bei AFK (>60s ohne Eingabe) - **Stuck-Detection** — bei >90s ohne Eingabe wird Stuck-Event gefeuert (Schwellwert aus runtime-config) - **Tab-Close-Beacon** — navigator.sendBeacon-Final-Heartbeat - **Fehlertoleranz** — Telemetry-Fetches die scheitern blockieren das Modul nicht (silent fail) - **Token-Expired-Handling** — bei 401 default Redirect zu `/v2beta/`, im iframe wird nur Warnung geloggt - **Mock-Erkennung** — wenn `?mock=1`, geht alles automatisch gegen die Mock-Plattform ## Was du selbst tun musst - **Activity-Tracking** für `click/touchstart/keydown` macht die Lib automatisch (an `document` registriert) - **A11y-Profil auswerten** — `tc.profile.preferences.highContrast` etc. → Modul setzt `body[data-high-contrast="…"]` - **Easy-Sprache laden** — `tc.session.lang` zeigt was geladen werden soll - **Level-Unlock-Sicht** — `tc.profile.levelUnlocks[moduleSlug]` für freien Modus - **Nachteilsausgleich** — `tc.profile.nachteilsausgleich === true` → Level-Wähler statt Auftrags-Vorgabe ## API-Referenz ### Konstruktor ```js new TelemetryClient({ moduleSlug: string, // PFLICHT moduleVersion: string, // PFLICHT specVersion: string, // PFLICHT heartbeatPreferenceS?: number, // default 15 onProfileLoaded?: (profile) => void, onError?: (err) => void, onTokenExpired?: (reason) => void, }) ``` ### Methoden | Methode | Zweck | |---|---| | `init()` | Async. Holt runtime-config + profile, startet Heartbeat + Stuck-Loop. | | `setPhase(phase, label?, step?)` | Phase wechseln; löst sofort einen Heartbeat aus. | | `updateScore(score, scoreMax?)` | Score aktualisieren. Wird in nächsten Heartbeats mitgesendet. | | `milestone(id, label, scoreDelta?)` | Milestone-Event posten (id muss im Manifest deklariert sein). | | `accessibilityBlocker({blockerType, hint})` | A11y-Blocker melden. | | `saveState(state, summary?)` | Resume-State speichern (PUT `/api/module/state`). | | `complete(result)` | Result-POST + Cleanup. Ruft destroy() automatisch. | | `exit(reason?)` | Abbruch melden + Cleanup. | | `destroy()` | Manueller Cleanup (Timer + Listener entfernen). | | `elapsedTotalS()`, `elapsedPhaseS()` | Hilfs-Getter für Sekunden seit Start / Phase-Start. | ### Properties (nach `await init()`) | Property | Inhalt | |---|---| | `tc.profile` | Schüler*innen-Profil (`/api/student/me`-Response inkl. Nachteilsausgleich + Level-Unlocks) | | `tc.config` | Runtime-Config-Werte (`heartbeat`, `stuck`, `telemetry`, `auth`) | | `tc.session` | URL-Param-Werte (`sessionToken`, `studentId`, `classId`, `mode`, `difficulty`, `lang`, `resume`, `isMock`) | ## Bekannte Limitierungen - **Token-Auto-Refresh** ist Stub. Echte Refresh-Logik kommt mit V2-Login. Aktuell: bei Token-Ablauf (401) → Redirect zu `/v2beta/`. - **`onTokenExpired` Callback** kann das überschreiben, falls Modul eigene Handhabung will (z.B. iframe-Eltern benachrichtigen). - **Mock-Erkennung** über `?mock=1`-URL-Param. Wenn dein Modul-Wrapper-Pfad nicht `v2-modules//public/` ist, musst du `apiBase` selbst überschreiben (nicht in der API vorgesehen — am besten Pfad anpassen).