Documentation

Référence

SDK @capibara-dev/sdk

Mis à jour le 4 septembre 2026Plateforme v1 Markdown brut

Statut : disponible (v0.3.0, registre npm)

npm install -D @capibara-dev/sdk

Sans accès au registre npm, le tarball servi par le portail fait la même
chose (curl -O https://capibara.fr/dev-sdk/capibara-dev-sdk-0.3.0.tgz && npm install -D ./capibara-dev-sdk-0.3.0.tgz). Zéro dépendance, compatible
Node ≥ 18, navigateurs et workers (fetch + WebCrypto). Les starters de la
CLI l’installent d’office.

Dans la sandbox, @capibara-dev/sdk est fourni par la plateforme (même
API, résolu par le bundler) : le paquet npm sert au typage, aux tests
locaux
et au backend externe.


Généré depuis les schémas de la plateforme

Le cœur du paquet (generated.ts) est produit par la plateforme à partir
des mêmes schémas Zod qui valident vos manifestes, vos documents du Kit et
les appels de façades. Un test anti-dérive dans la CI de Capibara garantit
qu’il correspond exactement au validateur — vous n’aurez jamais un type qui
accepte ce que le portail refuse.

Export Contenu
KitNode, KitStackNode, KitTableNode, KitFieldNode… Un type par composant du Kit (38), plus KitRefNode, KitEachNode, KitFallbackNode.
KitStackProps, KitButtonProps… + KitUiBuilders Les propriétés de chaque constructeur ui.<composant> — l’autocomplétion connaît chaque prop, chaque énumération.
APP_SCOPES, AppScope, APP_SCOPE_DEFS Le catalogue des scopes feuilles acceptés dans permissions, avec leur phrase de consentement.
AppApi, APP_FACADES, APP_FACADE_NAMES, CrmContactsListInput… Les façades typées : ctx.capibara.crm.contacts.list() côté hébergé, client.install(id).crm.contacts.list() côté externe, avec leurs entrées et sorties.
APP_EVENTS, AppEventType Le catalogue fermé des événements (events du manifeste, webhooks).
ADDON_CAPABILITIES, ADDON_CATEGORIES, APP_SURFACES, PANEL_SUBJECTS, DECLARATIVE_WIDGET_TEMPLATES Les listes fermées du manifeste.

Le code hébergé : defineApp + ui

import { defineApp, ui, toast, type AppContext } from '@capibara-dev/sdk';

export default defineApp({
  pages: {
    home: async ({ ctx }) => ui.doc(ui.stack({ gap: 'md' }, [
      ui.pageHeader({ title: 'Bonjour ' + (ctx.viewer?.name ?? 'vous') }),
      ui.kpi({ label: 'Compteur', value: (await ctx.kv.get<number>('count')) ?? 0 }),
      ui.button({ label: '+1', action: 'count.add' }),
    ])),
  },
  actions: {
    'count.add': async ({ ctx }) => {
      const n = ((await ctx.kv.get<number>('count')) ?? 0) + 1;
      await ctx.kv.put('count', n);
      return { ...toast('Compteur : ' + n, 'success'), refresh: true };
    },
  },
});
  • ui.<composant>(props, children?) pour chacun des 38 composants, plus
    ui.doc(root, { title, data, components, state }), ui.ref(component, props) (instance d’un composant réutilisable), ui.each(path, as, gabarit)
    (répétition liée aux données) et ui.fallback(text) (texte de repli).
  • toast(), navigate(), json(), text() : les réponses prêtes à
    renvoyer depuis une action ou une route HTTP.
  • Types du contrat : AppDefinition, AppContext (kv, data, files,
    fetch, capibara, log, followup…), RenderArgs, ActionArgs,
    ActionResult, KitDocument. Tout est décrit dans Le runtime.
  • Surfaces du site public : siteBlocks et sitePages — mêmes
    handlers, viewer toujours null, le visiteur dans visitor
    (AppVisitor) ; le harnais les rend avec
    renderSurface(app, 'siteBlock', 'promo', t, { params, visitor }).

Tester en local : @capibara-dev/sdk/testing

Le harnais exécute vos handlers hors de la plateforme, avec un ctx
simulé en mémoire — sans réseau, sans compte :

import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createTestContext, renderSurface, runAction, findNodes, hasText } from '@capibara-dev/sdk/testing';
import app from '../src/index';

test('la page et le compteur', async () => {
  const t = createTestContext({
    viewer: { name: 'Léa' },                       // null = anonyme (surface publique)
    install: { config: { defaultCity: 'Lyon' } },  // vos réglages / config de widget
    fetch: { 'api.example.com': { json: { ok: true } } },  // un faux par host déclaré
    capibara: { 'crm.contacts.list': async () => ({ items: [], nextCursor: null }) },
    data: { favorites: [{ key: 'lyon', data: { city: 'Lyon' } }] },
  });
  const doc = await renderSurface(app, 'page', 'home', t);   // KitDocument normalisé
  assert.equal(hasText(doc, 'Bonjour Léa'), true);
  assert.equal(findNodes(doc, 'kpi').length, 1);

  const r = await runAction(app, 'count.add', t);            // { toast, refresh, ui… }
  assert.equal(r.refresh, true);
  assert.equal(t.kv.get('count'), 1);
  assert.deepEqual(t.logs, []);                              // ctx.log capturé
});
  • createTestContext({ tenant, viewer, visitor, install, subject, state, fetch, capibara, kv, data, now }) → { ctx, kv, collections, files, logs, followups, calls, mails, notifications, calendar, searchDocs, approvals, decideApproval, tick } — kv/collections/files sont des Map
    inspectables, calls le journal de chaque appel ctx.*, mails /
    notifications / calendar / searchDocs / approvals ce que les
    moteurs ont reçu, decideApproval(id, décision)
    fabrique l’événement approval.decided à rejouer avec runEvent. Un
    host non déclaré dans fetch ou une façade non simulée lève une erreur
    explicite — comme la plateforme refuserait.
  • renderSurface(app, surface, key, t, { params, subject, state }),
    runAction(app, action, t, { form, params, formName, state }),
    runEvent(app, { type, data }, t), runSchedule(app, key, t),
    runHttp(app, path, t, { method, query, headers, body }).
  • Assertions : walkKit, findNodes(doc, type), kitStrings,
    hasText, jsonResponse, textResponse.

Le harnais ne remplace pas la validation du Kit (bornes, a11y) : elle vit
dans Kit Studio, capibara kit check et, en dernier
ressort, dans le journal de votre installation de test.


Hors de la sandbox : client API, backend externe, webhooks

  • CapibaraClient — client typé de l’API publique :
    Bearer clé API (ck_live_…) ou jeton OAuth, me(), facades(),
    install(installId) → les façades typées de cette installation
    (X-Capibara-Install), erreurs CapibaraApiError (statut, corps,
    retryAfterSeconds sur 429).
  • createBackend({ app, secret, capibara?, maxSkewSec?, onLog? }) →
    { handle, fetch, dispatch } — votre serveur sert le MÊME defineApp
    (manifeste backend: { url }). Tout le détail (signature, protocole,
    ctx réduit, tests) est sur Backend externe.
  • verifyBackendSignature(rawBody, header, secret) si vous branchez
    vous-même.
  • createServerHandler({ secret, handlers, maxAgeSec? }) — un seul endpoint pour vos
    webhooks sortants : signature HMAC vérifiée sur le
    corps brut (401 sinon), enveloppe typée { id, event, occurredAt, tenantId, data }, dispatch par type d’événement, 500 si votre handler
    lève (Capibara réessaie). handle(rawBody, headers) pour Express et
    consorts, fetch(request) pour les runtimes à Request. maxAgeSec
    refuse (401 stale) une livraison dont l’en-tête x-capibara-timestamp
    est absent ou trop ancien.
  • verifyWebhookSignature(rawBody, header, secret) si vous préférez
    brancher vous-même.
  • Aides OAuth PKCE — generateCodeVerifier(), codeChallengeS256(),
    buildAuthorizeUrl(), exchangeAuthorizationCode(),
    clientCredentialsToken() : Sign in with Capibara.
import { createServerHandler } from '@capibara-dev/sdk';

const webhooks = createServerHandler({
  secret: process.env.CAPIBARA_WEBHOOK_SECRET!,
  handlers: {
    'invoice.paid': async ({ tenantId, data }) => { /* votre traitement, idempotent */ },
  },
});
// Cloudflare Workers / Deno / Bun : export default { fetch: (req) => webhooks.fetch(req) }
// Node/Express : const { status, body } = await webhooks.handle(rawBody, req.headers);

Types du manifeste

AddonManifest, AddonCapability, AddonPermission (= AppScope),
AddonPricing, AddonMenuEntry, AddonWidgetContribution
(AddonKitWidget | AddonDeclarativeWidget | AddonEmbedWidget),
AddonPanelContribution, AddonSiteBlockContribution,
AddonSitePageContribution, AddonSchedule, AddonCollectionDecl,
AddonSettingField — le miroir typé de add-on.json.
La validation autoritaire reste celle du portail et de capibara dev.


Construire sans le SDK reste possible

Le SDK est un confort, pas un prérequis :

  • L’éditeur du portail applique en direct le même JSON Schema que les
    types du SDK — vos manifestes sont validés à la même source de vérité.
  • Le Kit se décrit en JSON ; ui.* ne fait que le
    construire.
  • L’API publique s’appelle avec n’importe quel
    client HTTP (fetch, curl…), le flux OAuth PKCE est
    documenté avec des séquences complètes.

Cette documentation (les pages du hub + les endpoints llms.txt décrits
dans Utiliser cette doc avec votre IA) est conçue pour
suffire, avec l’aide d’un assistant IA si vous le souhaitez, à construire une
app complète.

SDK @capibara-dev/sdk · Capibara for Developers