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.
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:
- 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. - 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 defiles. 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.