soustack 0.2.3 → 0.4.0
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/README.md +128 -18
- package/dist/cli/index.js +1706 -665
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +172 -28
- package/dist/index.d.ts +172 -28
- package/dist/index.js +2028 -662
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2022 -662
- package/dist/index.mjs.map +1 -1
- package/dist/{scrape.d.mts → scrape/index.d.mts} +38 -10
- package/dist/{scrape.d.ts → scrape/index.d.ts} +38 -10
- package/dist/{scrape.js → scrape/index.js} +268 -62
- package/dist/scrape/index.js.map +1 -0
- package/dist/{scrape.mjs → scrape/index.mjs} +268 -62
- package/dist/scrape/index.mjs.map +1 -0
- package/package.json +15 -9
- package/src/profiles/base.schema.json +2 -2
- package/src/profiles/cookable.schema.json +4 -4
- package/src/profiles/illustrated.schema.json +4 -4
- package/src/profiles/quantified.schema.json +4 -4
- package/src/profiles/scalable.schema.json +6 -6
- package/src/profiles/schedulable.schema.json +4 -4
- package/src/schema.json +15 -3
- package/src/soustack.schema.json +15 -3
- package/dist/scrape.js.map +0 -1
- package/dist/scrape.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Soustack Recipe Schema v0.
|
|
2
|
+
* Soustack Recipe Schema v0.3.0
|
|
3
3
|
* A portable, scalable, interoperable recipe format.
|
|
4
4
|
*/
|
|
5
5
|
interface SoustackRecipe {
|
|
6
|
+
/** Document marker for Soustack recipes */
|
|
7
|
+
'@type'?: 'Recipe';
|
|
6
8
|
/** Optional $schema pointer for profile-aware validation */
|
|
7
9
|
$schema?: string;
|
|
10
|
+
/** Optional declared validation profile */
|
|
11
|
+
profile?: string;
|
|
12
|
+
/** Recipe level: "lite" or "base" */
|
|
13
|
+
level?: "lite" | "base";
|
|
14
|
+
/** Stack declarations as a map: Record<stackName, versionNumber> */
|
|
15
|
+
stacks?: Record<string, number>;
|
|
16
|
+
/** Attribution stack payload */
|
|
17
|
+
attribution?: AttributionModule;
|
|
18
|
+
/** Taxonomy stack payload */
|
|
19
|
+
taxonomy?: TaxonomyModule;
|
|
20
|
+
/** Media stack payload */
|
|
21
|
+
media?: MediaModule;
|
|
22
|
+
/** Times stack payload */
|
|
23
|
+
times?: TimesModule;
|
|
8
24
|
/** Unique identifier (slug or UUID) */
|
|
9
25
|
id?: string;
|
|
10
26
|
/** Optional display title */
|
|
@@ -77,25 +93,25 @@ interface Equipment {
|
|
|
77
93
|
name: string;
|
|
78
94
|
required?: boolean;
|
|
79
95
|
label?: string;
|
|
80
|
-
capacity?: Quantity;
|
|
96
|
+
capacity?: Quantity$1;
|
|
81
97
|
scalingLimit?: number;
|
|
82
98
|
alternatives?: string[];
|
|
83
99
|
}
|
|
84
|
-
interface Quantity {
|
|
100
|
+
interface Quantity$1 {
|
|
85
101
|
amount: number;
|
|
86
102
|
/** Unit string (e.g. "g", "cup") or null for count-based items (e.g. "2 eggs") */
|
|
87
103
|
unit: string | null;
|
|
88
104
|
}
|
|
89
|
-
type IngredientItem = string | Ingredient | IngredientSubsection;
|
|
105
|
+
type IngredientItem = string | Ingredient$1 | IngredientSubsection;
|
|
90
106
|
interface IngredientSubsection {
|
|
91
107
|
subsection: string;
|
|
92
|
-
items: (string | Ingredient)[];
|
|
108
|
+
items: (string | Ingredient$1)[];
|
|
93
109
|
}
|
|
94
|
-
interface Ingredient {
|
|
110
|
+
interface Ingredient$1 {
|
|
95
111
|
id?: string;
|
|
96
112
|
/** Full human-readable text (e.g. "2 cups flour") */
|
|
97
113
|
item: string;
|
|
98
|
-
quantity?: Quantity;
|
|
114
|
+
quantity?: Quantity$1;
|
|
99
115
|
name?: string;
|
|
100
116
|
aisle?: string;
|
|
101
117
|
/** Required prep state (e.g. "diced") */
|
|
@@ -208,15 +224,27 @@ interface Alternative {
|
|
|
208
224
|
dietary?: string[];
|
|
209
225
|
}
|
|
210
226
|
interface NutritionFacts {
|
|
211
|
-
calories?:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
calories?: number;
|
|
228
|
+
protein_g?: number;
|
|
229
|
+
}
|
|
230
|
+
interface AttributionModule {
|
|
231
|
+
url?: string;
|
|
232
|
+
author?: string;
|
|
233
|
+
datePublished?: string;
|
|
234
|
+
}
|
|
235
|
+
interface TaxonomyModule {
|
|
236
|
+
keywords?: string[];
|
|
237
|
+
category?: string;
|
|
238
|
+
cuisine?: string;
|
|
239
|
+
}
|
|
240
|
+
interface MediaModule {
|
|
241
|
+
images?: string[];
|
|
242
|
+
videos?: string[];
|
|
243
|
+
}
|
|
244
|
+
interface TimesModule {
|
|
245
|
+
prepMinutes?: number;
|
|
246
|
+
cookMinutes?: number;
|
|
247
|
+
totalMinutes?: number;
|
|
220
248
|
}
|
|
221
249
|
|
|
222
250
|
interface ScaleRecipeOptions {
|
|
@@ -228,32 +256,76 @@ interface ScaleRecipeOptions {
|
|
|
228
256
|
}
|
|
229
257
|
declare function scaleRecipe(recipe: Recipe, options?: ScaleRecipeOptions): Recipe;
|
|
230
258
|
|
|
231
|
-
type
|
|
232
|
-
interface
|
|
259
|
+
type ConformanceSeverity = "error" | "warning";
|
|
260
|
+
interface ConformanceIssue {
|
|
261
|
+
code: string;
|
|
233
262
|
path: string;
|
|
234
263
|
message: string;
|
|
235
|
-
|
|
264
|
+
severity: ConformanceSeverity;
|
|
236
265
|
}
|
|
237
|
-
|
|
266
|
+
|
|
267
|
+
type ProfileName = "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed" | "minimal" | "core";
|
|
268
|
+
interface NormalizedError {
|
|
238
269
|
path: string;
|
|
239
270
|
message: string;
|
|
271
|
+
keyword?: string;
|
|
240
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Validation modes for recipe validation.
|
|
275
|
+
* - "schema": JSON Schema only
|
|
276
|
+
* - "full": JSON Schema + semantic conformance checks
|
|
277
|
+
*/
|
|
278
|
+
type ValidateMode = "schema" | "full";
|
|
241
279
|
interface ValidateOptions {
|
|
242
280
|
profile?: ProfileName;
|
|
243
281
|
schema?: string;
|
|
244
282
|
collectAllErrors?: boolean;
|
|
283
|
+
mode?: ValidateMode;
|
|
284
|
+
includeNormalized?: boolean;
|
|
245
285
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Result payload for recipe validation. Schema validation always runs first;
|
|
288
|
+
* conformance issues are only included when running in full mode.
|
|
289
|
+
*/
|
|
290
|
+
interface ValidateResult {
|
|
291
|
+
ok: boolean;
|
|
292
|
+
schemaErrors: NormalizedError[];
|
|
293
|
+
conformanceIssues: ConformanceIssue[];
|
|
294
|
+
warnings: string[];
|
|
295
|
+
normalizedRecipe?: Recipe;
|
|
251
296
|
}
|
|
252
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Legacy validateRecipe function - now uses the new validateRecipeSchema internally
|
|
299
|
+
* but maintains backward compatibility with profile/stack-based validation
|
|
300
|
+
* Also includes semantic conformance validation.
|
|
301
|
+
*/
|
|
302
|
+
/**
|
|
303
|
+
* Validates a recipe with explicit validation modes.
|
|
304
|
+
* - mode="schema": JSON Schema only
|
|
305
|
+
* - mode="full": schema + semantic conformance (only if schema passes)
|
|
306
|
+
*/
|
|
307
|
+
declare function validateRecipe(input: any, options?: ValidateOptions): ValidateResult;
|
|
253
308
|
declare function detectProfiles(recipe: any): ProfileName[];
|
|
254
309
|
|
|
255
310
|
declare function fromSchemaOrg(input: unknown): Recipe | null;
|
|
256
311
|
|
|
312
|
+
interface NormalizationResult {
|
|
313
|
+
recipe: Recipe;
|
|
314
|
+
warnings: string[];
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Normalizes a recipe input to the current spec format:
|
|
318
|
+
* - Rejects inputs with legacy field (unsupported)
|
|
319
|
+
* - Converts legacy `stacks` array format to map format
|
|
320
|
+
* - Ensures `stacks` exists even if empty
|
|
321
|
+
* - Preserves existing `stacks` map format
|
|
322
|
+
*
|
|
323
|
+
* @param input - Raw recipe input (may have legacy formats)
|
|
324
|
+
* @returns Normalized recipe with warnings for any issues encountered
|
|
325
|
+
* @throws Error if input contains legacy field
|
|
326
|
+
*/
|
|
327
|
+
declare function normalizeRecipe(input: unknown): NormalizationResult;
|
|
328
|
+
|
|
257
329
|
interface SchemaOrgRecipe$1 {
|
|
258
330
|
'@context'?: string | Array<string | Record<string, unknown>> | Record<string, unknown>;
|
|
259
331
|
'@type'?: string | string[];
|
|
@@ -276,6 +348,7 @@ interface SchemaOrgRecipe$1 {
|
|
|
276
348
|
datePublished?: string;
|
|
277
349
|
dateModified?: string;
|
|
278
350
|
nutrition?: NutritionInformation;
|
|
351
|
+
video?: SchemaOrgImage;
|
|
279
352
|
'@graph'?: unknown;
|
|
280
353
|
}
|
|
281
354
|
type SchemaOrgIngredientList = string | string[];
|
|
@@ -321,6 +394,14 @@ interface NutritionInformation {
|
|
|
321
394
|
[key: string]: string | number | null | undefined;
|
|
322
395
|
}
|
|
323
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Convert a Soustack recipe to Schema.org JSON-LD format.
|
|
399
|
+
*
|
|
400
|
+
* BREAKING CHANGE in v0.3.0: This function now targets the "minimal" profile
|
|
401
|
+
* and only includes stacks that are schemaOrgMappable (as defined in the
|
|
402
|
+
* stacks registry). Non-mappable stacks (e.g., nutrition@1, schedule@1)
|
|
403
|
+
* are excluded from the conversion.
|
|
404
|
+
*/
|
|
324
405
|
declare function toSchemaOrg(recipe: Recipe): SchemaOrgRecipe$1;
|
|
325
406
|
|
|
326
407
|
interface HowToStep {
|
|
@@ -348,6 +429,69 @@ interface SchemaOrgRecipe {
|
|
|
348
429
|
|
|
349
430
|
declare function extractSchemaOrgRecipeFromHTML(html: string): SchemaOrgRecipe | null;
|
|
350
431
|
|
|
351
|
-
declare const SOUSTACK_SPEC_VERSION = "0.
|
|
432
|
+
declare const SOUSTACK_SPEC_VERSION = "0.3.0";
|
|
433
|
+
|
|
434
|
+
type ConvertTarget = 'metric';
|
|
435
|
+
type ConvertMode = 'volume' | 'mass';
|
|
436
|
+
type RoundMode = 'none' | 'sane';
|
|
437
|
+
interface LineItem {
|
|
438
|
+
ingredient: string;
|
|
439
|
+
quantity: number;
|
|
440
|
+
unit: string | null;
|
|
441
|
+
}
|
|
442
|
+
interface ConvertedLineItem extends LineItem {
|
|
443
|
+
notes?: string;
|
|
444
|
+
}
|
|
445
|
+
declare class UnknownUnitError extends Error {
|
|
446
|
+
readonly unit: string;
|
|
447
|
+
constructor(unit: string);
|
|
448
|
+
}
|
|
449
|
+
declare class UnsupportedConversionError extends Error {
|
|
450
|
+
readonly unit: string;
|
|
451
|
+
readonly mode: ConvertMode;
|
|
452
|
+
constructor(unit: string, mode: ConvertMode);
|
|
453
|
+
}
|
|
454
|
+
declare class MissingEquivalencyError extends Error {
|
|
455
|
+
readonly ingredient: string;
|
|
456
|
+
readonly unit: string;
|
|
457
|
+
constructor(ingredient: string, unit: string);
|
|
458
|
+
}
|
|
459
|
+
declare function convertLineItemToMetric(item: LineItem, mode: ConvertMode, opts?: {
|
|
460
|
+
round?: RoundMode;
|
|
461
|
+
}): ConvertedLineItem;
|
|
462
|
+
|
|
463
|
+
interface Quantity {
|
|
464
|
+
amount: number;
|
|
465
|
+
unit?: string | null;
|
|
466
|
+
}
|
|
467
|
+
interface Ingredient {
|
|
468
|
+
id?: string;
|
|
469
|
+
item: string;
|
|
470
|
+
quantity?: Quantity;
|
|
471
|
+
name?: string;
|
|
472
|
+
prep?: string;
|
|
473
|
+
prepAction?: string;
|
|
474
|
+
prepActions?: string[];
|
|
475
|
+
form?: string;
|
|
476
|
+
prepTime?: number;
|
|
477
|
+
optional?: boolean;
|
|
478
|
+
notes?: string;
|
|
479
|
+
}
|
|
480
|
+
interface MiseEnPlaceTask {
|
|
481
|
+
category: 'prep' | 'state' | 'measure' | 'other';
|
|
482
|
+
action?: string;
|
|
483
|
+
form?: string;
|
|
484
|
+
items: Array<{
|
|
485
|
+
ingredient: string;
|
|
486
|
+
quantity?: Quantity;
|
|
487
|
+
optional?: boolean;
|
|
488
|
+
notes?: string;
|
|
489
|
+
}>;
|
|
490
|
+
}
|
|
491
|
+
interface MiseEnPlacePlan {
|
|
492
|
+
tasks: MiseEnPlaceTask[];
|
|
493
|
+
ungrouped: Ingredient[];
|
|
494
|
+
}
|
|
495
|
+
declare function miseEnPlace(ingredients: Ingredient[]): MiseEnPlacePlan;
|
|
352
496
|
|
|
353
|
-
export { type Alternative, type Equipment, type FrozenStorageMethod, type Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type MakeAheadComponent, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity, type Recipe, SOUSTACK_SPEC_VERSION, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type Time, type Yield, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, scaleRecipe, toSchemaOrg, validateRecipe };
|
|
497
|
+
export { type Alternative, type AttributionModule, type ConvertMode, type ConvertTarget, type ConvertedLineItem, type Equipment, type FrozenStorageMethod, type Ingredient$1 as Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type LineItem, type MakeAheadComponent, type MediaModule, type Ingredient as MiseEnPlaceIngredient, type MiseEnPlacePlan, type Quantity as MiseEnPlaceQuantity, type MiseEnPlaceTask, MissingEquivalencyError, type NormalizationResult, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity$1 as Quantity, type Recipe, type RoundMode, SOUSTACK_SPEC_VERSION, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type TaxonomyModule, type Time, type TimesModule, UnknownUnitError, UnsupportedConversionError, type ValidateMode, type ValidateResult, type Yield, convertLineItemToMetric, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, miseEnPlace, normalizeRecipe, scaleRecipe, toSchemaOrg, validateRecipe };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Soustack Recipe Schema v0.
|
|
2
|
+
* Soustack Recipe Schema v0.3.0
|
|
3
3
|
* A portable, scalable, interoperable recipe format.
|
|
4
4
|
*/
|
|
5
5
|
interface SoustackRecipe {
|
|
6
|
+
/** Document marker for Soustack recipes */
|
|
7
|
+
'@type'?: 'Recipe';
|
|
6
8
|
/** Optional $schema pointer for profile-aware validation */
|
|
7
9
|
$schema?: string;
|
|
10
|
+
/** Optional declared validation profile */
|
|
11
|
+
profile?: string;
|
|
12
|
+
/** Recipe level: "lite" or "base" */
|
|
13
|
+
level?: "lite" | "base";
|
|
14
|
+
/** Stack declarations as a map: Record<stackName, versionNumber> */
|
|
15
|
+
stacks?: Record<string, number>;
|
|
16
|
+
/** Attribution stack payload */
|
|
17
|
+
attribution?: AttributionModule;
|
|
18
|
+
/** Taxonomy stack payload */
|
|
19
|
+
taxonomy?: TaxonomyModule;
|
|
20
|
+
/** Media stack payload */
|
|
21
|
+
media?: MediaModule;
|
|
22
|
+
/** Times stack payload */
|
|
23
|
+
times?: TimesModule;
|
|
8
24
|
/** Unique identifier (slug or UUID) */
|
|
9
25
|
id?: string;
|
|
10
26
|
/** Optional display title */
|
|
@@ -77,25 +93,25 @@ interface Equipment {
|
|
|
77
93
|
name: string;
|
|
78
94
|
required?: boolean;
|
|
79
95
|
label?: string;
|
|
80
|
-
capacity?: Quantity;
|
|
96
|
+
capacity?: Quantity$1;
|
|
81
97
|
scalingLimit?: number;
|
|
82
98
|
alternatives?: string[];
|
|
83
99
|
}
|
|
84
|
-
interface Quantity {
|
|
100
|
+
interface Quantity$1 {
|
|
85
101
|
amount: number;
|
|
86
102
|
/** Unit string (e.g. "g", "cup") or null for count-based items (e.g. "2 eggs") */
|
|
87
103
|
unit: string | null;
|
|
88
104
|
}
|
|
89
|
-
type IngredientItem = string | Ingredient | IngredientSubsection;
|
|
105
|
+
type IngredientItem = string | Ingredient$1 | IngredientSubsection;
|
|
90
106
|
interface IngredientSubsection {
|
|
91
107
|
subsection: string;
|
|
92
|
-
items: (string | Ingredient)[];
|
|
108
|
+
items: (string | Ingredient$1)[];
|
|
93
109
|
}
|
|
94
|
-
interface Ingredient {
|
|
110
|
+
interface Ingredient$1 {
|
|
95
111
|
id?: string;
|
|
96
112
|
/** Full human-readable text (e.g. "2 cups flour") */
|
|
97
113
|
item: string;
|
|
98
|
-
quantity?: Quantity;
|
|
114
|
+
quantity?: Quantity$1;
|
|
99
115
|
name?: string;
|
|
100
116
|
aisle?: string;
|
|
101
117
|
/** Required prep state (e.g. "diced") */
|
|
@@ -208,15 +224,27 @@ interface Alternative {
|
|
|
208
224
|
dietary?: string[];
|
|
209
225
|
}
|
|
210
226
|
interface NutritionFacts {
|
|
211
|
-
calories?:
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
227
|
+
calories?: number;
|
|
228
|
+
protein_g?: number;
|
|
229
|
+
}
|
|
230
|
+
interface AttributionModule {
|
|
231
|
+
url?: string;
|
|
232
|
+
author?: string;
|
|
233
|
+
datePublished?: string;
|
|
234
|
+
}
|
|
235
|
+
interface TaxonomyModule {
|
|
236
|
+
keywords?: string[];
|
|
237
|
+
category?: string;
|
|
238
|
+
cuisine?: string;
|
|
239
|
+
}
|
|
240
|
+
interface MediaModule {
|
|
241
|
+
images?: string[];
|
|
242
|
+
videos?: string[];
|
|
243
|
+
}
|
|
244
|
+
interface TimesModule {
|
|
245
|
+
prepMinutes?: number;
|
|
246
|
+
cookMinutes?: number;
|
|
247
|
+
totalMinutes?: number;
|
|
220
248
|
}
|
|
221
249
|
|
|
222
250
|
interface ScaleRecipeOptions {
|
|
@@ -228,32 +256,76 @@ interface ScaleRecipeOptions {
|
|
|
228
256
|
}
|
|
229
257
|
declare function scaleRecipe(recipe: Recipe, options?: ScaleRecipeOptions): Recipe;
|
|
230
258
|
|
|
231
|
-
type
|
|
232
|
-
interface
|
|
259
|
+
type ConformanceSeverity = "error" | "warning";
|
|
260
|
+
interface ConformanceIssue {
|
|
261
|
+
code: string;
|
|
233
262
|
path: string;
|
|
234
263
|
message: string;
|
|
235
|
-
|
|
264
|
+
severity: ConformanceSeverity;
|
|
236
265
|
}
|
|
237
|
-
|
|
266
|
+
|
|
267
|
+
type ProfileName = "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed" | "minimal" | "core";
|
|
268
|
+
interface NormalizedError {
|
|
238
269
|
path: string;
|
|
239
270
|
message: string;
|
|
271
|
+
keyword?: string;
|
|
240
272
|
}
|
|
273
|
+
/**
|
|
274
|
+
* Validation modes for recipe validation.
|
|
275
|
+
* - "schema": JSON Schema only
|
|
276
|
+
* - "full": JSON Schema + semantic conformance checks
|
|
277
|
+
*/
|
|
278
|
+
type ValidateMode = "schema" | "full";
|
|
241
279
|
interface ValidateOptions {
|
|
242
280
|
profile?: ProfileName;
|
|
243
281
|
schema?: string;
|
|
244
282
|
collectAllErrors?: boolean;
|
|
283
|
+
mode?: ValidateMode;
|
|
284
|
+
includeNormalized?: boolean;
|
|
245
285
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
286
|
+
/**
|
|
287
|
+
* Result payload for recipe validation. Schema validation always runs first;
|
|
288
|
+
* conformance issues are only included when running in full mode.
|
|
289
|
+
*/
|
|
290
|
+
interface ValidateResult {
|
|
291
|
+
ok: boolean;
|
|
292
|
+
schemaErrors: NormalizedError[];
|
|
293
|
+
conformanceIssues: ConformanceIssue[];
|
|
294
|
+
warnings: string[];
|
|
295
|
+
normalizedRecipe?: Recipe;
|
|
251
296
|
}
|
|
252
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Legacy validateRecipe function - now uses the new validateRecipeSchema internally
|
|
299
|
+
* but maintains backward compatibility with profile/stack-based validation
|
|
300
|
+
* Also includes semantic conformance validation.
|
|
301
|
+
*/
|
|
302
|
+
/**
|
|
303
|
+
* Validates a recipe with explicit validation modes.
|
|
304
|
+
* - mode="schema": JSON Schema only
|
|
305
|
+
* - mode="full": schema + semantic conformance (only if schema passes)
|
|
306
|
+
*/
|
|
307
|
+
declare function validateRecipe(input: any, options?: ValidateOptions): ValidateResult;
|
|
253
308
|
declare function detectProfiles(recipe: any): ProfileName[];
|
|
254
309
|
|
|
255
310
|
declare function fromSchemaOrg(input: unknown): Recipe | null;
|
|
256
311
|
|
|
312
|
+
interface NormalizationResult {
|
|
313
|
+
recipe: Recipe;
|
|
314
|
+
warnings: string[];
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Normalizes a recipe input to the current spec format:
|
|
318
|
+
* - Rejects inputs with legacy field (unsupported)
|
|
319
|
+
* - Converts legacy `stacks` array format to map format
|
|
320
|
+
* - Ensures `stacks` exists even if empty
|
|
321
|
+
* - Preserves existing `stacks` map format
|
|
322
|
+
*
|
|
323
|
+
* @param input - Raw recipe input (may have legacy formats)
|
|
324
|
+
* @returns Normalized recipe with warnings for any issues encountered
|
|
325
|
+
* @throws Error if input contains legacy field
|
|
326
|
+
*/
|
|
327
|
+
declare function normalizeRecipe(input: unknown): NormalizationResult;
|
|
328
|
+
|
|
257
329
|
interface SchemaOrgRecipe$1 {
|
|
258
330
|
'@context'?: string | Array<string | Record<string, unknown>> | Record<string, unknown>;
|
|
259
331
|
'@type'?: string | string[];
|
|
@@ -276,6 +348,7 @@ interface SchemaOrgRecipe$1 {
|
|
|
276
348
|
datePublished?: string;
|
|
277
349
|
dateModified?: string;
|
|
278
350
|
nutrition?: NutritionInformation;
|
|
351
|
+
video?: SchemaOrgImage;
|
|
279
352
|
'@graph'?: unknown;
|
|
280
353
|
}
|
|
281
354
|
type SchemaOrgIngredientList = string | string[];
|
|
@@ -321,6 +394,14 @@ interface NutritionInformation {
|
|
|
321
394
|
[key: string]: string | number | null | undefined;
|
|
322
395
|
}
|
|
323
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Convert a Soustack recipe to Schema.org JSON-LD format.
|
|
399
|
+
*
|
|
400
|
+
* BREAKING CHANGE in v0.3.0: This function now targets the "minimal" profile
|
|
401
|
+
* and only includes stacks that are schemaOrgMappable (as defined in the
|
|
402
|
+
* stacks registry). Non-mappable stacks (e.g., nutrition@1, schedule@1)
|
|
403
|
+
* are excluded from the conversion.
|
|
404
|
+
*/
|
|
324
405
|
declare function toSchemaOrg(recipe: Recipe): SchemaOrgRecipe$1;
|
|
325
406
|
|
|
326
407
|
interface HowToStep {
|
|
@@ -348,6 +429,69 @@ interface SchemaOrgRecipe {
|
|
|
348
429
|
|
|
349
430
|
declare function extractSchemaOrgRecipeFromHTML(html: string): SchemaOrgRecipe | null;
|
|
350
431
|
|
|
351
|
-
declare const SOUSTACK_SPEC_VERSION = "0.
|
|
432
|
+
declare const SOUSTACK_SPEC_VERSION = "0.3.0";
|
|
433
|
+
|
|
434
|
+
type ConvertTarget = 'metric';
|
|
435
|
+
type ConvertMode = 'volume' | 'mass';
|
|
436
|
+
type RoundMode = 'none' | 'sane';
|
|
437
|
+
interface LineItem {
|
|
438
|
+
ingredient: string;
|
|
439
|
+
quantity: number;
|
|
440
|
+
unit: string | null;
|
|
441
|
+
}
|
|
442
|
+
interface ConvertedLineItem extends LineItem {
|
|
443
|
+
notes?: string;
|
|
444
|
+
}
|
|
445
|
+
declare class UnknownUnitError extends Error {
|
|
446
|
+
readonly unit: string;
|
|
447
|
+
constructor(unit: string);
|
|
448
|
+
}
|
|
449
|
+
declare class UnsupportedConversionError extends Error {
|
|
450
|
+
readonly unit: string;
|
|
451
|
+
readonly mode: ConvertMode;
|
|
452
|
+
constructor(unit: string, mode: ConvertMode);
|
|
453
|
+
}
|
|
454
|
+
declare class MissingEquivalencyError extends Error {
|
|
455
|
+
readonly ingredient: string;
|
|
456
|
+
readonly unit: string;
|
|
457
|
+
constructor(ingredient: string, unit: string);
|
|
458
|
+
}
|
|
459
|
+
declare function convertLineItemToMetric(item: LineItem, mode: ConvertMode, opts?: {
|
|
460
|
+
round?: RoundMode;
|
|
461
|
+
}): ConvertedLineItem;
|
|
462
|
+
|
|
463
|
+
interface Quantity {
|
|
464
|
+
amount: number;
|
|
465
|
+
unit?: string | null;
|
|
466
|
+
}
|
|
467
|
+
interface Ingredient {
|
|
468
|
+
id?: string;
|
|
469
|
+
item: string;
|
|
470
|
+
quantity?: Quantity;
|
|
471
|
+
name?: string;
|
|
472
|
+
prep?: string;
|
|
473
|
+
prepAction?: string;
|
|
474
|
+
prepActions?: string[];
|
|
475
|
+
form?: string;
|
|
476
|
+
prepTime?: number;
|
|
477
|
+
optional?: boolean;
|
|
478
|
+
notes?: string;
|
|
479
|
+
}
|
|
480
|
+
interface MiseEnPlaceTask {
|
|
481
|
+
category: 'prep' | 'state' | 'measure' | 'other';
|
|
482
|
+
action?: string;
|
|
483
|
+
form?: string;
|
|
484
|
+
items: Array<{
|
|
485
|
+
ingredient: string;
|
|
486
|
+
quantity?: Quantity;
|
|
487
|
+
optional?: boolean;
|
|
488
|
+
notes?: string;
|
|
489
|
+
}>;
|
|
490
|
+
}
|
|
491
|
+
interface MiseEnPlacePlan {
|
|
492
|
+
tasks: MiseEnPlaceTask[];
|
|
493
|
+
ungrouped: Ingredient[];
|
|
494
|
+
}
|
|
495
|
+
declare function miseEnPlace(ingredients: Ingredient[]): MiseEnPlacePlan;
|
|
352
496
|
|
|
353
|
-
export { type Alternative, type Equipment, type FrozenStorageMethod, type Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type MakeAheadComponent, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity, type Recipe, SOUSTACK_SPEC_VERSION, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type Time, type Yield, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, scaleRecipe, toSchemaOrg, validateRecipe };
|
|
497
|
+
export { type Alternative, type AttributionModule, type ConvertMode, type ConvertTarget, type ConvertedLineItem, type Equipment, type FrozenStorageMethod, type Ingredient$1 as Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type LineItem, type MakeAheadComponent, type MediaModule, type Ingredient as MiseEnPlaceIngredient, type MiseEnPlacePlan, type Quantity as MiseEnPlaceQuantity, type MiseEnPlaceTask, MissingEquivalencyError, type NormalizationResult, type NutritionFacts, type ParsedIngredient, type ParsedYield, type Quantity$1 as Quantity, type Recipe, type RoundMode, SOUSTACK_SPEC_VERSION, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingProportional, type SimpleTime, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type StructuredTime, type Substitution, type TaxonomyModule, type Time, type TimesModule, UnknownUnitError, UnsupportedConversionError, type ValidateMode, type ValidateResult, type Yield, convertLineItemToMetric, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, miseEnPlace, normalizeRecipe, scaleRecipe, toSchemaOrg, validateRecipe };
|