MallaDocs

SDK de plugins

definePlugin: conexiones, secretos, operaciones y la política de fetch saliente.

Guía para desarrolladores

Las integraciones son plugins. Los conectores integrados de REST, GraphQL y CMS están escritos con el mismo @malla/plugin-sdk que usa cualquier plugin: una conexión (configuración, campos secretos, una prueba) y operaciones (consultas y mutaciones) validadas con Zod.

Definir un plugin

ts
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()
      },
    }),
  },
})
  • Los ids de las operaciones son <plugin id>.<name>; definePlugin lo comprueba.
  • El esquema input se convierte en el formulario del editor (como JSON Schema) y valida las entradas resueltas; output alimenta el autocompletado.
  • connection: null significa que no hace falta una fila de integración, como en el CMS integrado.
  • icon define el icono en el editor; fetchTimeoutMs amplía el tiempo de espera de fetch para servicios lentos, como las API de modelos (120 s como máximo).

El contexto del plugin

ctx.configLa configuración de la integración, parseada con tu esquema de conexión.
ctx.secretsLos secretos descifrados que tienen valor. Nunca se envían al navegador.
ctx.fetchFetch que aplica la política: hosts permitidos, HTTPS, sin direcciones privadas, tiempos de espera, tamaño máximo.
ctx.publishedTrue en el sitio en vivo (datos en vivo), false en el editor (datos de trabajo).
ctx.cmsSolo para plugins propios: lista y obtiene ítems del CMS.
ctx.postgresSolo para plugins propios: una conexión TLS por llamada, con las mismas comprobaciones de dirección que fetch.
ctx.signalSe aborta cuando quien llama cancela.

Política de salida

ctx.fetch solo llega a los hosts que declara la conexión, por HTTPS. Resuelve los nombres de host (DNS over HTTPS) antes de cada solicitud y cada redirección, y bloquea las respuestas privadas, de loopback y link-local; sigue como máximo 5 redirecciones en el mismo host, se agota a los 10 segundos y limita las respuestas a 2 MB. ALLOW_LOCAL_INTEGRATIONS=1 quita las comprobaciones de dirección, solo para desarrollo local.

Secretos

Los valores secretos se guardan cifrados y la API nunca los devuelve; el editor solo sabe cuáles están definidos. Cambiar una configuración de secretBoundFields o el conjunto de hosts borra los secretos guardados, salvo que lleguen valores nuevos en la misma actualización, así que un token nunca puede redirigirse en silencio a otro host.

El catálogo de plugins

Los plugins se publican con un manifiesto que declara sus permisos, pasan por una revisión y se instalan por sitio con una versión fijada. Las actualizaciones son opcionales.