Aller au contenu principal

É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.

remarque

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 :

  1. 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.
  2. 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 de files. 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.