Pular para o conteúdo principal

Ejete seu painel

Publicar na Vibedasher é o fim normal do ciclo: é isso que mantém um painel hospedado, governado e ligado a dados que alguém mantém. Ejetar é a outra porta, e ela está aberta de propósito. O painel é React e SQL puros, e a qualquer momento você pode levar a árvore inteira e rodá-la no seu próprio app, na sua própria hospedagem, sob o seu próprio build.

Isso não é a exportação de uma imagem ou de um blob JSON que você teria de reimplementar. É o código-fonte que roda: componentes, páginas, gráficos, stores, hooks, um package.json reconstruído a partir dos imports reais da árvore, uma configuração do Tailwind, uma entrada CSS, um tsconfig.json, uma configuração do Vite, um index.html, e um main.tsx que o monta. npm install && npm run dev serve tudo como o seu próprio app React.

observação

Ejetar é o oposto de incorporar. Incorporar coloca o nosso painel renderizado dentro de um iframe na sua página. Ejetar entrega o código-fonte e nos tira inteiramente do caminho de renderização — nós ficamos apenas com o motor de dados por baixo. Se a pergunta é "esse código é meu?", ejetar é a resposta. Se você ainda está escolhendo entre as duas, Incorporar ou ejetar coloca as duas lado a lado.

As três chamadas​

  • eject_viz(viz_id, out_dir=...) — a árvore inteira mais um manifesto de conexões: o contrato de consulta, os tokens de tema, o ponto de entrada e a ordem de suas props, a lista de arquivos, a região, e a via de dados.
  • eject_viz_files(viz_id, paths=[...]) — o conteúdo dos arquivos sob demanda. É o acompanhamento quando uma resposta veio paginada, e é também como ler um arquivo de forma barata.
  • eject_instructions() — a referência de conexões, devolvida como dados estruturados em vez de prosa que você tem de interpretar. Tudo nesta página é uma leitura dela; chame-a quando quiser o original legível por máquina.

Passe out_dir, e saiba em qual transporte você está​

Um painel real fica na ordem de quarenta e tantos arquivos e meio milhão de caracteres. Com out_dir apontando para um caminho absoluto, a árvore é escrita direto em disco e a resposta continua pequena por maior que a árvore seja.

Sobre o transporte stdio — a inicialização padrão, onde o seu cliente MCP sobe o servidor como um subprocesso local sob o seu próprio usuário — esse caminho é o seu próprio disco, e é isso que torna legítimo escrever lá. Sobre o transporte HTTP seria o disco do servidor, então out_dir é recusado ali em vez de escrever arquivos em algum lugar que você não pode ler enquanto relata sucesso. A paginação é a única rota no HTTP.

Sem out_dir, os arquivos são empacotados na resposta até um orçamento de caracteres e o resto volta como delivery.pendingPaths. Essa lista é o contrato: uma resposta com um pendingPaths não vazio é uma página, não uma árvore. Continue chamando eject_viz_files com ela até que volte vazia.

Leia manifest.dataLane antes de escrever qualquer conexão de dados​

Esta é a primeira decisão e a que você não pode mudar depois, porque ela decide quem pode ler o painel.

Via A — dataLane.id == "wasm": ela já renderiza dados reais​

Para uma visualização pública e publicada, a ejeção emite um pequeno runner (vibedasherWasm.ts) e a árvore renderiza linhas reais com npm run dev sem credencial nenhuma: sem chave de API, sem token de incorporação, sem backend seu, e funciona de qualquer origem, incluindo localhost e um simples servidor de arquivos estáticos.

O mecanismo: um GET anônimo do plano wasm da visualização devolve um extrato Parquet pré-assinado por conjunto de dados; o runner registra cada um sob seu alias SQL no DuckDB-WASM e executa o SQL de cada gráfico no navegador do visitante. Sua única dependência, @duckdb/duckdb-wasm, está no npm. O main.tsx aguarda o aquecimento do runner antes de montar, então uma configuração quebrada é um painel pintado em vez de uma grade de tiles em branco.

Não há nada a conectar aqui. Não adicione uma credencial por cima — isso compra uma inicialização quebrada em troca de nada.

O que essa via não é: ela serve o extrato publicado, não uma consulta viva, então atualizar o que ela mostra significa republicar. E ela existe apenas enquanto a visualização permanecer pública e publicada.

O que cada visitante recebe, dito sem rodeios. O extrato é indexado por conjunto de dados e snapshot, nunca por uma consulta — o mesmo SELECT com e sem WHERE devolve o arquivo idêntico. Então ele contém todas as linhas de cada conjunto de dados que o painel lê, e é um único arquivo, o mesmo, para todo mundo. Um filtro que você adiciona na página muda o que é desenhado, nunca o que foi entregue, e os bytes estão na aba de rede de qualquer forma. Isso é inerente a colocar os dados no navegador. Se os visualizadores precisam ver linhas diferentes, tome a via B.

Via B — dataLane.id == "token": qualquer outra visualização​

Não existe via sem credenciais e a ejeção não emitiu nenhuma. O endpoint do plano wasm é protegido pela mesma checagem de público-e-publicado que o resto da superfície anônima, então apontar para ele à mão não ajuda. O npm run dev ainda monta, e pinta um painel nomeando o endpoint de que precisa — ele não falha silenciosamente e não substitui por dados falsos.

Duas ligações funcionam, e as duas colocam a credencial em um servidor que você controla:

  1. Seu app serve uma rota de token de incorporação. Mesma origem, respondendo application/json {"token": "..."}, emitido com a chave de API que ele guarda no servidor. Aponte a URL de token de incorporação da árvore para ela e construa o cliente com o token.
  2. Você consulta em código de servidor confiável com new Vibedasher({ apiKey }) e passa o resultado adiante, ou faz proxy de { sql, params } pela sua própria rota.

Nunca coloque uma chave de API em código de navegador. Ela carrega autoridade total da conta e não tem escopos. Um token de incorporação com escopo é a credencial do lado do cliente; uma chave de API não é.

Essas duas são também as únicas formas capazes de mostrar a um visualizador menos linhas do que a outro. Um token de incorporação pode carregar filters, que a plataforma acrescenta a toda consulta que autoriza — no servidor, antes de qualquer join, agregação ou LIMIT, onde a página não pode alargá-los. Em código de servidor confiável, você mesmo restringe o SQL.

Se a ejeção relatar a via token com a razão no-wasm-extract em uma visualização que é pública e publicada, é só que o conjunto de dados ainda não tem extrato. Pedir um é a única coisa que o constrói — veja A via de consulta wasm. Republicar não assa um extrato, então "publicar e ejetar de novo" roda em círculos para sempre.

Instale o cliente​

npm i @vibedasher/client
pip install vibedasher

O pacote npm é @vibedasher/client. Não existe @vibedasher/sdk e nunca vai existir. Instale os pacotes reais em vez de copiá-los para dentro do seu repositório.

Estilos, typecheck e o App Router​

O estilo são três peças e você precisa das três. tailwind.config.js (modo escuro por uma estratégia de seletor atrelada a um atributo que a árvore coloca na sua própria raiz, de modo que o modo escuro do painel fica delimitado independentemente do do seu app, mais uma safelist para as classes montadas em tempo de execução e portanto invisíveis ao scanner do Tailwind); styles/dashboard.css (a entrada do Tailwind e um bloco :root de tokens de design como padrões de tempo de build); e themeStyles.ts (os mesmos tokens como uma string que o app injeta em tempo de execução, por tema). O CSS dá a você uma primeira pintura tematizada; o themeStyles.ts dá a troca claro/escuro ao vivo. Se o seu app já usa Tailwind com modo escuro baseado em classe, não mescle as configurações — uma configuração do Tailwind suporta um único ajuste de modo escuro. No Tailwind v4, referencie a configuração que vem junto com @config.

Typecheck. npm run typecheck (tsc --noEmit) deve relatar zero erros em uma árvore recém-ejetada, e dois gates sustentam isso em cada execução da nossa suíte de testes — um faz typecheck de uma ejeção gerada completa, o outro instala, faz typecheck e constrói uma escrita à mão. Duas condições limitam a afirmação, e nenhuma delas é uma ressalva evasiva. Ela vale sob o tsconfig.json que vem junto, que define strict: false de propósito, porque os efeitos de gráfico gerados são escritos contra linhas de consulta fracamente tipadas; ligar o strict vai produzir erros que passam a ser seus para manter. E ela vale para uma árvore cujos arquivos de branch foram escritos recentemente — um painel publicado há tempo suficiente carrega arquivos congelados de uma forma mais antiga, dois pontos dos quais são conhecidos por falhar no tsc mesmo construindo e rodando corretamente, já que o Vite transpila sem fazer typecheck. A correção é republicar, o que reescreve os arquivos de branch a partir da forma atual.

Next.js App Router. Todo módulo é um server component por padrão. O App.tsx e os módulos stores/ já carregam 'use client' na ejeção — os stores tocam localStorage no escopo do módulo e quebrariam no servidor. Marcar o App.tsx coloca todo o seu grafo de imports no bundle do cliente, então nada mais precisa da diretiva, a menos que você importe um gráfico, componente ou store diretamente de um server component.

O que a ejeção não entrega a você​

  • Qualquer coisa em manifest.internalDescriptors — metadados de plataforma, já excluídos de files. Não os copie.
  • Qualquer coisa em manifest.hostOnlyImports — servido a partir do bundle próprio do nosso renderizador, em registro nenhum, deliberadamente ausente. Substitua-os antes de construir.
  • A plataforma de dados. A ejeção nos tira do caminho de renderização, não do caminho de dados. As conexões, conjuntos de dados, atualizações, projetos dbt, a governança e o motor de consultas por baixo ficam onde são mantidos — essa é a metade da divisão que você não estava tentando possuir. A via sem credenciais é a única exceção, e só porque ela está lendo um extrato publicado em vez de consultar ao vivo.