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) etui.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 :
siteBlocksetsitePages— mêmes
handlers,viewertoujoursnull, le visiteur dansvisitor
(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/filessont desMap
inspectables,callsle journal de chaque appelctx.*,mails/
notifications/calendar/searchDocs/approvalsce que les
moteurs ont reçu,decideApproval(id, décision)
fabrique l’événementapproval.decidedà rejouer avecrunEvent. Un
host non déclaré dansfetchou 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 :
Bearerclé API (ck_live_…) ou jeton OAuth,me(),facades(),
install(installId)→ les façades typées de cette installation
(X-Capibara-Install), erreursCapibaraApiError(statut, corps,
retryAfterSecondssur 429).createBackend({ app, secret, capibara?, maxSkewSec?, onLog? })→
{ handle, fetch, dispatch }— votre serveur sert le MÊMEdefineApp
(manifestebackend: { url }). Tout le détail (signature, protocole,
ctxré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 (401sinon), enveloppe typée{ id, event, occurredAt, tenantId, data }, dispatch par type d’événement,500si 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êtex-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.