# Configurer un workflow de synchronisation de spec OpenAPI dans ce projet
Je veux le même workflow de spec backend que celui que j'utilise dans un autre dépôt : un script qui compare la spec OpenAPI en direct avec un instantané local et écrit un rapport de changements orienté **frontend**, plus une commande slash `/api-sync` qui transforme ce rapport en plan par phases.
Remplissez ces éléments à partir du dépôt avant de commencer (demandez-moi seulement si vous ne pouvez pas les déterminer) :
- **Source de la spec** : trouvez-la vous-même - voir Partie 0. Ne me demandez pas l'URL avant d'avoir cherché. Ce que vous trouvez devient la valeur par défaut du script, remplaçable via une variable d'environnement `OPENAPI_URL`.
- **Chemin de l'instantané** : `api-spec/openapi.yaml` · **Chemin du rapport** : `api-spec/CHANGES.md`
- **Racine source pour la référence croisée** : `src/` (ajustez selon la structure de ce dépôt)
- **Bibliothèque de validation des réponses** : zod (ajustez si ce dépôt utilise autre chose)
- **Instantané dans git ?** Gardez le yaml **gitignoré** (trop volumineux/bruyant pour l'historique) mais **committez `CHANGES.md`** - le rapport généré est l'enregistrement durable de ce qui a changé et quand. Ignorez aussi `api-spec/openapi-*.yaml` et `api-spec/CHANGES-*.md` (archives manuelles datées).
Lisez d'abord ce dépôt (gestionnaire de paquets, conventions de script, comment les appels API et les schémas de réponse sont écrits) et correspondez à son style. N'inventez pas de chemins - grep pour les vrais.
---
## Partie 0 - trouvez la spec avant d'écrire quoi que ce soit
Faites cela d'abord et dites-moi ce que vous avez trouvé. Ne devinez pas une URL, et ne me demandez pas jusqu'à ce que cela revienne vide.
**1. La documentation - l'endroit le moins cher, et généralement le bon.** `README.md`, `CLAUDE.md`, `AGENTS.md`, `CONTRIBUTING.md`, tout ce qui se trouve sous `docs/`, `.github/`, `.cursor/rules/`, un fichier brouillon `*.http`/`*.rest`, ou un checkout wiki. L'URL est souvent dans la prose ("API docs : …/swagger"), dans une étape de configuration, ou à côté du lien du dépôt backend :
```bash
grep -rniE 'swagger|openapi|api-?docs|redoc|\/v3\/api-docs' --include='*.md' --include='*.mdx' --include='*.txt' --include='*.http' --include='*.rest' . | grep -v node_modules
```
Deux pièges : un **lien Swagger UI** (`…/swagger-ui/index.html`, `…/docs`, `…/redoc`) est une page HTML, pas la spec - dérivez l'URL machine à partir de celle-ci (`/swagger-ui/index.html` → `/v3/api-docs`, `/docs` → `/openapi.json`, `/redoc` → le `spec-url` dans son HTML) et vérifiez avec curl. Et une URL de documentation peut être **périmée** - confirmez qu'elle répond avant de l'adopter, et dites-moi si le README pointe vers quelque chose de mort.
**2. Un fichier de spec déjà dans ou près du dépôt** - quelqu'un en a généralement vendu un :
```bash
find . -path ./node_modules -prune -o -iregex '.*\(swagger\|openapi\|api-docs\).*\.\(ya?ml\|json\)' -print
git ls-files | grep -iE 'swagger|openapi|api-docs'
```
Vérifiez aussi `node_modules/.cache/`, `.next/cache/`, `dist/`, `build/`, `coverage/` et tout dossier `api/`, `api-spec/`, `docs/`, `schemas/` gitignoré - une exécution de génération de code précédente y a souvent laissé une copie. Une copie en cache périmée est toujours utile : c'est une **base de référence pour amorcer l'instantané**, afin que la première vraie comparaison soit significative au lieu de "tout est nouveau". Si vous en trouvez une, dites son âge (`git log -1` / mtime du fichier) avant de décider de lui faire confiance.
**3. Une configuration de générateur qui nomme déjà la source** - c'est la correspondance la plus pertinente, car elle pointe vers l'URL ou le chemin que l'équipe utilise réellement :
```bash
grep -rniE 'openapi|swagger|api-docs' --include='*.json' --include='*.ts' --include='*.js' --include='*.mjs' --include='*.yaml' --include='*.yml' --include='.env*' --include='Makefile' --include='*.sh' -l . | grep -v node_modules
```
Cherchez spécifiquement : la configuration `openapi-typescript` / `orval.config.*` / `kubb.config.*` / `swagger-typescript-api` / `@hey-api/openapi-ts`, un script npm de type `openapi` dans package.json, une URL de base API `.env*`, les URLs de service `docker-compose.yml`, les étapes de workflow CI, ou un client généré committé dont le commentaire d'en-tête cite sa spec source.
**4. Dérivez-la de l'URL de base de l'API.** Si vous ne trouvez qu'une URL de base, sondez les chemins conventionnels pour le framework de ce backend avant de me demander - FastAPI `/openapi.json`, Spring/springdoc `/v3/api-docs` (+ `.yaml`), ASP.NET `/swagger/v1/swagger.json`, NestJS `/api-json`, Rails/rswag `/api-docs/v1/swagger.yaml`, plus `/openapi.yaml` et `/swagger.json` simples :
```bash
curl -sS -o /dev/null -w '%{http_code} %{content_type} %{url_effective}\n' <BASE>/openapi.json
```
Rapportez lesquels ont répondu. S'ils nécessitent tous une authentification, dites-le - n'intégrez pas de jeton dans le script.
**5. Rien ne fonctionne ?** Alors demandez-moi, et dites-moi ce que vous avez éliminé.
### Si la spec n'est pas accessible via HTTP
Ne forcez pas la conception de la récupération. Faites de la source un `SPEC_SOURCE` unique qui peut être **une URL, un chemin local, ou une commande shell** (par exemple le `make openapi` du dépôt backend lui-même, ou un fichier généré d'un checkout frère), résolu dans cet ordre : drapeau `--to <fichier>` → env `OPENAPI_URL` → la valeur par défaut que vous avez découverte. Tout ce qui suit - comparaison, rapport, instantané - est inchangé. Dites dans `CLAUDE.md` lequel ce dépôt utilise et comment le rafraîchir.
## Partie 1 - `scripts/sync-api.mjs`
Un script Node ESM unique et léger en dépendances (`js-yaml` est la seule nouvelle dépendance ; utilisez le gestionnaire de paquets du dépôt). Drapeaux :
```
node scripts/sync-api.mjs récupérer le distant → comparer avec l'instantané → écrire le rapport + écraser l'instantané
node scripts/sync-api.mjs --check comparer uniquement, instantané intact, sortie 1 en cas de dérive (adapté au CI)
node scripts/sync-api.mjs --from <fichier> comparer avec <fichier> au lieu de l'instantané
node scripts/sync-api.mjs --to <fichier> traiter <fichier> comme "distant" au lieu de récupérer (hors ligne)
node scripts/sync-api.mjs --json imprimer aussi la comparaison brute en JSON sur stdout
```
Ajoutez `"sync:api": "node scripts/sync-api.mjs"` à package.json.
**Si aucun instantané n'existe encore** : écrivez la spec récupérée comme instantané, imprimez "amorcé - relancez après que le backend soit livré pour voir une différence", sortie 0. Ne signalez jamais toute l'API comme "nouvelle". Exception : si la Partie 0 a révélé une spec en cache/vendue plus ancienne, amorcez l'instantané à partir de **celle-ci** à la place et exécutez une vraie comparaison avec la spec en direct lors de la première exécution - dites-moi la date de la copie en cache pour que je sache ce que signifie la base de référence.
### Ce qu'il doit comparer
Aplatissez `paths` en une carte d'opérations `"GET /a/b"` → et comparez les opérations *et* `components.schemas` séparément :
**Opérations**
- ajoutées / supprimées / modifiées
- paramètres : nouveaux (drapeau `required`), inversions requis↔optionnel, valeurs d'enum ajoutées/supprimées - clé un paramètre par `in:name` (ou `ref:Name` pour les paramètres `$ref`), pas par index de tableau
- schéma du corps de requête et de la réponse de succès : si le nom `$ref` a changé, signalez le renommage ; si la forme est **inline** (pas de `$ref`), comparez ses propriétés ici - un schéma sans nom est comparé ici ou nulle part. Résolvez les wrappers `allOf: [$ref]` vers le nom sous-jacent, et rendez les unions `oneOf`/`anyOf` comme `A | B` (gagner/perdre un membre d'union est un vrai changement de comportement).
- nouveaux/supprimés codes de statut non-2xx
- changements d'exigences de sécurité, nouvellement `deprecated`
**Schémas**
- propriétés ajoutées (marquez requis) / supprimées / retypées
- valeurs d'enum ajoutées ou supprimées (sur le schéma et sur chaque propriété, y compris `items.enum`)
- inversions requis↔optionnel
### Les deux choses qui rendent ce rapport utile
1. **Extraction de `error_code` à partir de la prose des réponses.** Les codes d'erreur machine ne sont généralement documentés nulle part sauf dans le texte `description` de chaque réponse non-2xx ("… déjà un membre actif (already_member)"), donc une nouvelle branche que nous devons gérer ressemble à *rien n'a changé* pour une comparaison au niveau du schéma. Analysez-les par **contexte, pas par vocabulaire** : jetons entre parenthèses `(...)`, jetons après une prose de type `error_code`, et tout jeton snake_case qui apparaît dans l'enum littéral `error_code` d'un schéma. Ne **filtrez pas** les jetons qui entrent en collision avec des noms de champs ou des valeurs d'enum - ces collisions sont exactement les codes qui comptent le plus. Supprimez les jetons introduits par la formulation "champ X" (ce sont des noms de champs, pas des codes). Signalez les codes ajoutés, et pour un code qui cesse d'être documenté, grep le source pour `"that_code"` et dites **quel fichier branche dessus** - c'est une branche morte.
2. **Référence croisée de chaque changement avec le code réel.** Chargez chaque fichier source une fois via `git ls-files --cached --others --exclude-standard <racine src>` (incluez les non suivis pour qu'un site d'appel ajouté cette session compte ; ignorez les fichiers listés mais supprimés de l'arbre de travail), puis :
- **Sites d'appel** pour un chemin : transformez les paramètres de chemin en jokers à segment unique et exigez que la correspondance se termine à un guillemet/backtick/`?` pour que `/orgs/{id}` ne corresponde pas à `/orgs/${id}/archive`. Chaque opération supprimée/modifiée liste ses sites d'appel, ou "⛔ aucun site d'appel".
- **Miroirs de schéma** : trouvez le fichier contenant notre miroir de validation de réponse d'un schéma de spec - correspondez à `fooBarSchema` n'importe où, plus le nom PascalCase nu **uniquement à l'intérieur d'un `schemas.ts`** (ailleurs, il entre en collision avec des identifiants TS sans rapport). Adaptez la convention de nommage à ce que ce dépôt utilise réellement - grep d'abord.
- Une nouvelle opération dont le chemin est déjà référencé dans `src/` reçoit une note "⚠️ chemin déjà référencé - vérifiez la méthode".
### Format du rapport (`api-spec/CHANGES.md`)
En-tête avec date de génération, étiquette de base de référence, `info.version` de la spec, et comptes d'opérations et de schémas avant→après ; puis un petit tableau ajouté/supprimé/modifié ; puis les sections, dans cet ordre :
- `## 🔴 Opérations supprimées - cassant si nous les appelons` (avec sites d'appel)
- `## 🟡 Opérations modifiées` (puces imbriquées par changement + sites d'appel)
- `## 🟢 Nouvelles opérations`, groupées par zone de chemin (premier segment, avec cas spéciaux sensés pour les préfixes de cette API) - résumé, schéma de réponse, schéma du corps de requête
- `## 🔴 Schémas supprimés` (avec fichiers miroirs)
- `## 🟡 Schémas modifiés` - **les miroirs d'abord, triés et en gras avec le fichier miroir**, car ce sont ceux qui peuvent casser l'analyse aujourd'hui ; le reste est informatif
- `## 🟢 Nouveaux schémas` (une ligne séparée par des virgules)
Si rien n'a changé, le corps est exactement "Aucun changement depuis le dernier instantané." Mettez en haut du fichier "ne pas éditer à la main".
Gardez le script commenté là où une décision n'est pas évidente (l'heuristique error_code, l'ancre de fin du matcheur de chemin, pourquoi les fichiers non suivis sont inclus) - le moi futur lit ces commentaires.
## Partie 2 - `.claude/commands/api-sync.md`
Une commande slash (`/api-sync [portée]`, portée optionnelle, accepte aussi `implement`) qui exécute le workflow. Frontmatter : `description` + `argument-hint`. Étapes :
1. **Comparaison** - exécutez `npm run sync:api`, lisez `api-spec/CHANGES.md`. Signalez les deux jugements que le rapport peut signaler mais pas décider : un **corps de requête** modifié sur un site d'appel en direct est actionnable même sans miroir de schéma (nous construisons les corps à la main), et un nouveau **`error_code`** est une branche que nous n'avons pas encore - s'il s'agit d'une erreur de champ, elle doit s'afficher en ligne sur le champ, pas seulement comme un toast. Si le rapport dit qu'il n'y a aucun changement, dites-le et arrêtez - n'inventez pas de travail.
2. **Classez chaque élément** en : Cassant (P0) · Silencieusement faux - un schéma miroir a gagné un champ requis ou une enum a grandi avec des valeurs que notre validateur rejette (P0) · Maintenant-incomplet - un corps de requête construit à la main a gagné un champ, ou un nouveau `error_code` sur lequel nous ne branchons pas (P1) · Dé-mocke un écran (P1) · Étend un écran (P2) · Fonctionnalité entièrement nouvelle (P3) · Backend uniquement (supprimer). Ne sautez jamais un élément ; s'il ne correspond nulle part, listez-le comme question ouverte. Vérifiez chaque classification par rapport au code plutôt que de supposer - ouvrez le fichier miroir nommé, grep pour le fixture mock, confirmez que l'écran existe.
3. **Écrivez le plan** - une section datée dans `ROADMAP.md` (ou l'équivalent de ce dépôt ; créez-en un s'il n'y en a pas), ordonnée par ces priorités et par phases afin que chaque phase soit livrable indépendamment. Par élément : les endpoints et fichiers qui changent, ce que l'utilisateur peut faire ensuite qu'il ne peut pas aujourd'hui (le but du travail - pas "câbler le endpoint X"), et si c'est bloqué et par qui. Une ligne par élément. Puis mettez à jour les documents de couverture/suivi que ce dépôt conserve.
4. **Rapportez en chat** - ce que le backend a livré en un paragraphe en langage simple, tout ce qui est cassé maintenant avec le fichier à corriger, les phases une ligne chacune, et les vraies questions pour le développeur backend uniquement. Arrêtez là ; seulement si l'argument contient `implement`, construisez **la Phase 1 uniquement**, puis exécutez le typecheck + lint de ce dépôt et rapportez avant de continuer.
## Partie 3 - câblez-le
- Ajoutez les entrées gitignore.
- Ajoutez une courte section **Workflow de changement backend** à `CLAUDE.md` (créez-la si manquante) : l'instantané est local et gitignoré, `CHANGES.md` est l'enregistrement committé, `CHANGES.md` est généré donc ne l'éditez jamais à la main, `/api-sync` quand le développeur backend dit que quelque chose est livré, `npm run sync:api -- --check` pour détecter la dérive, et l'habitude d'archiver un `api-spec/openapi-YYYY-MM-DD.yaml` daté avant un gros changement backend comme point de référence committé.
- Amorcez l'instantané en exécutant le script une fois, et montrez-moi le premier rapport - plus une note d'une ligne sur l'origine de la spec et, si vous avez amorcé à partir d'une copie en cache, son degré de péremption.
Discussion
0 commentaires