Xuda Plugins

La categoría de un plugin no es una etiqueta. Es la regla de enrutamiento.

Hoy se distribuyen sesenta y dos paquetes propios, en exactamente seis categorías: 27 widget, 15 library, 10 ui, 5 graphics, 3 dbs y 2 framework. La categoría no es una convención de nombres ni una etiqueta. Es un campo plugin_type en el registro del plugin, y lo leen tres sistemas independientes: el editor decide en qué conector aparece el plugin, la validación decide qué archivos debe exportar y el runtime decide en cuál de las tres superficies de ejecución corre su código. Nada se conecta a mano después de una instalación. Un paquete del catálogo demuestra que la autoridad es el campo y no el nombre, porque tanto su directorio como sus exportaciones dicen widget mientras que su nombre publicado en npm dice ui, y todas las superficies de Studio lo enrutan por el campo. Un proyecto nuevo arranca con dos plugins ya instalados: el driver de datos nativo y el framework Tailwind. Todo lo que va más allá de esos dos es una instalación, y la categoría decide dónde aterriza.

Seis categorías, seis conectores distintos
Un plugin hijo necesita que su plugin padre lo nombre a su vez
El código de servidor de terceros se rechaza de forma predeterminada
La categoría de un plugin no es una etiqueta. Es la regla de enrutamiento.
62

Paquetes de plugin distribuidos, en exactamente seis categorías

111

Métodos declarados por los 42 paquetes que exportan alguno, sobre 632 campos

6

Nombres de archivo que la plataforma busca tras una instalación. La mayoría incluye dos

15

Paquetes que incluyen código de servidor. Ningún plugin widget o ui lo hace

La regla de enrutamiento

Seis categorías, y la categoría decide ambos extremos.

Instale uno y aparecerá en el lugar correcto sin nada que configurar, porque seis piezas distintas del editor y tres puntos de entrada distintos del runtime leen un único campo. La imagen de abajo es el contrato, no el selector: a la izquierda está cómo se alcanza un plugin de esa categoría, y a la derecha está la exportación exacta que llama el runtime y con qué firma. Léalo como seis contratos independientes colgando de un mismo campo, y no como una interfaz de plugin con seis variantes, porque eso es lo que es. Un plugin widget y uno ui no exportan la misma forma, no reciben los mismos argumentos y no aparecen en el mismo lugar, y un plugin graphics ni siquiera incluye el archivo alrededor del cual se construyen los otros cinco.

Dos notas al pie, ambas verificables. La categoría es un campo del registro y no la palabra que aparece en el nombre del paquete, y un paquete demuestra la diferencia: su directorio y su contrato dicen widget, su nombre publicado en npm dice ui, y cada conector de arriba lo enruta por el campo. Y la insignia Instalados sobre el panel de inserción no cuenta las seis categorías. Cuenta solo framework, graphics y widget, porque esas tres son las que aportan algo que se pueda arrastrar y soltar.
El contrato

Un contrato, seis formas, y las celdas vacías son el argumento.

Después del npm install, la plataforma busca seis nombres de archivo: index.mjs, setup.mjs, studio.mjs, runtime.mjs, server.mjs y style.css. Mira primero la raíz del paquete y luego dentro de dist, registra en cuál de las dos los encontró para que el runtime sepa si debe cargar además la hoja de estilos compilada, y después importa todos los archivos encontrados excepto style.css y server.mjs. Esa importación es la verdadera comprobación de si esto carga: un plugin cuyo index.mjs lanza un error al ser importado falla la validación justo ahí y queda con el texto del error almacenado en su registro, y por eso un plugin roto le muestra un motivo en lugar de un espacio en blanco. La tabla de abajo es lo que realmente incluyen las seis categorías. Las cruces sostienen tanto el argumento como el texto: la columna server.mjs está vacía para todas las categorías orientadas al navegador, que es el hecho sobre el que se apoyan tanto las superficies de ejecución como la barrera para terceros. Un archivo queda fuera de la tabla de forma deliberada. studio.mjs lo incluye exactamente un paquete de todo el catálogo, el driver de datos nativo, donde es una superficie de servidor de tiempo de diseño separada para el editor, así que recibe esta frase en lugar de una columna con cinco cruces.

index.mjs debe exportar
runtime.mjs debe exportar
server.mjs
setup.mjs
widget, 27 distribuidosmethods, más capabilities opcionalesuna función por método
library, 15 distribuidosmethodsun stub por método12 de 1512 de 15
ui, 10 distribuidospropertiesfn, y opcionalmente destroy
graphics, 5 distribuidostabs
dbs, 3 distribuidostabs y public_filter3 de 3
framework, 2 distribuidostabs, core y las primitivas de UIcore, obligatorio. resources, opcional
Instalación

Una instalación es un npm install real dentro de un directorio que pertenece a su proyecto.

Nada de esto es una metáfora de una instalación. Se escribe una entrada en el mapa de plugins adquiridos del proyecto, con el indicador de instalado, la versión, el nombre del plugin padre cuando lo hay, el manifiesto de archivos, el tipo de plugin y el indicador de segundo plano. Después se ejecuta npm install dentro de un directorio de plugins que pertenece solo a este proyecto, con todos los argumentos entrecomillados para el shell. Después ese directorio se sincroniza con rsync hacia los servidores regionales. Después se escanea el paquete instalado hasta una profundidad de diez y cada archivo se lee hacia una base de datos CouchDB que pertenece al proyecto, indexada por su ruta relativa, y una configuración existente se conserva a lo largo de la actualización en lugar de sobrescribirse.

  • Cada proyecto obtiene su propio árbol node_modules, y un paquete que no se descarga se elimina solo a sí mismo. Hay una nota en el código que explica el motivo: una versión anterior borraba el árbol entero ante un fallo, lo que desinstalaba todos los demás plugins del proyecto porque un paquete no se pudo descargar.
  • Desinstalar invierte el indicador de instalado, ejecuta npm uninstall y vacía el documento de plugin del lado del proyecto hasta dejar solo su id, su revisión y su configuración. La configuración sobrevive a una desinstalación y a una reinstalación posterior, que es la diferencia entre quitar un plugin y perder las credenciales que escribió en él.
  • Nada se empaqueta dentro de la compilación de una app. El runtime obtiene cada archivo de plugin por sesión a través de una ruta /plugins, que sirve desde el árbol propio de cada proyecto y exige o bien un id de app resoluble o bien un token válido. Para un archivo fuente sin empaquetar, esa ruta reescribe las importaciones ESM relativas a la salida, para que un navegador siquiera pueda cargarlas. Lo que el despliegue sí empaqueta es el mapa de configuración, recopilado en los datos de arranque de la app.
  • En una sesión de desarrollo, el runtime intenta src antes que dist para cualquier archivo .mjs y recurre a dist como respaldo, de modo que editar el código fuente del plugin se refleja sin recompilar. En los demás casos decide el manifiesto, y donde este registra que la copia compilada fue la encontrada, gana dist.
Una instalación
  1. Se escribe el registro
    instalado, versión, nombre del padre, manifiesto de archivos, tipo de plugin e indicador de segundo plano
  2. Se ejecuta npm install
    En el directorio de plugins de este proyecto únicamente, con todos los argumentos entrecomillados para el shell
  3. El árbol se sincroniza hacia fuera
    Ese directorio de plugins del proyecto se envía a los servidores regionales remotos
  4. Se toma una instantánea del paquete
    Cada archivo se lee hacia la base de datos del proyecto, indexado por su ruta relativa
Composición

Un plugin hijo nombra a su padre. El padre tiene que nombrar al hijo a su vez.

Un plugin puede declarar un padre, y la plataforma se niega a instalarlo salvo que dos declaraciones independientes coincidan. El hijo nombra al padre mediante un campo de dependencia en su propio registro. El padre tiene que listar al hijo a su vez, en un array de plugins autorizados dentro del registro del padre. Ninguna declaración basta por sí sola. Las comprobaciones se ejecutan en orden y cada fallo vuelve como un rechazo numerado en lugar de un stack trace, y todas se ejecutan antes de invocar npm, de modo que una instalación rechazada no deja nada en el disco. Esas mismas dos comprobaciones se repiten más adelante, en cada lectura y cada escritura de una configuración de plugin, y otra vez en el servidor antes de permitir que se ejecute un método de plugin.

El par decide dos cosas más allá del permiso. A un plugin hijo se le redirige la búsqueda de configuración hacia el documento del padre, de modo que existe exactamente una configuración de Stripe en lugar de una por cada widget que dibuje un campo de tarjeta. Y decide la confianza: como el servidor fusiona el archivo de servidor del padre en el mismo objeto que el del hijo, un plugin propio que depende de uno de terceros cuenta como de terceros.
Configuración

El plugin decide qué puede contar su propia configuración al navegador.

La configuración de un plugin no se guarda en el registro compartido. Vive como un documento de configuración dentro de la base de datos CouchDB del proyecto que la configuró, escrito por el formulario de configuración y leído por ambos lados. Cuando la lectura es pública, la plataforma importa el plugin y llama a una función que el propio plugin exporta, y devuelve únicamente lo que esa función devolvió. No existe una lista blanca de claves seguras a nivel de plataforma, y eso es a la vez la fortaleza y el límite de este arreglo: el autor del plugin es quien sabe cuál de los campos es el secreto, y el autor del plugin es quien escribe el filtro.

La lectura honesta es que esta frontera es exactamente tan buena como el plugin que la trazó. Un filtro ausente significa un documento sin filtrar, y la plataforma no cuestiona esa decisión. Ese mismo documento de configuración es el que recopila el paso de despliegue, una entrada por plugin instalado, dentro de los datos de arranque con los que se compila una app.
Ejecución

Tres superficies, y la del servidor se comprueba antes de la importación, no después.

El código de un plugin corre en exactamente tres lugares. En el navegador, como un widget que es dueño de un contenedor que el runtime creó para él, como un plugin ui que decora un elemento que usted ya tenía, como un framework, o como un plugin de segundo plano enganchado al ciclo de vida de la app. En un servicio de plugins dedicado, su propio proceso en el puerto 3008, para los métodos de servidor de library. Y en un despacho aparte para los drivers de datos, que nunca pasa por ese servicio y almacena en caché su conexión activa por app y por plugin. Ambas rutas de servidor se ejecutan bajo un techo de sesenta segundos.

  • Un método de library son dos archivos haciendo dos trabajos. El stub del navegador convierte los tipos de los campos declarados, exige los obligatorios y envía la petición; el archivo de servidor hace el trabajo y guarda la credencial. Esa división es toda la razón por la que una clave secreta de Stripe no tiene ninguna vía hacia el navegador: se lee en server.mjs, desde el documento de configuración que se le entrega, y en ningún otro sitio.
  • Antes de que el servicio cargue el archivo de servidor de un plugin, pregunta si el publicador es propio, y lo pregunta antes de la importación y no después. Importar un módulo ya ejecuta su código de nivel superior, así que una comprobación colocada después no comprueba nada. Los métodos de servidor de terceros se rechazan de forma predeterminada y el interruptor que los permitiría está en false en ambas configuraciones, de modo que hoy los plugins de terceros son una superficie de producto del lado del navegador.
  • Un plugin de segundo plano dispone de cinco hooks de ciclo de vida, disparados en el orden beforeInit, initialized, beforeMounted, mounted y systemReady alrededor del arranque de la app, y a cada uno se le entregan la sesión, el objeto de la app, la api y los datos de configuración. La casilla de ejecutar en segundo plano solo se ofrece en un plugin de tipo library, y el registro se omite por completo en el modo de bundle reducido.
  • Los campos que declara un método cumplen una doble función. En un Full Stack VPS, la plataforma recorre los plugins instalados y registra cada método como una herramienta MCP, nombrada plugin y luego método, con su esquema de entrada construido a partir de esos mismos campos declarados y su descripción tomada del tooltip que el método ya llevaba.
Una llamada a library
  1. Se dispara el paso del flujo de trabajo
    Nombrando un plugin, un método y las propiedades correspondientes
  2. El stub del navegador valida
    Los campos se convierten a sus tipos declarados y se exigen los obligatorios
  3. Envía la petición al servicio de plugins
    El token se verifica contra una sesión activa en la base de datos de su propio proyecto, nunca desde una caché
  4. El archivo de servidor se ejecuta, con un techo
    Sesenta segundos, con el documento de configuración entregado
Qué puede alcanzar el código de un plugin

A un plugin de navegador se le entrega un objeto, y esto es todo lo que contiene.

El runtime construye un objeto de parámetros por llamada y lo entrega como segundo argumento. Un widget no recurre a una variable global para encontrar la plataforma, y no hay una API de plugins aparte que aprender más allá de lo que llega en ese objeto. El anfitrión crea el div contenedor, lee el id del plugin y el nombre del método del nodo, importa index.mjs y comprueba las capacidades declaradas frente al entorno actual, resuelve y convierte cada campo declarado y exige los obligatorios, carga style.css y dist/runtime.css cuando el manifiesto los registró, obtiene la configuración del plugin, y solo entonces importa runtime.mjs y espera al método. Un error lanzado dentro de ese método se captura y se reporta en lugar de dejar que tumbe la página. La forma de abajo es la del widget. Un plugin ui recibe algo lo bastante distinto como para que valga la pena imprimirlo al lado, porque esa diferencia es toda la distinción entre las dos categorías: a un widget se le da un contenedor que le pertenece, a un plugin ui se le da un elemento que usted ya tenía.

runtime.mjs · export async function <method>(fields, params)
fields los campos declarados, convertidos a sus tipos y comprobados
params:
SESSION_ID, method, _session
dsP, sourceP, propsP fuente de datos, fila de origen, props resueltas
plugin_name
container_node el div que el runtime creó para este widget
container_data
plugin_setup la configuración filtrada, nunca el documento en crudo
report_error(desc, warn)
log_error(desc, warn)
call_plugin_api(name, data) la única vía hacia el servidor
set_SYS_GLOBAL_OBJ_WIDGET_INFO(doc)
run_widgetCallbackEvent() dispara el id de evento que declaró como campo
api_utils
un plugin ui recibe una forma completamente distinta:
fn(plugin_name, element, properties) sin contenedor, decora el suyo
Esto es una convención de llamada, no un sandbox. El código de un widget corre en su página como cualquier otro script en ella, y lo que le aporta el objeto es que la plataforma decide qué va dentro. Un plugin ui puede además exportar destroy, para liberar lo que haya construido cuando su elemento desaparece, y el runtime reproduce exactamente las propiedades que se le entregaron a fn en lugar de volver a resolverlas al desmontarlo, porque para entonces la fuente de datos detrás de esa fila puede haber sido retirada.
Cómo crear uno

El camino del desarrollador es npm, y nada en él es casero.

Aquí no hay una cadena de herramientas propietaria ni una CLI de plugins que aprender. Todo el camino es un paquete de sandbox publicado contra el que usted desarrolla, una compilación de librería con Vite normal y corriente, y npm. La plataforma solo se encuentra con su plugin al final del recorrido, cuando usted registra el nombre del paquete.

  1. 01

    Desarrolle con el sandbox

    El entorno de pruebas es un paquete publicado de verdad, @xuda.io/plugin-sandbox en la versión 1.0.27. Construye una página de dos paneles: a la izquierda un formulario Plugin Configurator, generado a partir de los campos que declara su método, un separador ajustable en el centro, y a la derecha un panel Live Preview con el div contenedor que se le entregará a su método. Un desplegable elige cuál de sus métodos probar. Cualquier campo que declare el tipo de renderizado editor obtiene un editor Monaco real conectado a su evento de cambio, con el lenguaje tomado de los parámetros de renderizado y javascript como valor predeterminado. La adopción es parcial y conviene decirlo con claridad: trece de los sesenta y dos paquetes distribuidos desarrollan con él, y veinte incluyen su propia página de prueba dentro de tests.

  2. 02

    Compile con Vite, hacia dist

    Todo paquete que usa el sandbox lleva la misma configuración de Vite. El punto de entrada de la librería es src/runtime.mjs, el formato es ES, las importaciones dinámicas se incorporan en línea, la salida aterriza en dist, cualquier hoja de estilos emitida se renombra a runtime.css, y un pequeño paso de copia mueve todo lo que hay en src salvo runtime.mjs hacia dist, junto a él. Esa convención es exactamente la razón por la que la plataforma registra un indicador de dist y uno de css por archivo cuando examina su paquete, y por la que el runtime sabe que debe cargar dist/runtime.css además del módulo.

  3. 03

    Publique y después regístrelo

    Los sesenta y dos archivos package.json distribuidos llevan el mismo script de publicación: npm version patch y después npm publish. Semver simple sin nada añadido encima, y los máximos alcanzados indican cuánto uso recibe. El driver de datos nativo va por la 1.0.320, el framework Tailwind por la 1.0.128, el generador de apps con IA por la 1.0.74. Registrarlo en la plataforma es un paso aparte que obtiene el manifiesto en vivo desde npm, toma la versión de allí y no de su formulario, y toma el icono del marketplace del campo svg_icon de su package.json si dejó el icono en blanco en el formulario.

El otro camino

O descríbalo, y el sistema de plugins construye un plugin con un plugin.

El generador no es una herramienta que el asistente de chat pueda alcanzar por su cuenta. Sus dos métodos están marcados como privados, así que nada los expone como herramientas de agente; la pestaña de plugins prepara la acción y el asistente la ejecuta como parte de ese trabajo. Lo que hace el trabajo es a su vez un plugin de library distribuido, el generador de apps con IA de Studio, invocado igual que cualquier otro método de library. Cada restricción de abajo está en el generador y no en un documento de políticas, y el estado final es un plugin privado, a propósito.

Las versiones dejan clara la intención: un plugin creado es 0.1.0 y una modificación incrementa el parche. Recibe su propio ámbito de npm en lugar del de primera parte, se instala desde el artefacto y no desde el registro, y hacerlo público es una acción explícita aparte en la tarjeta del plugin, que es exactamente lo que dice la respuesta del asistente. Ahora la corrección honesta. Ese escaneo es una coincidencia de patrones estática sobre texto generado, apoyada en un esquema de respuesta y una lista blanca de siete nombres de archivo. No es análisis dinámico y no es un sandbox.
Quién puede hacer qué

Cinco acciones, y no hay dos que estén condicionadas de la misma manera.

Dos de estas se describen habitualmente como una sola cosa, y no lo son. Una membresía Agency condiciona el registrar un plugin siquiera, y se aplica al método de subida. Una cuenta de vendedor de Stripe Connect activa condiciona el hacerlo público, y se aplica al método de publicación, que puede invocar cualquier persona con sesión iniciada. Detienen a personas distintas en momentos distintos. Instalar un plugin en un proyecto no está condicionado por ninguna de las dos: requiere autorización de administrador en ese proyecto, y si el plugin tiene un precio pasa antes por un paso de pago.

Autorización
Plan de membresía
La otra barrera
Instalar o desinstalar en un proyectoAdministrador del proyectoUn plugin con precio pasa antes por un paso de pago
Leer o escribir la configuración de un pluginAdministrador del proyectoLa cadena de plugin padre se vuelve a comprobar antes de tocar nada
Registrar un plugin siquieraAdministrador del proyectoAgencyLa propiedad se exige en cada actualización
Publicarlo en el MarketplaceCualquier usuario con sesión iniciadaUna cuenta de vendedor de Stripe Connect activa, o un 403
ArchivarloAdministrador del proyectoSolo el propietario
La barrera

Publicar es una acción separada de actualizar el código, de forma deliberada.

Subir una versión nueva, o dejar que el generador con IA reconstruya una, nunca vuelve a entrar en revisión. El ciclo de vida existente se mantiene tal cual: un plugin que era privado sigue privado, un plugin que era público sigue público, y el estado de revisión que ya tenía es el estado de revisión que conserva. Hacerlo público es un método distinto con una autorización distinta, ejecutado una vez, por el propietario, a propósito, y es la única llamada que activa la barrera. Todo lo de abajo cuelga de esa única llamada.

El revisor devuelve diez comprobaciones, cada una con su propia severidad: actividad automatizada, intención comercial no declarada, código dañino, actividad sospechosa, un nombre de cuenta mal formado, spam y baja calidad, suplantación y marcas registradas, contenido prohibido, plagio y duplicación, y secretos o datos personales filtrados. Conviene decir con claridad qué recibe el revisor, porque eso marca el techo de lo que cualquier veredicto puede significar: el nombre, la descripción, las etiquetas, la categoría, el precio y un resumen de código que es el nombre del paquete de npm, su versión y la lista de plataformas. Es una revisión de metadatos, no una lectura de su código fuente. Un publicador propio la omite por completo. Todo lo que quede retenido pasa a una persona, con un correo de operador, un ticket de soporte deduplicado por el id del elemento y un enlace de apelación en caso de rechazo, y no hay ningún plazo de respuesta escrito en ninguna parte del código.
Límites honestos

Nueve cosas que el sistema de plugins no hace.

  • Los plugins de terceros hoy no pueden ejecutar código de servidor. La política provisional es denegar, es un interruptor de configuración, y ese interruptor está apagado en ambas configuraciones, de modo que cualquier cosa que construya para que otra persona la instale es únicamente una superficie del lado del navegador.
  • El contrato de widget contempla una capacidad headless, para código de plugin que se ejecuta donde no hay documento. Ninguno de los 62 paquetes distribuidos la declara. El contrato está ahí, la prueba distribuida no.
  • El runtime admite una exportación destroy opcional en un plugin ui. Ninguno de los diez plugins ui distribuidos exporta una.
  • Dos de los tres drivers de datos son de solo lectura tal como se distribuyen. Solo el nativo implementa los seis servicios. MySQL y CouchDB exportan una lectura y una prueba de conexión y nada más, y pedirle a cualquiera de los dos algo más lanza un error nombrando el servicio ausente.
  • El selector de iconos busca dentro del kit que usted tiene seleccionado, no entre los kits que tiene instalados. Y un plugin graphics puede declarar como máximo una dependencia de npm, y por eso cada kit distribuido es un paquete upstream y su árbol de directorios.
  • Instalar un componente compartido comprueba solo dos tipos de dependencia de plugin: el framework de interfaz en un programa y el driver de datos en una tabla. Un componente que usa un plugin widget no lo declara, así que esa instalación puede tener éxito con el widget aún ausente.
  • El generador con IA se vigila antes de empaquetar el código, nunca mientras se ejecuta. Trece patrones prohibidos y un enum fijo de nombres de archivo, ambos aplicados sobre texto. No hay aislamiento en tiempo de ejecución esperando detrás de ellos.
  • Nada en la plataforma decide qué claves de configuración son seguras de publicar. Ese juicio se delega por completo en el autor del plugin, y un plugin que no emite ningún juicio entrega su documento de configuración completo al navegador.
  • La ruta de registro acepta dos cadenas de tipo más de las que expone el producto, y nada en el catálogo incluye ninguna de las dos. Los seis del selector son los seis que se enrutan a alguna parte.
Antes de decidirse

Las preguntas que se está haciendo ahora mismo

Cinco de estas seis respuestas terminan en un límite. Están aquí porque se los encontraría igualmente en la primera semana.

Vea

Vea cómo se instala un plugin y aparece en su conector.

Instale dos plugins y ya tiene una capa de datos y un sistema de diseño.

Eso es literalmente con lo que llega un proyecto nuevo: el driver de datos nativo y el framework Tailwind, y nada más. Todo lo que va más allá de esos dos es una instalación, y la categoría del registro decide dónde aparece, ya sea el panel de inserción, el selector de iconos, el selector de driver de la tabla, un interruptor en un elemento o la lista de acciones del flujo de trabajo.