Skip to content

Helper V3 dans les pages plugins ​

Cette fiche concerne le global KappappsHost dans le HTML viewers ou streamer/admin. Le SDK kapp tourne dans l’iframe de l’appli : ne le charge pas à la place du helper. Voir manifestes, scopes et service côté appli et protocole métier.

Charger le helper officiel ​

Une nouvelle app charge le helper V3 3.0.0 depuis l'origine Kappapps avec son URL absolue :

html
<script src="https://kappapps.app/assets/app-plugin/helper-v3.js"></script>

Les chemins racine /assets/app-plugin/helper.js et /assets/app-plugin/helper-v2.js sont dépréciés. Ils restent disponibles sur la Pull Zone Bunny uniquement comme fallback figé pour les versions historiques déjà publiées. Ne les utilise dans aucun nouveau plugin et ne publie jamais un futur helper sur Bunny : toute nouvelle version de helper reste exclusivement hébergée par Kappapps et se charge par son URL absolue.

Les fichiers relatifs au dossier du plugin (style.css, ./icons/x.svg, etc.) restent relatifs. Pour partager un asset avec l'overlay ou un autre plugin, déclare public-assets puis utilise la base signée du helper V3 :

js
KappappsHost.onReady(() => {
    const publicAssets = KappappsHost.publicAssetsUrl;
    if (!publicAssets) throw new Error('public-assets absent de infos.json');
    const imageUrl = `${publicAssets}maps/world.webp`;
});

publicAssetsUrl vaut null si aucun dossier n'est déclaré et se termine toujours par / sinon. Ne remonte pas hors du dossier plugin avec ../assets : les plugins publiés et les assets publics ont des périmètres CDN distincts. Ne construis jamais toi-même une URL /api/media/apps/{slug}/{version}/public_assets/... : ce bridge est déprécié et ne reste disponible que pour les versions historiques déjà publiées. KappappsHost.jwt reste également disponible avec le helper V3, quelle que soit l'origine réelle (Kappapps, localhost ou CDN) du document. Lis ces deux valeurs à partir du callback onReady, après le handshake avec la page hôte.

Initialisation et appels du helper V3 ​

Le global reste KappappsHost, version 3.0.0. API : emit(event, data?), emitWithReply(event, data?, options?), data(), onReady(callback), onSocketEvent(callback), send(type, payload?), request(pathname, method, data = {}, timeout = 30000), jwt et publicAssetsUrl. send, onReady et onSocketEvent restent synchrones ; emit, emitWithReply, data et request renvoient des Promises. onReady confirme uniquement l'initialisation du pont avec la page hôte : ni la présence de l'appli, ni sa disponibilité métier.

data() lit exclusivement les nouveaux stockages. L'URL dataV2 fournie par l'hôte est exposée comme KappappsHost.api.data. Aucun dataV2() public, aucun repli historique si cette URL manque. Le helper V2 est figé en 2.2.1 : son data() lit toujours V1. Les noms dataStorageV2, userDataStorageV2 et logsStorageV2 restent côté SDK app, où les anciennes API cohabitent.

emit() et data() attendent l'hôte pendant 5 s au maximum par appel. Un appel expiré est supprimé et ne partira pas si l'hôte arrive plus tard. Les émissions précoces gardent leur ordre, y compris devant celles lancées par onReady. Après initialisation, le délai HTTP est de 5 s par tentative d'émission et 30 s pour data() / request() par défaut. Un délai explicite positif peut être fourni à request ; 0, négatif ou non fini revient au défaut de 30 s.

Erreurs à traiter par code ​

Tout rejet de ces Promises est une véritable Error nommée KappappsHostError, avec code, operation (emit, emitWithReply, data ou request), message, status et details. Le statut vaut 0 sans réponse HTTP. Les détails ne contiennent ni page HTML d'erreur, ni JWT, ni payload. Ne pas analyser le texte du message.

CodeSituation / détails utiles
HOST_INIT_TIMEOUTHôte absent après 5 s ; timeoutMs.
HOST_API_UNAVAILABLEURL nécessaire absente ; pas de fallback V1.
INVALID_PAYLOADSérialisation JSON impossible ou HTTP 400.
PAYLOAD_TOO_LARGEDépassement local ou HTTP 413 ; pour emit, actualBytes / maxBytes.
QUEUE_FULLFile locale pleine ; capacity: 20.
RATE_LIMITEDHTTP 429 ; retryAfterMs, scope, limit, remaining, channelLimit / channelRemaining si fournis et lisibles.
REQUEST_TIMEOUTDélai HTTP expiré ; timeoutMs. Livraison inconnue.
REPLY_TIMEOUTDélai total de emitWithReply expiré sans réponse de l'appli ; timeoutMs. Résultat métier inconnu.
NETWORK_ERRORAucune réponse HTTP ; livraison inconnue.
RELAY_UNAVAILABLERéponse plugin_relay_unavailable.
INVALID_RESPONSESuccès sans JSON attendu (data attend un objet racine).
HTTP_ERRORAutre rejet HTTP ; lire status.

scope distingue les quotas viewer, streamer-actor, viewer-channel et streamer-channel. Les métadonnées de quota sont exposées au navigateur pour le marqueur X-Kappapps-Plugin-Helper: 3, envoyé par le V3. Ce marqueur ne change aucun droit, authentification, plafond ou corps de réponse historique.

js
async function sendAnswer(choice) {
    try {
        await KappappsHost.emit('answer', { choice });
    } catch (error) {
        switch (error.code) {
            case 'PAYLOAD_TOO_LARGE':
                console.warn('Réduire le message', error.details.actualBytes, error.details.maxBytes);
                break;
            case 'QUEUE_FULL':
            case 'RATE_LIMITED':
                console.warn('Ralentir les interactions', error.code, error.details.retryAfterMs);
                break;
            default:
                console.error('Envoi impossible', error.code, error.operation, error.status);
        }
        // Aucun retry immédiat : après timeout/réseau, vérifier l'état métier avant de renvoyer.
    }
}

KappappsHost.onReady(async () => {
    try {
        const initialData = await KappappsHost.data();
        // Streamer : {preferences, data, history} ; viewers : objet viewer avec appli_data.
        render(initialData); // fonction de l'interface de ton appli
    } catch (error) {
        console.error('Chargement impossible', error.code, error.operation, error.status);
    }
});

Les helpers V1/V2 restent figés ; leur page hôte signale leur usage par un avertissement en console. Migrer ensemble helper et stockages.

Quotas, file d'attente et erreurs ​

Ces plafonds s'appliquent aux émissions depuis une page plugin. emitWithReply ajoute une enveloppe de corrélation à la taille mesurée et son propre délai total ; voir confirmations. Le trajet inverse utilise le service SDK, avec ses propres erreurs et files.

  • Le corps JSON complet de emit(), enveloppe {event, data} incluse, est limité à 16 384 octets UTF-8 inclus. 16 385 est refusé localement avant mise en file, et le serveur conserve sa validation HTTP 413. Compter des caractères ne suffit pas (accents, emojis). Le plafond de 100 Kio du relais concerne une autre étape ; il ne relève pas cette limite. Mesurer les besoins avant toute hausse.
  • L'émission est sérialisée une seule fois, au moment de l'appel. Modifier l'objet ensuite ne change ni la taille mesurée, ni le corps envoyé, ni celui d'un retry. Une sérialisation impossible rejette immédiatement avec INVALID_PAYLOAD.
  • 20 événements en attente et 2 émissions actives, nouvelles tentatives comprises. Une émission en attente de retry garde sa place active. Avant l'initialisation, les émissions réservent leur place dans la file.
  • L'attente en file n'est pas le timeout HTTP : ce dernier démarre au départ de chaque requête. Il n'y a pas de délai total de file distinct ; le temps total peut dépasser 5 s selon les émissions précédentes et les pauses de quota.
  • Une émission refusée en 429 est retentée une seule fois, uniquement si Retry-After est valide (secondes entières ou date HTTP) et demande au plus 30 s. Pendant cette attente, la file est suspendue. Si le délai dépasse 30 s, le rejet contient le délai annoncé sans retry anticipé. Délai absent/invalide ou deuxième 429 : rejet.
  • Aucun retry automatique après timeout, erreur réseau ou 5xx. data() et request() ne retentent pas automatiquement, même un 429. La réussite HTTP d'un événement confirme sa prise en charge par le relais, pas l'exécution métier dans l'appli.
  • Toujours attendre la Promise et gérer son rejet. Ne boucle jamais immédiatement sur un échec ; garde l'interface utilisable et déduplique les actions métier si un renvoi manuel est nécessaire.

Réception et durée de la page ​

KappappsHost.onSocketEvent(callback) reçoit {event, data} pour les messages ordinaires de l’appli. Enregistre ce callback une fois par chargement de page, avant de demander l’état initial. Il n’existe pas de méthode publique offSocketEvent, ni de callback de reconnexion dans le helper V3. Un rechargement détruit les attentes et les variables de cette page ; il ne termine pas l’appli.

Les callbacks de onReady et onSocketEvent sont appelés sans attendre leur Promise. Intercepte les erreurs de toute opération asynchrone lancée dedans. onReady n’est pas un événement de reconnexion réseau et ne rejoue pas les messages manqués. Voir synchronisation et confirmations.

send(type, payload) envoie au parent un message du bridge local ; ce n’est pas un raccourci pour transmettre une action métier à l’appli. Utilise emit ou emitWithReply. request effectue une requête HTTP explicite ; il ne crée ni permission supplémentaire, ni confirmation métier.