Saltar al contenido principal

Expulsa tu panel

Publicar en Vibedasher es el final normal del ciclo: eso es lo que mantiene un panel alojado, gobernado y conectado a datos que alguien mantiene. Expulsar es la otra puerta, y está abierta a propósito. El panel es React y SQL sencillos, y en cualquier momento puedes llevarte el árbol completo y ejecutarlo en tu propia aplicación, en tu propio hosting, bajo tu propio build.

Esto no es una exportación de una imagen o un blob JSON que tendrías que reimplementar. Es el código que corre: componentes, páginas, gráficos, stores, hooks, un package.json reconstruido a partir de los imports reales del árbol, una configuración de Tailwind, una entrada CSS, un tsconfig.json, una configuración de Vite, un index.html, y un main.tsx que lo monta. npm install && npm run dev lo sirve como su propia aplicación React.

nota

Expulsar es lo opuesto a incrustar. Incrustar pone nuestro panel renderizado dentro de un iframe en tu página. Expulsar te entrega el código y nos saca por completo de la ruta de renderizado — solo conservamos el motor de datos por debajo. Si la pregunta es «¿este código es mío?», expulsar es la respuesta. Si todavía estás eligiendo entre las dos, Incrustar o expulsar las pone una al lado de la otra.

Las tres llamadas​

  • eject_viz(viz_id, out_dir=...) — el árbol completo más un manifiesto de conexión: el contrato de consulta, los tokens de tema, el punto de entrada y el orden de sus props, la lista de archivos, la región, y el carril de datos.
  • eject_viz_files(viz_id, paths=[...]) — el contenido de los archivos a demanda. Es el seguimiento cuando una respuesta vino paginada, y también es la forma de leer un archivo de forma barata.
  • eject_instructions() — la referencia de conexión, devuelta como datos estructurados en lugar de prosa que tengas que analizar. Todo lo de esta página es una lectura de ella; llámala cuando quieras el original legible por máquina.

Pasa out_dir, y ten claro en qué transporte estás​

Un panel real tiene del orden de cuarenta y tantos archivos y medio millón de caracteres. Con out_dir apuntando a una ruta absoluta, el árbol se escribe directamente a disco y la respuesta se mantiene pequeña por grande que sea el árbol.

Sobre el transporte stdio — el arranque estándar, donde tu cliente MCP lanza el servidor como un subproceso local bajo tu propio usuario — esa ruta es tu propio disco, que es lo que hace legítimo escribir ahí. Sobre el transporte HTTP sería el disco del servidor, así que out_dir se rechaza ahí en lugar de escribir archivos en un sitio que no puedes leer mientras informa de éxito. La paginación es la única vía en HTTP.

Sin out_dir, los archivos se empaquetan en la respuesta hasta un presupuesto de caracteres y el resto vuelve como delivery.pendingPaths. Esa lista es el contrato: una respuesta con un pendingPaths no vacío es una página, no un árbol. Sigue llamando a eject_viz_files con ella hasta que vuelva vacía.

Lee manifest.dataLane antes de escribir cualquier conexión de datos​

Esta es la primera decisión y la que no puedes cambiar después, porque decide quién puede leer el panel.

Carril A — dataLane.id == "wasm": ya renderiza datos reales​

Para una visualización pública y publicada, la expulsión emite un pequeño runner (vibedasherWasm.ts) y el árbol renderiza filas reales con npm run dev sin ninguna credencial: sin clave de API, sin token de incrustación, sin backend propio, y funciona desde cualquier origen incluidos localhost y un simple servidor de archivos estáticos.

El mecanismo: un GET anónimo del plan wasm de la visualización devuelve un extracto Parquet prefirmado por conjunto de datos; el runner registra cada uno bajo su alias SQL en DuckDB-WASM y ejecuta el SQL de cada gráfico en el navegador del visitante. Su única dependencia, @duckdb/duckdb-wasm, está en npm. main.tsx espera el calentamiento del runner antes de montar, así que una configuración rota es un panel pintado en lugar de una rejilla de mosaicos en blanco.

Aquí no hay nada que conectar. No añadas una credencial encima — te compra un arranque roto a cambio de nada.

Lo que este carril no es: sirve el extracto publicado, no una consulta viva, así que refrescar lo que muestra significa volver a publicar. Y existe solo mientras la visualización siga siendo pública y publicada.

Qué obtiene cada visitante, dicho con claridad. El extracto está indexado por conjunto de datos e instantánea, nunca por una consulta — el mismo SELECT con y sin WHERE devuelve el archivo idéntico. Así que contiene todas las filas de cada conjunto de datos que lee el panel, y es un único archivo, el mismo, para todo el mundo. Un filtro que añadas en la página cambia lo que se dibuja, nunca lo que se entregó, y los bytes están en la pestaña de red en cualquier caso. Eso es inherente a poner los datos en el navegador. Si los visores deben ver filas distintas, toma el carril B.

Carril B — dataLane.id == "token": cualquier otra visualización​

No hay carril sin credenciales y la expulsión no emitió ninguno. El endpoint del plan wasm está protegido por la misma comprobación de público-y-publicado que el resto de la superficie anónima, así que apuntar a él a mano no ayuda. npm run dev sigue montando, y pinta un panel que nombra el endpoint que necesita — no falla en silencio y no sustituye datos falsos.

Dos montajes funcionan, y los dos ponen la credencial en un servidor que tú controlas:

  1. Tu aplicación sirve una ruta de token de incrustación. Del mismo origen, respondiendo application/json {"token": "..."}, emitido con la clave de API que guarda en el servidor. Apunta la URL de token de incrustación del árbol a ella y construye el cliente con el token.
  2. Consultas en código de servidor confiable con new Vibedasher({ apiKey }) y pasas el resultado hacia abajo, o haces de proxy de { sql, params } a través de tu propia ruta.

Nunca pongas una clave de API en código de navegador. Lleva la autoridad completa de la cuenta y no tiene scopes. Un token de incrustación con alcance limitado es la credencial del lado cliente; una clave de API no lo es.

Esos dos son también las únicas formas que pueden mostrar a un visor menos filas que a otro. Un token de incrustación puede llevar filters, que la plataforma añade a cada consulta que autoriza — en el servidor, antes de cualquier join, agregación o LIMIT, donde la página no puede ensancharlos. En código de servidor confiable, acotas el SQL tú mismo.

Si la expulsión informa del carril token con la razón no-wasm-extract en una visualización que sí es pública y publicada, el conjunto de datos simplemente todavía no tiene extracto. Pedir uno es lo único que lo construye — mira El carril de consultas wasm. Volver a publicar no cuece un extracto, así que «publicar y volver a expulsar» da vueltas para siempre.

Instala el cliente​

npm i @vibedasher/client
pip install vibedasher

El paquete de npm es @vibedasher/client. No existe @vibedasher/sdk y nunca existirá. Instala los paquetes reales en lugar de copiarlos dentro de tu repo.

Estilos, typecheck, y el App Router​

Los estilos son tres piezas y necesitas las tres. tailwind.config.js (modo oscuro con una estrategia de selector ligada a un atributo que el árbol pone en su propia raíz, así que el modo oscuro del panel queda acotado independientemente del de tu aplicación, más una safelist para las clases ensambladas en ejecución y por tanto invisibles al escáner de Tailwind); styles/dashboard.css (la entrada de Tailwind y un bloque :root de tokens de diseño como valores por defecto en tiempo de build); y themeStyles.ts (los mismos tokens como una cadena que la aplicación inyecta en ejecución, por tema). El CSS te da un primer pintado tematizado; themeStyles.ts te da el cambio claro/oscuro en vivo. Si tu aplicación ya usa Tailwind con un modo oscuro basado en clase, no fusiones las configuraciones — una configuración de Tailwind soporta un único ajuste de modo oscuro. En Tailwind v4, referencia la configuración entregada con @config.

Typecheck. npm run typecheck (tsc --noEmit) debe informar de cero errores en un árbol recién expulsado, y dos gates lo sostienen en cada ejecución de nuestra suite de tests — uno hace typecheck de una expulsión generada completa, el otro instala, hace typecheck y construye una escrita a mano. Dos condiciones acotan la afirmación, y ninguna es una salida. Se cumple bajo el tsconfig.json entregado, que pone strict: false a propósito, porque los efectos de gráfico generados están escritos contra filas de consulta débilmente tipadas; activar strict producirá errores que luego serán tuyos de mantener. Y se cumple para un árbol cuyos archivos de rama se escribieron recientemente — un panel publicado hace suficiente tiempo lleva archivos congelados de una forma más antigua, dos puntos de los cuales se sabe que fallan tsc aunque construyan y corran correctamente, ya que Vite transpila sin hacer typecheck. El arreglo es volver a publicar, lo que reescribe los archivos de rama a partir de la forma actual.

Next.js App Router. Cada módulo es un server component por defecto. App.tsx y los módulos stores/ ya llevan 'use client' en la expulsión — los stores tocan localStorage en el ámbito del módulo y se caerían en el servidor. Marcar App.tsx mete todo su grafo de imports en el bundle del cliente, así que nada más necesita la directiva a menos que importes un gráfico, componente o store directamente desde un server component.

Qué no te entrega la expulsión​

  • Nada de lo que hay en manifest.internalDescriptors — metadatos de plataforma, ya excluidos de files. No los copies.
  • Nada de lo que hay en manifest.hostOnlyImports — servido desde el propio bundle de nuestro renderizador, en ningún registro, deliberadamente ausente. Reemplázalos antes de construir.
  • La plataforma de datos. Expulsar nos saca de la ruta de renderizado, no de la ruta de datos. Las conexiones, conjuntos de datos, actualizaciones, proyectos dbt, la gobernanza y el motor de consultas de debajo se quedan donde se mantienen — esa es la mitad de la división que no intentabas poseer. El carril sin credenciales es la única excepción, y solo porque está leyendo un extracto publicado en lugar de consultar en vivo.