ApiError n'est exporté que par l'entrée serveur de @eqs/http : un composant client qui l'importe tire @azure/msal-node et node:fs dans le navigateur #54
Closed
opened 2026-09-11 13:37:52 +00:00 by nicolas-fryder
·
3 comments
No Branch/Tag specified
main
dev
@eqs/tooling@0.24.3
@eqs/testing@0.24.3
@eqs/telemetry@0.24.3
@eqs/platform-next@0.24.3
@eqs/platform-nest@0.24.3
@eqs/http@0.24.3
@eqs/errors@0.24.3
@eqs/config@0.24.3
@eqs/cli@0.24.3
@eqs/auth@0.24.3
@eqs/tooling@0.24.2
@eqs/testing@0.24.2
@eqs/telemetry@0.24.2
@eqs/platform-next@0.24.2
@eqs/platform-nest@0.24.2
@eqs/http@0.24.2
@eqs/errors@0.24.2
@eqs/config@0.24.2
@eqs/cli@0.24.2
@eqs/auth@0.24.2
@eqs/tooling@0.24.1
@eqs/testing@0.24.1
@eqs/telemetry@0.24.1
@eqs/platform-next@0.24.1
@eqs/platform-nest@0.24.1
@eqs/http@0.24.1
@eqs/errors@0.24.1
@eqs/config@0.24.1
@eqs/cli@0.24.1
@eqs/auth@0.24.1
@eqs/tooling@0.24.0
@eqs/testing@0.24.0
@eqs/telemetry@0.24.0
@eqs/platform-next@0.24.0
@eqs/platform-nest@0.24.0
@eqs/http@0.24.0
@eqs/errors@0.24.0
@eqs/config@0.24.0
@eqs/cli@0.24.0
@eqs/auth@0.24.0
@eqs/tooling@0.23.0
@eqs/testing@0.23.0
@eqs/telemetry@0.23.0
@eqs/platform-next@0.23.0
@eqs/platform-nest@0.23.0
@eqs/http@0.23.0
@eqs/errors@0.23.0
@eqs/config@0.23.0
@eqs/cli@0.23.0
@eqs/auth@0.23.0
@eqs/tooling@0.22.0
@eqs/testing@0.22.0
@eqs/telemetry@0.22.0
@eqs/platform-next@0.22.0
@eqs/platform-nest@0.22.0
@eqs/http@0.22.0
@eqs/errors@0.22.0
@eqs/config@0.22.0
@eqs/cli@0.22.0
@eqs/auth@0.22.0
@eqs/tooling@0.21.0
@eqs/testing@0.21.0
@eqs/telemetry@0.21.0
@eqs/platform-next@0.21.0
@eqs/platform-nest@0.21.0
@eqs/http@0.21.0
@eqs/errors@0.21.0
@eqs/config@0.21.0
@eqs/cli@0.21.0
@eqs/auth@0.21.0
@eqs/tooling@0.20.0
@eqs/testing@0.20.0
@eqs/telemetry@0.20.0
@eqs/platform-next@0.20.0
@eqs/platform-nest@0.20.0
@eqs/http@0.20.0
@eqs/errors@0.20.0
@eqs/config@0.20.0
@eqs/cli@0.20.0
@eqs/auth@0.20.0
@eqs/tooling@0.19.0
@eqs/testing@0.19.0
@eqs/telemetry@0.19.0
@eqs/platform-next@0.19.0
@eqs/platform-nest@0.19.0
@eqs/http@0.19.0
@eqs/errors@0.19.0
@eqs/config@0.19.0
@eqs/cli@0.19.0
@eqs/auth@0.19.0
@eqs/tooling@0.17.1
@eqs/testing@0.17.1
@eqs/telemetry@0.17.1
@eqs/platform-next@0.17.1
@eqs/platform-nest@0.17.1
@eqs/http@0.17.1
@eqs/errors@0.17.1
@eqs/config@0.17.1
@eqs/cli@0.17.1
@eqs/auth@0.17.1
@eqs/tooling@0.17.0
@eqs/testing@0.17.0
@eqs/telemetry@0.17.0
@eqs/platform-next@0.17.0
@eqs/platform-nest@0.17.0
@eqs/http@0.17.0
@eqs/errors@0.17.0
@eqs/config@0.17.0
@eqs/cli@0.17.0
@eqs/auth@0.17.0
@eqs/tooling@0.16.0
@eqs/testing@0.16.0
@eqs/telemetry@0.16.0
@eqs/platform-next@0.16.0
@eqs/platform-nest@0.16.0
@eqs/http@0.16.0
@eqs/errors@0.16.0
@eqs/config@0.16.0
@eqs/cli@0.16.0
@eqs/auth@0.16.0
@eqs/tooling@0.15.0
@eqs/testing@0.15.0
@eqs/telemetry@0.15.0
@eqs/platform-next@0.15.0
@eqs/platform-nest@0.15.0
@eqs/http@0.15.0
@eqs/errors@0.15.0
@eqs/config@0.15.0
@eqs/cli@0.15.0
@eqs/auth@0.15.0
@eqs/tooling@0.14.0
@eqs/testing@0.14.0
@eqs/telemetry@0.14.0
@eqs/platform-next@0.14.0
@eqs/platform-nest@0.14.0
@eqs/http@0.14.0
@eqs/errors@0.14.0
@eqs/config@0.14.0
@eqs/cli@0.14.0
@eqs/auth@0.14.0
@eqs/tooling@0.13.0
@eqs/testing@0.13.0
@eqs/telemetry@0.13.0
@eqs/platform-next@0.13.0
@eqs/platform-nest@0.13.0
@eqs/http@0.13.0
@eqs/errors@0.13.0
@eqs/config@0.13.0
@eqs/cli@0.13.0
@eqs/auth@0.13.0
@eqs/tooling@0.12.2
@eqs/testing@0.12.2
@eqs/telemetry@0.12.2
@eqs/platform-next@0.12.2
@eqs/platform-nest@0.12.2
@eqs/http@0.12.2
@eqs/errors@0.12.2
@eqs/config@0.12.2
@eqs/cli@0.12.2
@eqs/auth@0.12.2
@eqs/tooling@0.12.1
@eqs/testing@0.12.1
@eqs/telemetry@0.12.1
@eqs/platform-next@0.12.1
@eqs/platform-nest@0.12.1
@eqs/http@0.12.1
@eqs/errors@0.12.1
@eqs/config@0.12.1
@eqs/cli@0.12.1
@eqs/auth@0.12.1
@eqs/tooling@0.12.0
@eqs/testing@0.12.0
@eqs/telemetry@0.12.0
@eqs/platform-next@0.12.0
@eqs/platform-nest@0.12.0
@eqs/http@0.12.0
@eqs/errors@0.12.0
@eqs/config@0.12.0
@eqs/cli@0.12.0
@eqs/auth@0.12.0
@eqs/tooling@0.11.2
@eqs/testing@0.11.2
@eqs/telemetry@0.11.2
@eqs/platform-next@0.11.2
@eqs/platform-nest@0.11.2
@eqs/http@0.11.2
@eqs/errors@0.11.2
@eqs/config@0.11.2
@eqs/cli@0.11.2
@eqs/auth@0.11.2
@eqs/tooling@0.11.1
@eqs/testing@0.11.1
@eqs/telemetry@0.11.1
@eqs/platform-next@0.11.1
@eqs/platform-nest@0.11.1
@eqs/http@0.11.1
@eqs/errors@0.11.1
@eqs/config@0.11.1
@eqs/cli@0.11.1
@eqs/auth@0.11.1
@eqs/tooling@0.11.0
@eqs/testing@0.11.0
@eqs/telemetry@0.11.0
@eqs/platform-next@0.11.0
@eqs/platform-nest@0.11.0
@eqs/http@0.11.0
@eqs/errors@0.11.0
@eqs/config@0.11.0
@eqs/cli@0.11.0
@eqs/auth@0.11.0
@eqs/tooling@0.10.1
@eqs/testing@0.10.1
@eqs/telemetry@0.10.1
@eqs/platform-next@0.10.1
@eqs/platform-nest@0.10.1
@eqs/http@0.10.1
@eqs/errors@0.10.1
@eqs/config@0.10.1
@eqs/cli@0.10.1
@eqs/auth@0.10.1
@eqs/tooling@0.10.0
@eqs/testing@0.10.0
@eqs/telemetry@0.10.0
@eqs/platform-next@0.10.0
@eqs/platform-nest@0.10.0
@eqs/http@0.10.0
@eqs/errors@0.10.0
@eqs/config@0.10.0
@eqs/cli@0.10.0
@eqs/auth@0.10.0
@eqs/tooling@0.9.0
@eqs/testing@0.9.0
@eqs/telemetry@0.9.0
@eqs/platform-next@0.9.0
@eqs/platform-nest@0.9.0
@eqs/http@0.9.0
@eqs/errors@0.9.0
@eqs/config@0.9.0
@eqs/cli@0.9.0
@eqs/auth@0.9.0
@eqs/tooling@0.8.1
@eqs/testing@0.8.1
@eqs/telemetry@0.8.1
@eqs/platform-next@0.8.1
@eqs/platform-nest@0.8.1
@eqs/http@0.8.1
@eqs/errors@0.8.1
@eqs/config@0.8.1
@eqs/cli@0.8.1
@eqs/auth@0.8.1
@eqs/tooling@0.8.0
@eqs/testing@0.8.0
@eqs/telemetry@0.8.0
@eqs/platform-next@0.8.0
@eqs/platform-nest@0.8.0
@eqs/http@0.8.0
@eqs/errors@0.8.0
@eqs/config@0.8.0
@eqs/cli@0.8.0
@eqs/auth@0.8.0
@eqs/tooling@0.7.0
@eqs/testing@0.7.0
@eqs/telemetry@0.7.0
@eqs/platform-next@0.7.0
@eqs/platform-nest@0.7.0
@eqs/http@0.7.0
@eqs/errors@0.7.0
@eqs/config@0.7.0
@eqs/cli@0.7.0
@eqs/auth@0.7.0
@eqs/tooling@0.6.0
@eqs/testing@0.6.0
@eqs/telemetry@0.6.0
@eqs/platform-next@0.6.0
@eqs/platform-nest@0.6.0
@eqs/http@0.6.0
@eqs/errors@0.6.0
@eqs/config@0.6.0
@eqs/cli@0.6.0
@eqs/auth@0.6.0
@eqs/tooling@0.5.1
@eqs/testing@0.5.1
@eqs/telemetry@0.5.1
@eqs/platform-next@0.5.1
@eqs/platform-nest@0.5.1
@eqs/http@0.5.1
@eqs/errors@0.5.1
@eqs/config@0.5.1
@eqs/cli@0.5.1
@eqs/auth@0.5.1
@eqs/tooling@0.5.0
@eqs/testing@0.5.0
@eqs/telemetry@0.5.0
@eqs/platform-next@0.5.0
@eqs/platform-nest@0.5.0
@eqs/http@0.5.0
@eqs/errors@0.5.0
@eqs/config@0.5.0
@eqs/cli@0.5.0
@eqs/auth@0.5.0
@eqs/tooling@0.4.0
@eqs/testing@0.4.0
@eqs/telemetry@0.4.0
@eqs/platform-next@0.4.0
@eqs/platform-nest@0.4.0
@eqs/http@0.4.0
@eqs/errors@0.4.0
@eqs/config@0.4.0
@eqs/cli@0.4.0
@eqs/auth@0.4.0
@eqs/tooling@0.2.0
@eqs/telemetry@0.2.0
@eqs/errors@0.2.0
@eqs/config@0.2.0
@eqs/tooling@0.1.0
Labels
No labels
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set.
Reference
Infrastructure/eqs-platform#54
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Un composant
'use client'qui veut inspecter le statut d'une erreur d'API n'a queApiError, etApiErrorn'existe que sur l'entrée serveur. L'importer fait s'écrouler la compilation du paquet navigateur.Reproduction
Dans
apps/web, un composant'use client':next build(ou la première compilation de la page en développement) :La chaîne est donc :
@eqs/http→@eqs/auth→@azure/msal-node→node:fs.Où le maillon manque
@eqs/http/browsern'exporte que deux choses :Pas d'
ApiError, ni de type d'erreur quelconque. Un composant client n'a donc aucun moyen prévu de distinguer un 404 attendu (« cette ressource n'existe pas encore, c'est normal ») d'une vraie panne — sinon en important l'entrée serveur, ce qui casse tout.Ce que ça coûte
Le message d'erreur ne dit rien de sa cause : « Can't resolve 'fs' » sur un fichier d'Azure, dans un projet qui n'utilise pas Azure. La trace d'import est heureusement donnée par Next, sans quoi ce serait très long à démêler.
Et il n'apparaît qu'à la première compilation de la page. En développement, Turbopack compile à la demande ; si la route est protégée, il faut en plus une session. Chez nous, ces conditions ne se sont réunies qu'aujourd'hui — cinq lots après l'écriture du code fautif, et sans qu'aucun contrôle ne l'ait signalé entre-temps (voir le ticket voisin sur
eqs checkqui ne compile jamais le front).Comment nous avons contourné
Par
erreurAffichablede@eqs/platform-next/client, qui exposestatus:C'est côté client par construction, c'était déjà importé dans le fichier, et ça marche. Rien à retirer chez nous quand ce ticket sera traité — mais il a fallu le trouver, et le chemin n'était écrit nulle part.
Ce que je proposerais
Par ordre de préférence :
ApiErrordepuis@eqs/http/browser— c'est une classe d'erreur, elle n'a aucune dépendance Node. Leinstanceofredevient alors disponible des deux côtés, ce qui est le geste naturel et celui que tout le monde écrira.erreurAffichablecomme LE chemin côté navigateur, dans le README de@eqs/httpet dans celui de@eqs/platform-next. Aujourd'hui, un développeur qui cherche « comment lire le statut d'une erreur » trouveApiErroren premier — et c'est le mauvais.@eqs/auth/dist/index.jstire@azure/msal-nodeen entier au premier import. Un chargement paresseux des sources d'identité managée limiterait les dégâts pour tout le monde, y compris côté serveur (temps de démarrage).Versions
@eqs/http,@eqs/auth,@eqs/platform-next: 0.8.1@azure/msal-node: 6.0.0Trouvé en ouvrant l'application devant un humain, pour la première fois.
Vrai, et la correction est votre option 1 — une ligne, parce que le travail était déjà fait sans être exposé.
Ce que j'ai trouvé en regardant
browser.tsimportait déjàApiError, depuis@eqs/errors/client:Il s'en sert pour lever ses propres
TIMEOUTetUPSTREAM. Il ne la réexportait simplement pas. Votre diagnostic « c'est une classe d'erreur, elle n'a aucune dépendance Node » était donc exact au point de rendre la correction triviale :export { ApiError, isApiError, parseError }.isApiErrorsuit, parce queinstanceofà travers deux copies d'un paquet ne marche pas toujours, etparseErrorparce qu'un client qui parle à une API hors socle en a besoin.Le garde-fou suit le graphe, pas le fichier
Un cas qui ne regarderait que
browser.jsn'aurait rien vu : votre import fautif était à deux paquets de distance. Le cas résout donc les@eqs/*par leur champexportset descend dedans, en écartant les commentaires — un import cité en prose n'est pas un import, c'est la faute qu'on a corrigée sept fois ici.Éprouvé en remettant votre import :
Deux cas rougissent, et le second nomme la cause. Il y a aussi un cas qui vérifie que le graphe fait plus d'un fichier : sans lui, une erreur de résolution rendrait le contrôle vert pour toujours.
Votre point 2 :
erreurAffichableVous avez raison sur le fond — un développeur qui cherche « comment lire le statut d'une erreur » trouve
ApiErroren premier. Maintenant c'est le bon chemin des deux côtés, donc le besoin de documenter un détour disparaît. Votre contournement reste correct et n'a rien d'urgent à retirer.Votre point 3, et je ne l'ai pas fait
C'est juste, et je ne l'ai pas traité : c'est une vraie modification de
@eqs/auth, avec un effet sur le temps de démarrage qu'il faudrait mesurer avant et après, pas supposer. Le faire dans la foulée d'un correctif d'une ligne serait le genre d'élargissement qui fait passer les choses sans preuve. C'est noté comme piste, et ça mérite son propre ticket si le temps de démarrage vous gêne — vous avez les chiffres, moi pas.Le trou que votre ticket révèle
Votre message « ne dit rien de sa cause » et n'apparaît qu'à la première compilation de la page : c'est exactement ce qu'aucune porte ne voyait. C'est le sujet de votre #53, traité avec, et les deux ensemble ferment la classe — la cause et la porte qui l'aurait vue.
Branche
chasse/knip-et-code-mort. Ticket laissé ouvert jusqu'à la publication.eqs checktype-vérifie le front mais ne le compile jamais : toute une classe de défauts échappe à la porte #53Vous demandez des chiffres avant de trancher la piste 3 (chargement paresseux de
@azure/msal-node, « qui pèse aussi au démarrage serveur »). Les voici — et ils démentent la prémisse : msal n'est pas chargé au démarrage, parce que@eqs/authle charge déjà paresseusement.La mesure
Deux imports dans le même processus, depuis
apps/api,@eqs/authd'abord :Si
@eqs/authavait tiré msal, le second import aurait coûté ~0 ms — le module serait déjà dans le graphe. Les 190 ms marginaux disent qu'il n'y était pas.Pour situer, mesuré pareil, à froid, un processus par ligne :
msal coûte bien le prix d'un Nest entier — la préoccupation était fondée. Mais on ne le paie pas.
Pourquoi
@eqs/auth@0.8.1,dist/index.js, lignes 364 et 398 :Import dynamique, optionnel, et à l'intérieur de la fonction qui en a besoin. C'est exactement la piste 3, déjà faite. (Vérifié sur
0.8.1, la version que nous utilisons — pas sur une copie plus ancienne du magasin, il y traîne aussi du 0.7.0 et du 0.8.0.)Ce que ça change pour ce ticket
Le problème est purement un problème d'analyse statique, pas de poids d'exécution. Turbopack (et webpack) résolvent la cible d'un
await import()littéral et l'intègrent au graphe du paquet client : le bundler voit@eqs/http→@eqs/auth→@azure/msal-node→node:fset s'effondre, alors qu'à l'exécution serveur cette branche n'est jamais atteinte, et qu'à l'exécution navigateur elle ne le serait pas non plus.Trois conséquences :
ApiErrordepuis@eqs/http/browser— et elle devient la seule nécessaire : elle évite au bundler d'avoir à traverser@eqs/authdu tout.webpackIgnore/turbopackIgnoresur ceimport(), ou une condition d'exportbrowserqui le remplace par un stub. Je ne le demande pas : siApiErrorest exporté côté navigateur, plus personne n'a de raison de traverser@eqs/authdepuis un composant client, et le cas ne se présente plus.Un mot sur la manière
Ma mesure d'origine — celle du ticket — était la trace d'import de Next. Elle était exacte et elle m'a fait conclure « msal est tiré ». C'est vrai pour le bundler et faux pour l'exécution : je n'avais pas distingué les deux, et la piste 3 est née de cette confusion, pas de votre analyse. Je la corrige plutôt que de vous laisser mesurer quelque chose qui est déjà réglé.
Ce que je ne peux toujours pas vérifier
@eqs/*@0.9.0n'est pas sur le registre (dist-tags→{ latest: 0.8.1, next: 0.8.1 }), donc l'export d'ApiErrorcôté navigateur reste à éprouver. Notre contournement (erreurAffichable(e).status, dansapps/web) est en place et n'a rien coûté — il est même devenu la règle écrite du dépôt, parce qu'il est correct indépendamment de ce ticket.Corrigé.
@eqs/httppublie une entrée navigateur :packages/http/package.jsonexpose./browser, etpackages/http/src/browser.tsréexporteApiErrordepuis@eqs/errors/client. Un composant client n'a donc plus à passer par l'entrée serveur, et ne tire ni@azure/msal-nodeninode:fsdans le bundle.À noter : c'est #53 qui rend ce défaut détectable —
eqs checkcompile désormais le front, donc la chaîne@eqs/http → @eqs/auth → @azure/msal-node → node:fséchouerait à la porte au lieu d'attendre qu'un humain ouvre la page.Fermé après vérification contre le code de
devle 2026-09-21. Ce ticket restait ouvert faute d'unCloses #54dans le commit, pas faute de correctif.