Xuda Studio

Una aplicación de Xuda es un conjunto de documentos tipados, y un único intérprete los ejecuta.

Studio no genera una base de código que usted deba mantener. Un proyecto es un conjunto plano de documentos, todos ellos marcados con docType studio, en doce tipos de nodo: componentes, tablas, unidades de datos, rutas, agentes y los demás. El árbol del panel izquierdo se deriva de esos documentos al cargar, a partir de un puntero al padre en cada uno, y nunca se almacena como un árbol propio. Esos mismos documentos son los que una vista previa pide por id, los que recorre una compilación y los que ejecuta el runtime cuando alguien abre la aplicación. Nada intermedio los compila en otra cosa.

Doce tipos de nodo, una sola forma de documento
Ningún documento de árbol en ninguna parte
Cualquier nodo es una URL que se puede compartir
Una aplicación de Xuda es un conjunto de documentos tipados, y un único intérprete los ejecuta.
12

Tipos de nodo con los que se construye un proyecto

11

Tipos de evento a los que puede reaccionar un componente

16

Tipos de despliegue con los que puede publicarse el mismo proyecto

62

Plugins propios, repartidos en seis categorías

La forma de un proyecto

Cinco árboles separados, y ningún documento de árbol en ninguna parte.

El panel izquierdo no contiene un árbol. Contiene cinco, y un control segmentado de cinco iconos situado encima decide cuál se monta, de modo que tablas, pantallas, estado compartido, URLs y agentes son cinco espacios de direcciones separados y no cinco ramas del mismo. Ninguno de los cinco se almacena como árbol tampoco. Cada nodo es un documento que lleva un único puntero parentId y una clave de orden order_ts, y un web worker reconstruye toda la estructura al cargar, buscando los hijos nivel por nivel y admitiendo solo los documentos cuyo stat es 3. Mover un nodo hacia arriba o hacia abajo no es mover un archivo: guarda exactamente dos documentos con sus valores order_ts intercambiados. Eliminar un subárbol escribe stat 4 en todos sus documentos y los deja en el almacenamiento, y por eso el árbol deja de mostrarlos y el índice de búsqueda deja de indexarlos en el mismo instante. Abra un componente, cambie a su pestaña UI y aparecerá un segundo árbol debajo del primero, el árbol de elementos de ese componente, con un divisor arrastrable entre ambos cuya posición se recuerda.

Vale la pena señalar un límite. La reconstrucción recorre cada documento una vez por nivel, que es precisamente el motivo por el que se ejecuta en un worker y no en el hilo principal.
Una corrección

Lo de las cuatro pestañas es cierto en un componente. Cualquier otro tipo abre otra cosa.

La versión de esta página que quizá usted leyó decía que pantallas, trabajos en segundo plano y endpoints de API son el mismo tipo de objeto con las mismas cuatro pestañas. La primera mitad es cierta, la segunda no. El editor construye su barra de pestañas según el tipo de nodo, y el envoltorio se encoge y se agranda: un componente obtiene cuatro pestañas, un batch o una unidad de datos obtiene tres y no tiene UI alguna, una unidad de API obtiene cuatro pero la cuarta es Output en lugar de UI, y cinco de los doce tipos abren una sola pestaña o ninguna: alert, javascript, route y ai_agent abren cada uno un editor, y una carpeta no abre nada. La barra solo se renderiza cuando un nodo tiene más de una pestaña, así que esos cinco llegan sin barra encima. Qué pestaña está abierta es un parámetro de consulta en la URL del editor, y la última pestaña que usted tenía en un nodo dado se recuerda por nodo, de modo que al volver queda donde lo dejó. La segunda columna es la otra mitad de la misma gramática. El panel Crear ofrece un conjunto distinto de tipos según el nodo sobre el que usted hizo clic derecho, y una carpeta simplemente hereda el conjunto de la sección en la que está.

Qué abre
Dónde se puede crear
ComponenteDatasource, Fields, Events, UIBajo Programs, o dentro de otro componente
APIDatasource, Fields, Events, OutputBajo Programs o cualquier otra unidad, nunca dentro de un componente
BatchDatasource, Fields, Events. Sin pestaña UIPrograms, dentro de un componente o dentro de cualquier otra unidad
Get data (get_data)Datasource, Fields, Events. Sin pestaña UIPrograms, dentro de un componente o dentro de cualquier otra unidad
Set data (set_data)Datasource, Fields, Events. Sin pestaña UIPrograms, dentro de un componente o dentro de cualquier otra unidad
AlertUna pestaña, AlertBajo Programs o dentro de un componente, en ningún otro sitio
JavaScriptUna pestaña, EditorBajo Programs o dentro de un componente, en ningún otro sitio
TablaData, Fields, IndexesBajo Database, donde Crear se deshabilita sobre la propia tabla
GlobalsFields, EventsNo desde el árbol: la raíz Globals rechaza nuevos hijos
RouteUna pestaña, RouteBajo Routes, el único tipo que ofrece esa sección
Agente de IA (ai_agent)Una pestaña, AI AgentBajo AI Agents, el único tipo que ofrece esa sección
CarpetaNada en absoluto. Abrir una carpeta termina de inmediatoBajo Programs o Database, o dentro de un componente o de cualquier otra unidad
Constructor de aplicaciones con IA

El asistente escribe en el mismo árbol que usted edita a mano, y hay un modo que no escribe nada.

Describa lo que quiere en el panel de IA de un proyecto abierto y el constructor propondrá tablas, componentes, campos y eventos como documentos normales dentro del árbol normal, y por eso usted puede abrir el resultado después y cambiar un solo campo. Los dos interruptores del panel no son el mismo interruptor: el alcance decide de qué trata la conversación, el modo decide si el turno puede escribir. Dentro de Studio el compositor además oculta el selector de modelo y el selector de agente que muestra el resto del panel de control, así que no hay modelo que elegir ni agente que elegir.

  • El modo Plan no escribe nada. El panel describe Auto como una ejecución de principio a fin sin interrupciones y Plan como una investigación que propone un enfoque sin hacer cambios. La elección viaja en la petición como plan_mode, y el servidor la convierte en un método distinto con el guardado desactivado durante todo el turno.
  • El alcance decide a qué queda ligado el turno. En alcance general la conversación hace referencia a la aplicación; en alcance de nodo hace referencia al nodo que usted tiene abierto, como studio_node y no como aplicación. Cambiar de alcance inicia un chat nuevo y recarga la lista de chats, porque los chats anteriores se recuperan según aquello a lo que hacen referencia.
  • Los chips de contexto son interruptores, no etiquetas. Aparecen hasta tres: el programa abierto en el editor, un nodo distinto seleccionado en el árbol y el elemento de UI seleccionado mientras la pestaña UI está abierta. Desactive uno y queda fuera de la petición. Desactive los tres y el turno se envía sin contexto alguno.
  • El constructor es un único paquete npm con métodos nombrados, y dos de esos métodos escriben plugins. app_builder guarda documentos de studio, app_planner no, y create_plugin_via_ai y modify_plugin_via_ai se exportan desde el mismo paquete. Lo que ese paquete hace por dentro no está en el código de la plataforma, así que esta página describe el punto de llamada y se detiene ahí.
Un turno
  1. Usted lo describe
    En el panel de IA de un proyecto abierto, en alcance general o ligado a un nodo
  2. Los chips fijan el contexto
    Programa abierto, un nodo seleccionado en otro punto del árbol, el elemento de UI seleccionado
  3. Los últimos 12 mensajes viajan con la petición
    Cada uno recortado a 2000 caracteres antes de enviarse
  4. app_builder guarda, app_planner no
    Aceptar un plan lo reenvía con la instrucción de ejecutarlo de principio a fin
Qué se recibe de vuelta

Un turno de construcción devuelve un comprobante que nombra lo que cambió.

La respuesta no es el modelo narrando lo que hizo. Un formateador de la plataforma lee el resultado estructurado y redacta el mensaje: cuenta los documentos, los agrupa por tipo de menú, enumera hasta doce por nombre con el id del documento entre corchetes e indica si se guardaron. Un turno de plan renderiza campos completamente distintos, un resumen, hasta doce unidades propuestas, ocho pasos de orden de construcción y ocho comprobaciones de aceptación, y siempre indica que mantuvo el turno en modo de planificación. Cuando el asistente necesita algo de usted para continuar, tampoco lo pide en prosa: emite un bloque de preguntas estructurado que el panel renderiza como un asistente por pasos, con dos o tres opciones por pregunta y una fila Otro de texto libre que añade la propia interfaz. Mientras el turno se ejecuta, el texto de estado rota por una lista fija de tres mensajes, una lista por modo, cambiando cada 1800 milisegundos.

respuesta del asistente · studio_method: app_builder
Se generaron 2 component, 1 table
y luego las unidades creadas, hasta 12 de ellas:
component: <menuName> [<_id>]
component: <menuName> [<_id>]
table: <menuName> [<_id>]
y una línea que indica si se guardaron
La prueba de humo en navegador con vista previa en vivo se superó para 2 componentes.
esa línea aparece solo cuando el turno ejecutó la prueba
un turno de plan indica esto en su lugar, sin haber creado nada:
Este turno se mantuvo en modo de planificación, así que no se guardaron documentos de studio.
Las etiquetas en gris son nuestras. Lo que el formateador fija es la línea de recuento, una línea por unidad creada con su id, la línea de la prueba de humo y la línea del modo de planificación. Los topes también son reales: pasadas doce unidades la lista se detiene, de modo que un turno muy grande se cuenta entero y se enumera solo en parte.
Datasource

Detrás de un único control segmentado hay dos motores.

En un datasource de tabla el modo de filtro es Index o Query, y cuando el ajuste está ausente el runtime infiere Index si hay un índice seleccionado y Query si no lo hay. No son dos apariencias sobre un mismo planificador. El modo Index lee una clave compuesta que la tabla ya escribió en cada registro. El modo Query lee un selector ensamblado en el momento de la petición. Lo que usted puede expresar, y lo que tiene garantizado, cambia con la posición de ese control.

El límite honesto está del lado del índice, y es silencioso. Si el índice seleccionado ya no está en la tabla, o si el índice tiene claves pero todas las celdas de rango se dejaron vacías, la construcción del filtro se abandona y la lectura se amplía a un recorrido de toda la tabla. El mismo catch se traga cualquier error lanzado mientras se ensambla el filtro, registrado como advertencia y nada más. Un índice mal configurado se percibe como lento, no como roto.
La pestaña UI

No hay registro de propiedades. Cada propiedad es un atributo.

Seleccione un elemento en el árbol y el panel de propiedades se llenará de filas. Esas filas no escriben en un bloque de propiedades privado asociado al nodo. Un elemento es un id, un nombre de etiqueta, un mapa de atributos, hijos y contenido de texto, y cada ajuste del panel es literalmente un atributo del espacio de nombres xu-. Active Bind y el marcado gana xu-bind. Escriba lo que quiera en Añadir atributo y quedará junto a los demás como un atributo normal. Por eso una clase que usted escribió a mano, un atributo que usted inventó y un control del panel acaban en el mismo sitio, y por eso Editar HTML le muestra toda la verdad sobre un elemento y no una interpretación de ella.

  • El panel se abre con tres filas, xu-show, xu-bind y xu-ref, y crece a medida que usted usa los acordeones de abajo. Otros quince nombres quedan fuera de la lista libre de atributos porque cada uno ya tiene su propio control dedicado, de modo que ningún atributo es editable en dos sitios a la vez.
  • Show y Render no son dos palabras para ocultar, y el runtime lo dice en su propio comentario. xu-show es un interruptor de visualización y el elemento sigue montado. Una condición de render mete o saca un subárbol del documento, así que el elemento simplemente no está en el DOM.
  • Cualquier atributo tiene un gemelo calculado: escriba xu-exp: delante del nombre, o pulse el botón fx junto al campo, y el valor pasa a ser una expresión que se recalcula cuando cambian sus dependencias. Es genérico y no una lista curada de propiedades enlazables, así que funciona sobre src, sobre un atributo que usted escribió hace un minuto y sobre campos declarados por un plugin. Un valor y su gemelo calculado son la misma ranura, no un valor por defecto más una anulación, así que Studio señala un elemento que lleve ambos y le ofrece eliminar el estático. Una excepción documentada: xu-exp:class sustituye al atributo class estático en lugar de fusionarse con él.
  • El límite, dicho sin rodeos: no hay interfaz de estilos. Ni controles de espaciado, ni controles de tipografía, ni selector de color, ni editor del modelo de caja. El estilo es una fila de chips de clase más un editor de CSS en crudo, así que un valor de padding es una cadena de clases de Tailwind o de Ionic que usted escribe o pega, y una clase condicional es un nombre de clase asociado a una expresión dentro de xu-class.
Un espacio de nombres, y todo él se publica
xu-ref, el nombre con el que el elemento se registra
xu-bind, el campo que lee y escribe
xu-show, un interruptor de visualización que lo deja montado
xu-if, xu-else-if, xu-else, una cadena de ramas
xu-for, con xu-for-val y xu-for-key nombrando las variables del bucle
xu-class, nombres de clase asociados a expresiones, almacenados como JSON
xu-script, xu-style y xu-cdn, asociados a un solo elemento
xu-exp: delante de cualquiera de los anteriores, calculado en vez de literal
Lienzo y runtime

El lienzo es una superficie de maquetación. El runtime está a un interruptor de distancia.

Ambas superficies leen el mismo árbol de elementos, y hacen cosas distintas con él. El lienzo de diseño renderiza su marcado para que usted pueda maquetar y seleccionar, pero no ejecuta su aplicación: los componentes anidados y los widgets se dibujan como marcadores de posición etiquetados, y las condiciones se comparan como texto en lugar de evaluarse. El interruptor de la barra de herramientas cambia el lienzo por la vista previa en vivo, que es el runtime real con un popover de Props para introducir valores de prueba. Saber sobre qué superficie está usted explica la mayoría de los momentos en que una pantalla se ve bien en un sitio y se comporta de otro modo en el otro.

Ambas superficies son honestas una vez que usted sabe cuál es cuál. En el lienzo de diseño, xu-panel y xu-widget nunca renderizan su contenido, y los atributos de condición se comparan como texto en lugar de evaluarse. Todo lo que realmente se ejecuta, se ejecuta al otro lado del interruptor.
Ejecución

Un flujo de trabajo es una cola de trabajos, no una lista de awaits.

Un evento no ejecuta sus acciones una tras otra. Recorre el flujo de trabajo una vez y añade un trabajo por cada paso habilitado a un único arreglo alojado en la sesión, y un bucle de vaciado independiente que se ejecuta cada milisegundo toma el primer trabajo que no esté ya marcado como ocupado y lo ejecuta. El orden es el orden del arreglo, de arriba abajo. Lo que inicia el paso siguiente no es que el paso anterior termine, sino que el paso anterior elimine su propio trabajo. La marca async es una línea dentro de ese bucle.

Lo que no aparece en esta imagen: ni ramas, ni bucles, ni bloques en paralelo. La repetición viene de un evento de intervalo en el cliente o de llamar a otro programa, y la única primitiva de concurrencia de la página es esa marca async por acción. El anidamiento es una inserción y no una pila: un paso que llama a otro programa hace que los trabajos de ese programa se inserten justo detrás de los suyos, así que se ejecutan antes del siguiente paso de quien llamó. En el código fuente no aparece ningún límite superior de acciones async concurrentes.
El catálogo de acciones

Veinticuatro acciones en nueve grupos, y el argumento que lee cada una.

El selector Añadir acción se construye a partir de un único archivo de catálogo, y fuera de las entradas de IA cada acción coincide en tres sitios: el catálogo, el mapa de manejadores del runtime y el registro de validación que anota la clave de payload que lee cada acción. Ese registro es la columna de la derecha. Dos cosas antes de la tabla. Invoke Action ofrece once valores en su desplegable y nueve funcionan, porque act_close y act_back están comentados tanto en el runtime como en la lista de permitidos del validador. Y siete acciones antiguas sobre elementos de UI se rechazan de plano por obsoletas, en lugar de tolerarse en silencio. También puede crear una acción sin el selector: arrastre un nodo del árbol hasta el flujo de trabajo y el tipo de nodo decide la acción, así que un nodo get data se convierte en Get Data y un componente en Call Page. Suéltelo sobre una fila que ya existe y redirige esa fila en lugar de añadir una nueva.

Las acciones del grupo
Qué lee la acción
Navegación, 3Call Page, Call Modal, Call PopoverCada una nombra el componente que se abrirá, y ese es todo el payload.
Base de datos, 3Get Data, Set Data, BatchLa unidad de datasource que se ejecutará. Set Data y Batch también nombran el campo donde aterriza su resultado.
JavaScript, 4Call Native JS, Call Evaluate JS, Execute Native JS, Execute Evaluate JSLas dos acciones Call nombran una unidad de programa JavaScript del árbol. Las dos acciones Execute llevan el código en sí, escrito dentro de la acción.
Manejadores de eventos, 4Raise Event, Emit Event, Invoke Action, DelayRaise Event nombra un evento y sus parámetros, cada uno un valor literal o una expresión. Invoke Action lee uno de los nueve valores permitidos. Delay lee un número de milisegundos.
Utilidades de UI, 4Call Alert, Alert, Loader On, Loader OffCall Alert nombra una unidad de alerta. Alert lleva su propio modo de visualización, tipo, título y cuerpo. Las dos acciones de loader no leen nada.
API, 3Call Project API, Call System API, Call External APIUna API de proyecto nombra una unidad de su propio árbol, una API de sistema nombra un método, y una API externa toma una URL y un método HTTP.
Conjunto de datos, 1UpdateUn solo valor, escrito como "@field: expression; @other: expression", o como un objeto JSON5.
Plugins, 1Call LibraryEl nombre del plugin y el método dentro de él, de modo que una biblioteca comprada se lee como cualquier otra acción de esta tabla.
Comentario, 1CommentNada. El registro la describe como documentación únicamente, y el manejador del runtime no hace más que eliminar su propio trabajo.
Hasta el código real

La vía de escape es un camino, no una caja de texto.

Siete lugares le permiten dejar de configurar y empezar a escribir código, y el más profundo de ellos no es siquiera una acción: un programa JavaScript es su propia unidad en el árbol, con su propia salida de compilación y un conjunto documentado de cosas que se le entregan. Dos límites merecen mencionarse aquí mismo. El validador no analiza el cuerpo de un JavaScript, así que un error de sintaxis aparece desde la compilación como un fallo de compilado y no desde el verificador, que solo advierte cuando el script está vacío. Y un atributo script o style en línea sobre un elemento no puede contener ningún carácter de comilla.

  • Al cuerpo de un programa se le entregan diez argumentos con nombre: params, xu, SESSION_ID, SESSION_OBJ, jobNoP, $container, func, glb, APP_OBJ y evt. Si no hay import ni export de nivel superior, se envuelve en una función async, de modo que await de nivel superior funciona.
  • Escriba un import o un export y pasa a ser un módulo ES real. Los especificadores relativos se reescriben respecto de la carpeta scripts de ese programa, así que una unidad puede importar sus propios archivos hermanos.
  • Guardar una unidad JavaScript dispara una compilación de Vite sin bloquear el guardado. En tiempo de ejecución el motor prefiere el módulo compilado, con cache busting por compilación, carga una hoja de estilos solo si el módulo exporta una, y como último recurso ejecuta el código fuente con el código de advertencia RUN_MSG_JS_010.
  • El objeto xu que recibe un script es una API de quince métodos: leer y escribir un campo, invocar un evento, cuatro llamadas a la base de datos, llamar a otro script, tres tipos de llamada a API, alerta, log y leer o escribir en el drive. El código en línea tiene otra forma distinta: se envuelve como una función async y se llama con el elemento y el evento del navegador.
Todas las puertas, y todas ellas se publican
Una ranura de expresión, incluidas ambas ranuras de condición
Una unidad de programa JavaScript, compilada al guardar
Call JavaScript, que ejecuta esa unidad desde un flujo de trabajo
Execute JavaScript, código en línea al que se le entregan el elemento y el evento
xu-script en un elemento, ejecutado cuando este se hace visible por primera vez
xu-style y xu-style-global, con o sin ámbito, en un elemento
xu-cdn, una URL externa de CSS o JS cargada antes de que el nodo se renderice
Vista previa en vivo

La vista previa es un par de su editor, y tiene permiso para darse por vencida con usted.

Una sesión de vista previa se abre en un canal con el nombre de su token de sesión y pide documentos a su pestaña abierta de uno en uno por id. Studio responde desde memoria con una copia del documento que hay en su pantalla, así que no hay ningún bundler en ese camino y un cambio guardado está en la sesión en ejecución de inmediato. También hay un plazo. Si su pestaña no ha respondido en cinco segundos, esa sesión pasa a la copia compilada del servidor y no vuelve a preguntarle en toda su vida.

Una corrección respecto de lo que decía antes este diagrama. El aviso al guardar no es document_changed. Un cambio de código guardado viaja como studio_doc_saved y lleva el documento completo, y por eso la vista previa vuelve a renderizar los paneles ligados a ese documento en lugar de recargar la página. document_changed es otro canal completamente distinto, retransmitido desde la sala de sockets del servidor, y lo único que hace es volver a pedir los datasources en tiempo real cuyo id de tabla coincida. El pestillo de cinco segundos es el coste honesto de todo este montaje, y es la razón por la que Recarga forzada es un botón de primer nivel y no un añadido de depuración.
La consola

La caja de comandos es eval, ejecutándose dentro de la página que usted está previsualizando.

No hay vocabulario de comandos que aprender, porque no hay conjunto de comandos. Lo que usted escribe se envía a la vista previa como una cadena, se evalúa allí, y el resultado vuelve y se renderiza como un árbol de dos niveles de profundidad. Todo lo que la página en ejecución puede ver, usted puede verlo: el objeto de sesión, los datasources, los ayudantes del runtime. Un error lanzado vuelve con una marca de error y se renderiza en rojo. Inspeccionar es la otra mitad: actívelo y todo elemento con un id de ui se resaltará bajo el cursor; al hacer clic en uno se devuelve el componente que lo contiene y el nodo dentro de él, con un botón que abre ese componente en su pestaña UI y selecciona el nodo en el árbol. Eso es ir de un píxel a un nodo en dos clics.

... conectando con la sesión
$ new URLSearchParams(location.search).get("engine_mode")
"live_preview"
$ typeof func.index.delete_pouch
"function"
$ SESSION_OB.root_element
SESSION_OB is not defined error: true
inspeccionar activado . todo elemento con un id de ui se resalta bajo el cursor
haga clic en un píxel de la vista previa
live_preview_element_reference_result { prog_id, node } [ Abrir ]
Abrir . el componente contenedor se abre en su pestaña UI, y ese nodo queda seleccionado en el árbol
obtener sesión . live_preview_session_result
{ __type: "Element", tagName, id, className } "[Circular]" "[Function name]"
root_element, WORKER_OBJ y DS_UI_EVENTS_GLB se eliminan antes del envío
El prompt es un signo de dólar en cuanto la sesión informa que está lista, y tres puntos mientras sigue conectando. La flecha arriba recorre los comandos que usted escribió, solo en memoria, así que una recarga le deja con el historial vacío.
Guardado

Un guardado es una escritura local. La versión la escribe otra cosa, después.

No hay guardado automático. Pulsar guardar entrega el documento a un worker, que lo sella y lo escribe en una base de datos dentro de su navegador, y eso es todo lo que hace su pulsación. Todo lo posterior es maquinaria distinta: una difusión a sus otras pestañas, la replicación hacia la base de datos de la aplicación y un proceso del servidor que observa esa base de datos, que es lo que realmente acuña una versión. El orden importa, porque le dice qué sobrevive a una caída de red y qué no.

La consecuencia es la parte honesta. Su trabajo está a salvo de una caída de red porque la primera escritura es local, pero el momento de la versión no lo decide usted: la píldora puede mostrar v y unos puntos suspensivos durante un instante mientras se registra la captura, y el popover vuelve a consultar con una espera fija de 1200, 3000, 6000, 12000 y 24000 milisegundos hasta que se resuelve. La misma estructura tiene una ventaja. Como la captura observa la base de datos y no su editor, una edición escrita por un plugin o por un proceso del servidor se versiona exactamente igual que una edición que usted escribió.
Historial

La píldora tiene cuatro estados, y uno de ellos dice que su pestaña está desactualizada.

La etiqueta junto al editor es derivada, no almacenada: compara la revisión que usted guardó con la instantánea más reciente del servidor. Actual significa que existe una instantánea exacta de su revisión, o que la diferencia son solo metadatos de compilación. Registrando significa que su revisión va por delante y la captura todavía no ha aterrizado. Atrasada significa que su revisión guardada es más antigua que la instantánea más reciente, es decir, que esta sesión del navegador se durmió durante varias revisiones, y el popover lo dice en un aviso en lugar de afirmar que usted está al día. Desconocida significa misma generación, contenido distinto, lo que el código fuente llama una rama divergente. Al seleccionar una fila más antigua se previsualiza en el sitio con su copia de trabajo guardada por referencia, y mientras hay una vista previa activa ambos caminos de guardado rechazan el documento de plano. Restaurar es la única acción que escribe: primero vuelve a leer la revisión viva del servidor, pregunta antes de sobrescribir un cambio que llegó mientras usted miraba, pregunta de nuevo antes de descartar sus ediciones sin guardar, y luego se encadena a la revisión más fresca conocida para que la restauración aterrice limpiamente en la cabeza.

Vista previa
vista previa en el sitio, el guardado está bloqueado, escape devuelve su copia de trabajo
Restaurar
restaurar guarda hacia adelante, así que la restauración también es reversible

guardar, capturado en el servidor un instante después

Lo que no hay, en detalle: ni ramas, ni vista de comparación o diferencias, ni fusión, ni mensajes de commit. El historial es por programa, no por proyecto. Los números de versión son posicionales, así que la v3 de hoy no es la v3 de la semana que viene. Se conservan cincuenta instantáneas por documento y el resto se eliminan en lugar de archivarse. La captura es asíncrona y del lado del servidor, así que un guardado reciente se muestra como registrando hasta que aterriza. Y deshacer es un sistema distinto que no comparte nada con este: cincuenta pasos por documento, agrupados con un margen de cuatrocientos milisegundos para que un arrastre o una ráfaga de escritura sea un solo paso, limitado al nodo que usted tenga seleccionado, guardado solo en la memoria del navegador y perdido en cuanto recarga. Tras una recarga, este carril es la única vuelta atrás.

Dos editores

Hay un bloqueo. No hay fusión.

El bloqueo es un check-in, y es por nodo. El guardado depende de él: un nodo con check-in de otra persona se rechaza indicando el nombre de quien lo tiene, y un nodo del que nadie ha hecho check-in se rechaza con Haga check-in, tras recorrer la cadena de padres para ver si algo por encima está retenido. Los borradores están exentos, y también una aplicación que nunca se ha guardado. Lo que no está bloqueado es el desenlace una vez que una revisión de otra persona sí llega a un documento que usted tiene abierto. Studio decide entre exactamente tres opciones, y calcula si usted tiene ediciones sin guardar antes de actualizar su referencia, y ese orden es lo que hace fiable la respuesta.

  1. 01

    Su copia está limpia, así que se queda con la de la otra persona entera

    No hay nada suyo en juego, así que Studio adelanta el documento abierto a la revisión de la otra persona por completo y refresca el indicador de la pestaña de inmediato, sin esperar al observador limitado. Usted se entera porque el contenido cambió bajo sus pies, no porque un diálogo se lo preguntara.

  2. 02

    Usted tiene ediciones, y la escritura de la otra parte fue del compilador

    Studio compara los dos documentos dejando fuera la lista de revisiones, los metadatos de studio, el resultado de compilación, el error de compilación, las tres marcas de tiempo y las dependencias del script. Cuando lo que queda es idéntico, la revisión entrante es el worker de compilación guardando su propia salida. Sus ediciones se quedan exactamente donde están, solo se adoptan la revisión y los campos de compilación, y no se le avisa, porque a usted no le ha pasado nada.

  3. 03

    Usted tiene ediciones, y el contenido de la otra parte sí difiere

    Se conservan todas sus ediciones y solo se incrementa la revisión, de modo que su siguiente guardado se encadena al de la otra persona en vez de colisionar. Después se le avisa durante doce segundos: un colaborador guardó este documento, y sus ediciones sin guardar sobrescribirán los cambios de esa persona cuando usted guarde. El aviso tiene exactamente una acción, descartar las mías y usar las suyas, que sustituye su copia de trabajo por el documento del servidor. Ignórelo y guarde, y su versión será la que sobreviva. No hay fusión a nivel de campo, ni comparación, ni presencia, ni cursores compartidos.

Validación

Un único archivo comprobador, ejecutado en su navegador o en el servidor según dónde esté usted.

La validación no es un servicio en otro sitio. Es un único archivo, runtime/js/modules/xuda-studio-checker.mjs, de 4714 líneas, importado directamente por el front end de Studio e importado por ruta en el servidor por el módulo de la aplicación. Studio decide en el momento de la llamada quién lo ejecuta. En línea, y en un proyecto que no es un borrador, lanza run_programs_checker como trabajo en segundo plano. Sin conexión, o dentro de un borrador, ejecuta la misma comprobación sobre cada programa en el navegador y registra un resultado con la misma forma. Los hallazgos se dividen en errores y advertencias por nada más que un campo de tipo E o W. Y ahora la parte honesta: no detiene una compilación. build_app recibe ignore_errors con valor por defecto true, ningún punto de llamada del producto envía nunca false, y el total de errores se escribe en la propia versión, así que cada versión registrada lleva el número de errores con los que se publicó.

  • Dieciocho funciones de comprobación se reparten entre once tipos de programa: globals, table, component, javascript, get_data, set_data, batch, api, alert, route y ai_agent. Una carpeta es deliberadamente una operación vacía, y cualquier tipo no reconocido genera CHK_MSG_GEN_050.
  • Una ejecución en el servidor escribe un documento de comprobación por programa, con clave en esa revisión del programa, de modo que un programa que nadie ha tocado desde la última pasada se omite directamente en vez de volver a comprobarse.
  • Cada código del catálogo generado lleva los mismos campos: título, severidad, dominio, categoría, resumen, causas, pasos de resolución, un slug de ayuda, patrones heredados y códigos relacionados. Se genera a partir del registro de errores, no se escribe a mano.
  • Los dos entornos no producen una salida idéntica. La ejecución en el servidor se inicializa con la lista de despliegues y puede marcar un programa como usado por un despliegue. La ejecución en el navegador no, así que el veredicto de no está en uso difiere entre ambas.
Familias de códigos de comprobación más grandes, por referencias en el código fuente del comprobador
CHK_MSG_OBJ_GEN
81 referencias
Reglas generales de objeto, con diferencia la familia con la que usted se topa primero
CHK_MSG_PRG_TRG
48 referencias
Disparadores de flujo de trabajo, la segunda superficie más cargada del árbol
CHK_MSG_PRG_FLD
17 referencias
Campos, incluido a partir de qué se puede calcular un campo
CHK_MSG_PRG_AGN
17 referencias
Agentes, empatados con los campos en el tercer puesto de diecisiete familias
Publicación

Una compilación y un despliegue son objetos distintos, y una versión se sitúa entre ambos.

Compilar escribe una instantánea numerada. Desplegar apunta una máquina hacia una de ellas. El número de la insignia, #37, es app_build_id, un contador por proyecto que sube de uno en uno cada vez que se ejecuta build_app. El 1.4.0 que aparece al lado es una cadena aparte que usted escribe, validada por un patrón simple de mayor, menor y parche en la propia insignia. Se mueven de forma independiente, y ambos quedan sellados en el documento del despliegue, de modo que un despliegue siempre registra exactamente qué instantánea está sirviendo.

Cuando aterriza la compilación #38, todo despliegue del proyecto con código de estado 4 recibe update_required en true y una notificación new_version_available con el nuevo id de compilación. Con eso no se copia nada a ninguna máquina, y ese despliegue sigue sirviendo la #37 hasta que alguien lo mueva. Mover un despliegue a una compilación nueva es update_app_version, un trabajo aparte de diez pasos con permiso de administrador. Vuelve a conectarse por SSH con hasta diez intentos a intervalos de diez segundos, sincroniza el drive con rsync, recalcula a qué programas y tablas puede acceder este despliegue, elimina y vuelve a copiar tanto el directorio del programa como el del runtime, reinstala el paquete de plugins, ejecuta pm2 delete all, reinicia el enrutador de websockets y el ecosistema, purga el CDN y escribe un paquete de arranque nuevo. Los procesos se caen y vuelven. Dos despliegues del mismo proyecto pueden quedarse en números de compilación distintos indefinidamente, y nada los reconcilia por usted.
Aprovisionamiento

Pulsar desplegar no instala nada.

La llamada de despliegue es la primera mitad de un apretón de manos. Crea registros, pide una máquina y retorna mientras la máquina todavía se está creando. La instalación empieza cuando la propia máquina llama de vuelta a la plataforma, y por eso la barra de progreso puede quedarse quieta un rato a mitad de camino y por eso la escalera de estados tiene un peldaño de espera: 1 creado, 2 instalando o esperando, 3 activo, 4 fallido o terminado.

  1. 01

    El formulario retorna antes de que exista nada

    deploy_app escribe el documento del despliegue, opcionalmente un documento de centro de datos, pide al proveedor que cree una máquina de forma asíncrona y le devuelve el control. No se ha instalado nada y no se ha copiado nada. El despliegue se queda en el código de estado 2 con la descripción servidor de la granja esperando a instalar, y realmente no hay nada más que mostrar hasta que el siguiente paso ocurra por su cuenta.

  2. 02

    La máquina llama de vuelta a la plataforma

    El primer arranque de la máquina nueva hace curl a un webhook de la plataforma con el id de la aplicación. Ese manejador reabre el mismo trabajo que usted ya está mirando, sustituye su lista de pasos por la lista de instalación de veinticuatro entradas y ejecuta install_hosting_resources. El trabajo que usted inició para la fase uno es el que informa de la fase dos, y por eso los nombres de los pasos cambian a mitad de camino.

  3. 03

    La instalación se ejecuta, reintenta si hace falta y aterriza en el estado 3

    La instalación se envuelve en cuatro intentos con treinta segundos de espera, porque una máquina recién creada puede seguir reiniciándose cuando llega la primera conexión SSH. Escribe el archivo de ecosistema de pm2, ejecuta pm2 delete y luego pm2 start contra él, y termina en el código de estado 3, servidor listo. Esta página no da una duración para nada de esto, porque en el código fuente no consta ninguna y nosotros no la medimos.

Tipos de despliegue

Dieciséis tipos de despliegue, y lo que ejecuta realmente cada uno.

El mismo proyecto se publica en dieciséis tipos de despliegue declarados, en cuatro grupos: seis web, cinco de servidor, tres de escritorio y dos móviles. Cada tipo declara además su propio conjunto de pestañas a partir de una lista base de catorce, así que un despliegue cron descarta lanzamiento, dominio, cortafuegos, analítica y asistencia al usuario y se queda en nueve pestañas, mientras que una MPA conserva las catorce y los cinco tipos de artefacto insertan una pestaña de publicación en su lugar. Agrupados por lo que la instalación escribe realmente en el archivo de ecosistema de pm2, quince de ellos se reducen a cinco formas. El decimosexto, mcp, se declara junto a los demás y no tiene una entrada de ecosistema propia que hayamos podido rastrear.

Qué se ejecuta en la máquina
Qué lanza
Web: spa, mpa, pwa, embedUna aplicación de pm2 llamada webserver, en modo de ejecución cluster con instances en max, más una unidad de systemd para el enrutador de websocketsEl paquete de runtime sellado por la compilación sobre HTTPS en el puerto 443, escuchando en un socket de doble pila
Web: previewLa misma aplicación webserver, y una segunda aplicación de pm2 llamada previewUna salida de servidor Nuxt en el puerto 3023 junto a la propia aplicación
Web: user groupLa misma aplicación webserver, y una segunda aplicación de pm2 llamada user_groupUna salida de servidor Nuxt en el puerto 3022 para la superficie de grupo
Servidor: cron, api, node, emitterUna única aplicación de pm2 en modo de ejecución fork, ejecutando el archivo de entrada de ese tipo, xuda-cron.mjs o xuda-api.mjs y así sucesivamenteEl paquete de servidor de esta compilación. Un disparo de cron bifurca un proceso hijo, espera a que informe que está listo, le envía la ejecución y registra lo que devuelve
Artefactos: ios, android, macos, windows, linuxNada. La ruta de macOS omite deliberadamente la llamada de despliegue y escribe un documento de despliegue sin ninguna máquina de alojamiento detrásUna pestaña de publicación y un envío a un servicio de compilación externo. Rastreamos lo suficiente para saber que es otra tubería distinta, no lo suficiente para describirla aquí
El runtime

Un intérprete, dos lados, una llamada.

Lo que un despliegue publica realmente no es código fuente generado ni un binario compilado. Es un paquete intérprete más su árbol de programas en JSON, y el mismo intérprete se ejecuta a ambos lados del cable. En el navegador, eso es una etiqueta script y un constructor. En el servidor, es un import dinámico del paquete hermano y una bandera. Cuando una página se renderiza del lado del servidor, el servidor pide al worker que ejecute un programa hasta obtener una cadena, y recibe de vuelta tanto el marcado como un payload. El payload se incrusta en la página, y el elemento de montaje se marca con el modo de renderizado y la activación de cliente que el servidor resolvió para él.

Dos límites que conviene llevarse de esta imagen. No hay tree shaking por aplicación: cada despliegue publica el paquete completo de su compilación y solo cambia el JSON de los programas, y aunque en el directorio del runtime existen paquetes slim y mini, los renderizadores de despliegue hacen referencia al completo. Y si el carril de la derecha existe siquiera es un ajuste por despliegue que se resuelve en cada renderizado de página: el modo de cómputo se resuelve como main, worker o server, y tanto main como worker fuerzan el renderizado en el cliente sin activación en servidor, lo que deja al navegador haciéndolo todo.
Plugins

La categoría del nombre del paquete decide dónde aparece un plugin.

Los 62 plugins propios se nombran de la misma manera, y la categoría de ese nombre también está en el registro del plugin. Esa es toda la regla de enrutamiento: instale uno y aparecerá en el selector de datasource, en la barra de inserción, en el selector de iconos o en la lista de acciones de flujo de trabajo, sin nada que conectar. Una instalación tampoco es una marca de funcionalidad. El servidor escribe un registro en el proyecto con la versión, el tipo y el manifiesto de archivos, ejecuta un npm install real en el directorio de plugins de ese proyecto, sincroniza ese directorio hacia los servidores y copia todo el árbol de archivos del paquete en la base de datos del proyecto con clave por ruta relativa.

Dos de los cuatro rechazos tienen que ver con ese par: menos tres cuando el padre indicado no está instalado en este proyecto, menos cuatro cuando el registro del padre no incluye a este hijo. Los otros dos son menos uno para un plugin que no existe y menos dos cuando quien llama pidió solo plugins gratuitos. El consentimiento va más hondo que la comodidad, porque el archivo de servidor de un plugin se fusiona en el mismo objeto de script que su padre, así que un plugin propio que depende de uno de terceros pasa a tener confianza de tercero.
Límites honestos

Nueve cosas que Studio no hace.

  • La caja de texto de la pantalla de inicio de Studio no construye nada: envía una petición de chat normal limitada a 1024 tokens y, con los mensajes ocultos, la respuesta ni siquiera se renderiza, porque el constructor vive en el panel de IA de un proyecto que ya está abierto.
  • Elegir Plantilla no abre una galería dentro del editor: abre el asistente de creación del panel de control en una segunda pestaña del navegador, y el proyecto que bifurca es una instantánea sin nada que después lo vincule con su origen.
  • Un borrador no está en un servidor: vive en un perfil de navegador de un solo dispositivo hasta que usted pulsa Guardar, y borrar los datos del sitio acaba con él.
  • El check-in que condiciona el guardado no es una concesión del servidor: es un campo del documento, así que un guardado que nunca llega al servidor nunca lo libera, y nada fusiona dos ediciones por usted.
  • Nada puede hacer que una compilación falle por errores de validación: el comprobador se ejecuta primero y su total de errores se escribe en la versión, pero ningún punto de llamada pide a la compilación que se niegue.
  • Una compilación terminada no toca nada que ya esté en producción, y dos despliegues de un mismo proyecto pueden quedarse en números de compilación distintos todo el tiempo que usted los deje así.
  • No hay despliegue por fases: la insignia de compilación puede mostrar un aviso de preparado pero no en producción, pero una compilación normal asigna el mismo valor al número preparado y al de producción, así que ese aviso no puede aparecer.
  • No hay recorte del runtime por aplicación, así que cada despliegue publica el mismo paquete intérprete completo de su compilación y solo cambia el JSON de los programas.
  • Un despliegue programado no ejecuta una pantalla, solo programas batch, get_data y set_data, y un paso de JavaScript del lado del servidor se detiene a los sesenta segundos.
Antes de decidirse

Las preguntas que se está haciendo ahora mismo

Cada una de estas siete respuestas termina en un límite. Están aquí porque usted se toparía con las siete en la primera semana de todos modos.

Vea

Vea a Studio abrir un proyecto y rellenar el árbol.

Abra un proyecto vacío y vea lo que se escribe.

Dos documentos, una ruta, y el editor está abierto. Nada llega a un servidor hasta que usted guarda, y nada llega a un visitante hasta que usted compila y luego mueve un despliegue a esa compilación a propósito.