# Espelli la tua dashboard

Pubblicare su Vibedasher è la normale fine del ciclo: è ciò che mantiene una dashboard ospitata, governata e collegata a dati che qualcuno mantiene. **L'espulsione è l'altra porta, ed è aperta di proposito.** La dashboard è semplice React e SQL, e in qualsiasi momento puoi prendere l'intero albero ed eseguirlo nella tua app, sul tuo hosting, sotto la tua build.

Non è l'export di un'immagine o di un blob JSON che dovresti reimplementare. Sono i sorgenti che girano: componenti, pagine, grafici, store, hook, un `package.json` ricostruito dagli import reali dell'albero, una configurazione Tailwind, un entry CSS, un `tsconfig.json`, una configurazione Vite, un `index.html`, e un `main.tsx` che monta il tutto. `npm install && npm run dev` lo serve come una sua app React.

note

L'espulsione è l'opposto dell'[incorporazione](/it/api/embedding-the-share-iframe.md). Incorporare mette la *nostra* dashboard renderizzata dentro un iframe nella tua pagina. Espellere ti consegna i sorgenti e ci toglie completamente dal percorso di rendering — noi manteniamo solo il motore dati sottostante. Se la domanda è "questo codice è mio?", l'espulsione è la risposta. Se stai ancora scegliendo tra le due, **[Incorporare o espellere](/it/guides/embed-or-eject.md)** le mette a confronto.

## Le tre chiamate[​](#le-tre-chiamate "Link diretto a Le tre chiamate")

* **`eject_viz(viz_id, out_dir=...)`** — l'intero albero più un **manifest di cablaggio**: il contratto di query, i token del tema, l'entry point e l'ordine delle sue prop, l'elenco dei file, la regione e la corsia dati.
* **`eject_viz_files(viz_id, paths=[...])`** — il contenuto dei file su richiesta. È il seguito quando una risposta è stata paginata, ed è anche il modo economico di leggere un singolo file.
* **`eject_instructions()`** — il riferimento di cablaggio, restituito come dati strutturati invece che come prosa da interpretare. Tutto in questa pagina è una lettura di esso; chiamalo quando vuoi l'originale leggibile dalla macchina.

### Passa `out_dir`, e sappi su quale trasporto sei[​](#passa-out_dir-e-sappi-su-quale-trasporto-sei "Link diretto a passa-out_dir-e-sappi-su-quale-trasporto-sei")

Una dashboard reale è nell'ordine di una quarantina di file e mezzo milione di caratteri. Con `out_dir` impostato su un percorso **assoluto**, l'albero viene scritto direttamente su disco e la risposta resta piccola per quanto grande sia l'albero.

Sul trasporto **stdio** — l'avvio standard, in cui il tuo client MCP lancia il server come sottoprocesso locale sotto il tuo utente — quel percorso è il tuo disco, ed è questo che rende legittimo scriverci. Sul trasporto **HTTP** sarebbe il disco del *server*, quindi lì `out_dir` viene **rifiutato** invece di scrivere file in un posto che non puoi leggere riportando successo. Sull'HTTP la paginazione è l'unica strada.

Senza `out_dir`, i file vengono impacchettati nella risposta fino a un budget di caratteri e il resto torna come **`delivery.pendingPaths`**. Quell'elenco è il contratto: **una risposta con un `pendingPaths` non vuoto è una pagina, non un albero.** Continua a chiamare `eject_viz_files` con esso finché non torna vuoto.

## Leggi `manifest.dataLane` prima di scrivere qualsiasi cablaggio dati[​](#leggi-manifestdatalane-prima-di-scrivere-qualsiasi-cablaggio-dati "Link diretto a leggi-manifestdatalane-prima-di-scrivere-qualsiasi-cablaggio-dati")

È la prima decisione e quella che non puoi cambiare dopo, perché decide chi può leggere la dashboard.

### Corsia A — `dataLane.id == "wasm"`: rende già dati reali[​](#corsia-a--datalaneid--wasm-rende-già-dati-reali "Link diretto a corsia-a--datalaneid--wasm-rende-già-dati-reali")

Per una viz **pubblica e pubblicata**, l'espulsione emette un piccolo runner (`vibedasherWasm.ts`) e l'albero rende **righe reali su `npm run dev` senza alcuna credenziale di alcun tipo**: nessuna chiave API, nessun token di incorporazione, nessun backend tuo, e funziona da qualsiasi origine incluso `localhost` e un semplice server di file statici.

Il meccanismo: una `GET` anonima per il piano wasm della viz restituisce un estratto Parquet prefirmato per dataset; il runner registra ciascuno sotto il suo alias SQL in DuckDB-WASM ed esegue l'SQL di ogni grafico nel browser del visitatore. La sua unica dipendenza, `@duckdb/duckdb-wasm`, è su npm. `main.tsx` attende il riscaldamento del runner prima di montare, così una configurazione rotta è **un pannello disegnato** invece di una griglia di tile vuoti.

Qui non c'è nulla da cablare. **Non aggiungerci una credenziale sopra** — ti compra un bootstrap rotto in cambio di niente.

Cosa questa corsia non è: serve l'estratto **pubblicato**, non una query dal vivo, quindi aggiornare ciò che mostra significa ripubblicare. Ed esiste solo finché la viz resta pubblica e pubblicata.

**Cosa ottiene ogni visitatore, detto chiaramente.** L'estratto ha come chiave il dataset e lo snapshot, mai una query — lo stesso `SELECT` con e senza `WHERE` restituisce lo stesso file identico. Quindi contiene tutte le righe di ogni dataset che la dashboard legge, ed è un unico file, lo stesso, per tutti. Un filtro che aggiungi nella pagina cambia ciò che viene disegnato, mai ciò che è stato consegnato, e i byte sono nel tab di rete in ogni caso. È inerente al mettere i dati nel browser. Se i visualizzatori devono vedere righe diverse, prendi la corsia B.

### Corsia B — `dataLane.id == "token"`: qualsiasi altra viz[​](#corsia-b--datalaneid--token-qualsiasi-altra-viz "Link diretto a corsia-b--datalaneid--token-qualsiasi-altra-viz")

Non c'è alcuna corsia senza credenziali e l'espulsione non ne ha emessa una. L'endpoint del piano wasm è protetto dallo stesso controllo pubblica-e-pubblicata del resto della superficie anonima, quindi puntarci a mano non aiuta. `npm run dev` monta comunque, e disegna un pannello che nomina l'endpoint che gli serve — non fallisce in silenzio e non sostituisce dati finti.

Due cablaggi funzionano, ed **entrambi mettono la credenziale su un server che controlli tu**:

1. **La tua app serve una route per il token di incorporazione.** Same-origin, che risponde `application/json` `{"token": "..."}`, generato con la chiave API che tiene lato server. Punta l'URL del token di incorporazione dell'albero su di essa e costruisci il client con il token.
2. **Interroghi in codice server fidato** con `new Vibedasher({ apiKey })` e passi il risultato più in basso, oppure fai da proxy a `{ sql, params }` attraverso una tua route.

**Non mettere mai una chiave API nel codice del browser.** Porta l'autorità completa sull'account e non ha scope. Un token di incorporazione con scope è la credenziale lato client; una chiave API no.

Questi due sono anche le **sole** forme che possono mostrare a un visualizzatore meno righe che a un altro. Un token di incorporazione può portare `filters`, che la piattaforma appende a ogni query che autorizza — lato server, prima di qualsiasi join, aggregazione o `LIMIT`, dove la pagina non può allargarli. In codice server fidato restringi tu stesso l'SQL.

Se l'espulsione riporta la corsia `token` con la ragione `no-wasm-extract` su una viz che *è* pubblica e pubblicata, il dataset semplicemente non ha ancora un estratto. Chiederlo è la sola cosa che lo costruisce — vedi [La corsia di query wasm](/it/guides/the-wasm-query-lane.md#you-have-to-ask-for-the-extract-before-it-exists). Ripubblicare non cuoce un estratto, quindi "pubblica ed espelli di nuovo" gira all'infinito.

## Installa il client[​](#installa-il-client "Link diretto a Installa il client")

```
npm i @vibedasher/client
```

```
pip install vibedasher
```

Il pacchetto npm è **`@vibedasher/client`**. Non esiste `@vibedasher/sdk` e non esisterà mai. Installa i pacchetti reali invece di copiarli dentro il tuo progetto.

## Stili, typecheck e l'App Router[​](#stili-typecheck-e-lapp-router "Link diretto a Stili, typecheck e l'App Router")

**Gli stili sono tre pezzi e ti servono tutti e tre.** `tailwind.config.js` (dark mode con una strategia a selettore legata a un attributo che l'albero mette sulla propria radice, così che il dark mode della dashboard sia delimitato indipendentemente da quello della tua app, più una safelist per le classi assemblate a runtime e quindi invisibili allo scanner di Tailwind); `styles/dashboard.css` (l'entry di Tailwind e un blocco `:root` di token di design come default a tempo di build); e `themeStyles.ts` (gli stessi token come stringa che l'app inietta a runtime, per tema). Il CSS ti dà un primo disegno già a tema; `themeStyles.ts` ti dà il passaggio dal vivo tra chiaro e scuro. Se la tua app usa già Tailwind con un dark mode basato su classi, non unire le configurazioni — una configurazione Tailwind supporta una sola impostazione di dark mode. Su Tailwind v4, fai riferimento alla configurazione fornita con `@config`.

**Typecheck.** `npm run typecheck` (`tsc --noEmit`) dovrebbe riportare zero errori su un albero appena espulso, e due gate lo garantiscono a ogni esecuzione della nostra suite di test — uno fa il typecheck di un'espulsione generata completa, l'altro installa, fa il typecheck *e* costruisce una scritta a mano. Due condizioni delimitano l'affermazione, e nessuna delle due è una riserva. Vale **sotto il `tsconfig.json` fornito**, che imposta `strict: false` di proposito, perché gli effetti grafico generati sono scritti su righe di query tipizzate in modo lasco; attivare strict produrrà errori che saranno poi tuoi da mantenere. E vale per un albero i cui file di branch sono stati scritti di recente — una dashboard pubblicata abbastanza tempo fa porta file congelati di una forma più vecchia, due punti della quale è noto che falliscono `tsc` pur costruendo e funzionando correttamente, dato che Vite traspila senza fare il typecheck. La soluzione è **ripubblicare**, che riscrive i file di branch dalla forma attuale.

**Next.js App Router.** Ogni modulo è un server component per default. `App.tsx` e i moduli `stores/` portano già `'use client'` nell'espulsione — gli store toccano `localStorage` a livello di modulo e andrebbero in crash sul server. Marcare `App.tsx` mette tutto il suo grafo di import nel bundle client, quindi nient'altro ha bisogno della direttiva a meno che non importi un grafico, un componente o uno store direttamente da un server component.

## Cosa l'espulsione non ti consegna[​](#cosa-lespulsione-non-ti-consegna "Link diretto a Cosa l'espulsione non ti consegna")

* **Tutto ciò che è in `manifest.internalDescriptors`** — metadati di piattaforma, già esclusi da `files`. Non copiarli.
* **Tutto ciò che è in `manifest.hostOnlyImports`** — serviti dal bundle del nostro renderer, su nessun registro, deliberatamente assenti. Sostituiscili prima di costruire.
* **La piattaforma dati.** L'espulsione ci toglie dal percorso di rendering, non dal percorso dei dati. Le connessioni, i dataset, gli aggiornamenti, i progetti dbt, la governance e il motore di query sottostanti restano dove sono mantenuti — è la metà della divisione che non stavi cercando di possedere. La corsia senza credenziali è l'unica eccezione, e solo perché legge un estratto pubblicato invece di interrogare dal vivo.
