@@ -5,12 +5,218 @@ import {
55 RecipeTime ,
66 Servings , Section , Ingredient , Cookware , Timer , Quantity , ScaledRecipeWithReport , GroupedQuantity ,
77 ingredient_should_be_listed , ingredient_display_name , grouped_quantity_is_empty , grouped_quantity_display ,
8- cookware_should_be_listed , cookware_display_name , Content , Step , quantity_display , GroupedIndexAndQuantity
8+ cookware_should_be_listed , cookware_display_name , Content , Step , quantity_display , GroupedIndexAndQuantity ,
9+ Value , Item
910} from "./pkg/cooklang_wasm.js" ;
1011
11- export { version , Parser } ;
12- export type { ScaledRecipeWithReport } from "./pkg/cooklang_wasm.js" ;
12+ export {
13+ version ,
14+ Parser ,
15+ ingredient_should_be_listed ,
16+ ingredient_display_name ,
17+ grouped_quantity_is_empty ,
18+ grouped_quantity_display ,
19+ cookware_should_be_listed ,
20+ cookware_display_name ,
21+ quantity_display
22+ } ;
23+ export type { ScaledRecipeWithReport , Value , Quantity , Ingredient , Cookware , Timer , Section , Content , Step , Item } from "./pkg/cooklang_wasm.js" ;
24+
25+ // ============================================================================
26+ // Numeric Value Extraction Helpers
27+ // ============================================================================
28+
29+ /**
30+ * Extract a numeric value from a WASM Value type.
31+ *
32+ * For ranges, returns the start value.
33+ * For text values, returns null.
34+ *
35+ * @param value - The Value to extract from
36+ * @returns The numeric value or null if not a number/range
37+ *
38+ * @example
39+ * ```typescript
40+ * const value = ingredient.quantity?.value;
41+ * const numeric = getNumericValue(value); // 2.5
42+ * ```
43+ */
44+ export function getNumericValue ( value : Value | null | undefined ) : number | null {
45+ if ( ! value ) {
46+ return null ;
47+ }
48+
49+ if ( value . type === 'number' ) {
50+ // WASM returns nested structure: { type: "number", value: { type: "regular", value: 3 } }
51+ // The type definitions are incomplete, so we need to cast
52+ const numValue = value . value as any ;
53+ return Number ( numValue . value ) ;
54+ } else if ( value . type === 'range' ) {
55+ // Range structure: { type: "range", value: { start: { type: "regular", value: X }, end: { ... } } }
56+ // Return start of range
57+ const rangeValue = value . value as any ;
58+ return Number ( rangeValue . start . value ) ;
59+ }
60+ return null ;
61+ }
62+
63+ /**
64+ * Extract the numeric value from a Quantity.
65+ *
66+ * Convenience wrapper around getNumericValue for Quantity objects.
67+ *
68+ * @param quantity - The Quantity to extract from
69+ * @returns The numeric value or null
70+ *
71+ * @example
72+ * ```typescript
73+ * const qty = getQuantityValue(ingredient.quantity); // 2.5
74+ * ```
75+ */
76+ export function getQuantityValue ( quantity : Quantity | null | undefined ) : number | null {
77+ return quantity ? getNumericValue ( quantity . value ) : null ;
78+ }
79+
80+ /**
81+ * Extract the unit string from a Quantity.
82+ *
83+ * @param quantity - The Quantity to extract from
84+ * @returns The unit string or null if no unit
85+ *
86+ * @example
87+ * ```typescript
88+ * const unit = getQuantityUnit(ingredient.quantity); // "cups"
89+ * ```
90+ */
91+ export function getQuantityUnit ( quantity : Quantity | null | undefined ) : string | null {
92+ return quantity ?. unit ?? null ;
93+ }
94+
95+ // ============================================================================
96+ // Flat List Helpers
97+ // ============================================================================
98+
99+ /**
100+ * Simple ingredient with display-ready values.
101+ * For more control, use recipe.groupedIngredients with the display functions.
102+ */
103+ export interface FlatIngredient {
104+ /** Display name of the ingredient */
105+ name : string ;
106+ /** Numeric quantity (start of range if range), or null if none */
107+ quantity : number | null ;
108+ /** Unit string, or null if none */
109+ unit : string | null ;
110+ /** Formatted display text for the quantity (e.g., "1-2 cups", "3/4 tsp") */
111+ displayText : string | null ;
112+ /** Optional note/modifier (e.g., "finely chopped") */
113+ note : string | null ;
114+ }
115+
116+ /**
117+ * Simple cookware with display-ready values.
118+ */
119+ export interface FlatCookware {
120+ /** Display name of the cookware */
121+ name : string ;
122+ /** Numeric quantity, or null if none */
123+ quantity : number | null ;
124+ /** Formatted display text for the quantity */
125+ displayText : string | null ;
126+ /** Optional note/modifier */
127+ note : string | null ;
128+ }
129+
130+ /**
131+ * Simple timer with display-ready values.
132+ */
133+ export interface FlatTimer {
134+ /** Optional timer name */
135+ name : string | null ;
136+ /** Numeric quantity (in seconds after unit conversion), or null if none */
137+ quantity : number | null ;
138+ /** Unit string (e.g., "minutes", "hours"), or null if none */
139+ unit : string | null ;
140+ /** Formatted display text for the quantity */
141+ displayText : string | null ;
142+ }
143+
144+ /**
145+ * Get a flat list of all ingredients with simple, display-ready values.
146+ *
147+ * This is a convenience function for simple use cases. For more control over
148+ * grouping and display, use recipe.groupedIngredients with the display functions.
149+ *
150+ * @param recipe - The parsed recipe
151+ * @returns Array of flat ingredient objects
152+ *
153+ * @example
154+ * ```typescript
155+ * const ingredients = getFlatIngredients(recipe);
156+ * ingredients.forEach(ing => {
157+ * console.log(`${ing.displayText || ''} ${ing.name}`);
158+ * });
159+ * ```
160+ */
161+ export function getFlatIngredients ( recipe : CooklangRecipe ) : FlatIngredient [ ] {
162+ return recipe . ingredients . map ( ing => ( {
163+ name : ingredient_display_name ( ing ) ,
164+ quantity : getQuantityValue ( ing . quantity ) ,
165+ unit : getQuantityUnit ( ing . quantity ) ,
166+ displayText : ing . quantity ? quantity_display ( ing . quantity ) : null ,
167+ note : ing . note
168+ } ) ) ;
169+ }
170+
171+ /**
172+ * Get a flat list of all cookware with simple, display-ready values.
173+ *
174+ * @param recipe - The parsed recipe
175+ * @returns Array of flat cookware objects
176+ *
177+ * @example
178+ * ```typescript
179+ * const cookware = getFlatCookware(recipe);
180+ * cookware.forEach(cw => {
181+ * console.log(`${cw.displayText || ''} ${cw.name}`);
182+ * });
183+ * ```
184+ */
185+ export function getFlatCookware ( recipe : CooklangRecipe ) : FlatCookware [ ] {
186+ return recipe . cookware . map ( cw => ( {
187+ name : cookware_display_name ( cw ) ,
188+ quantity : getQuantityValue ( cw . quantity ) ,
189+ displayText : cw . quantity ? quantity_display ( cw . quantity ) : null ,
190+ note : cw . note
191+ } ) ) ;
192+ }
193+
194+ /**
195+ * Get a flat list of all timers from the recipe.
196+ *
197+ * @param recipe - The parsed recipe
198+ * @returns Array of flat timer objects
199+ *
200+ * @example
201+ * ```typescript
202+ * const timers = getFlatTimers(recipe);
203+ * timers.forEach(timer => {
204+ * console.log(`${timer.name}: ${timer.displayText}`);
205+ * });
206+ * ```
207+ */
208+ export function getFlatTimers ( recipe : CooklangRecipe ) : FlatTimer [ ] {
209+ return recipe . timers . map ( tm => ( {
210+ name : tm . name ,
211+ quantity : getQuantityValue ( tm . quantity ) ,
212+ unit : getQuantityUnit ( tm . quantity ) ,
213+ displayText : tm . quantity ? quantity_display ( tm . quantity ) : null
214+ } ) ) ;
215+ }
13216
217+ // ============================================================================
218+ // Recipe and Parser Classes
219+ // ============================================================================
14220
15221export class CooklangRecipe {
16222 // Metadata
0 commit comments