batchCooking/apps/api/src/lib/recipe-source-errors.ts
Nicolas 4a1e46b283 feat(recipes): module générique d'adaptateurs de sources de recettes
Pose les bases du pipeline d'import décrit dans
specs/batch-cooking-architecture.md (Import depuis source → Traduction
en étapes → Sauvegarde), en commençant par le premier maillon :
récupérer et parser des recettes brutes depuis une source externe,
indépendamment du site/API concerné.

- RecipeSourceAdapter<TRawDetail> (recipe-source-adapter.ts) : contrat
  générique par source — list() pour parcourir un catalogue de façon
  paginée (l'utilisateur "browse" les recettes disponibles), puis
  fetchDetail(externalId) une fois une recette sélectionnée, puis
  parse(raw) pour la transformer en ParsedRecipe. parse() est pure et
  synchrone (même séparation I/O vs pur que tech-step-matcher.ts), ce
  qui la rend testable sans réseau.
- ParsedRecipe est volontairement distinct de CreateRecipeInput : les
  ingrédients restent en texte libre (pas d'ingredientId/unitId) — la
  résolution vers nos catalogues Ingredient/Unit est un sujet séparé,
  pas encore construit.
- recipe-source-registry.ts : registre en mémoire des adaptateurs,
  identifiés par une clé stable (même convention que Diet.key/
  Unit.key/TechStep.key), distinct de la table Source (schema.prisma)
  qui documente la provenance d'une recette déjà sauvegardée.
- recipe-source-errors.ts : RecipeSourceFetchError/RecipeSourceParseError,
  vocabulaire d'erreur dédié en attendant qu'une route les traduise en
  HttpError/ErrorCode.

Pas de route HTTP, pas d'écriture en base, pas d'implémentation
concrète pour l'instant — uniquement le module générique, validé par
un adaptateur factice dans les tests. Le câblage (endpoint, sourceId,
un vrai parseur) sera une PR suivante.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 07:15:50 +02:00

37 lines
1.8 KiB
TypeScript

/**
* Error vocabulary a {@link RecipeSourceAdapter} (recipe-source-adapter.ts)
* implementation throws when talking to its source fails — kept separate
* from `@batch-cooking/error-tools`'s `HttpError`/`ErrorCode` (used for
* *this API's* HTTP responses) since no route drives this module yet. A
* future import route would catch these and translate them into an
* `HttpError` with a dedicated `ErrorCode` the same way any other service
* error is; this module only needs a consistent shape to throw in the
* meantime, not that translation.
*/
/** Base class for every error a {@link RecipeSourceAdapter} can throw — lets a caller `catch (err) { if (err instanceof RecipeSourceError) ... }` regardless of which stage failed. */
export class RecipeSourceError extends Error {
/** The failing adapter's `key` (recipe-source-adapter.ts's `RecipeSourceAdapter.key`) — which source this error came from. */
readonly sourceKey: string;
constructor(sourceKey: string, message: string, options?: { cause?: unknown }) {
super(message, options);
this.sourceKey = sourceKey;
}
}
/** The source's `list`/`fetchDetail` failed — network error, non-2xx response, source unreachable, etc. */
export class RecipeSourceFetchError extends RecipeSourceError {
constructor(sourceKey: string, message: string, options?: { cause?: unknown }) {
super(sourceKey, message, options);
this.name = "RecipeSourceFetchError";
}
}
/** The source responded, but `parse` couldn't make sense of the raw payload (unexpected shape, missing required field, …). */
export class RecipeSourceParseError extends RecipeSourceError {
constructor(sourceKey: string, message: string, options?: { cause?: unknown }) {
super(sourceKey, message, options);
this.name = "RecipeSourceParseError";
}
}