recipe-scrapers-js 1.0.0-rc.1 → 1.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -4
- package/dist/index.d.mts +276 -163
- package/dist/index.mjs +76 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,26 +6,45 @@ All notable changes to this project will be documented in this file.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [1.0.0-rc.3] - 2025-12-21
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- Enforce positive integer values for recipe time fields (`totalTime`, `cookTime`, `prepTime`)
|
|
14
|
+
- Rename schema helper `zPositiveNumber` to `zPositiveInteger`
|
|
15
|
+
- Stop defaulting nullable schema fields to `null`
|
|
16
|
+
|
|
17
|
+
## [1.0.0-rc.2] - 2025-12-20
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Add `schemaVersion` to recipe schema
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Make `links` optional; it stays `undefined` unless link parsing is enabled
|
|
26
|
+
- Extract schema transform/refinement validations into `applyRecipeValidations`
|
|
27
|
+
|
|
9
28
|
## [1.0.0-rc.1] - 2025-12-20
|
|
10
29
|
|
|
11
30
|
### Added
|
|
12
31
|
|
|
13
|
-
-
|
|
32
|
+
- Add `tsdown` configuration file
|
|
14
33
|
|
|
15
34
|
### Fixed
|
|
16
35
|
|
|
17
|
-
-
|
|
36
|
+
- Fix `main`/`module`/`types` entries in `package.json`; add `exports`
|
|
18
37
|
|
|
19
38
|
## [1.0.0-rc.0] - 2025-12-20
|
|
20
39
|
|
|
21
40
|
### Added
|
|
22
41
|
|
|
23
42
|
- Optional ingredient parsing via [parse-ingredient](https://github.com/jakeboone02/parse-ingredient)
|
|
24
|
-
- `parse()` and `safeParse()` methods for Zod
|
|
43
|
+
- `parse()` and `safeParse()` methods for Zod-validated recipe extraction
|
|
25
44
|
|
|
26
45
|
### Changed
|
|
27
46
|
|
|
28
|
-
- **BREAKING**:
|
|
47
|
+
- **BREAKING**: Rename `toObject()` method to `toRecipeObject()` for clarity
|
|
29
48
|
- **BREAKING**: Ingredients and instructions now require grouped structures (each group has `name` and `items`) instead of flat arrays
|
|
30
49
|
|
|
31
50
|
---
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,45 @@ import { CheerioAPI } from "cheerio";
|
|
|
3
3
|
import z$1, { z } from "zod";
|
|
4
4
|
import { ParseIngredientOptions } from "parse-ingredient";
|
|
5
5
|
|
|
6
|
+
//#region src/logger.d.ts
|
|
7
|
+
declare enum LogLevel {
|
|
8
|
+
VERBOSE = 0,
|
|
9
|
+
DEBUG = 1,
|
|
10
|
+
INFO = 2,
|
|
11
|
+
WARN = 3,
|
|
12
|
+
ERROR = 4,
|
|
13
|
+
}
|
|
14
|
+
declare class Logger {
|
|
15
|
+
private context;
|
|
16
|
+
private logLevel;
|
|
17
|
+
constructor(context: string, logLevel?: LogLevel);
|
|
18
|
+
verbose(...args: unknown[]): void;
|
|
19
|
+
debug(...args: unknown[]): void;
|
|
20
|
+
log(...args: unknown[]): void;
|
|
21
|
+
info(...args: unknown[]): void;
|
|
22
|
+
warn(...args: unknown[]): void;
|
|
23
|
+
error(...args: unknown[]): void;
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/abstract-plugin.d.ts
|
|
27
|
+
declare abstract class AbstractPlugin {
|
|
28
|
+
readonly $: CheerioAPI;
|
|
29
|
+
/** The name of the plugin */
|
|
30
|
+
abstract name: string;
|
|
31
|
+
/** The priority of the plugin */
|
|
32
|
+
abstract priority: number;
|
|
33
|
+
constructor($: CheerioAPI);
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
6
36
|
//#region src/schemas/recipe.schema.d.ts
|
|
37
|
+
/**
|
|
38
|
+
* Current schema version for recipe objects.
|
|
39
|
+
* Increment this when making breaking changes to the schema.
|
|
40
|
+
*
|
|
41
|
+
* Version history:
|
|
42
|
+
* - 1.0.0: Initial schema version
|
|
43
|
+
*/
|
|
44
|
+
declare const RECIPE_SCHEMA_VERSION: "1.0.0";
|
|
7
45
|
/**
|
|
8
46
|
* Schema for a parsed ingredient from the parse-ingredient library.
|
|
9
47
|
* This represents the structured data extracted from an ingredient string.
|
|
@@ -35,7 +73,7 @@ declare const IngredientItemSchema: z.ZodObject<{
|
|
|
35
73
|
* Schema for a group of ingredients
|
|
36
74
|
*/
|
|
37
75
|
declare const IngredientGroupSchema: z.ZodObject<{
|
|
38
|
-
name: z.
|
|
76
|
+
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
39
77
|
items: z.ZodArray<z.core.$ZodType<{
|
|
40
78
|
value: string;
|
|
41
79
|
parsed?: {
|
|
@@ -63,7 +101,7 @@ declare const IngredientGroupSchema: z.ZodObject<{
|
|
|
63
101
|
* Must have at least one group with at least one ingredient
|
|
64
102
|
*/
|
|
65
103
|
declare const IngredientsSchema: z.ZodArray<z.ZodObject<{
|
|
66
|
-
name: z.
|
|
104
|
+
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
67
105
|
items: z.ZodArray<z.core.$ZodType<{
|
|
68
106
|
value: string;
|
|
69
107
|
parsed?: {
|
|
@@ -96,7 +134,7 @@ declare const InstructionItemSchema: z.ZodObject<{
|
|
|
96
134
|
* Schema for a group of instruction steps
|
|
97
135
|
*/
|
|
98
136
|
declare const InstructionGroupSchema: z.ZodObject<{
|
|
99
|
-
name: z.
|
|
137
|
+
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
100
138
|
items: z.ZodArray<z.core.$ZodType<{
|
|
101
139
|
value: string;
|
|
102
140
|
}, unknown, z.core.$ZodTypeInternals<{
|
|
@@ -108,7 +146,7 @@ declare const InstructionGroupSchema: z.ZodObject<{
|
|
|
108
146
|
* Must have at least one group with at least one step
|
|
109
147
|
*/
|
|
110
148
|
declare const InstructionsSchema: z.ZodArray<z.ZodObject<{
|
|
111
|
-
name: z.
|
|
149
|
+
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
112
150
|
items: z.ZodArray<z.core.$ZodType<{
|
|
113
151
|
value: string;
|
|
114
152
|
}, unknown, z.core.$ZodTypeInternals<{
|
|
@@ -123,15 +161,28 @@ declare const LinkSchema: z.ZodObject<{
|
|
|
123
161
|
text: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
124
162
|
}, z.core.$strip>;
|
|
125
163
|
/**
|
|
126
|
-
* Base RecipeObject schema without cross-field validations
|
|
127
|
-
*
|
|
164
|
+
* Base RecipeObject schema without cross-field validations.
|
|
165
|
+
* Use this schema when you need to extend the recipe object with custom fields.
|
|
166
|
+
*
|
|
167
|
+
* @example
|
|
168
|
+
* ```ts
|
|
169
|
+
* import { RecipeObjectBaseSchema, applyRecipeValidations } from 'recipe-scrapers-js'
|
|
170
|
+
*
|
|
171
|
+
* const MyCustomRecipeSchema = RecipeObjectBaseSchema.extend({
|
|
172
|
+
* customField: z.string(),
|
|
173
|
+
* })
|
|
174
|
+
*
|
|
175
|
+
* // Apply the standard recipe validations
|
|
176
|
+
* const MyValidatedRecipeSchema = applyRecipeValidations(MyCustomRecipeSchema)
|
|
177
|
+
* ```
|
|
128
178
|
*/
|
|
129
179
|
declare const RecipeObjectBaseSchema: z.ZodObject<{
|
|
180
|
+
schemaVersion: z.ZodDefault<z.ZodLiteral<"1.0.0">>;
|
|
130
181
|
host: z.ZodCustomStringFormat<"hostname">;
|
|
131
182
|
title: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
132
183
|
author: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
133
184
|
ingredients: z.ZodArray<z.ZodObject<{
|
|
134
|
-
name: z.
|
|
185
|
+
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
135
186
|
items: z.ZodArray<z.core.$ZodType<{
|
|
136
187
|
value: string;
|
|
137
188
|
parsed?: {
|
|
@@ -155,7 +206,7 @@ declare const RecipeObjectBaseSchema: z.ZodObject<{
|
|
|
155
206
|
}, unknown>>>;
|
|
156
207
|
}, z.core.$strip>>;
|
|
157
208
|
instructions: z.ZodArray<z.ZodObject<{
|
|
158
|
-
name: z.
|
|
209
|
+
name: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
159
210
|
items: z.ZodArray<z.core.$ZodType<{
|
|
160
211
|
value: string;
|
|
161
212
|
}, unknown, z.core.$ZodTypeInternals<{
|
|
@@ -164,22 +215,22 @@ declare const RecipeObjectBaseSchema: z.ZodObject<{
|
|
|
164
215
|
}, z.core.$strip>>;
|
|
165
216
|
canonicalUrl: z.ZodURL;
|
|
166
217
|
image: z.ZodURL;
|
|
167
|
-
totalTime: z.
|
|
168
|
-
cookTime: z.
|
|
169
|
-
prepTime: z.
|
|
218
|
+
totalTime: z.ZodNullable<z.ZodInt>;
|
|
219
|
+
cookTime: z.ZodNullable<z.ZodInt>;
|
|
220
|
+
prepTime: z.ZodNullable<z.ZodInt>;
|
|
170
221
|
ratings: z.ZodDefault<z.ZodNumber>;
|
|
171
222
|
ratingsCount: z.ZodDefault<z.ZodInt>;
|
|
172
223
|
yields: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
173
224
|
description: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
174
225
|
language: z.ZodDefault<z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
175
|
-
siteName: z.
|
|
176
|
-
cookingMethod: z.
|
|
226
|
+
siteName: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
227
|
+
cookingMethod: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
177
228
|
category: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
178
229
|
cuisine: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
179
230
|
keywords: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
180
231
|
dietaryRestrictions: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
181
232
|
equipment: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>>;
|
|
182
|
-
links: z.
|
|
233
|
+
links: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
183
234
|
href: z.ZodURL;
|
|
184
235
|
text: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
185
236
|
}, z.core.$strip>>>;
|
|
@@ -187,15 +238,38 @@ declare const RecipeObjectBaseSchema: z.ZodObject<{
|
|
|
187
238
|
reviews: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
188
239
|
}, z.core.$strip>;
|
|
189
240
|
/**
|
|
190
|
-
*
|
|
241
|
+
* Applies recipe-specific transformations and validations to a schema.
|
|
242
|
+
* Use this when extending RecipeObjectBaseSchema with custom fields.
|
|
243
|
+
*
|
|
244
|
+
* @param schema - A Zod object schema that includes
|
|
245
|
+
* all RecipeObjectBaseSchema fields
|
|
246
|
+
* @returns A schema with transforms and field validations applied
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```ts
|
|
250
|
+
* const CustomSchema = RecipeObjectBaseSchema.extend({
|
|
251
|
+
* tags: z.array(z.string()),
|
|
252
|
+
* })
|
|
253
|
+
*
|
|
254
|
+
* const ValidatedCustomSchema = applyRecipeValidations(CustomSchema)
|
|
255
|
+
* ```
|
|
191
256
|
*/
|
|
192
|
-
declare
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
257
|
+
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>>;
|
|
258
|
+
/**
|
|
259
|
+
* Strict RecipeObject schema with all validations enforced.
|
|
260
|
+
* This is the standard schema used by recipe scrapers.
|
|
261
|
+
*
|
|
262
|
+
* For custom extensions, use RecipeObjectBaseSchema.extend() and then
|
|
263
|
+
* apply validations with applyRecipeValidations().
|
|
264
|
+
*/
|
|
265
|
+
declare const RecipeObjectSchema: z.ZodPipe<z.ZodType<{
|
|
266
|
+
schemaVersion: "1.0.0";
|
|
267
|
+
host: string;
|
|
268
|
+
title: string;
|
|
269
|
+
author: string;
|
|
270
|
+
ingredients: {
|
|
271
|
+
name: string | null;
|
|
272
|
+
items: {
|
|
199
273
|
value: string;
|
|
200
274
|
parsed?: {
|
|
201
275
|
quantity: number | null;
|
|
@@ -205,7 +279,45 @@ declare const RecipeObjectSchema: z.ZodPipe<z.ZodObject<{
|
|
|
205
279
|
description: string;
|
|
206
280
|
isGroupHeader: boolean;
|
|
207
281
|
} | undefined;
|
|
208
|
-
}
|
|
282
|
+
}[];
|
|
283
|
+
}[];
|
|
284
|
+
instructions: {
|
|
285
|
+
name: string | null;
|
|
286
|
+
items: {
|
|
287
|
+
value: string;
|
|
288
|
+
}[];
|
|
289
|
+
}[];
|
|
290
|
+
canonicalUrl: string;
|
|
291
|
+
image: string;
|
|
292
|
+
totalTime: number | null;
|
|
293
|
+
cookTime: number | null;
|
|
294
|
+
prepTime: number | null;
|
|
295
|
+
ratings: number;
|
|
296
|
+
ratingsCount: number;
|
|
297
|
+
yields: string;
|
|
298
|
+
description: string;
|
|
299
|
+
language: string;
|
|
300
|
+
siteName: string | null;
|
|
301
|
+
cookingMethod: string | null;
|
|
302
|
+
category: string[];
|
|
303
|
+
cuisine: string[];
|
|
304
|
+
keywords: string[];
|
|
305
|
+
dietaryRestrictions: string[];
|
|
306
|
+
equipment: string[];
|
|
307
|
+
nutrients: Record<string, string>;
|
|
308
|
+
reviews: Record<string, string>;
|
|
309
|
+
links?: {
|
|
310
|
+
href: string;
|
|
311
|
+
text: string;
|
|
312
|
+
}[] | undefined;
|
|
313
|
+
}, unknown, z.core.$ZodTypeInternals<{
|
|
314
|
+
schemaVersion: "1.0.0";
|
|
315
|
+
host: string;
|
|
316
|
+
title: string;
|
|
317
|
+
author: string;
|
|
318
|
+
ingredients: {
|
|
319
|
+
name: string | null;
|
|
320
|
+
items: {
|
|
209
321
|
value: string;
|
|
210
322
|
parsed?: {
|
|
211
323
|
quantity: number | null;
|
|
@@ -215,40 +327,39 @@ declare const RecipeObjectSchema: z.ZodPipe<z.ZodObject<{
|
|
|
215
327
|
description: string;
|
|
216
328
|
isGroupHeader: boolean;
|
|
217
329
|
} | undefined;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
instructions:
|
|
221
|
-
name:
|
|
222
|
-
items:
|
|
223
|
-
value: string;
|
|
224
|
-
}, unknown, z.core.$ZodTypeInternals<{
|
|
330
|
+
}[];
|
|
331
|
+
}[];
|
|
332
|
+
instructions: {
|
|
333
|
+
name: string | null;
|
|
334
|
+
items: {
|
|
225
335
|
value: string;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
canonicalUrl:
|
|
229
|
-
image:
|
|
230
|
-
totalTime:
|
|
231
|
-
cookTime:
|
|
232
|
-
prepTime:
|
|
233
|
-
ratings:
|
|
234
|
-
ratingsCount:
|
|
235
|
-
yields:
|
|
236
|
-
description:
|
|
237
|
-
language:
|
|
238
|
-
siteName:
|
|
239
|
-
cookingMethod:
|
|
240
|
-
category:
|
|
241
|
-
cuisine:
|
|
242
|
-
keywords:
|
|
243
|
-
dietaryRestrictions:
|
|
244
|
-
equipment:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
},
|
|
336
|
+
}[];
|
|
337
|
+
}[];
|
|
338
|
+
canonicalUrl: string;
|
|
339
|
+
image: string;
|
|
340
|
+
totalTime: number | null;
|
|
341
|
+
cookTime: number | null;
|
|
342
|
+
prepTime: number | null;
|
|
343
|
+
ratings: number;
|
|
344
|
+
ratingsCount: number;
|
|
345
|
+
yields: string;
|
|
346
|
+
description: string;
|
|
347
|
+
language: string;
|
|
348
|
+
siteName: string | null;
|
|
349
|
+
cookingMethod: string | null;
|
|
350
|
+
category: string[];
|
|
351
|
+
cuisine: string[];
|
|
352
|
+
keywords: string[];
|
|
353
|
+
dietaryRestrictions: string[];
|
|
354
|
+
equipment: string[];
|
|
355
|
+
nutrients: Record<string, string>;
|
|
356
|
+
reviews: Record<string, string>;
|
|
357
|
+
links?: {
|
|
358
|
+
href: string;
|
|
359
|
+
text: string;
|
|
360
|
+
}[] | undefined;
|
|
361
|
+
}, unknown>>, z.ZodTransform<{
|
|
362
|
+
schemaVersion: "1.0.0";
|
|
252
363
|
host: string;
|
|
253
364
|
title: string;
|
|
254
365
|
author: string;
|
|
@@ -289,13 +400,14 @@ declare const RecipeObjectSchema: z.ZodPipe<z.ZodObject<{
|
|
|
289
400
|
keywords: string[];
|
|
290
401
|
dietaryRestrictions: string[];
|
|
291
402
|
equipment: string[];
|
|
292
|
-
links: {
|
|
293
|
-
href: string;
|
|
294
|
-
text: string;
|
|
295
|
-
}[];
|
|
296
403
|
nutrients: Record<string, string>;
|
|
297
404
|
reviews: Record<string, string>;
|
|
405
|
+
links?: {
|
|
406
|
+
href: string;
|
|
407
|
+
text: string;
|
|
408
|
+
}[] | undefined;
|
|
298
409
|
}, {
|
|
410
|
+
schemaVersion: "1.0.0";
|
|
299
411
|
host: string;
|
|
300
412
|
title: string;
|
|
301
413
|
author: string;
|
|
@@ -336,44 +448,13 @@ declare const RecipeObjectSchema: z.ZodPipe<z.ZodObject<{
|
|
|
336
448
|
keywords: string[];
|
|
337
449
|
dietaryRestrictions: string[];
|
|
338
450
|
equipment: string[];
|
|
339
|
-
links: {
|
|
340
|
-
href: string;
|
|
341
|
-
text: string;
|
|
342
|
-
}[];
|
|
343
451
|
nutrients: Record<string, string>;
|
|
344
452
|
reviews: Record<string, string>;
|
|
453
|
+
links?: {
|
|
454
|
+
href: string;
|
|
455
|
+
text: string;
|
|
456
|
+
}[] | undefined;
|
|
345
457
|
}>>;
|
|
346
|
-
type RecipeObjectValidated = z.infer<typeof RecipeObjectSchema>;
|
|
347
|
-
//#endregion
|
|
348
|
-
//#region src/logger.d.ts
|
|
349
|
-
declare enum LogLevel {
|
|
350
|
-
VERBOSE = 0,
|
|
351
|
-
DEBUG = 1,
|
|
352
|
-
INFO = 2,
|
|
353
|
-
WARN = 3,
|
|
354
|
-
ERROR = 4,
|
|
355
|
-
}
|
|
356
|
-
declare class Logger {
|
|
357
|
-
private context;
|
|
358
|
-
private logLevel;
|
|
359
|
-
constructor(context: string, logLevel?: LogLevel);
|
|
360
|
-
verbose(...args: unknown[]): void;
|
|
361
|
-
debug(...args: unknown[]): void;
|
|
362
|
-
log(...args: unknown[]): void;
|
|
363
|
-
info(...args: unknown[]): void;
|
|
364
|
-
warn(...args: unknown[]): void;
|
|
365
|
-
error(...args: unknown[]): void;
|
|
366
|
-
}
|
|
367
|
-
//#endregion
|
|
368
|
-
//#region src/abstract-plugin.d.ts
|
|
369
|
-
declare abstract class AbstractPlugin {
|
|
370
|
-
readonly $: CheerioAPI;
|
|
371
|
-
/** The name of the plugin */
|
|
372
|
-
abstract name: string;
|
|
373
|
-
/** The priority of the plugin */
|
|
374
|
-
abstract priority: number;
|
|
375
|
-
constructor($: CheerioAPI);
|
|
376
|
-
}
|
|
377
458
|
//#endregion
|
|
378
459
|
//#region src/types/recipe.interface.d.ts
|
|
379
460
|
type List = Set<string>;
|
|
@@ -405,6 +486,10 @@ type InstructionGroup = z.infer<typeof InstructionGroupSchema>;
|
|
|
405
486
|
* All recipe instructions as an array of groups
|
|
406
487
|
*/
|
|
407
488
|
type Instructions = InstructionGroup[];
|
|
489
|
+
/**
|
|
490
|
+
* The complete recipe object
|
|
491
|
+
*/
|
|
492
|
+
type RecipeObject = z.infer<typeof RecipeObjectSchema>;
|
|
408
493
|
/**
|
|
409
494
|
* A link with href and display text
|
|
410
495
|
*/
|
|
@@ -461,8 +546,9 @@ interface RecipeData {
|
|
|
461
546
|
/**
|
|
462
547
|
* An list of all links found in the page HTML defined within an anchor
|
|
463
548
|
* `<a>` element.
|
|
549
|
+
* Only present when `linksEnabled` option is set to `true`.
|
|
464
550
|
*/
|
|
465
|
-
links
|
|
551
|
+
links?: Link[];
|
|
466
552
|
/**
|
|
467
553
|
* A description of the recipe. This is normally a sentence or short
|
|
468
554
|
* paragraph describing the recipe. Often the website defines the
|
|
@@ -613,20 +699,7 @@ type RecipeFields = Omit<RecipeData, 'host'>;
|
|
|
613
699
|
/**
|
|
614
700
|
* The fields that aren't required for a recipe to be valid.
|
|
615
701
|
*/
|
|
616
|
-
type OptionalRecipeFields = Pick<RecipeFields, 'siteName' | 'category' | 'cookTime' | 'prepTime' | 'totalTime' | 'cuisine' | 'cookingMethod' | 'ratings' | 'ratingsCount' | 'equipment' | 'reviews' | 'nutrients' | 'dietaryRestrictions' | 'keywords'>;
|
|
617
|
-
/**
|
|
618
|
-
* The validated recipe object output from the schema.
|
|
619
|
-
* This represents a recipe that has passed all validation rules.
|
|
620
|
-
*/
|
|
621
|
-
interface RecipeObject extends Omit<RecipeData, 'category' | 'cuisine' | 'dietaryRestrictions' | 'equipment' | 'keywords' | 'nutrients' | 'reviews'> {
|
|
622
|
-
category: string[];
|
|
623
|
-
cuisine: string[];
|
|
624
|
-
dietaryRestrictions: string[];
|
|
625
|
-
equipment: string[];
|
|
626
|
-
keywords: string[];
|
|
627
|
-
nutrients: Record<string, string>;
|
|
628
|
-
reviews: Record<string, string>;
|
|
629
|
-
}
|
|
702
|
+
type OptionalRecipeFields = Pick<RecipeFields, 'siteName' | 'category' | 'cookTime' | 'prepTime' | 'totalTime' | 'cuisine' | 'cookingMethod' | 'ratings' | 'ratingsCount' | 'equipment' | 'reviews' | 'nutrients' | 'dietaryRestrictions' | 'keywords' | 'links'>;
|
|
630
703
|
//#endregion
|
|
631
704
|
//#region src/abstract-extractor-plugin.d.ts
|
|
632
705
|
declare abstract class ExtractorPlugin extends AbstractPlugin {
|
|
@@ -764,19 +837,21 @@ declare abstract class AbstractScraper {
|
|
|
764
837
|
scrape(): Promise<RecipeData>;
|
|
765
838
|
/**
|
|
766
839
|
* Converts the scraper's data into a JSON-serializable object.
|
|
840
|
+
* Note: schemaVersion is added during validation by parse() or safeParse().
|
|
767
841
|
*/
|
|
768
|
-
toRecipeObject(): Promise<RecipeObject
|
|
842
|
+
toRecipeObject(): Promise<Omit<RecipeObject, 'schemaVersion'>>;
|
|
769
843
|
/**
|
|
770
844
|
* Get the Zod schema to use for validation.
|
|
771
845
|
* Subclasses can override to provide custom schemas.
|
|
772
846
|
*/
|
|
773
|
-
protected getSchema(): z$1.ZodPipe<z$1.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
847
|
+
protected getSchema(): z$1.ZodPipe<z$1.ZodType<{
|
|
848
|
+
schemaVersion: "1.0.0";
|
|
849
|
+
host: string;
|
|
850
|
+
title: string;
|
|
851
|
+
author: string;
|
|
852
|
+
ingredients: {
|
|
853
|
+
name: string | null;
|
|
854
|
+
items: {
|
|
780
855
|
value: string;
|
|
781
856
|
parsed?: {
|
|
782
857
|
quantity: number | null;
|
|
@@ -786,7 +861,45 @@ declare abstract class AbstractScraper {
|
|
|
786
861
|
description: string;
|
|
787
862
|
isGroupHeader: boolean;
|
|
788
863
|
} | undefined;
|
|
789
|
-
}
|
|
864
|
+
}[];
|
|
865
|
+
}[];
|
|
866
|
+
instructions: {
|
|
867
|
+
name: string | null;
|
|
868
|
+
items: {
|
|
869
|
+
value: string;
|
|
870
|
+
}[];
|
|
871
|
+
}[];
|
|
872
|
+
canonicalUrl: string;
|
|
873
|
+
image: string;
|
|
874
|
+
totalTime: number | null;
|
|
875
|
+
cookTime: number | null;
|
|
876
|
+
prepTime: number | null;
|
|
877
|
+
ratings: number;
|
|
878
|
+
ratingsCount: number;
|
|
879
|
+
yields: string;
|
|
880
|
+
description: string;
|
|
881
|
+
language: string;
|
|
882
|
+
siteName: string | null;
|
|
883
|
+
cookingMethod: string | null;
|
|
884
|
+
category: string[];
|
|
885
|
+
cuisine: string[];
|
|
886
|
+
keywords: string[];
|
|
887
|
+
dietaryRestrictions: string[];
|
|
888
|
+
equipment: string[];
|
|
889
|
+
nutrients: Record<string, string>;
|
|
890
|
+
reviews: Record<string, string>;
|
|
891
|
+
links?: {
|
|
892
|
+
href: string;
|
|
893
|
+
text: string;
|
|
894
|
+
}[] | undefined;
|
|
895
|
+
}, unknown, z$1.core.$ZodTypeInternals<{
|
|
896
|
+
schemaVersion: "1.0.0";
|
|
897
|
+
host: string;
|
|
898
|
+
title: string;
|
|
899
|
+
author: string;
|
|
900
|
+
ingredients: {
|
|
901
|
+
name: string | null;
|
|
902
|
+
items: {
|
|
790
903
|
value: string;
|
|
791
904
|
parsed?: {
|
|
792
905
|
quantity: number | null;
|
|
@@ -796,40 +909,39 @@ declare abstract class AbstractScraper {
|
|
|
796
909
|
description: string;
|
|
797
910
|
isGroupHeader: boolean;
|
|
798
911
|
} | undefined;
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
instructions:
|
|
802
|
-
name:
|
|
803
|
-
items:
|
|
804
|
-
value: string;
|
|
805
|
-
}, unknown, z$1.core.$ZodTypeInternals<{
|
|
912
|
+
}[];
|
|
913
|
+
}[];
|
|
914
|
+
instructions: {
|
|
915
|
+
name: string | null;
|
|
916
|
+
items: {
|
|
806
917
|
value: string;
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
canonicalUrl:
|
|
810
|
-
image:
|
|
811
|
-
totalTime:
|
|
812
|
-
cookTime:
|
|
813
|
-
prepTime:
|
|
814
|
-
ratings:
|
|
815
|
-
ratingsCount:
|
|
816
|
-
yields:
|
|
817
|
-
description:
|
|
818
|
-
language:
|
|
819
|
-
siteName:
|
|
820
|
-
cookingMethod:
|
|
821
|
-
category:
|
|
822
|
-
cuisine:
|
|
823
|
-
keywords:
|
|
824
|
-
dietaryRestrictions:
|
|
825
|
-
equipment:
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
},
|
|
918
|
+
}[];
|
|
919
|
+
}[];
|
|
920
|
+
canonicalUrl: string;
|
|
921
|
+
image: string;
|
|
922
|
+
totalTime: number | null;
|
|
923
|
+
cookTime: number | null;
|
|
924
|
+
prepTime: number | null;
|
|
925
|
+
ratings: number;
|
|
926
|
+
ratingsCount: number;
|
|
927
|
+
yields: string;
|
|
928
|
+
description: string;
|
|
929
|
+
language: string;
|
|
930
|
+
siteName: string | null;
|
|
931
|
+
cookingMethod: string | null;
|
|
932
|
+
category: string[];
|
|
933
|
+
cuisine: string[];
|
|
934
|
+
keywords: string[];
|
|
935
|
+
dietaryRestrictions: string[];
|
|
936
|
+
equipment: string[];
|
|
937
|
+
nutrients: Record<string, string>;
|
|
938
|
+
reviews: Record<string, string>;
|
|
939
|
+
links?: {
|
|
940
|
+
href: string;
|
|
941
|
+
text: string;
|
|
942
|
+
}[] | undefined;
|
|
943
|
+
}, unknown>>, z$1.ZodTransform<{
|
|
944
|
+
schemaVersion: "1.0.0";
|
|
833
945
|
host: string;
|
|
834
946
|
title: string;
|
|
835
947
|
author: string;
|
|
@@ -870,13 +982,14 @@ declare abstract class AbstractScraper {
|
|
|
870
982
|
keywords: string[];
|
|
871
983
|
dietaryRestrictions: string[];
|
|
872
984
|
equipment: string[];
|
|
873
|
-
links: {
|
|
874
|
-
href: string;
|
|
875
|
-
text: string;
|
|
876
|
-
}[];
|
|
877
985
|
nutrients: Record<string, string>;
|
|
878
986
|
reviews: Record<string, string>;
|
|
987
|
+
links?: {
|
|
988
|
+
href: string;
|
|
989
|
+
text: string;
|
|
990
|
+
}[] | undefined;
|
|
879
991
|
}, {
|
|
992
|
+
schemaVersion: "1.0.0";
|
|
880
993
|
host: string;
|
|
881
994
|
title: string;
|
|
882
995
|
author: string;
|
|
@@ -917,12 +1030,12 @@ declare abstract class AbstractScraper {
|
|
|
917
1030
|
keywords: string[];
|
|
918
1031
|
dietaryRestrictions: string[];
|
|
919
1032
|
equipment: string[];
|
|
920
|
-
links: {
|
|
921
|
-
href: string;
|
|
922
|
-
text: string;
|
|
923
|
-
}[];
|
|
924
1033
|
nutrients: Record<string, string>;
|
|
925
1034
|
reviews: Record<string, string>;
|
|
1035
|
+
links?: {
|
|
1036
|
+
href: string;
|
|
1037
|
+
text: string;
|
|
1038
|
+
}[] | undefined;
|
|
926
1039
|
}>>;
|
|
927
1040
|
/**
|
|
928
1041
|
* Extract and validate recipe data.
|
|
@@ -931,14 +1044,14 @@ declare abstract class AbstractScraper {
|
|
|
931
1044
|
* @returns Validated recipe object
|
|
932
1045
|
* @throws {ZodError} If validation fails
|
|
933
1046
|
*/
|
|
934
|
-
parse(): Promise<
|
|
1047
|
+
parse(): Promise<RecipeObject>;
|
|
935
1048
|
/**
|
|
936
1049
|
* Extract and validate recipe data without throwing.
|
|
937
1050
|
* Returns a result object indicating success or failure.
|
|
938
1051
|
*
|
|
939
1052
|
* @returns Result object with either data or error
|
|
940
1053
|
*/
|
|
941
|
-
safeParse(): Promise<z$1.ZodSafeParseResult<
|
|
1054
|
+
safeParse(): Promise<z$1.ZodSafeParseResult<RecipeObject>>;
|
|
942
1055
|
}
|
|
943
1056
|
//#endregion
|
|
944
1057
|
//#region src/scrapers/allrecipes.d.ts
|
|
@@ -961,4 +1074,4 @@ declare const scrapers: {
|
|
|
961
1074
|
*/
|
|
962
1075
|
declare function getScraper(url: string): typeof AllRecipes;
|
|
963
1076
|
//#endregion
|
|
964
|
-
export { ExtractorPlugin, IngredientGroup, IngredientGroupSchema, IngredientItem, IngredientItemSchema, Ingredients, IngredientsSchema, InstructionGroup, InstructionGroupSchema, InstructionItem, InstructionItemSchema, Instructions, InstructionsSchema, Link, LinkSchema, List, LogLevel, Logger, OptionalRecipeFields, ParsedIngredient, ParsedIngredientSchema, PostProcessorPlugin, RecipeData, RecipeFields, RecipeObject, RecipeObjectBaseSchema, RecipeObjectSchema,
|
|
1077
|
+
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 };
|
package/dist/index.mjs
CHANGED
|
@@ -48,14 +48,22 @@ const zString = (fieldName, { min = 1, max = 0 } = {}) => z.string(`${fieldName}
|
|
|
48
48
|
*/
|
|
49
49
|
const zHttpUrl = (fieldName) => z.httpUrl(`${fieldName} must be a valid URL`);
|
|
50
50
|
/**
|
|
51
|
-
* Helper to create a positive
|
|
51
|
+
* Helper to create a positive integer field
|
|
52
52
|
*/
|
|
53
|
-
const
|
|
53
|
+
const zPositiveInteger = (fieldName) => z.int(`${fieldName} must be an integer`).positive(`${fieldName} must be positive`).nullable();
|
|
54
54
|
const zNonEmptyArray = (schema, fieldName) => z.array(schema, `${fieldName} items must be an array`).min(1, `${fieldName} group must have at least one item`);
|
|
55
55
|
|
|
56
56
|
//#endregion
|
|
57
57
|
//#region src/schemas/recipe.schema.ts
|
|
58
58
|
/**
|
|
59
|
+
* Current schema version for recipe objects.
|
|
60
|
+
* Increment this when making breaking changes to the schema.
|
|
61
|
+
*
|
|
62
|
+
* Version history:
|
|
63
|
+
* - 1.0.0: Initial schema version
|
|
64
|
+
*/
|
|
65
|
+
const RECIPE_SCHEMA_VERSION = "1.0.0";
|
|
66
|
+
/**
|
|
59
67
|
* Schema for a parsed ingredient from the parse-ingredient library.
|
|
60
68
|
* This represents the structured data extracted from an ingredient string.
|
|
61
69
|
* @see https://github.com/jakeboone02/parse-ingredient
|
|
@@ -79,7 +87,7 @@ const IngredientItemSchema = z.object({
|
|
|
79
87
|
* Schema for a group of ingredients
|
|
80
88
|
*/
|
|
81
89
|
const IngredientGroupSchema = z.object({
|
|
82
|
-
name: zString("Ingredient group name").nullable()
|
|
90
|
+
name: zString("Ingredient group name").nullable(),
|
|
83
91
|
items: zNonEmptyArray(IngredientItemSchema, "Ingredient")
|
|
84
92
|
});
|
|
85
93
|
/**
|
|
@@ -95,7 +103,7 @@ const InstructionItemSchema = z.object({ value: zString("Instruction value") });
|
|
|
95
103
|
* Schema for a group of instruction steps
|
|
96
104
|
*/
|
|
97
105
|
const InstructionGroupSchema = z.object({
|
|
98
|
-
name: zString("Instruction group name").nullable()
|
|
106
|
+
name: zString("Instruction group name").nullable(),
|
|
99
107
|
items: zNonEmptyArray(InstructionItemSchema, "Instruction")
|
|
100
108
|
});
|
|
101
109
|
/**
|
|
@@ -111,10 +119,23 @@ const LinkSchema = z.object({
|
|
|
111
119
|
text: zString("Link text")
|
|
112
120
|
});
|
|
113
121
|
/**
|
|
114
|
-
* Base RecipeObject schema without cross-field validations
|
|
115
|
-
*
|
|
122
|
+
* Base RecipeObject schema without cross-field validations.
|
|
123
|
+
* Use this schema when you need to extend the recipe object with custom fields.
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```ts
|
|
127
|
+
* import { RecipeObjectBaseSchema, applyRecipeValidations } from 'recipe-scrapers-js'
|
|
128
|
+
*
|
|
129
|
+
* const MyCustomRecipeSchema = RecipeObjectBaseSchema.extend({
|
|
130
|
+
* customField: z.string(),
|
|
131
|
+
* })
|
|
132
|
+
*
|
|
133
|
+
* // Apply the standard recipe validations
|
|
134
|
+
* const MyValidatedRecipeSchema = applyRecipeValidations(MyCustomRecipeSchema)
|
|
135
|
+
* ```
|
|
116
136
|
*/
|
|
117
137
|
const RecipeObjectBaseSchema = z.object({
|
|
138
|
+
schemaVersion: z.literal(RECIPE_SCHEMA_VERSION).default(RECIPE_SCHEMA_VERSION).describe("Schema version for recipe data migrations"),
|
|
118
139
|
host: z.hostname("Host must be a valid hostname"),
|
|
119
140
|
title: zString("Title", { max: 500 }),
|
|
120
141
|
author: zString("Author", { max: 255 }),
|
|
@@ -122,42 +143,67 @@ const RecipeObjectBaseSchema = z.object({
|
|
|
122
143
|
instructions: InstructionsSchema,
|
|
123
144
|
canonicalUrl: zHttpUrl("Canonical URL"),
|
|
124
145
|
image: zHttpUrl("Image"),
|
|
125
|
-
totalTime:
|
|
126
|
-
cookTime:
|
|
127
|
-
prepTime:
|
|
146
|
+
totalTime: zPositiveInteger("Total time"),
|
|
147
|
+
cookTime: zPositiveInteger("Cook time"),
|
|
148
|
+
prepTime: zPositiveInteger("Prep time"),
|
|
128
149
|
ratings: z.number("Ratings must be a number").min(0, "Ratings must be at least 0").max(5, "Ratings must be at most 5").default(0),
|
|
129
150
|
ratingsCount: z.int("Ratings count must be an integer").nonnegative("Ratings count must be non-negative").default(0),
|
|
130
151
|
yields: zString("Yields"),
|
|
131
152
|
description: zString("Description"),
|
|
132
153
|
language: zString("Language", { min: 2 }).optional().default("en"),
|
|
133
|
-
siteName: zString("Site name").nullable()
|
|
134
|
-
cookingMethod: zString("Cooking method").nullable()
|
|
154
|
+
siteName: zString("Site name").nullable(),
|
|
155
|
+
cookingMethod: zString("Cooking method").nullable(),
|
|
135
156
|
category: z.array(zString("Category item"), "Category must be an array").default([]),
|
|
136
157
|
cuisine: z.array(zString("Cuisine item"), "Cuisine must be an array").default([]),
|
|
137
158
|
keywords: z.array(zString("Keyword item"), "Keywords must be an array").default([]),
|
|
138
159
|
dietaryRestrictions: z.array(zString("Dietary restriction item"), "Dietary restrictions must be an array").default([]),
|
|
139
160
|
equipment: z.array(zString("Equipment item"), "Equipment must be an array").default([]),
|
|
140
|
-
links: z.array(LinkSchema, "Links must be an array").
|
|
161
|
+
links: z.array(LinkSchema, "Links must be an array").optional(),
|
|
141
162
|
nutrients: z.record(z.string(), z.string(), "Nutrients must be an object").default({}),
|
|
142
163
|
reviews: z.record(z.string(), z.string(), "Reviews must be an object").default({})
|
|
143
164
|
});
|
|
144
165
|
/**
|
|
145
|
-
*
|
|
166
|
+
* Applies recipe-specific transformations and validations to a schema.
|
|
167
|
+
* Use this when extending RecipeObjectBaseSchema with custom fields.
|
|
168
|
+
*
|
|
169
|
+
* @param schema - A Zod object schema that includes
|
|
170
|
+
* all RecipeObjectBaseSchema fields
|
|
171
|
+
* @returns A schema with transforms and field validations applied
|
|
172
|
+
*
|
|
173
|
+
* @example
|
|
174
|
+
* ```ts
|
|
175
|
+
* const CustomSchema = RecipeObjectBaseSchema.extend({
|
|
176
|
+
* tags: z.array(z.string()),
|
|
177
|
+
* })
|
|
178
|
+
*
|
|
179
|
+
* const ValidatedCustomSchema = applyRecipeValidations(CustomSchema)
|
|
180
|
+
* ```
|
|
146
181
|
*/
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}, {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}).refine((data) =>
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
182
|
+
function applyRecipeValidations(schema) {
|
|
183
|
+
return schema.transform((data) => {
|
|
184
|
+
if (!data.totalTime && !isNull(data.cookTime) && !isNull(data.prepTime)) data.totalTime = data.cookTime + data.prepTime;
|
|
185
|
+
return data;
|
|
186
|
+
}).refine(({ totalTime, cookTime, prepTime }) => {
|
|
187
|
+
if (!isNull(totalTime) && !isNull(cookTime) && !isNull(prepTime)) return totalTime >= cookTime + prepTime;
|
|
188
|
+
return true;
|
|
189
|
+
}, {
|
|
190
|
+
message: "Total time should be at least the sum of cook time and prep time",
|
|
191
|
+
path: ["totalTime"]
|
|
192
|
+
}).refine((data) => {
|
|
193
|
+
return data.ratings === 0 || data.ratingsCount > 0;
|
|
194
|
+
}, {
|
|
195
|
+
message: "Ratings count should be greater than 0 when ratings exist",
|
|
196
|
+
path: ["ratingsCount"]
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Strict RecipeObject schema with all validations enforced.
|
|
201
|
+
* This is the standard schema used by recipe scrapers.
|
|
202
|
+
*
|
|
203
|
+
* For custom extensions, use RecipeObjectBaseSchema.extend() and then
|
|
204
|
+
* apply validations with applyRecipeValidations().
|
|
205
|
+
*/
|
|
206
|
+
const RecipeObjectSchema = applyRecipeValidations(RecipeObjectBaseSchema);
|
|
161
207
|
|
|
162
208
|
//#endregion
|
|
163
209
|
//#region src/exceptions/index.ts
|
|
@@ -1235,7 +1281,7 @@ var AbstractScraper = class {
|
|
|
1235
1281
|
return "en";
|
|
1236
1282
|
}
|
|
1237
1283
|
links() {
|
|
1238
|
-
if (!this.options.linksEnabled) return
|
|
1284
|
+
if (!this.options.linksEnabled) return void 0;
|
|
1239
1285
|
return this.$("a[href]").map((_, el) => {
|
|
1240
1286
|
const href = this.$(el).attr("href");
|
|
1241
1287
|
if (!href?.startsWith("http")) return null;
|
|
@@ -1282,6 +1328,7 @@ var AbstractScraper = class {
|
|
|
1282
1328
|
}
|
|
1283
1329
|
/**
|
|
1284
1330
|
* Converts the scraper's data into a JSON-serializable object.
|
|
1331
|
+
* Note: schemaVersion is added during validation by parse() or safeParse().
|
|
1285
1332
|
*/
|
|
1286
1333
|
async toRecipeObject() {
|
|
1287
1334
|
const { category, cuisine, dietaryRestrictions, equipment, keywords, nutrients, reviews, ...rest } = await this.scrape();
|
|
@@ -1600,4 +1647,4 @@ function getScraper(url) {
|
|
|
1600
1647
|
}
|
|
1601
1648
|
|
|
1602
1649
|
//#endregion
|
|
1603
|
-
export { ExtractorPlugin, IngredientGroupSchema, IngredientItemSchema, IngredientsSchema, InstructionGroupSchema, InstructionItemSchema, InstructionsSchema, LinkSchema, LogLevel, Logger, ParsedIngredientSchema, PostProcessorPlugin, RecipeObjectBaseSchema, RecipeObjectSchema, getScraper, scrapers };
|
|
1650
|
+
export { ExtractorPlugin, IngredientGroupSchema, IngredientItemSchema, IngredientsSchema, InstructionGroupSchema, InstructionItemSchema, InstructionsSchema, LinkSchema, LogLevel, Logger, ParsedIngredientSchema, PostProcessorPlugin, RECIPE_SCHEMA_VERSION, RecipeObjectBaseSchema, RecipeObjectSchema, applyRecipeValidations, getScraper, scrapers };
|