L'endpoint IA que list() utilisait pour toute recherche (SEARCH_URL,
/genius/query/) répond avec un corps de réponse vide dès que query est
vide — vérifié en direct. Résultat : parcourir la source 750g sans filtre
ne remontait jamais aucune recette.
Corrigé en lisant un endpoint différent quand query est vide/absent :
dernieres-recettes.htm, le vrai catalogue paginé "dernières recettes" de
750g.com (pagination réelle via &page=N, contrairement à l'endpoint de
recherche). nextCursor suit désormais cette même distinction : toujours
null pour une recherche par texte (l'endpoint ne pagine pas), calculé
normalement pour le parcours sans filtre (une page sans aucune carte en
est le signal de fin, cet endpoint ne renvoyant ni 404 ni redirection une
fois la dernière page dépassée).
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
* feat(recipes): ajoute un adaptateur RecipeSourceAdapter pour Marmiton
Étend jsonLdRecipeAdapter (json-ld-recipe.ts) plutôt que de dupliquer sa
logique : marmitonAdapter délègue fetchDetail/parse directement à
l'adaptateur générique JSON-LD (une page recette marmiton.org expose un
Recipe schema.org standard), et n'ajoute que ce que l'adaptateur
générique ne peut pas offrir — un list() qui lit l'ItemList schema.org
embarqué sur la page de résultats de recherche de marmiton.org (pagination
via &page=N, fin de résultats détectée via la réponse 404 renvoyée
au-delà de la dernière page).
extractJsonLdBlocks est exporté depuis json-ld-recipe.ts pour être
réutilisé par marmiton.ts sans dupliquer le regex d'extraction des blocs
<script type="application/ld+json">.
Enregistre marmitonAdapter dans registerAllRecipeSources (sources/index.ts)
— contrairement à jsonLdRecipeAdapter lui-même, c'est un adaptateur concret
par site, donc une Source household-toggleable légitime.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat(recipes): ajoute un adaptateur RecipeSourceAdapter pour 750g
Suit le même schéma que marmitonAdapter (construit sur jsonLdRecipeAdapter),
avec deux différences propres à 750g.com :
- list() n'a pas d'ItemList JSON-LD à lire sur ses résultats de recherche
(la recherche du site est un widget client-side) — appelle donc
directement le endpoint GET que ce widget interroge lui-même en interne
(un « moteur de réponse IA » qui renvoie un lot de recettes pour une
requête en texte libre), et scrape les cartes de résultat par regex en
associant à chaque lien de recette sa dernière image précédente plutôt
qu'un zip naïf par index (des images décoratives sans carte associée
existent réellement dans ce fragment). Vérifié en direct : demander une
« page 2 » revient toujours vide, donc nextCursor vaut toujours null,
comme theMealDbAdapter.
- parse() ne délègue pas aussi directement à jsonLdRecipeAdapter.parse que
marmitonAdapter — le générateur JSON-LD de 750g.com a deux bugs réels :
des caractères de contrôle bruts non échappés dans certaines chaînes JSON
(~1 recette sur 3 dans un échantillon vérifié en direct, sinon
JSON.parse échoue et jsonLdRecipeAdapter rapporte à tort « aucun
Recipe trouvé »), et un texte parfois doublement encodé en entités HTML
(ex. un vrai « é » devient &eacute; au lieu de é). Les deux
sont corrigés en pré/post-traitement autour de la même délégation, pas
une réimplémentation.
Enregistre sevenFiftyGAdapter dans registerAllRecipeSources
(sources/index.ts), au même titre que marmitonAdapter.
Complète aussi test/sources/sources-index.test.ts, qui ne couvrait encore
que TheMealDB malgré l'ajout de Marmiton dans une PR précédente.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* feat(recipes): ajoute un adaptateur RecipeSourceAdapter pour Manger Bouger
Suit le même schéma que marmitonAdapter/sevenFiftyGAdapter (construit sur
jsonLdRecipeAdapter), avec des différences propres à mangerbouger.fr
(« La Fabrique à Menus », Santé publique France) :
- list() n'utilise pas de JSON-LD du tout — la page de résultats (une app
Next.js) n'embarque aucun ItemList. Elle est cependant rendue
côté serveur et expose le même state Redux que le client hydrate, via un
<script id="__NEXT_DATA__">, qui contient déjà tout ce dont list() a
besoin (slug/nom/image, pagination). Vérifié en direct : ?query=<texte
libre> filtre bien côté serveur, et hasMorePages donne un signal de fin
de pagination plus propre que le 404 de Marmiton ou l'absence de vraie
pagination de 750g.
- parse() délègue à jsonLdRecipeAdapter mais corrige deux lacunes réelles
et systématiques de son propre JSON-LD (vérifiées sur 9 recettes,
72 étapes) : recipeInstructions[].text est un document Slate.js
sérialisé en JSON (pas du texte) plutôt qu'être aplati ; recipeYield est
absent partout alors que le nombre de portions existe bien côté site
(__NEXT_DATA__) — les deux sont corrigés par un patch structuré (parse →
mutation → réécriture) avant délégation, pas une réimplémentation.
Enregistre mangerBougerAdapter dans registerAllRecipeSources
(sources/index.ts) et complète sources-index.test.ts.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>