Éjecter votre tableau de bord
Publier sur Vibedasher est la fin normale de la boucle : c'est ce qui garde un tableau de bord hébergé, gouverné et relié à des données que quelqu'un maintient. L'éjection est l'autre porte, et elle est ouverte exprès. Le tableau de bord est du React et du SQL ordinaires, et à tout moment vous pouvez prendre l'arbre entier et le faire tourner dans votre propre application, sur votre propre hébergement, sous votre propre build.
Ce n'est pas un export d'une image ou d'un blob JSON qu'il faudrait réimplémenter. C'est la
source qui tourne : composants, pages, graphiques, stores, hooks, un package.json reconstruit
à partir des vrais imports de l'arbre, une configuration Tailwind, une entrée CSS, un
tsconfig.json, une configuration Vite, un index.html, et un main.tsx qui le monte.
npm install && npm run dev le sert comme sa propre application React.
L'éjection est le contraire de l'intégration. L'intégration met notre tableau de bord rendu dans une iframe de votre page. L'éjection vous remet la source et nous retire entièrement du chemin de rendu — nous ne gardons que le moteur de données en dessous. Si la question est « est-ce que ce code est à moi », l'éjection est la réponse. Si vous êtes encore en train de choisir entre les deux, Intégrer ou éjecter les met côte à côte.
Les trois appels
eject_viz(viz_id, out_dir=...)— l'arbre entier plus un manifeste de câblage : le contrat de requête, les tokens de thème, le point d'entrée et l'ordre de ses props, la liste des fichiers, la région, et la voie de données.eject_viz_files(viz_id, paths=[...])— le contenu des fichiers à la demande. C'est la relance quand une réponse a été paginée, et c'est aussi la façon de lire un fichier à bas coût.eject_instructions()— la référence de câblage, renvoyée sous forme de données structurées plutôt que de prose à analyser. Tout ce qui est sur cette page en est une lecture ; appelez-la quand vous voulez l'original lisible par une machine.
Passez out_dir, et sachez sur quel transport vous êtes
Un vrai tableau de bord fait de l'ordre de quarante et quelques fichiers et un demi-million de
caractères. Avec out_dir défini sur un chemin absolu, l'arbre est écrit directement sur
disque et la réponse reste petite quelle que soit la taille de l'arbre.
Sur le transport stdio — le lancement standard, où votre client MCP démarre le serveur
comme un sous-processus local sous votre propre utilisateur — ce chemin est votre propre
disque, et c'est ce qui rend légitime le fait d'y écrire. Sur le transport HTTP ce serait le
disque du serveur, donc out_dir y est refusé plutôt que d'écrire des fichiers quelque
part où vous ne pourriez pas les lire tout en rapportant un succès. La pagination est la seule
route en HTTP.
Sans out_dir, les fichiers sont empaquetés dans la réponse jusqu'à un budget de caractères et
le reste revient sous forme de delivery.pendingPaths. Cette liste est le contrat : une
réponse avec un pendingPaths non vide est une page, pas un arbre. Continuez d'appeler
eject_viz_files avec elle jusqu'à ce qu'elle revienne vide.
Lisez manifest.dataLane avant d'écrire le moindre câblage de données
C'est la première décision et celle que vous ne pourrez pas changer ensuite, parce qu'elle décide qui peut lire le tableau de bord.
Voie A — dataLane.id == "wasm" : il affiche déjà de vraies données
Pour une visualisation publique et publiée, l'éjection émet un petit runner
(vibedasherWasm.ts) et l'arbre affiche de vraies lignes avec npm run dev sans aucun
identifiant : pas de clé d'API, pas de jeton d'intégration, pas de backend à vous, et cela
fonctionne depuis n'importe quelle origine y compris localhost et un simple serveur de
fichiers statiques.
Le mécanisme : un GET anonyme du plan wasm de la visualisation renvoie un extrait Parquet
présigné par jeu de données ; le runner enregistre chacun sous son alias SQL dans DuckDB-WASM
et exécute le SQL de chaque graphique dans le navigateur du visiteur. Sa seule dépendance,
@duckdb/duckdb-wasm, est sur npm. main.tsx attend la mise en chauffe du runner avant de
monter, si bien qu'une configuration cassée donne un seul panneau peint plutôt qu'une
grille de tuiles vides.
Il n'y a rien à câbler ici. N'ajoutez pas d'identifiant par-dessus — cela vous achète un démarrage cassé en échange de rien.
Ce que cette voie n'est pas : elle sert l'extrait publié, pas une requête vivante, donc rafraîchir ce qu'elle montre veut dire republier. Et elle n'existe que tant que la visualisation reste publique et publiée.
Ce que reçoit chaque visiteur, dit franchement. L'extrait est indexé par jeu de données et
instantané, jamais par une requête — le même SELECT avec et sans WHERE renvoie le fichier
identique. Donc il contient toutes les lignes de chaque jeu de données que lit le tableau de
bord, et c'est un seul fichier, le même, pour tout le monde. Un filtre que vous ajoutez dans la
page change ce qui est dessiné, jamais ce qui a été livré, et les octets sont dans l'onglet
réseau dans les deux cas. C'est inhérent au fait de mettre les données dans le navigateur. Si
les visionneurs doivent voir des lignes différentes, prenez la voie B.
Voie B — dataLane.id == "token" : toute autre visualisation
Il n'y a pas de voie sans identifiants et l'éjection n'en a pas émis une. L'endpoint du plan
wasm est protégé par la même vérification public-et-publié que le reste de la surface anonyme,
donc le pointer à la main n'aide pas. npm run dev monte quand même, et peint un panneau qui
nomme l'endpoint dont il a besoin — il n'échoue pas silencieusement et il ne substitue pas de
fausses données.
Deux câblages fonctionnent, et les deux mettent l'identifiant sur un serveur que vous contrôlez :
- Votre application sert une route de jeton d'intégration. Même origine, répondant en
application/json{"token": "..."}, frappé avec la clé d'API qu'elle détient côté serveur. Pointez l'URL de jeton d'intégration de l'arbre dessus et construisez le client avec le jeton. - Vous interrogez dans du code serveur de confiance avec
new Vibedasher({ apiKey })et passez le résultat vers le bas, ou relayez{ sql, params }à travers votre propre route.
Ne mettez jamais une clé d'API dans du code de navigateur. Elle porte l'autorité complète du compte et n'a aucun scope. Un jeton d'intégration à portée restreinte est l'identifiant côté client ; une clé d'API ne l'est pas.
Ces deux-là sont aussi les seules formes capables de montrer moins de lignes à un visionneur
qu'à un autre. Un jeton d'intégration peut porter des filters, que la plateforme ajoute à
chaque requête qu'elle autorise — côté serveur, avant toute jointure, tout agrégat ou tout
LIMIT, là où la page ne peut pas les élargir. Dans du code serveur de confiance, vous
restreignez le SQL vous-même.
Si l'éjection rapporte la voie token avec la raison no-wasm-extract sur une visualisation
qui est publique et publiée, c'est simplement que le jeu de données n'a pas encore d'extrait.
En demander un est la seule chose qui le construit — voir
La voie de requête wasm.
Republier ne cuit pas d'extrait, donc « publier et rééjecter » boucle indéfiniment.
Installez le client
npm i @vibedasher/client
pip install vibedasher
Le paquet npm est @vibedasher/client. Il n'y a pas de @vibedasher/sdk et il n'y en aura
jamais. Installez les vrais paquets plutôt que de les recopier chez vous.
Styles, typage, et l'App Router
Le style est trois pièces et il vous faut les trois. tailwind.config.js (mode sombre sur
une stratégie de sélecteur liée à un attribut que l'arbre pose sur sa propre racine, si bien que
le mode sombre du tableau de bord est délimité indépendamment de celui de votre application,
plus une safelist pour les classes assemblées à l'exécution et donc invisibles au scanner de
Tailwind) ; styles/dashboard.css (l'entrée Tailwind et un bloc :root de tokens de design
comme valeurs par défaut au build) ; et themeStyles.ts (les mêmes tokens sous forme de chaîne
que l'application injecte à l'exécution, par thème). Le CSS vous donne un premier rendu thémé ;
themeStyles.ts vous donne le basculement clair/sombre à chaud. Si votre application utilise
déjà Tailwind avec un mode sombre basé sur une classe, ne fusionnez pas les configurations — une
configuration Tailwind ne supporte qu'un seul réglage de mode sombre. Sur Tailwind v4, référencez
la configuration livrée avec @config.
Typage. npm run typecheck (tsc --noEmit) est censé rapporter zéro erreur sur un arbre
fraîchement éjecté, et deux gates le garantissent à chaque exécution de notre suite de tests —
l'un type-vérifie une éjection générée complète, l'autre installe, type-vérifie et construit
une éjection écrite à la main. Deux conditions bornent cette affirmation, et ni l'une ni l'autre
n'est une échappatoire. Elle tient sous le tsconfig.json livré, qui met strict: false
exprès, parce que les effets de graphique générés sont écrits contre des lignes de requête
faiblement typées ; activer strict produira des erreurs qui seront alors à vous de maintenir. Et
elle tient pour un arbre dont les fichiers de branche ont été écrits récemment — un tableau de
bord publié il y a assez longtemps porte des fichiers gelés d'une forme plus ancienne, dont deux
endroits sont connus pour échouer à tsc tout en se construisant et en s'exécutant
correctement, puisque Vite transpile sans type-vérifier. Le correctif est de republier, ce
qui réécrit les fichiers de branche à partir de la forme actuelle.
Next.js App Router. Chaque module est un composant serveur par défaut. App.tsx et les
modules stores/ portent déjà 'use client' dans l'éjection — les stores touchent
localStorage à la portée du module et planteraient sur le serveur. Marquer App.tsx met tout
son graphe d'imports dans le bundle client, si bien que rien d'autre n'a besoin de la directive
à moins que vous n'importiez un graphique, un composant ou un store directement depuis un
composant serveur.
Ce que l'éjection ne vous remet pas
- Tout ce qui est dans
manifest.internalDescriptors— des métadonnées de plateforme, déjà exclues defiles. Ne les copiez pas. - Tout ce qui est dans
manifest.hostOnlyImports— servi depuis le bundle propre à notre moteur de rendu, sur aucun registre, délibérément absent. Remplacez-les avant de construire. - La plateforme de données. L'éjection nous retire du chemin de rendu, pas du chemin de données. Les connexions, jeux de données, actualisations, projets dbt, la gouvernance et le moteur de requêtes en dessous restent là où ils sont maintenus — c'est la moitié du partage que vous ne cherchiez pas à posséder. La voie sans identifiants est la seule exception, et seulement parce qu'elle lit un extrait publié au lieu d'interroger en direct.