Saltar al contenido

Arquitectura ​

Contenido del paquete ​

plugin.json       clave, título, versión (semver), apiVersion y, si hay admin, { entry, css }
checksums.json    sha256 de cada fichero (todos menos plugin.sig y checksums.json)
plugin.sig        firma Ed25519 (base64) de los bytes de checksums.json
api/index.mjs     módulo de la API (export default ZModule), compilado con esbuild
migrations/*.sql  migraciones propias, en orden alfabético, idempotentes
admin/            index.js (ESM) + chunks/ + style.css + i18n/{en,ca,fr}.json
assets/           recursos del paquete (p. ej. la portada cover.png)

El instalador (api/src/plugins/package.ts) abre el zip, rechaza rutas absolutas o con .., verifica la firma de checksums.json contra las claves aceptadas, comprueba que cada fichero coincide con su huella y valida plugin.json y la apiVersion. Nada se escribe en disco hasta que todo cuadra.

Cómo se carga ​

API. Al arrancar, plugins/loader.ts lee plugin_installs, importa <PLUGINS_DIR>/<clave>/<versión>/api/index.mjs y lo añade a la lista de módulos. A partir de ahí es un módulo más: gate MODULE_DISABLED, permisos, jobs, schedules y eventos de dominio. Un plugin que no carga no tumba la API: se prueba la versión anterior y, si tampoco, se omite.

Panel. Tras iniciar sesión, admin/src/plugins/runtime.ts pide GET /plugins/runtime, carga el diccionario del idioma, la hoja de estilos y el módulo ESM, y registra las rutas (router.addRoute("shell", …)), la entrada de menú, el tutorial y los slots. Un plugin apagado no se importa.

Reinicio. Instalar, actualizar, volver atrás o quitar cambia plugin_installs. Un vigilante (cada 15 s) en la API y en el worker detecta la diferencia con lo cargado y reinicia el proceso; Docker lo levanta. El panel espera y se recarga.

El contrato con el host ​

Un plugin no empaqueta librerías del host. El compilador convierte sus importaciones en lecturas de objetos globales que rellena el host antes de cargar el plugin:

En tu códigoSe compila aLo rellena
import … from "@host/<ruta>", kysely, zod, fastify, pdfkit, qrcode…globalThis.__HURY_API__[<ruta>]api/src/plugins/hostRegistry.ts
import … from "@plugin/<clave>"globalThis.__HURY_PLUGINS__[<clave>]el cargador (el plugin debe estar en requires)
import … from "vue", "vue-router"window.__HURY_ADMIN__[…]admin/src/plugins/runtime.ts
import … from "@/…"await window.__HURY_ADMIN__.load("@/…")runtime.ts (solo lo que enumera su import.meta.glob)

Consecuencias que conviene tener presentes:

  • Solo existe lo que el host expone. Un import de algo que no está falla al cargar con «Cannot read properties of undefined» (API) o «El panel no expone…» (panel). La lista real está en Módulos del host de la API y Módulos del admin.
  • No se admite import * as x from "@host/…" ni "@/…": se importan los nombres.
  • Los imports de solo tipo (import type …) se borran al compilar y no cuentan.
  • La versión del contrato es apiVersion (actualmente 2). Un plugin con una apiVersion mayor que la del host se rechaza. Añadir módulos o exportaciones es compatible; cambiar o quitar algo existente sube la versión.

Qué comparte un plugin con el núcleo ​

Núcleo (pos, crm, catalog, commerce, insights, posv3…)
   │   emite eventos de dominio y llama ganchos; no importa plugins
   ▼
Registro de eventos y ganchos  ◄── domainHandlers / hooks de cada ZModule
   ▲
Plugins instalados (rutas, jobs, schedules, permisos, migraciones)
   │   piden al núcleo lo que necesitan con @host/…
   ▼
Host (db, http/*, lib/*, modules/*/…service)

El núcleo nunca importa un plugin. Se comunica con ellos por eventos de dominio (síncronos, dentro de la transacción del emisor) y ganchos (puntos de extensión con valor de vuelta). Sin nadie enganchado, el núcleo sigue su camino por defecto. Ver Ganchos y eventos.

Qué no hace el sistema ​

  • No hay sandbox. El código del plugin corre con los mismos permisos que la API, y el del panel en el navegador de quien lo abre. Solo se instala lo firmado por una clave en la que el dueño de la instalación confía.
  • No deshace migraciones al volver a una versión anterior: tienen que ser compatibles hacia atrás.
  • No cobra nada. Los precios de lista son orientativos; no hay contratación ni facturación conectadas.

Contrato con el host: apiVersion 2. Sitio estático: no recoge datos ni usa servicios de terceros.