soustack 0.4.0 → 0.4.1
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 +4 -4
- package/dist/cli/index.js +4412 -1275
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.mts +106 -80
- package/dist/index.d.ts +106 -80
- package/dist/index.js +4527 -1360
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +4527 -1360
- package/dist/index.mjs.map +1 -1
- package/dist/scrape/index.d.mts +86 -74
- package/dist/scrape/index.d.ts +86 -74
- package/dist/scrape/index.js +91 -64
- package/dist/scrape/index.js.map +1 -1
- package/dist/scrape/index.mjs +91 -64
- package/dist/scrape/index.mjs.map +1 -1
- package/package.json +15 -6
- package/spec/.sync-meta.json +149 -0
- package/spec/SOUSTACK_SPEC_VERSION +1 -0
- package/spec/defs/common.schema.json +46 -0
- package/spec/defs/duration.schema.json +33 -0
- package/spec/defs/entities.schema.json +111 -0
- package/spec/defs/ingredientQuantified.schema.json +9 -0
- package/spec/defs/quantity.schema.json +16 -0
- package/spec/defs/scalingRule.schema.json +127 -0
- package/spec/defs/temperature.schema.json +63 -0
- package/spec/fixtures/content/illustrated-step.valid.json +24 -0
- package/spec/fixtures/invalid/equipment-unknown-reference.invalid.json +38 -0
- package/spec/fixtures/invalid/mise-en-place-unknown-equipment.invalid.json +37 -0
- package/spec/fixtures/invalid/mise-en-place-unknown-input.invalid.json +41 -0
- package/spec/fixtures/invalid/storage-leftovers-missing-method.invalid.json +31 -0
- package/spec/fixtures/invalid/storage-leftovers-wrong-type.invalid.json +23 -0
- package/spec/fixtures/level/base-full.valid.json +162 -0
- package/spec/fixtures/level/base-missing-yield.invalid.json +12 -0
- package/spec/fixtures/level/lite-min.valid.json +14 -0
- package/spec/fixtures/profile/profile-base.valid.json +20 -0
- package/spec/fixtures/profile/profile-equipped.valid.json +28 -0
- package/spec/fixtures/profile/profile-illustrated.valid.json +28 -0
- package/spec/fixtures/profile/profile-lite.valid.json +13 -0
- package/spec/fixtures/profile/profile-prepped.valid.json +31 -0
- package/spec/fixtures/profile/profile-scalable-missing-scaling.invalid.json +29 -0
- package/spec/fixtures/profile/profile-scalable.valid.json +49 -0
- package/spec/fixtures/profile/profile-timed-missing-structured.invalid.json +30 -0
- package/spec/fixtures/scaling/bakers-percent-missing-ref.invalid.json +41 -0
- package/spec/fixtures/scaling/bakers-percent.valid.json +51 -0
- package/spec/fixtures/scaling/discrete-range.invalid.json +36 -0
- package/spec/fixtures/scaling/missing-quantified.invalid.json +40 -0
- package/spec/fixtures/scaling/reject-bakersPercentage.invalid.json +50 -0
- package/spec/fixtures/stacks/compute-missing-timed.invalid.json +32 -0
- package/spec/fixtures/stacks/dietary-no-signal.invalid.json +16 -0
- package/spec/fixtures/stacks/illustrated-empty.invalid.json +13 -0
- package/spec/fixtures/stacks/quantified-string.invalid.json +22 -0
- package/spec/fixtures/stacks/referenced-missing-input.invalid.json +32 -0
- package/spec/fixtures/stacks/storage-min.valid.json +20 -0
- package/spec/fixtures/stacks/storage-no-duration.invalid.json +16 -0
- package/spec/fixtures/stacks/timed-implies-structured.valid.json +50 -0
- package/spec/fixtures/stacks/timed-range.invalid.json +33 -0
- package/spec/fixtures/valid/equipment-scaling-rules.valid.json +76 -0
- package/spec/fixtures/valid/equipment-strings.valid.json +31 -0
- package/spec/fixtures/valid/equipment-structured-uses.valid.json +47 -0
- package/spec/fixtures/valid/mise-en-place-basic.valid.json +31 -0
- package/spec/fixtures/valid/mise-en-place-referenced-equipment.valid.json +51 -0
- package/spec/fixtures/valid/prep-ingredient-strings.valid.json +48 -0
- package/spec/fixtures/valid/prep-ingredient-structured.valid.json +45 -0
- package/spec/fixtures/valid/profile-equipped.valid.json +29 -0
- package/spec/fixtures/valid/profile-prepped.valid.json +32 -0
- package/spec/fixtures/valid/quantified-nested-ingredient-sections.valid.json +61 -0
- package/spec/fixtures/valid/referenced-scaling.valid.json +67 -0
- package/spec/fixtures/valid/storage-leftovers-simple.valid.json +27 -0
- package/spec/fixtures/valid/storage-leftovers-structured.valid.json +43 -0
- package/spec/fixtures/valid/structured-nested-step-sections.valid.json +84 -0
- package/spec/schemas/stacks-registry.schema.json +108 -0
- package/spec/soustack.schema.json +2379 -0
- package/spec/stacks/compute.schema.json +7 -0
- package/spec/stacks/compute@1.md +22 -0
- package/spec/stacks/dietary.schema.json +45 -0
- package/spec/stacks/dietary@1.md +24 -0
- package/spec/stacks/equipment.schema.json +98 -0
- package/spec/stacks/equipment@1.md +244 -0
- package/spec/stacks/illustrated.schema.json +54 -0
- package/spec/stacks/illustrated@1.md +24 -0
- package/spec/stacks/prep.schema.json +76 -0
- package/spec/stacks/prep@1.md +276 -0
- package/spec/stacks/quantified.schema.json +74 -0
- package/spec/stacks/quantified@1.md +24 -0
- package/spec/stacks/referenced.schema.json +96 -0
- package/spec/stacks/referenced@1.md +23 -0
- package/spec/stacks/registry.json +112 -0
- package/spec/stacks/scaling.schema.json +99 -0
- package/spec/stacks/scaling@1.md +238 -0
- package/spec/stacks/storage.schema.json +132 -0
- package/spec/stacks/storage@1.md +256 -0
- package/spec/stacks/structured.schema.json +48 -0
- package/spec/stacks/structured@1.md +24 -0
- package/spec/stacks/substitutions.schema.json +43 -0
- package/spec/stacks/substitutions@1.md +24 -0
- package/spec/stacks/techniques.schema.json +28 -0
- package/spec/stacks/techniques@1.md +23 -0
- package/spec/stacks/timed.schema.json +60 -0
- package/spec/stacks/timed@1.md +23 -0
- package/src/defs/common.schema.json +46 -0
- package/src/defs/duration.schema.json +33 -0
- package/src/defs/entities.schema.json +111 -0
- package/src/defs/ingredientQuantified.schema.json +9 -0
- package/src/defs/quantity.schema.json +16 -0
- package/src/defs/scalingRule.schema.json +127 -0
- package/src/defs/temperature.schema.json +63 -0
- package/src/profiles/base.schema.json +2 -2
- package/src/profiles/equipped.schema.json +10 -0
- package/src/profiles/illustrated.schema.json +4 -4
- package/src/profiles/lite.schema.json +10 -0
- package/src/profiles/prepped.schema.json +10 -0
- package/src/profiles/scalable.schema.json +6 -6
- package/src/profiles/timed.schema.json +10 -0
- package/src/schema.json +2271 -248
- package/src/schemas/stacks-registry.schema.json +108 -0
- package/src/soustack.schema.json +2271 -248
- package/src/stacks/compute.schema.json +7 -0
- package/src/stacks/compute@1.md +22 -0
- package/src/stacks/dietary.schema.json +45 -0
- package/src/stacks/dietary@1.md +24 -0
- package/src/stacks/equipment.schema.json +98 -0
- package/src/stacks/equipment@1.md +244 -0
- package/src/stacks/illustrated.schema.json +54 -0
- package/src/stacks/illustrated@1.md +24 -0
- package/src/stacks/prep.schema.json +76 -0
- package/src/stacks/prep@1.md +276 -0
- package/src/stacks/quantified.schema.json +74 -0
- package/src/stacks/quantified@1.md +24 -0
- package/src/stacks/referenced.schema.json +96 -0
- package/src/stacks/referenced@1.md +23 -0
- package/src/stacks/registry.json +112 -0
- package/src/stacks/scaling.schema.json +99 -0
- package/src/stacks/scaling@1.md +238 -0
- package/src/stacks/storage.schema.json +132 -0
- package/src/stacks/storage@1.md +256 -0
- package/src/stacks/structured.schema.json +48 -0
- package/src/stacks/structured@1.md +24 -0
- package/src/stacks/substitutions.schema.json +43 -0
- package/src/stacks/substitutions@1.md +24 -0
- package/src/stacks/techniques.schema.json +28 -0
- package/src/stacks/techniques@1.md +23 -0
- package/src/stacks/timed.schema.json +60 -0
- package/src/stacks/timed@1.md +23 -0
- package/src/profiles/cookable.schema.json +0 -18
- package/src/profiles/quantified.schema.json +0 -43
- package/src/profiles/schedulable.schema.json +0 -43
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Soustack Recipe Schema v0.
|
|
2
|
+
* Soustack Recipe Schema v0.0.2
|
|
3
3
|
* A portable, scalable, interoperable recipe format.
|
|
4
4
|
*/
|
|
5
5
|
interface SoustackRecipe {
|
|
@@ -7,20 +7,10 @@ interface SoustackRecipe {
|
|
|
7
7
|
'@type'?: 'Recipe';
|
|
8
8
|
/** Optional $schema pointer for profile-aware validation */
|
|
9
9
|
$schema?: string;
|
|
10
|
-
/** Optional declared validation profile */
|
|
11
|
-
profile?:
|
|
12
|
-
/** Recipe level: "lite" or "base" */
|
|
13
|
-
level?: "lite" | "base";
|
|
10
|
+
/** Optional declared validation profile (vNext only) */
|
|
11
|
+
profile?: "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed";
|
|
14
12
|
/** Stack declarations as a map: Record<stackName, versionNumber> */
|
|
15
13
|
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;
|
|
24
14
|
/** Unique identifier (slug or UUID) */
|
|
25
15
|
id?: string;
|
|
26
16
|
/** Optional display title */
|
|
@@ -37,6 +27,10 @@ interface SoustackRecipe {
|
|
|
37
27
|
/** Additional tags for filtering */
|
|
38
28
|
tags?: string[];
|
|
39
29
|
/** URL(s) to recipe image(s) */
|
|
30
|
+
images?: string[];
|
|
31
|
+
/** URL(s) to recipe video(s) */
|
|
32
|
+
videos?: string[];
|
|
33
|
+
/** Legacy image field preserved for compatibility */
|
|
40
34
|
image?: string | string[];
|
|
41
35
|
/** ISO 8601 date string */
|
|
42
36
|
dateAdded?: string;
|
|
@@ -74,19 +68,18 @@ interface ParsedYield {
|
|
|
74
68
|
description?: string;
|
|
75
69
|
}
|
|
76
70
|
/**
|
|
77
|
-
* Time
|
|
78
|
-
*
|
|
71
|
+
* Time uses DurationMinutes format (vNext).
|
|
72
|
+
* Required total field with minutes.
|
|
79
73
|
*/
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
cookTime?: string;
|
|
74
|
+
interface Time {
|
|
75
|
+
total: DurationMinutes;
|
|
76
|
+
metadata?: Record<string, unknown>;
|
|
77
|
+
[k: `x-${string}`]: unknown;
|
|
78
|
+
}
|
|
79
|
+
interface DurationMinutes {
|
|
80
|
+
minutes: number;
|
|
81
|
+
metadata?: Record<string, unknown>;
|
|
82
|
+
[k: `x-${string}`]: unknown;
|
|
90
83
|
}
|
|
91
84
|
interface Equipment {
|
|
92
85
|
id?: string;
|
|
@@ -96,99 +89,144 @@ interface Equipment {
|
|
|
96
89
|
capacity?: Quantity$1;
|
|
97
90
|
scalingLimit?: number;
|
|
98
91
|
alternatives?: string[];
|
|
92
|
+
count?: number;
|
|
93
|
+
countScaling?: EquipmentCountScaling;
|
|
94
|
+
upgrades?: EquipmentUpgradeRule[];
|
|
99
95
|
}
|
|
100
96
|
interface Quantity$1 {
|
|
101
97
|
amount: number;
|
|
102
98
|
/** Unit string (e.g. "g", "cup") or null for count-based items (e.g. "2 eggs") */
|
|
103
99
|
unit: string | null;
|
|
104
100
|
}
|
|
101
|
+
type EquipmentCountScaling = 'fixed' | 'linear' | EquipmentThresholdScaling;
|
|
102
|
+
interface EquipmentThresholdScaling {
|
|
103
|
+
mode: 'threshold';
|
|
104
|
+
steps: EquipmentThresholdStep[];
|
|
105
|
+
}
|
|
106
|
+
interface EquipmentThresholdStep {
|
|
107
|
+
maxFactor: number;
|
|
108
|
+
count: number;
|
|
109
|
+
}
|
|
110
|
+
interface EquipmentUpgradeRule {
|
|
111
|
+
minFactor: number;
|
|
112
|
+
use: string;
|
|
113
|
+
}
|
|
114
|
+
interface EquipmentUpgradeRecommendation {
|
|
115
|
+
fromId: string;
|
|
116
|
+
use: string;
|
|
117
|
+
minFactor: number;
|
|
118
|
+
}
|
|
119
|
+
interface ScalingMetadata {
|
|
120
|
+
multiplier: number;
|
|
121
|
+
equipment?: {
|
|
122
|
+
upgrades: EquipmentUpgradeRecommendation[];
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
type ScaledRecipe = Recipe & {
|
|
126
|
+
scaling?: ScalingMetadata;
|
|
127
|
+
};
|
|
105
128
|
type IngredientItem = string | Ingredient$1 | IngredientSubsection;
|
|
106
129
|
interface IngredientSubsection {
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
section: string;
|
|
131
|
+
ingredients: IngredientItem[];
|
|
109
132
|
}
|
|
110
133
|
interface Ingredient$1 {
|
|
111
134
|
id?: string;
|
|
112
|
-
/**
|
|
113
|
-
|
|
135
|
+
/** Ingredient name (required in vNext) */
|
|
136
|
+
name: string;
|
|
114
137
|
quantity?: Quantity$1;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/** Required prep state (e.g. "diced") */
|
|
118
|
-
prep?: string;
|
|
119
|
-
prepAction?: string;
|
|
120
|
-
prepTime?: number;
|
|
138
|
+
/** Required prep state (e.g. "diced") or array of prep items */
|
|
139
|
+
prep?: string | string[];
|
|
121
140
|
/** ID of equipment where this ingredient goes */
|
|
122
141
|
destination?: string;
|
|
123
142
|
scaling?: Scaling;
|
|
124
|
-
critical?: boolean;
|
|
125
143
|
optional?: boolean;
|
|
126
144
|
notes?: string;
|
|
145
|
+
temperature?: Temperature;
|
|
146
|
+
metadata?: Record<string, unknown>;
|
|
147
|
+
[k: `x-${string}`]: unknown;
|
|
127
148
|
}
|
|
128
149
|
interface ParsedIngredient {
|
|
129
|
-
|
|
150
|
+
name: string;
|
|
130
151
|
quantity?: {
|
|
131
152
|
amount: number | null;
|
|
132
153
|
unit: string | null;
|
|
133
154
|
};
|
|
134
|
-
name?: string;
|
|
135
155
|
prep?: string;
|
|
136
156
|
optional?: boolean;
|
|
137
157
|
notes?: string;
|
|
138
158
|
scaling?: Scaling;
|
|
139
159
|
}
|
|
140
160
|
/**
|
|
141
|
-
* Intelligent Scaling Logic
|
|
161
|
+
* Intelligent Scaling Logic (vNext format)
|
|
142
162
|
* Defines how an ingredient behaves when the recipe yield changes.
|
|
143
163
|
*/
|
|
144
|
-
type Scaling = ScalingLinear | ScalingDiscrete | ScalingProportional | ScalingFixed | ScalingBakersPercentage;
|
|
164
|
+
type Scaling = ScalingLinear | ScalingDiscrete | ScalingProportional | ScalingFixed | ScalingToTaste | ScalingBakersPercentage;
|
|
145
165
|
interface ScalingBase {
|
|
146
166
|
min?: number;
|
|
147
167
|
max?: number;
|
|
148
168
|
}
|
|
149
169
|
interface ScalingLinear extends ScalingBase {
|
|
150
|
-
|
|
170
|
+
mode: "linear";
|
|
151
171
|
}
|
|
152
172
|
interface ScalingDiscrete extends ScalingBase {
|
|
153
|
-
|
|
154
|
-
|
|
173
|
+
mode: "discrete";
|
|
174
|
+
step?: number;
|
|
175
|
+
rounding?: "nearest" | "ceil" | "floor";
|
|
155
176
|
}
|
|
156
177
|
interface ScalingProportional extends ScalingBase {
|
|
157
|
-
|
|
178
|
+
mode: "proportional";
|
|
158
179
|
factor?: number;
|
|
159
180
|
}
|
|
160
181
|
interface ScalingFixed extends ScalingBase {
|
|
161
|
-
|
|
182
|
+
mode: "fixed";
|
|
162
183
|
}
|
|
163
|
-
interface
|
|
164
|
-
|
|
184
|
+
interface ScalingToTaste {
|
|
185
|
+
mode: "toTaste";
|
|
186
|
+
}
|
|
187
|
+
interface ScalingBakersPercentage {
|
|
188
|
+
mode: "bakersPercent";
|
|
189
|
+
/** The percentage relative to the reference (e.g. 2 for 2%) */
|
|
190
|
+
percent: number;
|
|
165
191
|
/** The ID of the flour/base ingredient this is relative to */
|
|
166
|
-
|
|
167
|
-
/** The percentage relative to the reference (e.g. 0.02 for 2%) */
|
|
168
|
-
factor?: number;
|
|
192
|
+
of: string;
|
|
169
193
|
}
|
|
170
194
|
type InstructionItem = string | Instruction | InstructionSubsection;
|
|
171
195
|
interface InstructionSubsection {
|
|
172
|
-
|
|
173
|
-
|
|
196
|
+
section: string;
|
|
197
|
+
steps: InstructionItem[];
|
|
174
198
|
}
|
|
175
199
|
interface SoustackInstruction {
|
|
176
200
|
id?: string;
|
|
177
201
|
text: string;
|
|
178
|
-
destination?: string;
|
|
179
202
|
/** IDs of steps that must complete before this one starts */
|
|
180
203
|
dependsOn?: string[];
|
|
181
204
|
/** IDs of ingredients used in this step */
|
|
182
205
|
inputs?: string[];
|
|
206
|
+
/** IDs of techniques used in this step */
|
|
207
|
+
techniqueIds?: string[];
|
|
208
|
+
/** IDs of equipment used in this step */
|
|
209
|
+
usesEquipment?: string[];
|
|
183
210
|
timing?: StepTiming;
|
|
184
|
-
|
|
185
|
-
|
|
211
|
+
temperature?: Temperature;
|
|
212
|
+
images?: string[];
|
|
213
|
+
videos?: string[];
|
|
214
|
+
metadata?: Record<string, unknown>;
|
|
215
|
+
[k: `x-${string}`]: unknown;
|
|
186
216
|
}
|
|
187
217
|
type Instruction = SoustackInstruction;
|
|
188
218
|
interface StepTiming {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
219
|
+
activity: "active" | "passive";
|
|
220
|
+
duration?: DurationMinutes | DurationRange;
|
|
221
|
+
completionCue?: string;
|
|
222
|
+
metadata?: Record<string, unknown>;
|
|
223
|
+
[k: `x-${string}`]: unknown;
|
|
224
|
+
}
|
|
225
|
+
interface DurationRange {
|
|
226
|
+
minMinutes: number;
|
|
227
|
+
maxMinutes: number;
|
|
228
|
+
metadata?: Record<string, unknown>;
|
|
229
|
+
[k: `x-${string}`]: unknown;
|
|
192
230
|
}
|
|
193
231
|
interface Storage {
|
|
194
232
|
roomTemp?: StorageMethod;
|
|
@@ -227,24 +265,11 @@ interface NutritionFacts {
|
|
|
227
265
|
calories?: number;
|
|
228
266
|
protein_g?: number;
|
|
229
267
|
}
|
|
230
|
-
interface
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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;
|
|
268
|
+
interface Temperature {
|
|
269
|
+
value: number;
|
|
270
|
+
unit: "celsius" | "fahrenheit";
|
|
271
|
+
metadata?: Record<string, unknown>;
|
|
272
|
+
[k: `x-${string}`]: unknown;
|
|
248
273
|
}
|
|
249
274
|
|
|
250
275
|
interface ScaleRecipeOptions {
|
|
@@ -264,7 +289,7 @@ interface ConformanceIssue {
|
|
|
264
289
|
severity: ConformanceSeverity;
|
|
265
290
|
}
|
|
266
291
|
|
|
267
|
-
type ProfileName = "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed"
|
|
292
|
+
type ProfileName = "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed";
|
|
268
293
|
interface NormalizedError {
|
|
269
294
|
path: string;
|
|
270
295
|
message: string;
|
|
@@ -350,6 +375,7 @@ interface SchemaOrgRecipe$1 {
|
|
|
350
375
|
nutrition?: NutritionInformation;
|
|
351
376
|
video?: SchemaOrgImage;
|
|
352
377
|
'@graph'?: unknown;
|
|
378
|
+
$schema?: string;
|
|
353
379
|
}
|
|
354
380
|
type SchemaOrgIngredientList = string | string[];
|
|
355
381
|
type SchemaOrgInstructionList = string | HowToStep$1 | HowToSection | Array<string | HowToStep$1 | HowToSection>;
|
|
@@ -397,7 +423,7 @@ interface NutritionInformation {
|
|
|
397
423
|
/**
|
|
398
424
|
* Convert a Soustack recipe to Schema.org JSON-LD format.
|
|
399
425
|
*
|
|
400
|
-
* BREAKING CHANGE in v0.
|
|
426
|
+
* BREAKING CHANGE in v0.0.2: This function now targets the "minimal" profile
|
|
401
427
|
* and only includes stacks that are schemaOrgMappable (as defined in the
|
|
402
428
|
* stacks registry). Non-mappable stacks (e.g., nutrition@1, schedule@1)
|
|
403
429
|
* are excluded from the conversion.
|
|
@@ -429,7 +455,7 @@ interface SchemaOrgRecipe {
|
|
|
429
455
|
|
|
430
456
|
declare function extractSchemaOrgRecipeFromHTML(html: string): SchemaOrgRecipe | null;
|
|
431
457
|
|
|
432
|
-
declare const SOUSTACK_SPEC_VERSION = "0.
|
|
458
|
+
declare const SOUSTACK_SPEC_VERSION = "0.0.2";
|
|
433
459
|
|
|
434
460
|
type ConvertTarget = 'metric';
|
|
435
461
|
type ConvertMode = 'volume' | 'mass';
|
|
@@ -494,4 +520,4 @@ interface MiseEnPlacePlan {
|
|
|
494
520
|
}
|
|
495
521
|
declare function miseEnPlace(ingredients: Ingredient[]): MiseEnPlacePlan;
|
|
496
522
|
|
|
497
|
-
export { type Alternative, type
|
|
523
|
+
export { type Alternative, type ConvertMode, type ConvertTarget, type ConvertedLineItem, type DurationMinutes, type DurationRange, type Equipment, type EquipmentCountScaling, type EquipmentThresholdScaling, type EquipmentThresholdStep, type EquipmentUpgradeRecommendation, type EquipmentUpgradeRule, type FrozenStorageMethod, type Ingredient$1 as Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type LineItem, type MakeAheadComponent, 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 ScaledRecipe, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingMetadata, type ScalingProportional, type ScalingToTaste, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type Substitution, type Temperature, type Time, UnknownUnitError, UnsupportedConversionError, type ValidateMode, type ValidateResult, type Yield, convertLineItemToMetric, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, miseEnPlace, normalizeRecipe, scaleRecipe, toSchemaOrg, validateRecipe };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Soustack Recipe Schema v0.
|
|
2
|
+
* Soustack Recipe Schema v0.0.2
|
|
3
3
|
* A portable, scalable, interoperable recipe format.
|
|
4
4
|
*/
|
|
5
5
|
interface SoustackRecipe {
|
|
@@ -7,20 +7,10 @@ interface SoustackRecipe {
|
|
|
7
7
|
'@type'?: 'Recipe';
|
|
8
8
|
/** Optional $schema pointer for profile-aware validation */
|
|
9
9
|
$schema?: string;
|
|
10
|
-
/** Optional declared validation profile */
|
|
11
|
-
profile?:
|
|
12
|
-
/** Recipe level: "lite" or "base" */
|
|
13
|
-
level?: "lite" | "base";
|
|
10
|
+
/** Optional declared validation profile (vNext only) */
|
|
11
|
+
profile?: "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed";
|
|
14
12
|
/** Stack declarations as a map: Record<stackName, versionNumber> */
|
|
15
13
|
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;
|
|
24
14
|
/** Unique identifier (slug or UUID) */
|
|
25
15
|
id?: string;
|
|
26
16
|
/** Optional display title */
|
|
@@ -37,6 +27,10 @@ interface SoustackRecipe {
|
|
|
37
27
|
/** Additional tags for filtering */
|
|
38
28
|
tags?: string[];
|
|
39
29
|
/** URL(s) to recipe image(s) */
|
|
30
|
+
images?: string[];
|
|
31
|
+
/** URL(s) to recipe video(s) */
|
|
32
|
+
videos?: string[];
|
|
33
|
+
/** Legacy image field preserved for compatibility */
|
|
40
34
|
image?: string | string[];
|
|
41
35
|
/** ISO 8601 date string */
|
|
42
36
|
dateAdded?: string;
|
|
@@ -74,19 +68,18 @@ interface ParsedYield {
|
|
|
74
68
|
description?: string;
|
|
75
69
|
}
|
|
76
70
|
/**
|
|
77
|
-
* Time
|
|
78
|
-
*
|
|
71
|
+
* Time uses DurationMinutes format (vNext).
|
|
72
|
+
* Required total field with minutes.
|
|
79
73
|
*/
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
cookTime?: string;
|
|
74
|
+
interface Time {
|
|
75
|
+
total: DurationMinutes;
|
|
76
|
+
metadata?: Record<string, unknown>;
|
|
77
|
+
[k: `x-${string}`]: unknown;
|
|
78
|
+
}
|
|
79
|
+
interface DurationMinutes {
|
|
80
|
+
minutes: number;
|
|
81
|
+
metadata?: Record<string, unknown>;
|
|
82
|
+
[k: `x-${string}`]: unknown;
|
|
90
83
|
}
|
|
91
84
|
interface Equipment {
|
|
92
85
|
id?: string;
|
|
@@ -96,99 +89,144 @@ interface Equipment {
|
|
|
96
89
|
capacity?: Quantity$1;
|
|
97
90
|
scalingLimit?: number;
|
|
98
91
|
alternatives?: string[];
|
|
92
|
+
count?: number;
|
|
93
|
+
countScaling?: EquipmentCountScaling;
|
|
94
|
+
upgrades?: EquipmentUpgradeRule[];
|
|
99
95
|
}
|
|
100
96
|
interface Quantity$1 {
|
|
101
97
|
amount: number;
|
|
102
98
|
/** Unit string (e.g. "g", "cup") or null for count-based items (e.g. "2 eggs") */
|
|
103
99
|
unit: string | null;
|
|
104
100
|
}
|
|
101
|
+
type EquipmentCountScaling = 'fixed' | 'linear' | EquipmentThresholdScaling;
|
|
102
|
+
interface EquipmentThresholdScaling {
|
|
103
|
+
mode: 'threshold';
|
|
104
|
+
steps: EquipmentThresholdStep[];
|
|
105
|
+
}
|
|
106
|
+
interface EquipmentThresholdStep {
|
|
107
|
+
maxFactor: number;
|
|
108
|
+
count: number;
|
|
109
|
+
}
|
|
110
|
+
interface EquipmentUpgradeRule {
|
|
111
|
+
minFactor: number;
|
|
112
|
+
use: string;
|
|
113
|
+
}
|
|
114
|
+
interface EquipmentUpgradeRecommendation {
|
|
115
|
+
fromId: string;
|
|
116
|
+
use: string;
|
|
117
|
+
minFactor: number;
|
|
118
|
+
}
|
|
119
|
+
interface ScalingMetadata {
|
|
120
|
+
multiplier: number;
|
|
121
|
+
equipment?: {
|
|
122
|
+
upgrades: EquipmentUpgradeRecommendation[];
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
type ScaledRecipe = Recipe & {
|
|
126
|
+
scaling?: ScalingMetadata;
|
|
127
|
+
};
|
|
105
128
|
type IngredientItem = string | Ingredient$1 | IngredientSubsection;
|
|
106
129
|
interface IngredientSubsection {
|
|
107
|
-
|
|
108
|
-
|
|
130
|
+
section: string;
|
|
131
|
+
ingredients: IngredientItem[];
|
|
109
132
|
}
|
|
110
133
|
interface Ingredient$1 {
|
|
111
134
|
id?: string;
|
|
112
|
-
/**
|
|
113
|
-
|
|
135
|
+
/** Ingredient name (required in vNext) */
|
|
136
|
+
name: string;
|
|
114
137
|
quantity?: Quantity$1;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/** Required prep state (e.g. "diced") */
|
|
118
|
-
prep?: string;
|
|
119
|
-
prepAction?: string;
|
|
120
|
-
prepTime?: number;
|
|
138
|
+
/** Required prep state (e.g. "diced") or array of prep items */
|
|
139
|
+
prep?: string | string[];
|
|
121
140
|
/** ID of equipment where this ingredient goes */
|
|
122
141
|
destination?: string;
|
|
123
142
|
scaling?: Scaling;
|
|
124
|
-
critical?: boolean;
|
|
125
143
|
optional?: boolean;
|
|
126
144
|
notes?: string;
|
|
145
|
+
temperature?: Temperature;
|
|
146
|
+
metadata?: Record<string, unknown>;
|
|
147
|
+
[k: `x-${string}`]: unknown;
|
|
127
148
|
}
|
|
128
149
|
interface ParsedIngredient {
|
|
129
|
-
|
|
150
|
+
name: string;
|
|
130
151
|
quantity?: {
|
|
131
152
|
amount: number | null;
|
|
132
153
|
unit: string | null;
|
|
133
154
|
};
|
|
134
|
-
name?: string;
|
|
135
155
|
prep?: string;
|
|
136
156
|
optional?: boolean;
|
|
137
157
|
notes?: string;
|
|
138
158
|
scaling?: Scaling;
|
|
139
159
|
}
|
|
140
160
|
/**
|
|
141
|
-
* Intelligent Scaling Logic
|
|
161
|
+
* Intelligent Scaling Logic (vNext format)
|
|
142
162
|
* Defines how an ingredient behaves when the recipe yield changes.
|
|
143
163
|
*/
|
|
144
|
-
type Scaling = ScalingLinear | ScalingDiscrete | ScalingProportional | ScalingFixed | ScalingBakersPercentage;
|
|
164
|
+
type Scaling = ScalingLinear | ScalingDiscrete | ScalingProportional | ScalingFixed | ScalingToTaste | ScalingBakersPercentage;
|
|
145
165
|
interface ScalingBase {
|
|
146
166
|
min?: number;
|
|
147
167
|
max?: number;
|
|
148
168
|
}
|
|
149
169
|
interface ScalingLinear extends ScalingBase {
|
|
150
|
-
|
|
170
|
+
mode: "linear";
|
|
151
171
|
}
|
|
152
172
|
interface ScalingDiscrete extends ScalingBase {
|
|
153
|
-
|
|
154
|
-
|
|
173
|
+
mode: "discrete";
|
|
174
|
+
step?: number;
|
|
175
|
+
rounding?: "nearest" | "ceil" | "floor";
|
|
155
176
|
}
|
|
156
177
|
interface ScalingProportional extends ScalingBase {
|
|
157
|
-
|
|
178
|
+
mode: "proportional";
|
|
158
179
|
factor?: number;
|
|
159
180
|
}
|
|
160
181
|
interface ScalingFixed extends ScalingBase {
|
|
161
|
-
|
|
182
|
+
mode: "fixed";
|
|
162
183
|
}
|
|
163
|
-
interface
|
|
164
|
-
|
|
184
|
+
interface ScalingToTaste {
|
|
185
|
+
mode: "toTaste";
|
|
186
|
+
}
|
|
187
|
+
interface ScalingBakersPercentage {
|
|
188
|
+
mode: "bakersPercent";
|
|
189
|
+
/** The percentage relative to the reference (e.g. 2 for 2%) */
|
|
190
|
+
percent: number;
|
|
165
191
|
/** The ID of the flour/base ingredient this is relative to */
|
|
166
|
-
|
|
167
|
-
/** The percentage relative to the reference (e.g. 0.02 for 2%) */
|
|
168
|
-
factor?: number;
|
|
192
|
+
of: string;
|
|
169
193
|
}
|
|
170
194
|
type InstructionItem = string | Instruction | InstructionSubsection;
|
|
171
195
|
interface InstructionSubsection {
|
|
172
|
-
|
|
173
|
-
|
|
196
|
+
section: string;
|
|
197
|
+
steps: InstructionItem[];
|
|
174
198
|
}
|
|
175
199
|
interface SoustackInstruction {
|
|
176
200
|
id?: string;
|
|
177
201
|
text: string;
|
|
178
|
-
destination?: string;
|
|
179
202
|
/** IDs of steps that must complete before this one starts */
|
|
180
203
|
dependsOn?: string[];
|
|
181
204
|
/** IDs of ingredients used in this step */
|
|
182
205
|
inputs?: string[];
|
|
206
|
+
/** IDs of techniques used in this step */
|
|
207
|
+
techniqueIds?: string[];
|
|
208
|
+
/** IDs of equipment used in this step */
|
|
209
|
+
usesEquipment?: string[];
|
|
183
210
|
timing?: StepTiming;
|
|
184
|
-
|
|
185
|
-
|
|
211
|
+
temperature?: Temperature;
|
|
212
|
+
images?: string[];
|
|
213
|
+
videos?: string[];
|
|
214
|
+
metadata?: Record<string, unknown>;
|
|
215
|
+
[k: `x-${string}`]: unknown;
|
|
186
216
|
}
|
|
187
217
|
type Instruction = SoustackInstruction;
|
|
188
218
|
interface StepTiming {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
219
|
+
activity: "active" | "passive";
|
|
220
|
+
duration?: DurationMinutes | DurationRange;
|
|
221
|
+
completionCue?: string;
|
|
222
|
+
metadata?: Record<string, unknown>;
|
|
223
|
+
[k: `x-${string}`]: unknown;
|
|
224
|
+
}
|
|
225
|
+
interface DurationRange {
|
|
226
|
+
minMinutes: number;
|
|
227
|
+
maxMinutes: number;
|
|
228
|
+
metadata?: Record<string, unknown>;
|
|
229
|
+
[k: `x-${string}`]: unknown;
|
|
192
230
|
}
|
|
193
231
|
interface Storage {
|
|
194
232
|
roomTemp?: StorageMethod;
|
|
@@ -227,24 +265,11 @@ interface NutritionFacts {
|
|
|
227
265
|
calories?: number;
|
|
228
266
|
protein_g?: number;
|
|
229
267
|
}
|
|
230
|
-
interface
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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;
|
|
268
|
+
interface Temperature {
|
|
269
|
+
value: number;
|
|
270
|
+
unit: "celsius" | "fahrenheit";
|
|
271
|
+
metadata?: Record<string, unknown>;
|
|
272
|
+
[k: `x-${string}`]: unknown;
|
|
248
273
|
}
|
|
249
274
|
|
|
250
275
|
interface ScaleRecipeOptions {
|
|
@@ -264,7 +289,7 @@ interface ConformanceIssue {
|
|
|
264
289
|
severity: ConformanceSeverity;
|
|
265
290
|
}
|
|
266
291
|
|
|
267
|
-
type ProfileName = "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed"
|
|
292
|
+
type ProfileName = "base" | "equipped" | "illustrated" | "lite" | "prepped" | "scalable" | "timed";
|
|
268
293
|
interface NormalizedError {
|
|
269
294
|
path: string;
|
|
270
295
|
message: string;
|
|
@@ -350,6 +375,7 @@ interface SchemaOrgRecipe$1 {
|
|
|
350
375
|
nutrition?: NutritionInformation;
|
|
351
376
|
video?: SchemaOrgImage;
|
|
352
377
|
'@graph'?: unknown;
|
|
378
|
+
$schema?: string;
|
|
353
379
|
}
|
|
354
380
|
type SchemaOrgIngredientList = string | string[];
|
|
355
381
|
type SchemaOrgInstructionList = string | HowToStep$1 | HowToSection | Array<string | HowToStep$1 | HowToSection>;
|
|
@@ -397,7 +423,7 @@ interface NutritionInformation {
|
|
|
397
423
|
/**
|
|
398
424
|
* Convert a Soustack recipe to Schema.org JSON-LD format.
|
|
399
425
|
*
|
|
400
|
-
* BREAKING CHANGE in v0.
|
|
426
|
+
* BREAKING CHANGE in v0.0.2: This function now targets the "minimal" profile
|
|
401
427
|
* and only includes stacks that are schemaOrgMappable (as defined in the
|
|
402
428
|
* stacks registry). Non-mappable stacks (e.g., nutrition@1, schedule@1)
|
|
403
429
|
* are excluded from the conversion.
|
|
@@ -429,7 +455,7 @@ interface SchemaOrgRecipe {
|
|
|
429
455
|
|
|
430
456
|
declare function extractSchemaOrgRecipeFromHTML(html: string): SchemaOrgRecipe | null;
|
|
431
457
|
|
|
432
|
-
declare const SOUSTACK_SPEC_VERSION = "0.
|
|
458
|
+
declare const SOUSTACK_SPEC_VERSION = "0.0.2";
|
|
433
459
|
|
|
434
460
|
type ConvertTarget = 'metric';
|
|
435
461
|
type ConvertMode = 'volume' | 'mass';
|
|
@@ -494,4 +520,4 @@ interface MiseEnPlacePlan {
|
|
|
494
520
|
}
|
|
495
521
|
declare function miseEnPlace(ingredients: Ingredient[]): MiseEnPlacePlan;
|
|
496
522
|
|
|
497
|
-
export { type Alternative, type
|
|
523
|
+
export { type Alternative, type ConvertMode, type ConvertTarget, type ConvertedLineItem, type DurationMinutes, type DurationRange, type Equipment, type EquipmentCountScaling, type EquipmentThresholdScaling, type EquipmentThresholdStep, type EquipmentUpgradeRecommendation, type EquipmentUpgradeRule, type FrozenStorageMethod, type Ingredient$1 as Ingredient, type IngredientItem, type IngredientSubsection, type Instruction, type InstructionItem, type InstructionSubsection, type LineItem, type MakeAheadComponent, 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 ScaledRecipe, type Scaling, type ScalingBakersPercentage, type ScalingBase, type ScalingDiscrete, type ScalingFixed, type ScalingLinear, type ScalingMetadata, type ScalingProportional, type ScalingToTaste, type Source, type SoustackInstruction, type SoustackRecipe, type StepTiming, type Storage, type StorageMethod, type Substitution, type Temperature, type Time, UnknownUnitError, UnsupportedConversionError, type ValidateMode, type ValidateResult, type Yield, convertLineItemToMetric, detectProfiles, extractSchemaOrgRecipeFromHTML, fromSchemaOrg, miseEnPlace, normalizeRecipe, scaleRecipe, toSchemaOrg, validateRecipe };
|