Les intégrations sont des plugins. Les connecteurs intégrés REST, GraphQL et CMS sont écrits avec le même @malla/plugin-sdk que n'importe quel plugin : une connection (connexion : réglages, champs secrets, un test) et des operations (opérations : requêtes et mutations) validées avec Zod.
Définir un plugin
import { definePlugin, defineOperation } from '@malla/plugin-sdk'
import { z } from 'zod'
const config = z.object({
baseUrl: z.url().describe('API base URL'),
})
export default definePlugin({
id: 'weather',
name: 'Weather',
version: '1.0.0',
description: 'Current conditions by city.',
connection: {
config,
secrets: { apiKey: { label: 'API key' } },
secretBoundFields: ['baseUrl'], // changing it clears the stored key
hosts: (c) => [new URL(c.baseUrl).host], // the only hosts ctx.fetch may reach
test: async (ctx) => {
const res = await ctx.fetch(`${ctx.config.baseUrl}/ping`)
if (!res.ok) throw new Error('Weather API unreachable')
},
},
operations: {
'weather.current': defineOperation({
kind: 'query',
name: 'Current weather',
input: z.object({ city: z.string().min(1) }),
output: z.object({ tempC: z.number(), summary: z.string() }),
run: async (ctx, input) => {
const url = `${ctx.config.baseUrl}/current?city=${encodeURIComponent(input.city)}`
const res = await ctx.fetch(url, { headers: { 'x-api-key': ctx.secrets.apiKey ?? '' } })
return res.json()
},
}),
},
})
- Les ids d'opération sont de la forme
<plugin id>.<name>;definePluginle vérifie. - Le schéma
inputdevient le formulaire de l'éditeur (sous forme de JSON Schema) et valide les entrées résolues ;outputalimente l'autocomplétion. connection: nullsignifie qu'aucune ligne d'intégration n'est nécessaire, comme pour le CMS intégré.icondéfinit l'icône dans l'éditeur ;fetchTimeoutMsaugmente le délai d'expiration de fetch pour les services amont lents comme les API de modèles (120 s au maximum).
Le contexte du plugin
Politique des requêtes sortantes
ctx.fetch n'atteint que les hôtes que déclare la connexion, en HTTPS. Il résout les noms d'hôte (DNS over HTTPS) avant chaque requête et chaque redirection, et bloque les réponses privées, de loopback et link-local ; il suit au maximum 5 redirections sur le même hôte, expire au bout de 10 secondes et plafonne les réponses à 2 Mo. ALLOW_LOCAL_INTEGRATIONS=1 lève les vérifications d'adresse, uniquement pour le développement local.
Secrets
Les valeurs secrètes sont chiffrées au repos et jamais renvoyées par l'API ; l'éditeur sait seulement lesquelles sont définies. Modifier un réglage de secretBoundFields ou l'ensemble des hôtes efface les secrets stockés, sauf si de nouvelles valeurs arrivent dans la même mise à jour : un jeton ne peut donc jamais être redirigé en silence vers un autre hôte.
Le catalogue de plugins
Les plugins sont publiés avec un manifeste qui déclare leurs permissions, passent une revue, puis sont installés par site, figés sur une version. Les mises à jour se font sur demande.