Arquitectura del Plugin
Cómo funcionan juntos el modelo iframe, el protocolo App Bridge y el sandbox de seguridad.
El Modelo Iframe
Cada plugin de Whatalo se ejecuta dentro de un <iframe> con sandbox en el admin. El src del iframe apunta a tu servidor — tu plugin se carga ahí, aislada del contexto JavaScript del admin.
La política del sandbox es:
sandbox="allow-scripts allow-forms allow-same-origin"allow-scripts— tu JavaScript se ejecuta normalmenteallow-forms— los envíos de formularios funcionanallow-same-origin— tu plugin puede acceder a sus propias cookies y localStorage (en tu dominio, no el del admin)
allow-same-origin es seguro aquí porque la Política del Mismo Origen bloquea el acceso entre orígenes al almacenamiento del admin. La excepción: Whatalo rechaza activamente los plugins cuyo appUrl comparte el mismo origen que el dashboard del admin — esa configuración omitiría el aislamiento del sandbox.
Los iframes de modales (abiertos mediante bridge.modal.open(...)) usan un sandbox más estricto — solo allow-scripts allow-forms, sin allow-same-origin.
El Protocolo App Bridge
La comunicación entre tu plugin y el admin usa la API window.postMessage del navegador. El paquete @whatalo/plugin-sdk/bridge maneja los detalles del protocolo para que trabajes con hooks de React limpios.
Hay cuatro tipos de mensajes:
| Tipo de mensaje | Dirección | Propósito |
|---|---|---|
whatalo:init | HOST → PLUGIN | Handshake — envía el origen del admin al plugin |
whatalo:action | PLUGIN → HOST | El plugin solicita una acción (toast, navegar, redimensionar, facturación...) |
whatalo:context | HOST → PLUGIN | El host envía datos de tienda/usuario/tema al plugin |
whatalo:ack | HOST → PLUGIN | El host confirma que una acción fue recibida y procesada |
Ciclo de Vida del Handshake
iframe carga
│
▼
HOST envía whatalo:init { origin: "https://admin.whatalo.com" }
│
▼ (el plugin almacena el origen del parent para todas las futuras llamadas postMessage)
PLUGIN envía whatalo:action { action: "ready" }
│
▼
HOST envía whatalo:context { storeId, storeName, user, theme, currentPage, ... }
│
▼
Plugin renderiza — useWhataloContext().isReady === trueEl handshake whatalo:init es crítico para la seguridad: sin él, el plugin tendría que usar postMessage("*", ...) que difunde a cualquier oyente. Después de recibir whatalo:init, el plugin solo apunta al origen del admin para todos los mensajes salientes.
Datos de Contexto
Una vez que el handshake se completa, tu plugin recibe un objeto WhataloContext:
interface WhataloContext {
storeId: string; // Identificador público de la tienda (NO el UUID interno)
storeName: string; // Nombre de la tienda
user: {
id: string;
name: string;
email: string;
role: "owner" | "admin" | "editor" | "staff" | "viewer";
};
appId: string; // ID de tu plugin del manifiesto
currentPage: string; // Ruta de la página activa (ej: "settings")
locale: string; // Locale de la tienda (ej: "es", "en")
theme: "light" | "dark"; // Esquema de colores actual del admin
initialHeight: number; // Altura de inicio sugerida en píxeles
orderId?: string; // Establecido cuando se abre desde contexto de pedido
productId?: string; // Establecido cuando se abre desde contexto de producto
}storeId es el identificador público — nunca el UUID interno de la base de datos.
Enrutamiento de Páginas
No hay router del lado del cliente. El admin le dice a tu plugin qué página mostrar pasando currentPage en el contexto. Tu plugin lee este valor y renderiza el componente correspondiente:
const context = useWhataloContext();
switch (context.currentPage) {
case "settings":
return <SettingsPage />;
case "dashboard":
default:
return <DashboardPage />;
}El valor de currentPage coincide con el campo path de la entrada de página en adminUI.pages en tu manifiesto.
Sincronización de Tema
El admin observa los cambios de modo oscuro/claro usando un MutationObserver en document.documentElement. Cuando el tema del admin cambia, envía un whatalo:context actualizado con el nuevo valor del tema. Tu plugin lo recibe, y el hook useWhataloContext() se vuelve a renderizar con el nuevo tema.
La plantilla incluye useThemeSync() que aplica el tema al document root del plugin automáticamente.
Acciones Disponibles
Las acciones son solicitudes que tu plugin envía al host del admin. Cada acción se confirma con un mensaje whatalo:ack que contiene { success: boolean, error?: string }. Las acciones tienen un timeout de 5 segundos si no se recibe confirmación.
| Acción | Qué hace |
|---|---|
toast | Muestra un banner de notificación en el admin (3 por 10s por plugin) |
navigate | Navega el admin a una ruta (5 por 10s) |
modal.open | Abre una superposición modal cargando una URL de tu dominio (3 por 10s) |
modal.close | Cierra el modal actualmente abierto |
resize | Actualiza la altura del iframe (30 por 10s) |
billing.* | Operaciones de facturación — planes, suscripciones, cambios (10 por 10s) |
Los límites de velocidad se aplican con una ventana deslizante por tipo de acción, sincronizada entre pestañas del navegador vía BroadcastChannel.
Modelo de Seguridad
Validación de Origen
El host valida el origen de cada mensaje entrante contra el appUrl registrado del plugin. Los mensajes de cualquier otro origen se descartan silenciosamente. Esto evita que otros iframes o scripts se hagan pasar por tu plugin.
Bloqueo del Mismo Origen
Si tu appUrl comparte el mismo origen que el admin (p.ej., ambos servidos desde el mismo dominio), el admin muestra un error y se niega a cargar el iframe. Esta es una regla de seguridad estricta — evita que el sandbox sea omitido.
Restricción de Navegación
La acción navigate solo acepta rutas que comienzan con /store/ o /admin/. Los plugins no pueden navegar el admin a URLs externas ni a rutas arbitrarias del admin que podrían exponer UI sensible.
Validación de URL de Modal
Las URLs de modal deben ser:
- URLs absolutas válidas (solo
http:ohttps:) - Del origen de tu plugin (mismo esquema + host + puerto)
- Sin usar protocolos peligrosos (
javascript:,data:,blob:,file:)
Las URLs de localhost también están permitidas en modo desarrollo, pero solo en desarrollo.
Límite de Velocidad
Cada tipo de acción tiene un límite de velocidad por plugin aplicado con una ventana deslizante de 10 segundos. Los límites de velocidad también se sincronizan entre pestañas usando BroadcastChannel, por lo que abrir tu plugin en múltiples pestañas no multiplica el límite.
Redimensionar y Scroll
Los plugins reportan su altura de contenido al host vía la acción resize. El admin establece la altura del iframe para que coincida, luego gestiona el scroll a nivel de página — lo que crea una experiencia de scroll nativa y fluida en lugar de una barra de scroll dentro del iframe.
El hook useAutoResize() de la plantilla hace esto automáticamente usando un ResizeObserver en tu elemento raíz.
Próximos Pasos
- Requisitos Previos — qué necesitas antes de empezar
- Tu Primer Plugin — tutorial práctico
- Referencia App Bridge — documentación completa de la API