recipe-scrapers-js
Version:
A recipe scrapers library
1,041 lines (1,040 loc) • 32.3 kB
text/typescript
import * as cheerio from "cheerio";
import { CheerioAPI } from "cheerio";
import z$1, { z } from "zod";
import { ParseIngredientOptions } from "parse-ingredient";
//#region src/logger.d.ts
declare enum LogLevel {
VERBOSE = 0,
DEBUG = 1,
INFO = 2,
WARN = 3,
ERROR = 4,
}
declare class Logger {
private context;
private logLevel;
constructor(context: string, logLevel?: LogLevel);
verbose(...args: unknown[]): void;
debug(...args: unknown[]): void;
log(...args: unknown[]): void;
info(...args: unknown[]): void;
warn(...args: unknown[]): void;
error(...args: unknown[]): void;
}
//#endregion
//#region src/abstract-plugin.d.ts
declare abstract class AbstractPlugin {
readonly $: CheerioAPI;
/** The name of the plugin */
abstract name: string;
/** The priority of the plugin */
abstract priority: number;
constructor($: CheerioAPI);
}
//#endregion
//#region src/schemas/recipe.schema.d.ts
/**
* Current schema version for recipe objects.
* Increment this when making breaking changes to the schema.
*
* Version history:
* - 1.0.0: Initial schema version
*/
declare const RECIPE_SCHEMA_VERSION: "1.0.0";
/**
* Schema for a parsed ingredient from the parse-ingredient library.
* This represents the structured data extracted from an ingredient string.
* @see https://github.com/jakeboone02/parse-ingredient
*/
declare const ParsedIngredientSchema: z.ZodObject<{
quantity: z.ZodNullable<z.ZodNumber>;
quantity2: z.ZodNullable<z.ZodNumber>;
unitOfMeasureID: z.ZodNullable<z.ZodString>;
unitOfMeasure: z.ZodNullable<z.ZodString>;
description: z.ZodString;
isGroupHeader: z.ZodBoolean;
}, z.core.$strip>;
/**
* Schema for a single ingredient item
*/
declare const IngredientItemSchema: z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
parsed: z.ZodNullable<z.ZodOptional<z.ZodObject<{
quantity: z.ZodNullable<z.ZodNumber>;
quantity2: z.ZodNullable<z.ZodNumber>;
unitOfMeasureID: z.ZodNullable<z.ZodString>;
unitOfMeasure: z.ZodNullable<z.ZodString>;
description: z.ZodString;
isGroupHeader: z.ZodBoolean;
}, z.core.$strip>>>;
}, z.core.$strip>;
/**
* Schema for a group of ingredients
*/
declare const IngredientGroupSchema: z.ZodObject<{
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
items: z.ZodArray<z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
parsed: z.ZodNullable<z.ZodOptional<z.ZodObject<{
quantity: z.ZodNullable<z.ZodNumber>;
quantity2: z.ZodNullable<z.ZodNumber>;
unitOfMeasureID: z.ZodNullable<z.ZodString>;
unitOfMeasure: z.ZodNullable<z.ZodString>;
description: z.ZodString;
isGroupHeader: z.ZodBoolean;
}, z.core.$strip>>>;
}, z.core.$strip>>;
}, z.core.$strip>;
/**
* Schema for all recipe ingredients
* Must have at least one group with at least one ingredient
*/
declare const IngredientsSchema: z.ZodArray<z.ZodObject<{
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
items: z.ZodArray<z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
parsed: z.ZodNullable<z.ZodOptional<z.ZodObject<{
quantity: z.ZodNullable<z.ZodNumber>;
quantity2: z.ZodNullable<z.ZodNumber>;
unitOfMeasureID: z.ZodNullable<z.ZodString>;
unitOfMeasure: z.ZodNullable<z.ZodString>;
description: z.ZodString;
isGroupHeader: z.ZodBoolean;
}, z.core.$strip>>>;
}, z.core.$strip>>;
}, z.core.$strip>>;
/**
* Schema for a single instruction step
*/
declare const InstructionItemSchema: z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
}, z.core.$strip>;
/**
* Schema for a group of instruction steps
*/
declare const InstructionGroupSchema: z.ZodObject<{
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
items: z.ZodArray<z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
}, z.core.$strip>>;
}, z.core.$strip>;
/**
* Schema for all recipe instructions
* Must have at least one group with at least one step
*/
declare const InstructionsSchema: z.ZodArray<z.ZodObject<{
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
items: z.ZodArray<z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
}, z.core.$strip>>;
}, z.core.$strip>>;
/**
* Schema for a link object
*/
declare const LinkSchema: z.ZodObject<{
href: z.ZodURL;
text: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
}, z.core.$strip>;
/**
* Base RecipeObject schema without cross-field validations.
* Use this schema when you need to extend the recipe object with custom fields.
*
* @example
* ```ts
* import { RecipeObjectBaseSchema, applyRecipeValidations } from 'recipe-scrapers-js'
*
* const MyCustomRecipeSchema = RecipeObjectBaseSchema.extend({
* customField: z.string(),
* })
*
* // Apply the standard recipe validations
* const MyValidatedRecipeSchema = applyRecipeValidations(MyCustomRecipeSchema)
* ```
*/
declare const RecipeObjectBaseSchema: z.ZodObject<{
schemaVersion: z.ZodDefault<z.ZodLiteral<"1.0.0">>;
host: z.ZodCustomStringFormat<"hostname">;
title: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
author: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
ingredients: z.ZodArray<z.ZodObject<{
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
items: z.ZodArray<z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
parsed: z.ZodNullable<z.ZodOptional<z.ZodObject<{
quantity: z.ZodNullable<z.ZodNumber>;
quantity2: z.ZodNullable<z.ZodNumber>;
unitOfMeasureID: z.ZodNullable<z.ZodString>;
unitOfMeasure: z.ZodNullable<z.ZodString>;
description: z.ZodString;
isGroupHeader: z.ZodBoolean;
}, z.core.$strip>>>;
}, z.core.$strip>>;
}, z.core.$strip>>;
instructions: z.ZodArray<z.ZodObject<{
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
items: z.ZodArray<z.ZodObject<{
value: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
}, z.core.$strip>>;
}, z.core.$strip>>;
canonicalUrl: z.ZodURL;
image: z.ZodURL;
totalTime: z.ZodNullable<z.ZodInt>;
cookTime: z.ZodNullable<z.ZodInt>;
prepTime: z.ZodNullable<z.ZodInt>;
ratings: z.ZodDefault<z.ZodNumber>;
ratingsCount: z.ZodDefault<z.ZodInt>;
yields: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
description: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
language: z.ZodDefault<z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
siteName: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
cookingMethod: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
category: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
cuisine: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
keywords: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
dietaryRestrictions: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
equipment: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
links: z.ZodOptional<z.ZodArray<z.ZodObject<{
href: z.ZodURL;
text: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
}, z.core.$strip>>>;
nutrients: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
reviews: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
}, z.core.$strip>;
/**
* Applies recipe-specific transformations and validations to a schema.
* Use this when extending RecipeObjectBaseSchema with custom fields.
*
* @param schema - A Zod object schema that includes
* all RecipeObjectBaseSchema fields
* @returns A schema with transforms and field validations applied
*
* @example
* ```ts
* const CustomSchema = RecipeObjectBaseSchema.extend({
* tags: z.array(z.string()),
* })
*
* const ValidatedCustomSchema = applyRecipeValidations(CustomSchema)
* ```
*/
declare function applyRecipeValidations<T extends z.infer<typeof RecipeObjectBaseSchema>>(schema: z.ZodType<T>): z.ZodPipe<z.ZodType<T, unknown, z.core.$ZodTypeInternals<T, unknown>>, z.ZodTransform<Awaited<T>, T>>;
/**
* Strict RecipeObject schema with all validations enforced.
* This is the standard schema used by recipe scrapers.
*
* For custom extensions, use RecipeObjectBaseSchema.extend() and then
* apply validations with applyRecipeValidations().
*/
declare const RecipeObjectSchema: z.ZodPipe<z.ZodType<{
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}, unknown, z.core.$ZodTypeInternals<{
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}, unknown>>, z.ZodTransform<{
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}, {
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}>>;
//#endregion
//#region src/types/recipe.interface.d.ts
type List = Set<string>;
/**
* Parsed ingredient data from the parse-ingredient library
*/
type ParsedIngredient = z.infer<typeof ParsedIngredientSchema>;
/**
* A single ingredient item
*/
type IngredientItem = z.infer<typeof IngredientItemSchema>;
/**
* A group of ingredients with an optional group name
*/
type IngredientGroup = z.infer<typeof IngredientGroupSchema>;
/**
* All recipe ingredients as an array of groups
*/
type Ingredients = IngredientGroup[];
/**
* A single instruction step
*/
type InstructionItem = z.infer<typeof InstructionItemSchema>;
/**
* A group of instruction steps with an optional group name
*/
type InstructionGroup = z.infer<typeof InstructionGroupSchema>;
/**
* All recipe instructions as an array of groups
*/
type Instructions = InstructionGroup[];
/**
* The complete recipe object
*/
type RecipeObject = z.infer<typeof RecipeObjectSchema>;
/**
* A link with href and display text
*/
type Link = z.infer<typeof LinkSchema>;
interface RecipeData {
/**
* The host name of the website the Scraper class is for.
* @example 'bbcgoodfood.com'
*/
host: string;
/**
* The website name, as defined in the page's HTML.
* @default null
*/
siteName: string | null;
/**
* The author of the recipe. This is typically a person's name
* but can sometimes be an organization or the name of the website
* the recipe came from. If the recipe does not explicitly define an author,
* this should return the name of the website.
* @example 'Good Food team'
*/
author: string;
/**
* The title of the recipe, usually a short sentence or phrase.
*/
title: string;
/**
* The URL to the main image associated with the recipe,
* usually a photograph of the completed recipe.
*/
image: string;
/**
* The canonical URL for the scraped recipe.
* The canonical URL is the direct URL (defined by the website) at which the
* recipe can be found. This URL will generally not contain any query
* parameters or fragments, except those required to load the recipe.
*/
canonicalUrl: string;
/**
* The language of the recipe page, as defined within the page's HTML.
* This is typically a two-letter BCP 47 language code, such as 'en' for
* English or 'de' for German, but may also include the dialect or
* variation, such as 'en-US' for American English.
*
* The language code is based on BCP 47 standards.
* For a comprehensive list of BCP 47 language codes,
* refer to this GitHub Gist:
* @ref https://gist.github.com/typpo/b2b828a35e683b9bf8db91b5404f1bd1
*
* @default 'en'
*/
language: string;
/**
* An list of all links found in the page HTML defined within an anchor
* `<a>` element.
* Only present when `linksEnabled` option is set to `true`.
*/
links?: Link[];
/**
* A description of the recipe. This is normally a sentence or short
* paragraph describing the recipe. Often the website defines the
* description, but sometimes it has to be inferred from the page content.
*/
description: string;
/**
* The ingredients needed to make the recipe.
*
* This is an array of ingredient groups, where each group has a name
* (e.g., "For the sauce", "For the dough") and a list of ingredient items.
* When there are no groups, the group name is null.
*
* @example
* [
* {
* name: 'For the sauce',
* items: [
* { value: '2 tablespoons olive oil' },
* { value: '1 onion, chopped' },
* ]
* }
* ]
*/
ingredients: Ingredients;
/**
* A list of instructions for preparing the recipe.
* This is usually a step-by-step guide on how to make the recipe.
*
* This is an array of instruction groups, where each group has a name
* (e.g., "For the sauce", "For assembly") and a list of instruction steps.
* When there are no groups, the group name is null.
*
* @example
* [
* {
* name: 'For the dough',
* items: [
* { value: 'Mix flour and water.' },
* { value: 'Knead for 10 minutes.' },
* ]
* }
* ]
*/
instructions: Instructions;
/**
* The category or categories that the recipe belongs to.
* This can contain a mix of cuisine type (for example, country names),
* mealtime (breakfast/dinner/etc) and dietary properties (gluten-free,
* vegetarian). The value is defined by the website, so it may overlap with
* other fields (e.g. `cuisine`).
* @default Set()
* @example ['Italian', 'Vegetarian', 'Dinner']
*/
category: List;
/**
* The number of items or servings the recipe will make,
* including the quantity and unit of the yield.
* @example '4 servings', '6 items', '12 cookies'.
*/
yields: string;
/**
* The total time (in minutes) required to complete the recipe.
* @example 45
*/
totalTime: number | null;
/**
* The time (in minutes) to cook the recipe, excluding any time
* to prepare the ingredients.
* @default null
* @example 30
*/
cookTime: number | null;
/**
* The time (in minutes) to prepare the ingredients for the recipe.
* @default null
* @example 15
*/
prepTime: number | null;
/**
* A list of cuisines that the recipe belongs to.
* This is a `Set` of strings representing the cuisine types.
* @example ['Italian', 'Vegetarian']
*/
cuisine: List;
/**
* The method of cooking the recipe.
* @default null
*/
cookingMethod: string | null;
/**
* The recipe rating. When available, this is usually the average
* of all the ratings given to the recipe on the website.
* @example 4.5
*/
ratings: number;
/**
* The total number of ratings contributed to the recipes rating.
* @example 150
*/
ratingsCount: number;
/**
* A list of cooking equipment needed for the recipe.
* @default Set()
* @example ['Mixing Bowl', 'Whisk', 'Baking Tray']
*/
equipment: List;
/**
* Reviews of the recipe from the website.
* The keys of the `Map` are the reviewer's name
* and the values are the review text.
* @default Map()
*/
reviews: Map<string, string>;
/**
* The nutrition information for the recipe.
* Each nutrition entry is usually given per unit of yield,
* i.e. per servings, or per item.
* The keys of the `Map` are the nutrients (including calories)
* and the values are the amount of that nutrient, including the unit.
* @default Map()
* @example
* {
* calories: '389 calories',
* fatContent: '19 grams fat',
* // ...
* }
*/
nutrients: Map<string, string>;
/**
* The dietary restrictions specified by the recipe.
* @default Set()
* @example ['Vegan Diet', 'Vegetarian Diet']
*/
dietaryRestrictions: List;
/**
* A list of keywords associated with a recipe.
* @example ['easy', 'quick', 'dinner']
*/
keywords: List;
}
/**
* The fields of a recipe that can be extracted by scraping the HTML.
* The 'host' field is omitted because it is a static field
* that is not scraped.
*/
type RecipeFields = Omit<RecipeData, 'host'>;
/**
* The fields that aren't required for a recipe to be valid.
*/
type OptionalRecipeFields = Pick<RecipeFields, 'siteName' | 'category' | 'cookTime' | 'prepTime' | 'totalTime' | 'cuisine' | 'cookingMethod' | 'ratings' | 'ratingsCount' | 'equipment' | 'reviews' | 'nutrients' | 'dietaryRestrictions' | 'keywords' | 'links'>;
//#endregion
//#region src/abstract-extractor-plugin.d.ts
declare abstract class ExtractorPlugin extends AbstractPlugin {
/** Whether this plugin can extract the given field */
abstract supports(field: keyof RecipeFields): boolean;
/**
* Extracts the field from the cheerio root.
* @param field The field to extract
* @returns The extracted field value
*/
abstract extract<Key extends keyof RecipeFields>(field: Key): RecipeFields[Key] | Promise<RecipeFields[Key]>;
}
//#endregion
//#region src/abstract-postprocessor-plugin.d.ts
declare abstract class PostProcessorPlugin {
/** The name of the plugin */
abstract name: string;
/** The priority of the plugin */
abstract priority: number;
abstract shouldProcess<Key extends keyof RecipeFields>(field: Key): boolean;
abstract process<T>(field: keyof RecipeFields, value: T): T | Promise<T>;
}
//#endregion
//#region src/plugin-manager.d.ts
declare class PluginManager {
private extractorPlugins;
private postProcessorPlugins;
constructor(baseExtractors: ExtractorPlugin[], basePostProcessors: PostProcessorPlugin[], extraExtractors?: ExtractorPlugin[], extraPostProcessors?: PostProcessorPlugin[]);
getExtractors(): ExtractorPlugin[];
getPostProcessors(): PostProcessorPlugin[];
}
//#endregion
//#region src/recipe-extractor.d.ts
declare class RecipeExtractor {
private plugins;
private readonly scraperName;
private readonly options;
private readonly logger;
constructor(plugins: ExtractorPlugin[], scraperName: string, options?: {
logLevel?: LogLevel;
});
private getContext;
extract<Key extends keyof RecipeFields>(field: Key, extractor?: (prevValue: RecipeFields[Key] | undefined) => RecipeFields[Key] | Promise<RecipeFields[Key]>): Promise<RecipeFields[Key]>;
}
//#endregion
//#region src/types/scraper.interface.d.ts
interface ScraperOptions {
/**
* Additional extractors to be used by the scraper.
* These extractors will be added to the default set of extractors.
* Extractors are applied according to their priority.
* Higher priority extractors will run first.
* @default []
*/
extraExtractors?: ExtractorPlugin[];
/**
* Additional post-processors to be used by the scraper.
* These post-processors will be added to the default set of post-processors.
* Post-processors are applied after all extractors have run.
* Post-processors are also applied according to their priority.
* Higher priority post-processors will run first.
* @default []
*/
extraPostProcessors?: PostProcessorPlugin[];
/**
* Whether link scraping is enabled.
* @default false
*/
linksEnabled?: boolean;
/**
* Logging level for the scraper.
* This controls the verbosity of logs produced by the scraper.
* @default LogLevel.Warn
*/
logLevel?: LogLevel;
/**
* Enable ingredient parsing using the parse-ingredient library.
* When enabled, each ingredient item will include a `parsed` field
* containing structured data (quantity, unit, description, etc.).
*
* Can be set to `true` to enable with default options, or pass
* an options object to customize parsing behavior.
*
* @see https://github.com/jakeboone02/parse-ingredient
* @default false
*
* @example
* // Enable with defaults
* { parseIngredients: true }
*
* @example
* // Enable with custom options
* { parseIngredients: { normalizeUOM: true } }
*/
parseIngredients?: boolean | ParseIngredientOptions;
}
//#endregion
//#region src/abstract-scraper.d.ts
declare abstract class AbstractScraper {
protected readonly html: string;
protected readonly url: string;
protected readonly options: ScraperOptions;
protected readonly logger: Logger;
protected readonly pluginManager: PluginManager;
protected readonly recipeExtractor: RecipeExtractor;
readonly $: cheerio.CheerioAPI;
recipeData: RecipeData | null;
constructor(html: string, url: string, options?: ScraperOptions);
/**
* Site-specific extractors (implemented by subclasses)
* Each extractor is a function that takes the previous value
* returned by the extractor chain (if any) and returns the field value.
*/
abstract extractors: { [K in keyof RecipeFields]?: (prevValue: RecipeFields[K] | undefined) => RecipeFields[K] | Promise<RecipeFields[K]> };
/**
* Main extraction method - tries site-specific first, then plugins,
* then applies post-processing.
*/
extract<Key extends keyof RecipeFields>(field: Key): Promise<RecipeFields[Key]>;
/**
* Static method to get the host of the scraper.
* This should be implemented by subclasses to return the specific host.
*/
static host(): string;
/*****************************************************************************
* Default implementations for common fields that can be overridden
* by subclasses.
****************************************************************************/
canonicalUrl(): RecipeFields['canonicalUrl'];
language(): RecipeFields['language'];
links(): RecipeFields['links'];
/**
* Scrape's the recipe and caches the data.
*/
scrape(): Promise<RecipeData>;
/**
* Converts the scraper's data into a JSON-serializable object.
* Note: schemaVersion is added during validation by parse() or safeParse().
*/
toRecipeObject(): Promise<Omit<RecipeObject, 'schemaVersion'>>;
/**
* Get the Zod schema to use for validation.
* Subclasses can override to provide custom schemas.
*/
protected getSchema(): z$1.ZodPipe<z$1.ZodType<{
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}, unknown, z$1.core.$ZodTypeInternals<{
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}, unknown>>, z$1.ZodTransform<{
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}, {
schemaVersion: "1.0.0";
host: string;
title: string;
author: string;
ingredients: {
name: string | null;
items: {
value: string;
parsed?: {
quantity: number | null;
quantity2: number | null;
unitOfMeasureID: string | null;
unitOfMeasure: string | null;
description: string;
isGroupHeader: boolean;
} | null | undefined;
}[];
}[];
instructions: {
name: string | null;
items: {
value: string;
}[];
}[];
canonicalUrl: string;
image: string;
totalTime: number | null;
cookTime: number | null;
prepTime: number | null;
ratings: number;
ratingsCount: number;
yields: string;
description: string;
language: string;
siteName: string | null;
cookingMethod: string | null;
category: string[];
cuisine: string[];
keywords: string[];
dietaryRestrictions: string[];
equipment: string[];
nutrients: Record<string, string>;
reviews: Record<string, string>;
links?: {
href: string;
text: string;
}[] | undefined;
}>>;
/**
* Extract and validate recipe data.
* Throws ZodError if validation fails.
*
* @returns Validated recipe object
* @throws {ZodError} If validation fails
*/
parse(): Promise<RecipeObject>;
/**
* Extract and validate recipe data without throwing.
* Returns a result object indicating success or failure.
*
* @returns Result object with either data or error
*/
safeParse(): Promise<z$1.ZodSafeParseResult<RecipeObject>>;
}
//#endregion
//#region src/scrapers/allrecipes.d.ts
declare class AllRecipes extends AbstractScraper {
static host(): string;
extractors: {};
}
//#endregion
//#region src/scrapers/_index.d.ts
/**
* A map of all scrapers.
*/
declare const scrapers: {
readonly [x: string]: typeof AllRecipes;
};
//#endregion
//#region src/index.d.ts
/**
* Returns a scraper class for the given URL, if implemented.
*/
declare function getScraper(url: string): typeof AllRecipes;
//#endregion
export { ExtractorPlugin, IngredientGroup, IngredientGroupSchema, IngredientItem, IngredientItemSchema, Ingredients, IngredientsSchema, InstructionGroup, InstructionGroupSchema, InstructionItem, InstructionItemSchema, Instructions, InstructionsSchema, Link, LinkSchema, List, LogLevel, Logger, OptionalRecipeFields, ParsedIngredient, ParsedIngredientSchema, PostProcessorPlugin, RECIPE_SCHEMA_VERSION, RecipeData, RecipeFields, RecipeObject, RecipeObjectBaseSchema, RecipeObjectSchema, ScraperOptions, applyRecipeValidations, getScraper, scrapers };