upload (900->427 l.) et api-detail (700->330 l.) : le template inline géant
passe en templateUrl. Aucun changement de comportement (build + eslint a11y
+ runtime OK). Composants bien plus navigables ; les .html sont désormais
lintés par angular-eslint (a11y).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extrait le fil d'Ariane DSFR dupliqué dans browse/api-detail/upload vers un
composant présentationnel piloté par une liste d'items. Chaque page construit
ses items (computed pour browse/api-detail, statique pour upload).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rejette les fichiers trop volumineux à la sélection avec un message clair,
avant lecture FileReader/envoi serveur.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- data-source.ts (CLI) + scripts migration:generate/run/revert/show
- migration InitialSchema : schéma complet (categories, api_entries),
vérifiée en l'appliquant sur une base vierge (crée aussi l'ext uuid-ossp)
- app.module: charge les migrations et migrationsRun en prod uniquement.
Corrige un trou : la prod avait synchronize:false SANS migrations
(schéma vide au déploiement). Dev inchangé (synchronize:true).
Bascule de dev vers migrations-only (synchronize:false + baseline de la base
dev existante) = étape ultérieure à faire en supervisé.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Vitest (cohérent avec le back) pour les tests de logique/services purs ;
vitest-setup charge @angular/compiler (fallback JIT requis pour importer
les symboles @angular/* partiellement compilés)
- error.service.spec.ts : 6 cas couvrant toMessage()
- script test -> vitest run (+ test:watch). Tests de composants TestBed =
plugin Angular dédié, hors périmètre pour l'instant.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Ajoute le provider de coverage + section coverage dans vitest.config (reporter
text/html/lcov, exclusions modules/entities/dto/main) et un script test:cov.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CreateApiUploadDto : valide le multipart POST /uploads via ValidationPipe
(types, @IsIn énums partagés, @IsUUID, @MaxLength(255)). Emails validés
seulement si renseignés (@ValidateIf) -> n'casse pas les contacts vides.
Remplace le body inline + les checks manuels du contrôleur.
- create-api-entry.dto: @MaxLength(255) sur name/provider/contacts (matche les
colonnes entité) + @MaxLength sur emails.
Vérifié: email invalide -> 400 message clair, contacts vides -> 201 OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Toutes les souscriptions des composants passent par
takeUntilDestroyed(this.destroyRef) : browse, catalog, api-detail, upload,
category-form-modal, category-delete-modal. api-detail abandonne le pattern
manuel Subscription/ngOnDestroy au profit de DestroyRef (ngOnDestroy ne garde
que le clearPoll du setInterval).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- src/environments/{environment,environment.prod}.ts + fileReplacements :
baseUrl API configurable au lieu de '/api' hardcodé dans les services
- ErrorService + errorInterceptor (HttpInterceptorFn) ; app.config passe à
withInterceptors([...]) (remplace withInterceptorsFromDi legacy)
- @gouvfr/dsfr déplacé en dependencies (chargé au runtime)
- api.service: extrait ValidateResponse (dédup du type inline)
- fix: formValues passe en protected (utilisé dans le template -> build NG1
cassé, le conteneur servait un ancien bundle)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ConfigModule.forRoot({ validate }) : schéma class-validator des variables
d'env, l'app refuse de démarrer si une var manque/mal typée ; suppression
des fallbacks de credentials DB (getOrThrow)
- setCurrent() enveloppé dans une transaction (plus de fenêtre incohérente
sur isCurrent entre les 2 updates)
- browse() : compteurs de toutes les catégories en 2 requêtes + calcul en
mémoire au lieu d'un N+1 récursif par enfant
- console.* -> Logger Nest partout (apis, uploads, seeder, bootstrap) ;
.catch(console.error) -> logger.error avec contexte
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- API_STATUSES inclut 'NO_YAML' (était manquant alors que le type et le
code l'utilisent)
- ApiType/ApiStatus/ApiConvention dérivés des constantes (typeof X[number])
-> plus de double maintenance type <-> tableau
- DTO back réutilisent API_CONVENTIONS/API_TYPES dans @IsIn (fin de la
triple duplication des valeurs)
- front: déclare la dépendance @datacat/shared (workspace:*)
- shared: exports map + métadonnées + README (double build front/back)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Refactoring minimal : le bloc de parsing de métadonnées (titre, version,
description) est extrait en méthode privée extractMetadata() dans
UploadsController. Le comportement de validate() est identique.
12 tests unitaires couvrent tous les cas : version standard (1.2.3, 1.0, 2,
0.0.0), extraction nom/description, et cas limites (version non-numérique,
absente, vide, info absent).
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- 21 nouveaux tests couvrant create, findOneOrThrow, remove,
findVersionsOf, setCurrent et regenerate (pattern vi.fn() sans NestJS TestingModule)
- apis.service.ts : ConflictException sur doublon de version (name+convention+version)
- upload.component.ts : verrouillage convention/name/type lors d'une nouvelle
version, bannière d'info, alerte et blocage si version déjà existante
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Déplace "Voir la documentation" hors du dropdown Actions
en bouton principal à gauche du menu (désactivé si status ≠ GENERATED)
- nest-cli.json : copie automatique des assets YAML du seeder
(src/modules/seeder/assets/**/*.yaml → dist, watchAssets: true)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Backend : seuil word_similarity >= 0.4 (au lieu de > 0.4) sur cat.name
- Frontend : correction erreur TypeScript dans CategoryService.list()
(params typé Record<string,string> pour satisfaire Angular HttpClient)
— le code précédent ne compilait pas, la recherche était ignorée
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Supprime le badge statut (GENERATED/PENDING/ERROR) des tuiles browse et cartes search
- Ajoute un badge version (ex: v1.2.0) sur les tuiles et cartes
- Traduit le statut en français sur la fiche détail (Générée / En attente / Erreur)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- name/provider : seuil 0.4 (textes courts, scores plus précis)
- description : seuil 0.5 (textes longs, évite les faux positifs)
- "auten" trouve désormais "Authentification JWT" via fuzzy sur le nom
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- parseSearchTokens() côté backend : extrait type (async/openapi),
version (v1, 1.2.3) et latestOnly depuis la chaîne brute
- Recherche fuzzy via word_similarity (pg_trgm, seuil 0.5) + ILIKE
- latestOnly automatique quand champ non vide sans version explicite
- Suppression de parseSearch() dans BrowseComponent et CatalogComponent
- Suppression de version/latestOnly de ApiListQuery (géré par le backend)
- Activation de pg_trgm via CREATE EXTENSION dans SeederService
- Tests unitaires Vitest pour parseSearchTokens (24 cas)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- GET /api/apis/:id/versions — retourne toutes les versions du même API
(même name + convention), triées par version DESC
- Sélecteur DSFR sur la page détail quand plusieurs versions existent ;
changement de version navigue vers /catalog/:newId en rechargeant
le composant via abonnement à paramMap (fix réutilisation instance Angular)
- Bouton "Nouvelle version" → /upload?from=:id avec pré-remplissage du
formulaire (convention, name, provider, contacts, version patch+1)
- Seeder : 2 versions démo supplémentaires de "Référentiel Produits"
(v3.0.3, v3.0.2) pour tester le sélecteur
- Fix import ApiStatus manquant dans apis.service.ts
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Remplace le confirm() natif par une vraie modale de confirmation avec gestion d'erreur inline
- Ajoute un bouton crayon dans le <h1> pour éditer la catégorie courante (visible uniquement dans /browse/:id)
- Signature openEdit() assouplie pour accepter Category (au lieu de CategoryWithCounts)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Nouveau schéma api_entries : convention+name, version X.X.X, provider,
contacts splitées (name/entity/email), suppression des anciens champs
- Formulaire upload étape 2 : convention+nom, version 3 champs, fournisseur,
contacts métier et technique obligatoires ; type détecté auto → select désactivé
- Validation YAML : redocly.yaml (extends: spec), support noms avec espaces,
extraction métadonnées (name, version, description) depuis info YAML
- Dossiers : modale création avec champ Emplacement pré-initialisé depuis
le contexte de navigation, options └ avec indentation par profondeur
- Import depuis un dossier : pré-sélection de la catégorie via queryParam
- Recherche étendue : name, provider, description, contacts
- Résolution @datacat/shared via workspace:* + compilation CJS avant NestJS
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Nouvel endpoint POST /api/uploads/validate : validation en 3 niveaux
(syntaxe js-yaml → détection type → asyncapi validate / redocly lint)
- Extraction automatique de info.title/version/description pour pré-remplir le formulaire
- Validation déclenchée immédiatement à la sélection, résultat affiché inline
- Bouton "Suivant" désactivé pendant la validation et si le fichier est invalide
- Sans fichier sélectionné, on peut toujours passer à l'étape 2
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Erreurs affichées sous chaque champ obligatoire (fr-error-text DSFR)
au lieu d'un message global en haut du formulaire
- Déclenchement sur blur (sortie du champ) et effacement sur input
- Concerne la modale édition API et la modale catégorie
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Bouton "Modifier" sur la page détail ouvre une modale avec tous les
champs éditables (titre, version, catégorie, description, docs, contacts,
droits d'accès)
- ApiService.update() étendu à tous les champs du UpdateApiEntryDto
- Remplacement des labels "Obligatoire"/"Optionnel" par un astérisque
rouge sur les champs requis dans tous les formulaires
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Bouton modifier réduit à l'icône crayon, placé à côté du titre de la tuile
- apiCount dans CategoryWithCounts inclut désormais les APIs de tous les
sous-dossiers récursivement (countApisRecursively)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
dsfr.min.css référence les SVG via ../icons/ (résout vers assets/icons/)
alors que icons.min.css les référence via ../../icons/ (assets/dsfr/icons/).
Les deux chemins sont maintenant mappés dans angular.json.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Recherche simultanée dans les dossiers (filtrage client-side) et les
APIs (backend) via forkJoin — affiche dossiers en tuiles puis APIs en
cartes, avec labels "Dossiers"/"APIs" si les deux ont des résultats
- Loader instantané dès la frappe (avant le debounce 400ms)
- Fix angular.json : ajout du mapping dist/icons/ → assets/dsfr/icons/
pour servir les SVG référencés par le CSS DSFR (tous les icônes étaient
cassés car le chemin relatif ../../icons/ donnait 404)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Barre de recherche intégrée dans BrowseComponent
- Mode navigation (tuiles) si recherche vide, mode liste (cartes) si texte
- Debounce 400ms + pagination pour les résultats de recherche
- Suppression de l'entrée "Catalogue" du menu de navigation
- Redirection /catalog → /browse (vieux liens toujours fonctionnels)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Validation syntaxique js-yaml avant import (BadRequestException 400 avec message précis)
- Logger dans UploadsController : log du fichier principal détecté pour multi-upload
- AllExceptionsFilter : log complet avec stack trace pour toutes les erreurs 5xx
- @asyncapi/bundler version corrigée (0.9.0 → ^1.0.1, version inexistante)
- @types/js-yaml ajouté en devDependencies
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Ajout @asyncapi/html-template@3.5.6 en dépendance locale
- Suppression du flag --install (template trouvé dans node_modules)
- Évite l'appel réseau vers registry.npmjs.org lors de la génération
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
execFileAsync ne voit pas node_modules/.bin par défaut — on l'ajoute
explicitement à l'env du process enfant pour asyncapi et redocly.
Fonctionne aussi bien en local qu'en Docker (priorité locale, fallback global).
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>