@tmlmt/cooklang-parser 2.1.7 → 3.0.0-alpha.4
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 +1 -1
- package/dist/index.cjs +1922 -378
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +664 -68
- package/dist/index.d.ts +664 -68
- package/dist/index.js +1918 -378
- package/dist/index.js.map +1 -1
- package/package.json +11 -7
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/classes/category_config.ts","../src/classes/section.ts","../node_modules/.pnpm/human-regex@2.1.5_patch_hash=6d6bd9e233f99785a7c2187fd464edc114b76d47001dbb4eb6b5d72168de7460/node_modules/human-regex/src/human-regex.ts","../src/regex.ts","../src/units.ts","../src/errors.ts","../src/parser_helpers.ts","../src/classes/recipe.ts","../src/classes/shopping_list.ts"],"sourcesContent":["import { CategoryConfig } from \"./classes/category_config\";\nimport { Recipe } from \"./classes/recipe\";\nimport { ShoppingList } from \"./classes/shopping_list\";\nimport { Section } from \"./classes/section\";\n\nimport type {\n Metadata,\n Ingredient,\n IngredientFlag,\n IngredientExtras,\n FixedValue,\n Range,\n DecimalValue,\n FractionValue,\n TextValue,\n Timer,\n TextItem,\n IngredientItem,\n CookwareItem,\n TimerItem,\n Item,\n Step,\n Note,\n Cookware,\n CookwareFlag,\n CategorizedIngredients,\n AddedRecipe,\n RecipeWithFactor,\n RecipeWithServings,\n CategoryIngredient,\n Category,\n QuantityPart,\n} from \"./types\";\n\nexport {\n Recipe,\n ShoppingList,\n CategoryConfig,\n Metadata,\n Ingredient,\n IngredientFlag,\n IngredientExtras,\n FixedValue,\n Range,\n DecimalValue,\n FractionValue,\n TextValue,\n Timer,\n TextItem,\n IngredientItem,\n CookwareItem,\n TimerItem,\n Item,\n Step,\n Note,\n Cookware,\n CookwareFlag,\n CategorizedIngredients,\n AddedRecipe,\n RecipeWithFactor,\n RecipeWithServings,\n CategoryIngredient,\n Category,\n Section,\n QuantityPart,\n};\n","import type { Category, CategoryIngredient } from \"../types\";\n\n/**\n * Parser for category configurations specified à-la-cooklang.\n *\n * ## Usage\n *\n * You can either directly provide the category configuration string when creating the instance\n * e.g. `const categoryConfig = new CategoryConfig(<...>)`, or create it first and then pass\n * the category configuration string to the {@link CategoryConfig.parse | parse()} method.\n *\n * The initialized `CategoryConfig` can then be fed to a {@link ShoppingList}\n *\n * @example\n * ```typescript\n * import { CategoryConfig } from @tmlmt/cooklang-parser;\n *\n * const categoryConfigString = `\n * [Dairy]\n * milk\n * butter\n *\n * [Bakery]\n * flour\n * sugar`;\n *\n * const categoryConfig = new CategoryConfig(categoryConfigString);\n * ```\n *\n * @see [Category Configuration](https://cooklang.org/docs/spec/#shopping-lists) section of the cooklang specs\n *\n * @category Classes\n */\nexport class CategoryConfig {\n /**\n * The parsed categories of ingredients.\n */\n categories: Category[] = [];\n\n /**\n * Creates a new CategoryConfig instance.\n * @param config - The category configuration to parse.\n */\n constructor(config?: string) {\n if (config) {\n this.parse(config);\n }\n }\n\n /**\n * Parses a category configuration from a string into property\n * {@link CategoryConfig.categories | categories}\n * @param config - The category configuration to parse.\n */\n parse(config: string) {\n let currentCategory: Category | null = null;\n const categoryNames = new Set<string>();\n const ingredientNames = new Set<string>();\n\n for (const line of config.split(\"\\n\")) {\n const trimmedLine = line.trim();\n\n if (trimmedLine.length === 0) {\n continue;\n }\n\n if (trimmedLine.startsWith(\"[\") && trimmedLine.endsWith(\"]\")) {\n const categoryName = trimmedLine\n .substring(1, trimmedLine.length - 1)\n .trim();\n\n if (categoryNames.has(categoryName)) {\n throw new Error(`Duplicate category found: ${categoryName}`);\n }\n categoryNames.add(categoryName);\n\n currentCategory = { name: categoryName, ingredients: [] };\n this.categories.push(currentCategory);\n } else {\n if (currentCategory === null) {\n throw new Error(\n `Ingredient found without a category: ${trimmedLine}`,\n );\n }\n\n const aliases = trimmedLine.split(\"|\").map((s) => s.trim());\n for (const alias of aliases) {\n if (ingredientNames.has(alias)) {\n throw new Error(`Duplicate ingredient/alias found: ${alias}`);\n }\n ingredientNames.add(alias);\n }\n\n const ingredient: CategoryIngredient = {\n name: aliases[0]!, // We know this exists because trimmedLine is not empty\n aliases: aliases,\n };\n currentCategory.ingredients.push(ingredient);\n }\n }\n }\n}\n","import type { Step, Note } from \"../types\";\n\n/**\n * Represents a recipe section\n *\n * Wrapped as a _Class_ and not defined as a simple _Type_ to expose some useful helper\n * classes (e.g. {@link Section.isBlank | isBlank()})\n *\n * @category Types\n */\nexport class Section {\n /**\n * The name of the section. Can be an empty string for the default (first) section.\n * @defaultValue `\"\"`\n */\n name: string;\n /** An array of steps and notes that make up the content of the section. */\n content: (Step | Note)[] = [];\n\n /**\n * Creates an instance of Section.\n * @param name - The name of the section. Defaults to an empty string.\n */\n constructor(name: string = \"\") {\n this.name = name;\n }\n\n /**\n * Checks if the section is blank (has no name and no content).\n * Used during recipe parsing\n * @returns `true` if the section is blank, otherwise `false`.\n */\n isBlank(): boolean {\n return this.name === \"\" && this.content.length === 0;\n }\n}\n","type PartialBut<T, K extends keyof T> = Partial<T> & Pick<T, K>;\n\nconst escapeCache = new Map<string, string>();\n\nconst Flags = {\n GLOBAL: \"g\",\n NON_SENSITIVE: \"i\",\n MULTILINE: \"m\",\n DOT_ALL: \"s\",\n UNICODE: \"u\",\n STICKY: \"y\",\n} as const;\n\nconst Ranges = Object.freeze({\n digit: \"0-9\",\n lowercaseLetter: \"a-z\",\n uppercaseLetter: \"A-Z\",\n letter: \"a-zA-Z\",\n alphanumeric: \"a-zA-Z0-9\",\n anyCharacter: \".\",\n});\n\ntype RangeKeys = keyof typeof Ranges;\n\nconst Quantifiers = Object.freeze({\n zeroOrMore: \"*\",\n oneOrMore: \"+\",\n optional: \"?\",\n});\n\ntype Quantifiers =\n | \"exactly\"\n | \"atLeast\"\n | \"atMost\"\n | \"between\"\n | \"oneOrMore\"\n | \"zeroOrMore\"\n | \"repeat\";\ntype QuantifierMethods = Quantifiers | \"optional\" | \"lazy\";\n\ntype WithLazy = HumanRegex;\ntype Base = Omit<HumanRegex, \"lazy\">;\ntype AtStart = Omit<Base, QuantifierMethods | \"endGroup\">;\ntype AfterAnchor = Omit<Base, QuantifierMethods | \"or\">;\ntype SimpleQuantifier = Omit<Base, Quantifiers>;\ntype LazyQuantifier = Omit<WithLazy, Quantifiers>;\n\nclass HumanRegex {\n private parts: string[];\n private flags: Set<string>;\n\n constructor() {\n this.parts = [];\n this.flags = new Set<string>();\n }\n\n digit(): Base {\n return this.add(\"\\\\d\");\n }\n\n special(): Base {\n return this.add(\"(?=.*[!@#$%^&*])\");\n }\n\n word(): Base {\n return this.add(\"\\\\w\");\n }\n\n whitespace(): Base {\n return this.add(\"\\\\s\");\n }\n\n nonWhitespace(): Base {\n return this.add(\"\\\\S\");\n }\n\n literal(text: string): this {\n return this.add(escapeLiteral(text));\n }\n\n or(): AfterAnchor {\n return this.add(\"|\");\n }\n\n range(name: RangeKeys): Base {\n const range = Ranges[name];\n if (!range) throw new Error(`Unknown range: ${name}`);\n return this.add(`[${range}]`);\n }\n\n notRange(name: RangeKeys): Base {\n const range = Ranges[name];\n if (!range) throw new Error(`Unknown range: ${name}`);\n return this.add(`[^${range}]`);\n }\n\n anyOf(chars: string): Base {\n return this.add(`[${chars}]`);\n }\n\n notAnyOf(chars: string): Base {\n return this.add(`[^${chars}]`);\n }\n\n lazy(): Base {\n const lastPart = this.parts.pop();\n if (!lastPart) throw new Error(\"No quantifier to make lazy\");\n return this.add(`${lastPart}?`);\n }\n\n letter(): Base {\n return this.add(\"[a-zA-Z]\");\n }\n\n anyCharacter(): Base {\n return this.add(\".\");\n }\n\n newline(): Base {\n return this.add(\"(?:\\\\r\\\\n|\\\\r|\\\\n)\"); // Windows: \\r\\n, Unix: \\n, Old Macs: \\r\n }\n\n negativeLookahead(pattern: string): Base {\n return this.add(`(?!${pattern})`);\n }\n\n positiveLookahead(pattern: string): Base {\n return this.add(`(?=${pattern})`);\n }\n\n positiveLookbehind(pattern: string): Base {\n return this.add(`(?<=${pattern})`);\n }\n\n negativeLookbehind(pattern: string): Base {\n return this.add(`(?<!${pattern})`);\n }\n\n hasSpecialCharacter(): Base {\n return this.add(\"(?=.*[!@#$%^&*])\");\n }\n\n hasDigit(): Base {\n return this.add(\"(?=.*\\\\d)\");\n }\n\n hasLetter(): Base {\n return this.add(\"(?=.*[a-zA-Z])\");\n }\n\n optional(): SimpleQuantifier {\n return this.add(Quantifiers.optional);\n }\n\n exactly(n: number): SimpleQuantifier {\n return this.add(`{${n}}`);\n }\n\n atLeast(n: number): LazyQuantifier {\n return this.add(`{${n},}`);\n }\n\n atMost(n: number): LazyQuantifier {\n return this.add(`{0,${n}}`);\n }\n\n between(min: number, max: number): LazyQuantifier {\n return this.add(`{${min},${max}}`);\n }\n\n oneOrMore(): LazyQuantifier {\n return this.add(Quantifiers.oneOrMore);\n }\n\n zeroOrMore(): LazyQuantifier {\n return this.add(Quantifiers.zeroOrMore);\n }\n\n startNamedGroup(name: string): AfterAnchor {\n return this.add(`(?<${name}>`);\n }\n\n startGroup(): AfterAnchor {\n return this.add(\"(?:\");\n }\n\n startCaptureGroup(): AfterAnchor {\n return this.add(\"(\");\n }\n\n wordBoundary(): Base {\n return this.add(\"\\\\b\");\n }\n\n nonWordBoundary(): Base {\n return this.add(\"\\\\B\");\n }\n\n endGroup(): Base {\n return this.add(\")\");\n }\n\n startAnchor(): AfterAnchor {\n return this.add(\"^\");\n }\n\n endAnchor(): AfterAnchor {\n return this.add(\"$\");\n }\n\n global(): this {\n this.flags.add(Flags.GLOBAL);\n return this;\n }\n\n nonSensitive(): this {\n this.flags.add(Flags.NON_SENSITIVE);\n return this;\n }\n\n multiline(): this {\n this.flags.add(Flags.MULTILINE);\n return this;\n }\n\n dotAll(): this {\n this.flags.add(Flags.DOT_ALL);\n return this;\n }\n\n sticky(): this {\n this.flags.add(Flags.STICKY);\n return this;\n }\n\n unicodeChar(variant?: \"u\" | \"l\" | \"t\" | \"m\" | \"o\"): Base {\n this.flags.add(Flags.UNICODE);\n const validVariants = new Set([\"u\", \"l\", \"t\", \"m\", \"o\"] as const);\n\n if (variant !== undefined && !validVariants.has(variant)) {\n throw new Error(`Invalid Unicode letter variant: ${variant}`);\n }\n\n return this.add(`\\\\p{L${variant ?? \"\"}}`);\n }\n\n unicodeDigit(): Base {\n this.flags.add(Flags.UNICODE);\n return this.add(\"\\\\p{N}\");\n }\n\n unicodePunctuation(): Base {\n this.flags.add(Flags.UNICODE);\n return this.add(\"\\\\p{P}\");\n }\n\n unicodeSymbol(): Base {\n this.flags.add(Flags.UNICODE);\n return this.add(\"\\\\p{S}\");\n }\n\n repeat(count: number): Base {\n if (this.parts.length === 0) {\n throw new Error(\"No pattern to repeat\");\n }\n\n const lastPart = this.parts.pop();\n this.parts.push(`(${lastPart}){${count}}`);\n return this;\n }\n\n ipv4Octet(): Base {\n return this.add(\"(25[0-5]|2[0-4]\\\\d|1\\\\d\\\\d|[1-9]\\\\d|\\\\d)\");\n }\n\n protocol(): Base {\n return this.add(\"https?://\");\n }\n\n www(): Base {\n return this.add(\"(www\\\\.)?\");\n }\n\n tld(): Base {\n return this.add(\"(com|org|net)\");\n }\n\n path(): Base {\n return this.add(\"(/\\\\w+)*\");\n }\n\n private add(part: string): this {\n this.parts.push(part);\n return this;\n }\n\n toString(): string {\n return this.parts.join(\"\");\n }\n\n toRegExp(): RegExp {\n return new RegExp(this.toString(), [...this.flags].join(\"\"));\n }\n}\n\nfunction escapeLiteral(text: string): string {\n if (!escapeCache.has(text)) {\n escapeCache.set(text, text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n }\n return escapeCache.get(text)!;\n}\n\nconst createRegex = (): AtStart => new HumanRegex();\n\nconst Patterns = (() => {\n const createCachedPattern = (\n builder: () => PartialBut<HumanRegex, \"toRegExp\">\n ) => {\n const regex = builder().toRegExp();\n return () => new RegExp(regex.source, regex.flags);\n };\n\n return {\n email: createCachedPattern(() =>\n createRegex()\n .startAnchor()\n .word()\n .oneOrMore()\n .literal(\"@\")\n .word()\n .oneOrMore()\n .startGroup()\n .literal(\".\")\n .word()\n .oneOrMore()\n .endGroup()\n .zeroOrMore()\n .literal(\".\")\n .letter()\n .atLeast(2)\n .endAnchor()\n ),\n url: createCachedPattern(() =>\n createRegex()\n .startAnchor()\n .protocol()\n .www()\n .word()\n .oneOrMore()\n .literal(\".\")\n .tld()\n .path()\n .endAnchor()\n ),\n phoneInternational: createCachedPattern(() =>\n createRegex()\n .startAnchor()\n .literal(\"+\")\n .digit()\n .between(1, 3)\n .literal(\"-\")\n .digit()\n .between(3, 14)\n .endAnchor()\n ),\n };\n})();\n\nexport { createRegex, Patterns, Flags, Ranges, Quantifiers };\n","import { createRegex } from \"human-regex\";\n\nexport const metadataRegex = createRegex()\n .literal(\"---\").newline()\n .startCaptureGroup().anyCharacter().zeroOrMore().optional().endGroup()\n .newline().literal(\"---\")\n .dotAll().toRegExp();\n\nexport const scalingMetaValueRegex = (varName: string): RegExp => createRegex()\n .startAnchor()\n .literal(varName)\n .literal(\":\")\n .anyOf(\"\\\\t \").zeroOrMore()\n .startCaptureGroup()\n .startCaptureGroup()\n .notAnyOf(\",\\\\n\").oneOrMore()\n .endGroup()\n .startGroup()\n .literal(\",\")\n .whitespace().zeroOrMore()\n .startCaptureGroup()\n .anyCharacter().oneOrMore()\n .endGroup()\n .endGroup().optional()\n .endGroup()\n .endAnchor()\n .multiline()\n .toRegExp()\n\nconst nonWordChar = \"\\\\s@#~\\\\[\\\\]{(,;:!?\"\n\nconst multiwordIngredient = createRegex()\n .literal(\"@\")\n .startNamedGroup(\"mIngredientModifiers\")\n .anyOf(\"@\\\\-&?\").zeroOrMore()\n .endGroup().optional()\n .startNamedGroup(\"mIngredientRecipeAnchor\")\n .literal(\"./\")\n .endGroup().optional()\n .startNamedGroup(\"mIngredientName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .startGroup()\n .whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore()\n .endGroup().oneOrMore()\n .notAnyOf(\"\\\\.\"+nonWordChar)\n .endGroup()\n .positiveLookahead(\"\\\\s*(?:\\\\{[^\\\\}]*\\\\}|\\\\([^)]*\\\\))\")\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"mIngredientQuantityModifier\")\n .literal(\"=\").exactly(1)\n .endGroup().optional()\n .startNamedGroup(\"mIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"mIngredientUnit\")\n .notAnyOf(\"}\").oneOrMore().lazy()\n .endGroup()\n .endGroup().optional()\n .literal(\"}\")\n .endGroup().optional()\n .startGroup()\n .literal(\"(\")\n .startNamedGroup(\"mIngredientPreparation\")\n .notAnyOf(\")\").oneOrMore().lazy()\n .endGroup()\n .literal(\")\")\n .endGroup().optional()\n .toRegExp();\n\nconst singleWordIngredient = createRegex()\n .literal(\"@\")\n .startNamedGroup(\"sIngredientModifiers\")\n .anyOf(\"@\\\\-&?\").zeroOrMore()\n .endGroup().optional()\n .startNamedGroup(\"sIngredientRecipeAnchor\")\n .literal(\"./\")\n .endGroup().optional()\n .startNamedGroup(\"sIngredientName\")\n .notAnyOf(nonWordChar).zeroOrMore()\n .notAnyOf(\"\\\\.\"+nonWordChar)\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"sIngredientQuantityModifier\")\n .literal(\"=\").exactly(1)\n .endGroup().optional()\n .startNamedGroup(\"sIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"sIngredientUnit\")\n .notAnyOf(\"}\").oneOrMore().lazy()\n .endGroup()\n .endGroup().optional()\n .literal(\"}\")\n .endGroup().optional()\n .startGroup()\n .literal(\"(\")\n .startNamedGroup(\"sIngredientPreparation\")\n .notAnyOf(\")\").oneOrMore().lazy()\n .endGroup()\n .literal(\")\")\n .endGroup().optional()\n .toRegExp();\n\nexport const ingredientAliasRegex = createRegex()\n .startAnchor()\n .startNamedGroup(\"ingredientListName\")\n .notAnyOf(\"|\").oneOrMore()\n .endGroup()\n .literal(\"|\")\n .startNamedGroup(\"ingredientDisplayName\")\n .notAnyOf(\"|\").oneOrMore()\n .endGroup()\n .endAnchor()\n .toRegExp();\n\nconst multiwordCookware = createRegex()\n .literal(\"#\")\n .startNamedGroup(\"mCookwareModifiers\")\n .anyOf(\"\\\\-&?\").zeroOrMore()\n .endGroup()\n .startNamedGroup(\"mCookwareName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .startGroup()\n .whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore()\n .endGroup().oneOrMore()\n .notAnyOf(\"\\\\.\"+nonWordChar)\n .endGroup().positiveLookahead(\"\\\\s*(?:\\\\{[^\\\\}]*\\\\})\")\n .literal(\"{\")\n .startNamedGroup(\"mCookwareQuantity\")\n .anyCharacter().zeroOrMore().lazy()\n .endGroup()\n .literal(\"}\")\n .toRegExp();\n\nconst singleWordCookware = createRegex()\n .literal(\"#\")\n .startNamedGroup(\"sCookwareModifiers\")\n .anyOf(\"\\\\-&?\").zeroOrMore()\n .endGroup()\n .startNamedGroup(\"sCookwareName\")\n .notAnyOf(nonWordChar).zeroOrMore()\n .notAnyOf(\"\\\\.\"+nonWordChar)\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"sCookwareQuantity\")\n .anyCharacter().zeroOrMore().lazy()\n .endGroup()\n .literal(\"}\")\n .endGroup().optional()\n .toRegExp();\n\nconst timer = createRegex()\n .literal(\"~\")\n .startNamedGroup(\"timerName\")\n .anyCharacter().zeroOrMore().lazy()\n .endGroup()\n .literal(\"{\")\n .startNamedGroup(\"timerQuantity\")\n .anyCharacter().oneOrMore().lazy()\n .endGroup()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"timerUnit\")\n .anyCharacter().oneOrMore().lazy()\n .endGroup()\n .endGroup().optional()\n .literal(\"}\")\n .toRegExp()\n\nexport const tokensRegex = new RegExp(\n [\n multiwordIngredient,\n singleWordIngredient,\n multiwordCookware,\n singleWordCookware,\n timer,\n ]\n .map((r) => r.source)\n .join(\"|\"),\n \"gu\",\n);\n\nexport const commentRegex = createRegex()\n .literal(\"--\")\n .anyCharacter().zeroOrMore()\n .global()\n .toRegExp();\n\nexport const blockCommentRegex = createRegex()\n .literal(\"[-\")\n .anyCharacter().zeroOrMore().lazy()\n .literal(\"-]\")\n .whitespace().zeroOrMore()\n .global()\n .toRegExp();\n\nexport const shoppingListRegex = createRegex()\n .literal(\"[\")\n .startNamedGroup(\"name\")\n .anyCharacter().oneOrMore()\n .endGroup()\n .literal(\"]\")\n .newline()\n .startNamedGroup(\"items\")\n .anyCharacter().zeroOrMore().lazy()\n .endGroup()\n .startGroup()\n .newline().newline()\n .or()\n .endAnchor()\n .endGroup()\n .global()\n .toRegExp()\n\nexport const rangeRegex = createRegex()\n .startAnchor()\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".,/\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .literal(\"-\")\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".,/\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .endAnchor()\n .toRegExp()\n\nexport const numberLikeRegex = createRegex()\n .startAnchor()\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".,/\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .endAnchor()\n .toRegExp()\n\nexport const floatRegex = createRegex()\n .startAnchor()\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .endAnchor()\n .toRegExp()","import type { FixedValue, Range, DecimalValue, FractionValue } from \"./types\";\nimport Big from \"big.js\";\nexport type UnitType = \"mass\" | \"volume\" | \"count\";\nexport type UnitSystem = \"metric\" | \"imperial\";\n\nexport interface UnitDefinition {\n name: string; // canonical name, e.g. 'g'\n type: UnitType;\n system: UnitSystem;\n aliases: string[]; // e.g. ['gram', 'grams']\n toBase: number; // conversion factor to the base unit of its type\n}\n\nexport interface Quantity {\n value: FixedValue | Range;\n unit?: string;\n}\n\n// Base units: mass -> gram (g), volume -> milliliter (ml)\nconst units: UnitDefinition[] = [\n // Mass (Metric)\n {\n name: \"g\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"gram\", \"grams\", \"grammes\"],\n toBase: 1,\n },\n {\n name: \"kg\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"kilogram\", \"kilograms\", \"kilogrammes\", \"kilos\", \"kilo\"],\n toBase: 1000,\n },\n // Mass (Imperial)\n {\n name: \"oz\",\n type: \"mass\",\n system: \"imperial\",\n aliases: [\"ounce\", \"ounces\"],\n toBase: 28.3495,\n },\n {\n name: \"lb\",\n type: \"mass\",\n system: \"imperial\",\n aliases: [\"pound\", \"pounds\"],\n toBase: 453.592,\n },\n\n // Volume (Metric)\n {\n name: \"ml\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"milliliter\", \"milliliters\", \"millilitre\", \"millilitres\", \"cc\"],\n toBase: 1,\n },\n {\n name: \"cl\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"centiliter\", \"centiliters\", \"centilitre\", \"centilitres\"],\n toBase: 10,\n },\n {\n name: \"dl\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"deciliter\", \"deciliters\", \"decilitre\", \"decilitres\"],\n toBase: 100,\n },\n {\n name: \"l\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"liter\", \"liters\", \"litre\", \"litres\"],\n toBase: 1000,\n },\n {\n name: \"tsp\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"teaspoon\", \"teaspoons\"],\n toBase: 5,\n },\n {\n name: \"tbsp\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"tablespoon\", \"tablespoons\"],\n toBase: 15,\n },\n\n // Volume (Imperial)\n {\n name: \"fl-oz\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"fluid ounce\", \"fluid ounces\"],\n toBase: 29.5735,\n },\n {\n name: \"cup\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"cups\"],\n toBase: 236.588,\n },\n {\n name: \"pint\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"pints\"],\n toBase: 473.176,\n },\n {\n name: \"quart\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"quarts\"],\n toBase: 946.353,\n },\n {\n name: \"gallon\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"gallons\"],\n toBase: 3785.41,\n },\n\n // Count units (no conversion, but recognized as a type)\n {\n name: \"piece\",\n type: \"count\",\n system: \"metric\",\n aliases: [\"pieces\", \"pc\"],\n toBase: 1,\n },\n];\n\nconst unitMap = new Map<string, UnitDefinition>();\nfor (const unit of units) {\n unitMap.set(unit.name.toLowerCase(), unit);\n for (const alias of unit.aliases) {\n unitMap.set(alias.toLowerCase(), unit);\n }\n}\n\nexport function normalizeUnit(unit: string = \"\"): UnitDefinition | undefined {\n return unitMap.get(unit.toLowerCase().trim());\n}\n\nexport class CannotAddTextValueError extends Error {\n constructor() {\n super(\"Cannot add a quantity with a text value.\");\n this.name = \"CannotAddTextValueError\";\n }\n}\n\nexport class IncompatibleUnitsError extends Error {\n constructor(unit1: string, unit2: string) {\n super(\n `Cannot add quantities with incompatible or unknown units: ${unit1} and ${unit2}`,\n );\n this.name = \"IncompatibleUnitsError\";\n }\n}\n\nfunction gcd(a: number, b: number): number {\n return b === 0 ? a : gcd(b, a % b);\n}\n\nexport function simplifyFraction(\n num: number,\n den: number,\n): DecimalValue | FractionValue {\n if (den === 0) {\n throw new Error(\"Denominator cannot be zero.\");\n }\n\n const commonDivisor = gcd(Math.abs(num), Math.abs(den));\n let simplifiedNum = num / commonDivisor;\n let simplifiedDen = den / commonDivisor;\n if (simplifiedDen < 0) {\n simplifiedNum = -simplifiedNum;\n simplifiedDen = -simplifiedDen;\n }\n\n if (simplifiedDen === 1) {\n return { type: \"decimal\", value: simplifiedNum };\n } else {\n return { type: \"fraction\", num: simplifiedNum, den: simplifiedDen };\n }\n}\n\nexport function multiplyNumericValue(\n v: DecimalValue | FractionValue,\n factor: number | Big,\n): DecimalValue | FractionValue {\n if (v.type === \"decimal\") {\n return { type: \"decimal\", value: Big(v.value).times(factor).toNumber() };\n }\n return simplifyFraction(Big(v.num).times(factor).toNumber(), v.den);\n}\n\nexport function addNumericValues(\n val1: DecimalValue | FractionValue,\n val2: DecimalValue | FractionValue,\n): DecimalValue | FractionValue {\n let num1: number;\n let den1: number;\n let num2: number;\n let den2: number;\n\n if (val1.type === \"decimal\") {\n num1 = val1.value;\n den1 = 1;\n } else {\n num1 = val1.num;\n den1 = val1.den;\n }\n\n if (val2.type === \"decimal\") {\n num2 = val2.value;\n den2 = 1;\n } else {\n num2 = val2.num;\n den2 = val2.den;\n }\n\n // Return 0 if both values are 0\n if (num1 === 0 && num2 === 0) {\n return { type: \"decimal\", value: 0 };\n }\n\n // We only return a fraction where both input values are fractions themselves or only one while the other is 0\n if (\n (val1.type === \"fraction\" && val2.type === \"fraction\") ||\n (val1.type === \"fraction\" && val2.type === \"decimal\" && val2.value === 0) ||\n (val2.type === \"fraction\" && val1.type === \"decimal\" && val1.value === 0)\n ) {\n const commonDen = den1 * den2;\n const sumNum = num1 * den2 + num2 * den1;\n return simplifyFraction(sumNum, commonDen);\n } else {\n return {\n type: \"decimal\",\n value: Big(num1).div(den1).add(Big(num2).div(den2)).toNumber(),\n };\n }\n}\n\nconst toRoundedDecimal = (v: DecimalValue | FractionValue): DecimalValue => {\n const value = v.type === \"decimal\" ? v.value : v.num / v.den;\n return { type: \"decimal\", value: Math.floor(value * 100) / 100 };\n};\n\nexport function multiplyQuantityValue(\n value: FixedValue | Range,\n factor: number | Big,\n): FixedValue | Range {\n if (value.type === \"fixed\") {\n const newValue = multiplyNumericValue(\n value.value as DecimalValue | FractionValue,\n Big(factor),\n );\n if (\n factor === parseInt(factor.toString()) || // e.g. 2 === int\n Big(1).div(factor).toNumber() === parseInt(Big(1).div(factor).toString()) // e.g. 0.25 => 4 === int\n ) {\n // Preserve fractions\n return {\n type: \"fixed\",\n value: newValue,\n };\n }\n // We might multiply with big decimal number so rounding into decimal value\n return {\n type: \"fixed\",\n value: toRoundedDecimal(newValue),\n };\n }\n\n return {\n type: \"range\",\n min: multiplyNumericValue(value.min, factor),\n max: multiplyNumericValue(value.max, factor),\n };\n}\n\nconst convertQuantityValue = (\n value: FixedValue | Range,\n def: UnitDefinition,\n targetDef: UnitDefinition,\n): FixedValue | Range => {\n if (def.name === targetDef.name) return value;\n\n const factor = def.toBase / targetDef.toBase;\n\n return multiplyQuantityValue(value, factor);\n};\n\n/**\n * Get the default / neutral quantity which can be provided to addQuantity\n * for it to return the other value as result\n *\n * @return zero\n */\nexport function getDefaultQuantityValue(): FixedValue {\n return { type: \"fixed\", value: { type: \"decimal\", value: 0 } };\n}\n\n/**\n * Adds two quantity values together.\n *\n * - Adding two {@link FixedValue}s returns a new {@link FixedValue}.\n * - Adding a {@link Range} to any value returns a {@link Range}.\n *\n * @param v1 - The first quantity value.\n * @param v2 - The second quantity value.\n * @returns A new quantity value representing the sum.\n */\nexport function addQuantityValues(v1: FixedValue, v2: FixedValue): FixedValue;\nexport function addQuantityValues(\n v1: FixedValue | Range,\n v2: FixedValue | Range,\n): Range;\n\nexport function addQuantityValues(\n v1: FixedValue | Range,\n v2: FixedValue | Range,\n): FixedValue | Range {\n if (\n (v1.type === \"fixed\" && v1.value.type === \"text\") ||\n (v2.type === \"fixed\" && v2.value.type === \"text\")\n ) {\n throw new CannotAddTextValueError();\n }\n\n if (v1.type === \"fixed\" && v2.type === \"fixed\") {\n const res = addNumericValues(\n v1.value as DecimalValue | FractionValue,\n v2.value as DecimalValue | FractionValue,\n );\n return { type: \"fixed\", value: res };\n }\n const r1 =\n v1.type === \"range\" ? v1 : { type: \"range\", min: v1.value, max: v1.value };\n const r2 =\n v2.type === \"range\" ? v2 : { type: \"range\", min: v2.value, max: v2.value };\n const newMin = addNumericValues(\n r1.min as DecimalValue | FractionValue,\n r2.min as DecimalValue | FractionValue,\n );\n const newMax = addNumericValues(\n r1.max as DecimalValue | FractionValue,\n r2.max as DecimalValue | FractionValue,\n );\n return { type: \"range\", min: newMin, max: newMax };\n}\n\n/**\n * Adds two quantities, returning the result in the most appropriate unit.\n */\nexport function addQuantities(q1: Quantity, q2: Quantity): Quantity {\n const v1 = q1.value;\n const v2 = q2.value;\n\n // Case 1: one of the two values is a text, we throw an error we can catch on the other end\n if (\n (v1.type === \"fixed\" && v1.value.type === \"text\") ||\n (v2.type === \"fixed\" && v2.value.type === \"text\")\n ) {\n throw new CannotAddTextValueError();\n }\n\n const unit1Def = normalizeUnit(q1.unit);\n const unit2Def = normalizeUnit(q2.unit);\n\n const addQuantityValuesAndSetUnit = (\n val1: FixedValue | Range,\n val2: FixedValue | Range,\n unit: string | undefined,\n ): Quantity => ({ value: addQuantityValues(val1, val2), unit });\n\n // Case 2: one of the two values doesn't have a unit, we preserve its value and consider its unit to be that of the other one\n // If at least one of the two units is \"\", this preserves it versus setting the resulting unit as undefined\n if ((q1.unit === \"\" || q1.unit === undefined) && q2.unit !== undefined) {\n return addQuantityValuesAndSetUnit(v1, v2, q2.unit); // Prefer q2's unit\n }\n if ((q2.unit === \"\" || q2.unit === undefined) && q1.unit !== undefined) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit); // Prefer q1's unit\n }\n\n // Case 3: the two quantities have the exact same unit\n if (\n (!q1.unit && !q2.unit) ||\n (q1.unit && q2.unit && q1.unit.toLowerCase() === q2.unit.toLowerCase())\n ) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit);\n }\n\n // Case 4: the two quantities have different units of known type\n if (unit1Def && unit2Def) {\n // Case 4.1: different unit type => we can't add quantities\n\n if (unit1Def.type !== unit2Def.type) {\n throw new IncompatibleUnitsError(\n `${unit1Def.type} (${q1.unit})`,\n `${unit2Def.type} (${q2.unit})`,\n );\n }\n\n let targetUnitDef: UnitDefinition;\n\n // Case 4.2: same unit type but different system => we convert to metric\n if (unit1Def.system !== unit2Def.system) {\n const metricUnitDef = unit1Def.system === \"metric\" ? unit1Def : unit2Def;\n targetUnitDef = units\n .filter((u) => u.type === metricUnitDef.type && u.system === \"metric\")\n .reduce((prev, current) =>\n prev.toBase > current.toBase ? prev : current,\n );\n }\n // Case 4.3: same unit type, same system but different unit => we use the biggest unit of the two\n else {\n targetUnitDef = unit1Def.toBase >= unit2Def.toBase ? unit1Def : unit2Def;\n }\n const convertedV1 = convertQuantityValue(v1, unit1Def, targetUnitDef);\n const convertedV2 = convertQuantityValue(v2, unit2Def, targetUnitDef);\n\n return addQuantityValuesAndSetUnit(\n convertedV1,\n convertedV2,\n targetUnitDef.name,\n );\n }\n\n // Case 5: the two quantities have different units of unknown type\n throw new IncompatibleUnitsError(q1.unit!, q2.unit!);\n}\n","import { IngredientFlag, CookwareFlag } from \"./types\";\n\nexport class ReferencedItemCannotBeRedefinedError extends Error {\n constructor(\n item_type: \"ingredient\" | \"cookware\",\n item_name: string,\n new_modifier: IngredientFlag | CookwareFlag,\n ) {\n super(\n `The referenced ${item_type} \"${item_name}\" cannot be redefined as ${new_modifier}.\nYou can either remove the reference to create a new ${item_type} defined as ${new_modifier} or add the ${new_modifier} flag to the original definition of the ${item_type}`,\n );\n this.name = \"ReferencedItemCannotBeRedefinedError\";\n }\n}\n","import type {\n MetadataExtract,\n Metadata,\n FixedValue,\n Range,\n TextValue,\n DecimalValue,\n FractionValue,\n} from \"./types\";\nimport {\n metadataRegex,\n rangeRegex,\n numberLikeRegex,\n scalingMetaValueRegex,\n} from \"./regex\";\nimport { Section as SectionObject } from \"./classes/section\";\nimport type { Ingredient, Note, Step, Cookware } from \"./types\";\nimport {\n addQuantities,\n getDefaultQuantityValue,\n CannotAddTextValueError,\n IncompatibleUnitsError,\n Quantity,\n addQuantityValues,\n} from \"./units\";\nimport { ReferencedItemCannotBeRedefinedError } from \"./errors\";\n\n/**\n * Pushes a pending note to the section content if it's not empty.\n * @param section - The current section object.\n * @param note - The note content.\n * @returns An empty string if the note was pushed, otherwise the original note.\n */\nexport function flushPendingNote(\n section: SectionObject,\n note: Note[\"note\"],\n): Note[\"note\"] {\n if (note.length > 0) {\n section.content.push({ type: \"note\", note });\n return \"\";\n }\n return note;\n}\n\n/**\n * Pushes pending step items and a pending note to the section content.\n * @param section - The current section object.\n * @param items - The list of step items. This array will be cleared.\n * @returns true if the items were pushed, otherwise false.\n */\nexport function flushPendingItems(\n section: SectionObject,\n items: Step[\"items\"],\n): boolean {\n if (items.length > 0) {\n section.content.push({ type: \"step\", items: [...items] });\n items.length = 0;\n return true;\n }\n return false;\n}\n\n/**\n * Finds an ingredient in the list (case-insensitively) and updates it, or adds it if not present.\n * This function mutates the `ingredients` array.\n * @param ingredients - The list of ingredients.\n * @param newIngredient - The ingredient to find or add.\n * @param isReference - Whether this is a reference ingredient (`&` modifier).\n * @returns The index of the ingredient in the list.\n * @returns An object containing the index of the ingredient and its quantity part in the list.\n */\nexport function findAndUpsertIngredient(\n ingredients: Ingredient[],\n newIngredient: Ingredient,\n isReference: boolean,\n): {\n ingredientIndex: number;\n quantityPartIndex: number | undefined;\n} {\n const { name, quantity, unit } = newIngredient;\n\n // New ingredient\n if (isReference) {\n const indexFind = ingredients.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (indexFind === -1) {\n throw new Error(\n `Referenced ingredient \"${name}\" not found. A referenced ingredient must be declared before being referenced with '&'.`,\n );\n }\n\n // Ingredient already exists, update it\n const existingIngredient = ingredients[indexFind]!;\n\n // Checking whether any provided flags are the same as the original ingredient\n for (const flag of newIngredient.flags!) {\n /* v8 ignore else -- @preserve */\n if (!existingIngredient.flags!.includes(flag)) {\n throw new ReferencedItemCannotBeRedefinedError(\n \"ingredient\",\n existingIngredient.name,\n flag,\n );\n }\n }\n\n let quantityPartIndex = undefined;\n if (quantity !== undefined) {\n const currentQuantity: Quantity = {\n value: existingIngredient.quantity ?? getDefaultQuantityValue(),\n unit: existingIngredient.unit ?? \"\",\n };\n const newQuantity = { value: quantity, unit: unit ?? \"\" };\n try {\n const total = addQuantities(currentQuantity, newQuantity);\n existingIngredient.quantity = total.value;\n existingIngredient.unit = total.unit || undefined;\n if (existingIngredient.quantityParts) {\n existingIngredient.quantityParts.push(\n ...newIngredient.quantityParts!,\n );\n } else {\n existingIngredient.quantityParts = newIngredient.quantityParts;\n }\n quantityPartIndex = existingIngredient.quantityParts!.length - 1;\n } catch (e) {\n /* v8 ignore else -- expliciting error types -- @preserve */\n if (\n e instanceof IncompatibleUnitsError ||\n e instanceof CannotAddTextValueError\n ) {\n // Addition not possible, so add as a new ingredient.\n return {\n ingredientIndex: ingredients.push(newIngredient) - 1,\n quantityPartIndex: 0,\n };\n }\n }\n }\n return {\n ingredientIndex: indexFind,\n quantityPartIndex,\n };\n }\n\n // Not a reference, so add as a new ingredient.\n return {\n ingredientIndex: ingredients.push(newIngredient) - 1,\n quantityPartIndex: newIngredient.quantity ? 0 : undefined,\n };\n}\n\nexport function findAndUpsertCookware(\n cookware: Cookware[],\n newCookware: Cookware,\n isReference: boolean,\n): {\n cookwareIndex: number;\n quantityPartIndex: number | undefined;\n} {\n const { name, quantity } = newCookware;\n\n if (isReference) {\n const index = cookware.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (index === -1) {\n throw new Error(\n `Referenced cookware \"${name}\" not found. A referenced cookware must be declared before being referenced with '&'.`,\n );\n }\n\n const existingCookware = cookware[index]!;\n\n // Checking whether any provided flags are the same as the original cookware\n for (const flag of newCookware.flags) {\n /* v8 ignore else -- @preserve */\n if (!existingCookware.flags.includes(flag)) {\n throw new ReferencedItemCannotBeRedefinedError(\n \"cookware\",\n existingCookware.name,\n flag,\n );\n }\n }\n\n let quantityPartIndex = undefined;\n if (quantity !== undefined) {\n if (!existingCookware.quantity) {\n existingCookware.quantity = quantity;\n existingCookware.quantityParts = newCookware.quantityParts;\n quantityPartIndex = 0;\n } else {\n try {\n existingCookware.quantity = addQuantityValues(\n existingCookware.quantity,\n quantity,\n );\n if (!existingCookware.quantityParts) {\n existingCookware.quantityParts = newCookware.quantityParts;\n quantityPartIndex = 0;\n } else {\n quantityPartIndex =\n existingCookware.quantityParts.push(\n ...newCookware.quantityParts!,\n ) - 1;\n }\n } catch (e) {\n /* v8 ignore else -- expliciting error type -- @preserve */\n if (e instanceof CannotAddTextValueError) {\n return {\n cookwareIndex: cookware.push(newCookware) - 1,\n quantityPartIndex: 0,\n };\n }\n }\n }\n }\n return {\n cookwareIndex: index,\n quantityPartIndex,\n };\n }\n\n return {\n cookwareIndex: cookware.push(newCookware) - 1,\n quantityPartIndex: quantity ? 0 : undefined,\n };\n}\n\n// Parser when we know the input is either a number-like value\nexport const parseFixedValue = (\n input_str: string,\n): TextValue | DecimalValue | FractionValue => {\n if (!numberLikeRegex.test(input_str)) {\n return { type: \"text\", value: input_str };\n }\n\n // After this we know that s is either a fraction or a decimal value\n const s = input_str.trim().replace(\",\", \".\");\n\n // fraction\n if (s.includes(\"/\")) {\n const parts = s.split(\"/\");\n\n const num = Number(parts[0]);\n const den = Number(parts[1]);\n\n return { type: \"fraction\", num, den };\n }\n\n // decimal\n return { type: \"decimal\", value: Number(s) };\n};\n\nexport function parseQuantityInput(input_str: string): FixedValue | Range {\n const clean_str = String(input_str).trim();\n\n if (rangeRegex.test(clean_str)) {\n const range_parts = clean_str.split(\"-\");\n // As we've tested for it, we know that we have Number-like Quantities to parse\n const min = parseFixedValue(range_parts[0]!.trim()) as\n | DecimalValue\n | FractionValue;\n const max = parseFixedValue(range_parts[1]!.trim()) as\n | DecimalValue\n | FractionValue;\n return { type: \"range\", min, max };\n }\n\n return { type: \"fixed\", value: parseFixedValue(clean_str) };\n}\n\nexport function parseSimpleMetaVar(content: string, varName: string) {\n const varMatch = content.match(\n new RegExp(`^${varName}:\\\\s*(.*(?:\\\\r?\\\\n\\\\s+.*)*)+`, \"m\"),\n );\n return varMatch\n ? varMatch[1]?.trim().replace(/\\s*\\r?\\n\\s+/g, \" \")\n : undefined;\n}\n\nexport function parseScalingMetaVar(\n content: string,\n varName: string,\n): [number, string] | undefined {\n const varMatch = content.match(scalingMetaValueRegex(varName));\n if (!varMatch) return undefined;\n if (isNaN(Number(varMatch[2]?.trim()))) {\n throw new Error(\"Scaling variables should be numbers\");\n }\n return [Number(varMatch[2]?.trim()), varMatch[1]!.trim()];\n}\n\nexport function parseListMetaVar(content: string, varName: string) {\n // Handle both inline and YAML-style tags\n const listMatch = content.match(\n new RegExp(\n `^${varName}:\\\\s*(?:\\\\[([^\\\\]]*)\\\\]|((?:\\\\r?\\\\n\\\\s*-\\\\s*.+)+))`,\n \"m\",\n ),\n );\n if (!listMatch) return undefined;\n\n /* v8 ignore else -- @preserve */\n if (listMatch[1] !== undefined) {\n // Inline list: tags: [one, two, three]\n return listMatch[1].split(\",\").map((tag) => tag.trim());\n } else if (listMatch[2]) {\n // YAML list:\n // tags:\n // - one\n // - two\n return listMatch[2]\n .split(\"\\n\")\n .filter((line) => line.trim() !== \"\")\n .map((line) => line.replace(/^\\s*-\\s*/, \"\").trim());\n }\n}\n\nexport function extractMetadata(content: string): MetadataExtract {\n const metadata: Metadata = {};\n let servings: number | undefined = undefined;\n\n // Is there front-matter at all?\n const metadataContent = content.match(metadataRegex)?.[1];\n if (!metadataContent) {\n return { metadata };\n }\n\n // String metadata variables\n for (const metaVar of [\n \"title\",\n \"source\",\n \"source.name\",\n \"source.url\",\n \"author\",\n \"source.author\",\n \"prep time\",\n \"time.prep\",\n \"cook time\",\n \"time.cook\",\n \"time required\",\n \"time\",\n \"duration\",\n \"locale\",\n \"introduction\",\n \"description\",\n \"course\",\n \"category\",\n \"diet\",\n \"cuisine\",\n \"difficulty\",\n \"image\",\n \"picture\",\n ] as (keyof Pick<\n Metadata,\n | \"title\"\n | \"source\"\n | \"source.name\"\n | \"source.url\"\n | \"author\"\n | \"source.author\"\n | \"prep time\"\n | \"time.prep\"\n | \"cook time\"\n | \"time.cook\"\n | \"time required\"\n | \"time\"\n | \"duration\"\n | \"locale\"\n | \"introduction\"\n | \"description\"\n | \"course\"\n | \"category\"\n | \"diet\"\n | \"cuisine\"\n | \"difficulty\"\n | \"image\"\n | \"picture\"\n >)[]) {\n const stringMetaValue = parseSimpleMetaVar(metadataContent, metaVar);\n if (stringMetaValue) metadata[metaVar] = stringMetaValue;\n }\n\n // String metadata variables\n for (const metaVar of [\"serves\", \"yield\", \"servings\"] as (keyof Pick<\n Metadata,\n \"servings\" | \"yield\" | \"serves\"\n >)[]) {\n const scalingMetaValue = parseScalingMetaVar(metadataContent, metaVar);\n if (scalingMetaValue && scalingMetaValue[1]) {\n metadata[metaVar] = scalingMetaValue[1];\n servings = scalingMetaValue[0];\n }\n }\n\n // List metadata variables\n for (const metaVar of [\"tags\", \"images\", \"pictures\"] as (keyof Pick<\n Metadata,\n \"tags\" | \"images\" | \"pictures\"\n >)[]) {\n const listMetaValue = parseListMetaVar(metadataContent, metaVar);\n if (listMetaValue) metadata[metaVar] = listMetaValue;\n }\n\n return { metadata, servings };\n}\n","import type {\n Metadata,\n Ingredient,\n IngredientExtras,\n IngredientItem,\n Timer,\n Step,\n Note,\n Cookware,\n MetadataExtract,\n CookwareItem,\n IngredientFlag,\n CookwareFlag,\n} from \"../types\";\nimport { Section } from \"./section\";\nimport {\n tokensRegex,\n commentRegex,\n blockCommentRegex,\n metadataRegex,\n ingredientAliasRegex,\n floatRegex,\n} from \"../regex\";\nimport {\n flushPendingItems,\n flushPendingNote,\n findAndUpsertIngredient,\n findAndUpsertCookware,\n parseQuantityInput,\n extractMetadata,\n} from \"../parser_helpers\";\nimport {\n addQuantities,\n getDefaultQuantityValue,\n multiplyQuantityValue,\n type Quantity,\n} from \"../units\";\nimport Big from \"big.js\";\n\n/**\n * Recipe parser.\n *\n * ## Usage\n *\n * You can either directly provide the recipe string when creating the instance\n * e.g. `const recipe = new Recipe('Add @eggs{3}')`, or create it first and then pass\n * the recipe string to the {@link Recipe.parse | parse()} method.\n *\n * Look at the [properties](#properties) to see how the recipe's properties are parsed.\n *\n * @category Classes\n *\n * @example\n * ```typescript\n * import { Recipe } from \"@tmlmt/cooklang-parser\";\n *\n * const recipeString = `\n * ---\n * title: Pancakes\n * tags: [breakfast, easy]\n * ---\n * Crack the @eggs{3} with @flour{100%g} and @milk{200%mL}\n *\n * Melt some @butter{50%g} in a #pan on medium heat.\n *\n * Cook for ~{5%minutes} on each side.\n * `\n * const recipe = new Recipe(recipeString);\n * ```\n */\nexport class Recipe {\n /**\n * The parsed recipe metadata.\n */\n metadata: Metadata = {};\n /**\n * The parsed recipe ingredients.\n */\n ingredients: Ingredient[] = [];\n /**\n * The parsed recipe sections.\n */\n sections: Section[] = [];\n /**\n * The parsed recipe cookware.\n */\n cookware: Cookware[] = [];\n /**\n * The parsed recipe timers.\n */\n timers: Timer[] = [];\n /**\n * The parsed recipe servings. Used for scaling. Parsed from one of\n * {@link Metadata.servings}, {@link Metadata.yield} or {@link Metadata.serves}\n * metadata fields.\n *\n * @see {@link Recipe.scaleBy | scaleBy()} and {@link Recipe.scaleTo | scaleTo()} methods\n */\n servings?: number;\n\n /**\n * Creates a new Recipe instance.\n * @param content - The recipe content to parse.\n */\n constructor(content?: string) {\n if (content) {\n this.parse(content);\n }\n }\n\n /**\n * Parses a recipe from a string.\n * @param content - The recipe content to parse.\n */\n parse(content: string) {\n const cleanContent = content\n .replace(metadataRegex, \"\")\n .replace(commentRegex, \"\")\n .replace(blockCommentRegex, \"\")\n .trim()\n .split(/\\r\\n?|\\n/);\n\n const { metadata, servings }: MetadataExtract = extractMetadata(content);\n this.metadata = metadata;\n this.servings = servings;\n\n let blankLineBefore = true;\n let section: Section = new Section();\n const items: Step[\"items\"] = [];\n let note: Note[\"note\"] = \"\";\n let inNote = false;\n\n for (const line of cleanContent) {\n if (line.trim().length === 0) {\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n blankLineBefore = true;\n inNote = false;\n continue;\n }\n\n if (line.startsWith(\"=\")) {\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n\n if (this.sections.length === 0 && section.isBlank()) {\n section.name = line.replace(/^=+|=+$/g, \"\").trim();\n } else {\n /* v8 ignore else -- @preserve */\n if (!section.isBlank()) {\n this.sections.push(section);\n }\n section = new Section(line.replace(/^=+|=+$/g, \"\").trim());\n }\n blankLineBefore = true;\n inNote = false;\n continue;\n }\n\n if (blankLineBefore && line.startsWith(\">\")) {\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n note += line.substring(1).trim();\n inNote = true;\n blankLineBefore = false;\n continue;\n }\n\n if (inNote) {\n if (line.startsWith(\">\")) {\n note += \" \" + line.substring(1).trim();\n } else {\n note += \" \" + line.trim();\n }\n blankLineBefore = false;\n continue;\n }\n\n note = flushPendingNote(section, note);\n\n let cursor = 0;\n for (const match of line.matchAll(tokensRegex)) {\n const idx = match.index;\n /* v8 ignore else -- @preserve */\n if (idx > cursor) {\n items.push({ type: \"text\", value: line.slice(cursor, idx) });\n }\n\n const groups = match.groups!;\n\n if (groups.mIngredientName || groups.sIngredientName) {\n let name = (groups.mIngredientName || groups.sIngredientName)!;\n const scalableQuantity =\n (groups.mIngredientQuantityModifier ||\n groups.sIngredientQuantityModifier) !== \"=\";\n const quantityRaw =\n groups.mIngredientQuantity || groups.sIngredientQuantity;\n const unit = groups.mIngredientUnit || groups.sIngredientUnit;\n const preparation =\n groups.mIngredientPreparation || groups.sIngredientPreparation;\n const modifiers =\n groups.mIngredientModifiers || groups.sIngredientModifiers;\n const reference = modifiers !== undefined && modifiers.includes(\"&\");\n const flags: IngredientFlag[] = [];\n if (modifiers !== undefined && modifiers.includes(\"?\")) {\n flags.push(\"optional\");\n }\n if (modifiers !== undefined && modifiers.includes(\"-\")) {\n flags.push(\"hidden\");\n }\n if (\n (modifiers !== undefined && modifiers.includes(\"@\")) ||\n groups.mIngredientRecipeAnchor ||\n groups.sIngredientRecipeAnchor\n ) {\n flags.push(\"recipe\");\n }\n\n let extras: IngredientExtras | undefined = undefined;\n // If the ingredient is a recipe, we need to extract the name from the path given\n if (flags.includes(\"recipe\")) {\n extras = { path: `${name}.cook` };\n name = name.substring(name.lastIndexOf(\"/\") + 1);\n }\n\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n const aliasMatch = name.match(ingredientAliasRegex);\n let listName, displayName: string;\n if (\n aliasMatch &&\n aliasMatch.groups!.ingredientListName!.trim().length > 0 &&\n aliasMatch.groups!.ingredientDisplayName!.trim().length > 0\n ) {\n listName = aliasMatch.groups!.ingredientListName!.trim();\n displayName = aliasMatch.groups!.ingredientDisplayName!.trim();\n } else {\n listName = name;\n displayName = name;\n }\n\n const newIngredient: Ingredient = {\n name: listName,\n quantity,\n quantityParts: quantity\n ? [\n {\n value: quantity,\n unit,\n scalable: scalableQuantity,\n },\n ]\n : undefined,\n unit,\n preparation,\n flags,\n };\n\n if (extras) {\n newIngredient.extras = extras;\n }\n\n const idxsInList = findAndUpsertIngredient(\n this.ingredients,\n newIngredient,\n reference,\n );\n\n const newItem: IngredientItem = {\n type: \"ingredient\",\n index: idxsInList.ingredientIndex,\n displayName,\n };\n if (idxsInList.quantityPartIndex !== undefined) {\n newItem.quantityPartIndex = idxsInList.quantityPartIndex;\n }\n items.push(newItem);\n } else if (groups.mCookwareName || groups.sCookwareName) {\n const name = (groups.mCookwareName || groups.sCookwareName)!;\n const modifiers =\n groups.mCookwareModifiers || groups.sCookwareModifiers;\n const quantityRaw =\n groups.mCookwareQuantity || groups.sCookwareQuantity;\n const reference = modifiers !== undefined && modifiers.includes(\"&\");\n const flags: CookwareFlag[] = [];\n if (modifiers !== undefined && modifiers.includes(\"?\")) {\n flags.push(\"optional\");\n }\n if (modifiers !== undefined && modifiers.includes(\"-\")) {\n flags.push(\"hidden\");\n }\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n\n const idxsInList = findAndUpsertCookware(\n this.cookware,\n {\n name,\n quantity,\n quantityParts: quantity ? [quantity] : undefined,\n flags,\n },\n reference,\n );\n items.push({\n type: \"cookware\",\n index: idxsInList.cookwareIndex,\n quantityPartIndex: idxsInList.quantityPartIndex,\n } as CookwareItem);\n }\n // Then it's necessarily a timer which was matched\n else {\n const durationStr = groups.timerQuantity!.trim();\n const unit = (groups.timerUnit || \"\").trim();\n if (!unit) {\n throw new Error(\"Timer missing unit\");\n }\n const name = groups.timerName || undefined;\n const duration = parseQuantityInput(durationStr);\n const timerObj: Timer = {\n name,\n duration,\n unit,\n };\n items.push({ type: \"timer\", index: this.timers.push(timerObj) - 1 });\n }\n\n cursor = idx + match[0].length;\n }\n\n if (cursor < line.length) {\n items.push({ type: \"text\", value: line.slice(cursor) });\n }\n\n blankLineBefore = false;\n }\n\n // End of content reached: pushing all temporarily saved elements\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n if (!section.isBlank()) {\n this.sections.push(section);\n }\n }\n\n /**\n * Scales the recipe to a new number of servings. In practice, it calls\n * {@link Recipe.scaleBy | scaleBy} with a factor corresponding to the ratio between `newServings`\n * and the recipe's {@link Recipe.servings | servings} value.\n * @param newServings - The new number of servings.\n * @returns A new Recipe instance with the scaled ingredients.\n * @throws `Error` if the recipe does not contains an initial {@link Recipe.servings | servings} value\n */\n scaleTo(newServings: number): Recipe {\n const originalServings = this.getServings();\n\n if (originalServings === undefined || originalServings === 0) {\n throw new Error(\"Error scaling recipe: no initial servings value set\");\n }\n\n const factor = Big(newServings).div(originalServings);\n return this.scaleBy(factor);\n }\n\n /**\n * Scales the recipe by a factor.\n * @param factor - The factor to scale the recipe by. While integers can be passed as-is, it is recommended to pass fractions as\n * [Big](https://github.com/MikeMcl/big.js/) values, e.g. `Big(num).div(den)` in order to avoid undesirable floating point operation inaccuracies.\n * @returns A new Recipe instance with the scaled ingredients.\n */\n scaleBy(factor: number | Big): Recipe {\n const newRecipe = this.clone();\n\n const originalServings = newRecipe.getServings();\n\n if (originalServings === undefined || originalServings === 0) {\n throw new Error(\"Error scaling recipe: no initial servings value set\");\n }\n\n newRecipe.ingredients = newRecipe.ingredients\n .map((ingredient) => {\n // Scale first individual parts of total quantity depending on whether they are scalable or not\n if (ingredient.quantityParts) {\n ingredient.quantityParts = ingredient.quantityParts.map(\n (quantityPart) => {\n if (\n quantityPart.value.type === \"fixed\" &&\n quantityPart.value.value.type === \"text\"\n ) {\n return quantityPart;\n }\n return {\n ...quantityPart,\n value: multiplyQuantityValue(\n quantityPart.value,\n quantityPart.scalable ? Big(factor) : 1,\n ),\n };\n },\n );\n // Recalculate total quantity from quantity parts\n if (ingredient.quantityParts.length === 1) {\n ingredient.quantity = ingredient.quantityParts[0]!.value;\n ingredient.unit = ingredient.quantityParts[0]!.unit;\n } else {\n const totalQuantity = ingredient.quantityParts.reduce(\n (acc, val) =>\n addQuantities(acc, { value: val.value, unit: val.unit }),\n { value: getDefaultQuantityValue() } as Quantity,\n );\n ingredient.quantity = totalQuantity.value;\n ingredient.unit = totalQuantity.unit;\n }\n }\n return ingredient;\n })\n .filter((ingredient) => ingredient.quantity !== null);\n\n newRecipe.servings = Big(originalServings).times(factor).toNumber();\n\n /* v8 ignore else -- @preserve */\n if (newRecipe.metadata.servings && this.metadata.servings) {\n if (\n floatRegex.test(String(this.metadata.servings).replace(\",\", \".\").trim())\n ) {\n const servingsValue = parseFloat(\n String(this.metadata.servings).replace(\",\", \".\"),\n );\n newRecipe.metadata.servings = String(\n Big(servingsValue).times(factor).toNumber(),\n );\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (newRecipe.metadata.yield && this.metadata.yield) {\n if (\n floatRegex.test(String(this.metadata.yield).replace(\",\", \".\").trim())\n ) {\n const yieldValue = parseFloat(\n String(this.metadata.yield).replace(\",\", \".\"),\n );\n newRecipe.metadata.yield = String(\n Big(yieldValue).times(factor).toNumber(),\n );\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (newRecipe.metadata.serves && this.metadata.serves) {\n if (\n floatRegex.test(String(this.metadata.serves).replace(\",\", \".\").trim())\n ) {\n const servesValue = parseFloat(\n String(this.metadata.serves).replace(\",\", \".\"),\n );\n newRecipe.metadata.serves = String(\n Big(servesValue).times(factor).toNumber(),\n );\n }\n }\n\n return newRecipe;\n }\n\n /**\n * Gets the number of servings for the recipe.\n * @private\n * @returns The number of servings, or undefined if not set.\n */\n private getServings(): number | undefined {\n if (this.servings) {\n return this.servings;\n }\n return undefined;\n }\n\n /**\n * Clones the recipe.\n * @returns A new Recipe instance with the same properties.\n */\n clone(): Recipe {\n const newRecipe = new Recipe();\n // deep copy\n newRecipe.metadata = JSON.parse(JSON.stringify(this.metadata)) as Metadata;\n newRecipe.ingredients = JSON.parse(\n JSON.stringify(this.ingredients),\n ) as Ingredient[];\n newRecipe.sections = JSON.parse(JSON.stringify(this.sections)) as Section[];\n newRecipe.cookware = JSON.parse(\n JSON.stringify(this.cookware),\n ) as Cookware[];\n newRecipe.timers = JSON.parse(JSON.stringify(this.timers)) as Timer[];\n newRecipe.servings = this.servings;\n return newRecipe;\n }\n}\n","import { CategoryConfig } from \"./category_config\";\nimport { Recipe } from \"./recipe\";\nimport type {\n Ingredient,\n CategorizedIngredients,\n AddedRecipe,\n AddedIngredient,\n} from \"../types\";\nimport { addQuantities, type Quantity } from \"../units\";\n\n/**\n * Shopping List generator.\n *\n * ## Usage\n *\n * - Create a new ShoppingList instance with an optional category configuration (see {@link ShoppingList.\"constructor\" | constructor})\n * - Add recipes, scaling them as needed (see {@link ShoppingList.add_recipe | add_recipe()})\n * - Categorize the ingredients (see {@link ShoppingList.categorize | categorize()})\n *\n * @example\n *\n * ```typescript\n * import * as fs from \"fs\";\n * import { ShoppingList } from @tmlmt/cooklang-parser;\n *\n * const categoryConfig = fs.readFileSync(\"./myconfig.txt\", \"utf-8\")\n * const recipe1 = new Recipe(fs.readFileSync(\"./myrecipe.cook\", \"utf-8\"));\n * const shoppingList = new ShoppingList();\n * shoppingList.set_category_config(categoryConfig);\n * // Quantities are automatically calculated and ingredients categorized\n * // when adding a recipe\n * shoppingList.add_recipe(recipe1);\n * ```\n *\n * @category Classes\n */\nexport class ShoppingList {\n /**\n * The ingredients in the shopping list.\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipes in the shopping list.\n */\n recipes: AddedRecipe[] = [];\n /**\n * The category configuration for the shopping list.\n */\n category_config?: CategoryConfig;\n /**\n * The categorized ingredients in the shopping list.\n */\n categories?: CategorizedIngredients;\n\n /**\n * Creates a new ShoppingList instance\n * @param category_config_str - The category configuration to parse.\n */\n constructor(category_config_str?: string | CategoryConfig) {\n if (category_config_str) {\n this.set_category_config(category_config_str);\n }\n }\n\n private calculate_ingredients() {\n this.ingredients = [];\n for (const addedRecipe of this.recipes) {\n let scaledRecipe: Recipe;\n if (\"factor\" in addedRecipe) {\n const { recipe, factor } = addedRecipe;\n scaledRecipe = factor === 1 ? recipe : recipe.scaleBy(factor);\n } else {\n scaledRecipe = addedRecipe.recipe.scaleTo(addedRecipe.servings);\n }\n\n for (const ingredient of scaledRecipe.ingredients) {\n // Do not add hidden ingredients to the shopping list\n if (ingredient.flags && ingredient.flags.includes(\"hidden\")) {\n continue;\n }\n\n const existingIngredient = this.ingredients.find(\n (i) => i.name === ingredient.name,\n );\n\n let addSeparate = false;\n try {\n if (existingIngredient && ingredient.quantity) {\n if (existingIngredient.quantity) {\n const newQuantity: Quantity = addQuantities(\n {\n value: existingIngredient.quantity,\n unit: existingIngredient.unit ?? \"\",\n },\n {\n value: ingredient.quantity,\n unit: ingredient.unit ?? \"\",\n },\n );\n existingIngredient.quantity = newQuantity.value;\n if (newQuantity.unit) {\n existingIngredient.unit = newQuantity.unit;\n }\n } else {\n existingIngredient.quantity = ingredient.quantity;\n\n /* v8 ignore else -- only set unit if it is given -- @preserve */\n if (ingredient.unit) {\n existingIngredient.unit = ingredient.unit;\n }\n }\n }\n } catch {\n // Cannot add quantities, adding as separate ingredients\n addSeparate = true;\n }\n\n if (!existingIngredient || addSeparate) {\n const newIngredient: AddedIngredient = { name: ingredient.name };\n if (ingredient.quantity) {\n newIngredient.quantity = ingredient.quantity;\n }\n if (ingredient.unit) {\n newIngredient.unit = ingredient.unit;\n }\n this.ingredients.push(newIngredient);\n }\n }\n }\n }\n\n /**\n * Adds a recipe to the shopping list, then automatically\n * recalculates the quantities and recategorize the ingredients.\n * @param recipe - The recipe to add.\n * @param scaling - The scaling option for the recipe. Can be either a factor or a number of servings\n */\n add_recipe(\n recipe: Recipe,\n scaling?: { factor: number } | { servings: number },\n ): void;\n /**\n * Adds a recipe to the shopping list, then automatically\n * recalculates the quantities and recategorize the ingredients.\n * @param recipe - The recipe to add.\n * @param factor - The factor to scale the recipe by.\n * @deprecated since v2.0.3. Use the other call signature with `scaling` instead. Will be removed in v3\n */\n add_recipe(recipe: Recipe, factor?: number): void;\n add_recipe(\n recipe: Recipe,\n scaling?: { factor: number } | { servings: number } | number,\n ): void {\n if (typeof scaling === \"number\" || scaling === undefined) {\n this.recipes.push({ recipe, factor: scaling ?? 1 });\n } else {\n if (\"factor\" in scaling) {\n this.recipes.push({ recipe, factor: scaling.factor });\n } else {\n this.recipes.push({ recipe, servings: scaling.servings });\n }\n }\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Removes a recipe from the shopping list, then automatically\n * recalculates the quantities and recategorize the ingredients.s\n * @param index - The index of the recipe to remove.\n */\n remove_recipe(index: number) {\n if (index < 0 || index >= this.recipes.length) {\n throw new Error(\"Index out of bounds\");\n }\n this.recipes.splice(index, 1);\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Sets the category configuration for the shopping list\n * and automatically categorize current ingredients from the list.\n * @param config - The category configuration to parse.\n */\n set_category_config(config: string | CategoryConfig) {\n if (typeof config === \"string\")\n this.category_config = new CategoryConfig(config);\n else if (config instanceof CategoryConfig) this.category_config = config;\n else throw new Error(\"Invalid category configuration\");\n this.categorize();\n }\n\n /**\n * Categorizes the ingredients in the shopping list\n * Will use the category config if any, otherwise all ingredients will be placed in the \"other\" category\n */\n categorize() {\n if (!this.category_config) {\n this.categories = { other: this.ingredients };\n return;\n }\n\n const categories: CategorizedIngredients = { other: [] };\n for (const category of this.category_config.categories) {\n categories[category.name] = [];\n }\n\n for (const ingredient of this.ingredients) {\n let found = false;\n for (const category of this.category_config.categories) {\n for (const categoryIngredient of category.ingredients) {\n if (categoryIngredient.aliases.includes(ingredient.name)) {\n categories[category.name]!.push(ingredient);\n found = true;\n break;\n }\n }\n if (found) {\n break;\n }\n }\n if (!found) {\n categories.other!.push(ingredient);\n }\n }\n\n this.categories = categories;\n }\n}\n"],"mappings":"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","names":["escapeCache","Map","Flags","GLOBAL","NON_SENSITIVE","MULTILINE","DOT_ALL","UNICODE","STICKY","Ranges","Object","freeze","digit","lowercaseLetter","uppercaseLetter","letter","alphanumeric","anyCharacter","Quantifiers","zeroOrMore","oneOrMore","optional","HumanRegex","constructor","this","parts","flags","Set","add","special","word","whitespace","nonWhitespace","text","has","set","replace","get","or","name","range","Error","chars","lazy","lastPart","pop","newline","pattern","hasSpecialCharacter","hasDigit","hasLetter","n","min","max","startGroup","startCaptureGroup","wordBoundary","nonWordBoundary","endGroup","startAnchor","endAnchor","global","nonSensitive","multiline","dotAll","sticky","variant","validVariants","unicodeDigit","unicodePunctuation","unicodeSymbol","count","length","push","ipv4Octet","protocol","www","tld","path","part","toString","join","toRegExp","RegExp","createRegex","Patterns","createCachedPattern","builder","regex","source","email","literal","atLeast","url","phoneInternational","between","r","a","Big","i","e","import_big","Big","i"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/classes/category_config.ts","../src/classes/product_catalog.ts","../node_modules/.pnpm/human-regex@2.2.0/node_modules/human-regex/src/human-regex.ts","../src/regex.ts","../src/units/definitions.ts","../src/quantities/numeric.ts","../src/errors.ts","../src/utils/type_guards.ts","../src/quantities/mutations.ts","../src/utils/parser_helpers.ts","../src/classes/section.ts","../src/quantities/alternatives.ts","../src/units/conversion.ts","../src/units/lookup.ts","../src/utils/general.ts","../src/classes/recipe.ts","../src/classes/shopping_list.ts","../src/classes/shopping_cart.ts"],"sourcesContent":["import { CategoryConfig } from \"./classes/category_config\";\nimport { ProductCatalog } from \"./classes/product_catalog\";\nimport { Recipe } from \"./classes/recipe\";\nimport { ShoppingList } from \"./classes/shopping_list\";\nimport {\n ShoppingCart,\n type ShoppingCartOptions,\n type ShoppingCartSummary,\n} from \"./classes/shopping_cart\";\nimport { Section } from \"./classes/section\";\n\nexport {\n CategoryConfig,\n ProductCatalog,\n Recipe,\n ShoppingList,\n ShoppingCart,\n Section,\n};\n\nimport type {\n Metadata,\n Ingredient,\n IngredientFlag,\n IngredientExtras,\n FixedValue,\n Range,\n DecimalValue,\n FractionValue,\n TextValue,\n FixedNumericValue,\n Timer,\n TextItem,\n IngredientItem,\n IngredientItemQuantity,\n IngredientAlternative,\n CookwareItem,\n TimerItem,\n Item,\n Step,\n Note,\n Cookware,\n CookwareFlag,\n CategorizedIngredients,\n AddedRecipe,\n AddedRecipeOptions,\n AddedIngredient,\n RecipeWithFactor,\n RecipeWithServings,\n CategoryIngredient,\n Category,\n ProductOptionBase,\n ProductOptionCore,\n ProductOption,\n ProductSize,\n ProductSelection,\n CartContent,\n ProductMatch,\n CartMatch,\n ProductMisMatch,\n CartMisMatch,\n NoProductMatchErrorCode,\n ComputedIngredient,\n QuantityBase,\n QuantityWithPlainUnit,\n QuantityWithExtendedUnit,\n QuantityWithUnitDef,\n QuantityWithUnitLike,\n Unit,\n UnitSystem,\n UnitType,\n UnitDefinition,\n UnitDefinitionLike,\n MaybeNestedGroup,\n MaybeNestedAndGroup,\n MaybeNestedOrGroup,\n IngredientQuantityGroup,\n IngredientQuantityAndGroup,\n AlternativeIngredientRef,\n RecipeChoices,\n RecipeAlternatives,\n} from \"./types\";\n\nexport {\n ShoppingCartOptions,\n ShoppingCartSummary,\n Metadata,\n Ingredient,\n IngredientFlag,\n IngredientExtras,\n FixedValue,\n Range,\n DecimalValue,\n FractionValue,\n TextValue,\n FixedNumericValue,\n Timer,\n TextItem,\n IngredientItem,\n IngredientItemQuantity,\n IngredientAlternative,\n CookwareItem,\n TimerItem,\n Item,\n Step,\n Note,\n Cookware,\n CookwareFlag,\n CategorizedIngredients,\n AddedRecipe,\n AddedRecipeOptions,\n AddedIngredient,\n RecipeWithFactor,\n RecipeWithServings,\n CategoryIngredient,\n Category,\n ProductOptionBase,\n ProductOptionCore,\n ProductOption,\n ProductSize,\n ProductSelection,\n CartContent,\n ProductMatch,\n CartMatch,\n ProductMisMatch,\n CartMisMatch,\n NoProductMatchErrorCode,\n ComputedIngredient,\n QuantityBase,\n QuantityWithPlainUnit,\n QuantityWithExtendedUnit,\n QuantityWithUnitDef,\n QuantityWithUnitLike,\n Unit,\n UnitSystem,\n UnitType,\n UnitDefinition,\n UnitDefinitionLike,\n MaybeNestedGroup,\n MaybeNestedAndGroup,\n MaybeNestedOrGroup,\n IngredientQuantityGroup,\n IngredientQuantityAndGroup,\n AlternativeIngredientRef,\n RecipeChoices,\n RecipeAlternatives,\n};\n\nimport {\n NoProductCatalogForCartError,\n NoShoppingListForCartError,\n} from \"./errors\";\n\nexport { NoProductCatalogForCartError, NoShoppingListForCartError };\n","import type { Category, CategoryIngredient } from \"../types\";\n\n/**\n * Parser for category configurations specified à-la-cooklang.\n *\n * ## Usage\n *\n * You can either directly provide the category configuration string when creating the instance\n * e.g. `const categoryConfig = new CategoryConfig(<...>)`, or create it first and then pass\n * the category configuration string to the {@link CategoryConfig.parse | parse()} method.\n *\n * The initialized `CategoryConfig` can then be fed to a {@link ShoppingList}\n *\n * @example\n * ```typescript\n * import { CategoryConfig } from @tmlmt/cooklang-parser;\n *\n * const categoryConfigString = `\n * [Dairy]\n * milk\n * butter\n *\n * [Bakery]\n * flour\n * sugar`;\n *\n * const categoryConfig = new CategoryConfig(categoryConfigString);\n * ```\n *\n * @see [Category Configuration](https://cooklang.org/docs/spec/#shopping-lists) section of the cooklang specs\n *\n * @category Classes\n */\nexport class CategoryConfig {\n /**\n * The parsed categories of ingredients.\n */\n categories: Category[] = [];\n\n /**\n * Creates a new CategoryConfig instance.\n * @param config - The category configuration to parse.\n */\n constructor(config?: string) {\n if (config) {\n this.parse(config);\n }\n }\n\n /**\n * Parses a category configuration from a string into property\n * {@link CategoryConfig.categories | categories}\n * @param config - The category configuration to parse.\n */\n parse(config: string) {\n let currentCategory: Category | null = null;\n const categoryNames = new Set<string>();\n const ingredientNames = new Set<string>();\n\n for (const line of config.split(\"\\n\")) {\n const trimmedLine = line.trim();\n\n if (trimmedLine.length === 0) {\n continue;\n }\n\n if (trimmedLine.startsWith(\"[\") && trimmedLine.endsWith(\"]\")) {\n const categoryName = trimmedLine\n .substring(1, trimmedLine.length - 1)\n .trim();\n\n if (categoryNames.has(categoryName)) {\n throw new Error(`Duplicate category found: ${categoryName}`);\n }\n categoryNames.add(categoryName);\n\n currentCategory = { name: categoryName, ingredients: [] };\n this.categories.push(currentCategory);\n } else {\n if (currentCategory === null) {\n throw new Error(\n `Ingredient found without a category: ${trimmedLine}`,\n );\n }\n\n const aliases = trimmedLine.split(\"|\").map((s) => s.trim());\n for (const alias of aliases) {\n if (ingredientNames.has(alias)) {\n throw new Error(`Duplicate ingredient/alias found: ${alias}`);\n }\n ingredientNames.add(alias);\n }\n\n const ingredient: CategoryIngredient = {\n name: aliases[0]!, // We know this exists because trimmedLine is not empty\n aliases: aliases,\n };\n currentCategory.ingredients.push(ingredient);\n }\n }\n }\n}\n","import TOML from \"smol-toml\";\nimport type {\n FixedNumericValue,\n ProductOption,\n ProductOptionToml,\n ProductSize,\n} from \"../types\";\nimport type { TomlTable } from \"smol-toml\";\nimport {\n isPositiveIntegerString,\n parseQuantityInput,\n stringifyQuantityValue,\n} from \"../utils/parser_helpers\";\nimport { InvalidProductCatalogFormat } from \"../errors\";\n\n/**\n * Product Catalog Manager: used in conjunction with {@link ShoppingCart}\n *\n * ## Usage\n *\n * You can either directly populate the products by feeding the {@link ProductCatalog.products | products} property. Alternatively,\n * you can provide a catalog in TOML format to either the constructor itself or to the {@link ProductCatalog.parse | parse()} method.\n *\n * @category Classes\n *\n * @example\n * ```typescript\n * import { ProductCatalog } from \"@tmlmt/cooklang-parser\";\n *\n * const catalog = `\n * [eggs]\n * aliases = [\"oeuf\", \"huevo\"]\n * 01123 = { name = \"Single Egg\", size = \"1\", price = 2 }\n * 11244 = { name = \"Pack of 6 eggs\", size = \"6\", price = 10 }\n *\n * [flour]\n * aliases = [\"farine\", \"Mehl\"]\n * 01124 = { name = \"Small pack\", size = \"100%g\", price = 1.5 }\n * 14141 = { name = \"Big pack\", size = \"6%kg\", price = 10 }\n * `\n * const catalog = new ProductCatalog(catalog);\n * const eggs = catalog.find(\"oeuf\");\n * ```\n */\nexport class ProductCatalog {\n public products: ProductOption[] = [];\n\n constructor(tomlContent?: string) {\n if (tomlContent) this.parse(tomlContent);\n }\n\n /**\n * Parses a TOML string into a list of product options.\n * @param tomlContent - The TOML string to parse.\n * @returns A parsed list of `ProductOption`.\n */\n public parse(tomlContent: string): ProductOption[] {\n const catalogRaw = TOML.parse(tomlContent);\n\n // Reset internal state\n this.products = [];\n\n if (!this.isValidTomlContent(catalogRaw)) {\n throw new InvalidProductCatalogFormat();\n }\n\n for (const [ingredientName, ingredientData] of Object.entries(catalogRaw)) {\n const ingredientTable = ingredientData as TomlTable;\n const aliases = ingredientTable.aliases as string[] | undefined;\n\n for (const [key, productData] of Object.entries(ingredientTable)) {\n if (key === \"aliases\") {\n continue;\n }\n\n const productId = key;\n const { name, size, price, ...rest } =\n productData as unknown as ProductOptionToml;\n\n // Handle size as string or string[]\n const sizeStrings = Array.isArray(size) ? size : [size];\n const sizes: ProductSize[] = sizeStrings.map((sizeStr) => {\n const sizeAndUnitRaw = sizeStr.split(\"%\");\n const sizeParsed = parseQuantityInput(\n sizeAndUnitRaw[0]!,\n ) as FixedNumericValue;\n const productSize: ProductSize = { size: sizeParsed };\n if (sizeAndUnitRaw.length > 1) {\n productSize.unit = sizeAndUnitRaw[1]!;\n }\n return productSize;\n });\n\n const productOption: ProductOption = {\n id: productId,\n productName: name,\n ingredientName: ingredientName,\n price: price,\n sizes,\n ...rest,\n };\n if (aliases) {\n productOption.ingredientAliases = aliases;\n }\n\n this.products.push(productOption);\n }\n }\n\n return this.products;\n }\n\n /**\n * Stringifies the catalog to a TOML string.\n * @returns The TOML string representation of the catalog.\n */\n public stringify(): string {\n const grouped: Record<string, TomlTable> = {};\n\n for (const product of this.products) {\n const {\n id,\n ingredientName,\n ingredientAliases,\n sizes,\n productName,\n ...rest\n } = product;\n if (!grouped[ingredientName]) {\n grouped[ingredientName] = {};\n }\n if (ingredientAliases && !grouped[ingredientName].aliases) {\n grouped[ingredientName].aliases = ingredientAliases;\n }\n\n // Stringify each size as \"value%unit\" or just \"value\"\n const sizeStrings = sizes.map((s) =>\n s.unit\n ? `${stringifyQuantityValue(s.size)}%${s.unit}`\n : stringifyQuantityValue(s.size),\n );\n\n grouped[ingredientName][id] = {\n ...rest,\n name: productName,\n // Use array if multiple sizes, otherwise single string\n size: sizeStrings.length === 1 ? sizeStrings[0]! : sizeStrings,\n };\n }\n\n return TOML.stringify(grouped);\n }\n\n /**\n * Adds a product to the catalog.\n * @param productOption - The product to add.\n */\n public add(productOption: ProductOption): void {\n this.products.push(productOption);\n }\n\n /**\n * Removes a product from the catalog by its ID.\n * @param productId - The ID of the product to remove.\n */\n public remove(productId: string): void {\n this.products = this.products.filter((product) => product.id !== productId);\n }\n\n private isValidTomlContent(catalog: TomlTable): boolean {\n for (const productsRaw of Object.values(catalog)) {\n if (typeof productsRaw !== \"object\" || productsRaw === null) {\n return false;\n }\n\n for (const [id, obj] of Object.entries(productsRaw)) {\n if (id === \"aliases\") {\n if (!Array.isArray(obj)) {\n return false;\n }\n } else {\n if (!isPositiveIntegerString(id)) {\n return false;\n }\n if (typeof obj !== \"object\" || obj === null) {\n return false;\n }\n\n const record = obj as Record<string, unknown>;\n const keys = Object.keys(record);\n\n const mandatoryKeys = [\"name\", \"size\", \"price\"];\n\n if (mandatoryKeys.some((key) => !keys.includes(key))) {\n return false;\n }\n\n const hasProductName = typeof record.name === \"string\";\n // Size can be a string or an array of strings\n const hasSize =\n typeof record.size === \"string\" ||\n (Array.isArray(record.size) &&\n record.size.every((s) => typeof s === \"string\"));\n const hasPrice = typeof record.price === \"number\";\n\n if (!(hasProductName && hasSize && hasPrice)) {\n return false;\n }\n }\n }\n }\n\n return true;\n }\n}\n","type PartialBut<T, K extends keyof T> = Partial<T> & Pick<T, K>;\n\nconst escapeCache = new Map<string, string>();\n\nconst Flags = {\n GLOBAL: \"g\",\n NON_SENSITIVE: \"i\",\n MULTILINE: \"m\",\n DOT_ALL: \"s\",\n UNICODE: \"u\",\n STICKY: \"y\",\n} as const;\n\nconst Ranges = Object.freeze({\n digit: \"0-9\",\n lowercaseLetter: \"a-z\",\n uppercaseLetter: \"A-Z\",\n letter: \"a-zA-Z\",\n alphanumeric: \"a-zA-Z0-9\",\n anyCharacter: \".\",\n});\n\ntype RangeKeys = keyof typeof Ranges;\n\nconst Quantifiers = Object.freeze({\n zeroOrMore: \"*\",\n oneOrMore: \"+\",\n optional: \"?\",\n});\n\ntype Quantifiers =\n | \"exactly\"\n | \"atLeast\"\n | \"atMost\"\n | \"between\"\n | \"oneOrMore\"\n | \"zeroOrMore\"\n | \"repeat\";\ntype QuantifierMethods = Quantifiers | \"optional\" | \"lazy\";\n\ntype WithLazy = HumanRegex;\ntype Base = Omit<HumanRegex, \"lazy\">;\ntype AtStart = Omit<Base, QuantifierMethods | \"endGroup\">;\ntype AfterAnchor = Omit<Base, QuantifierMethods | \"or\">;\ntype SimpleQuantifier = Omit<Base, Quantifiers>;\ntype LazyQuantifier = Omit<WithLazy, Quantifiers>;\n\nclass HumanRegex {\n private parts: string[];\n private flags: Set<string>;\n\n constructor() {\n this.parts = [];\n this.flags = new Set<string>();\n }\n\n digit(): Base {\n return this.add(\"\\\\d\");\n }\n\n special(): Base {\n return this.add(\"(?=.*[!@#$%^&*])\");\n }\n\n word(): Base {\n return this.add(\"\\\\w\");\n }\n\n whitespace(): Base {\n return this.add(\"\\\\s\");\n }\n\n nonWhitespace(): Base {\n return this.add(\"\\\\S\");\n }\n\n literal(text: string): this {\n return this.add(escapeLiteral(text));\n }\n\n or(): AfterAnchor {\n return this.add(\"|\");\n }\n\n range(name: RangeKeys): Base {\n const range = Ranges[name];\n if (!range) throw new Error(`Unknown range: ${name}`);\n return this.add(`[${range}]`);\n }\n\n notRange(name: RangeKeys): Base {\n const range = Ranges[name];\n if (!range) throw new Error(`Unknown range: ${name}`);\n return this.add(`[^${range}]`);\n }\n\n anyOf(chars: string): Base {\n return this.add(`[${chars}]`);\n }\n\n notAnyOf(chars: string): Base {\n return this.add(`[^${chars}]`);\n }\n\n lazy(): Base {\n const lastPart = this.parts.pop();\n if (!lastPart) throw new Error(\"No quantifier to make lazy\");\n return this.add(`${lastPart}?`);\n }\n\n letter(): Base {\n return this.add(\"[a-zA-Z]\");\n }\n\n anyCharacter(): Base {\n return this.add(\".\");\n }\n\n newline(): Base {\n return this.add(\"(\\\\r\\\\n|\\\\r|\\\\n)\"); // Windows: \\r\\n, Unix: \\n, Old Macs: \\r\n }\n\n negativeLookahead(pattern: string): Base {\n return this.add(`(?!${pattern})`);\n }\n\n positiveLookahead(pattern: string): Base {\n return this.add(`(?=${pattern})`);\n }\n\n positiveLookbehind(pattern: string): Base {\n return this.add(`(?<=${pattern})`);\n }\n\n negativeLookbehind(pattern: string): Base {\n return this.add(`(?<!${pattern})`);\n }\n\n hasSpecialCharacter(): Base {\n return this.add(\"(?=.*[!@#$%^&*])\");\n }\n\n hasDigit(): Base {\n return this.add(\"(?=.*\\\\d)\");\n }\n\n hasLetter(): Base {\n return this.add(\"(?=.*[a-zA-Z])\");\n }\n\n optional(): SimpleQuantifier {\n return this.add(Quantifiers.optional);\n }\n\n exactly(n: number): SimpleQuantifier {\n return this.add(`{${n}}`);\n }\n\n atLeast(n: number): LazyQuantifier {\n return this.add(`{${n},}`);\n }\n\n atMost(n: number): LazyQuantifier {\n return this.add(`{0,${n}}`);\n }\n\n between(min: number, max: number): LazyQuantifier {\n return this.add(`{${min},${max}}`);\n }\n\n oneOrMore(): LazyQuantifier {\n return this.add(Quantifiers.oneOrMore);\n }\n\n zeroOrMore(): LazyQuantifier {\n return this.add(Quantifiers.zeroOrMore);\n }\n\n startNamedGroup(name: string): AfterAnchor {\n return this.add(`(?<${name}>`);\n }\n\n startGroup(): AfterAnchor {\n return this.add(\"(?:\");\n }\n\n startCaptureGroup(): AfterAnchor {\n return this.add(\"(\");\n }\n\n wordBoundary(): Base {\n return this.add(\"\\\\b\");\n }\n\n nonWordBoundary(): Base {\n return this.add(\"\\\\B\");\n }\n\n endGroup(): Base {\n return this.add(\")\");\n }\n\n startAnchor(): AfterAnchor {\n return this.add(\"^\");\n }\n\n endAnchor(): AfterAnchor {\n return this.add(\"$\");\n }\n\n global(): this {\n this.flags.add(Flags.GLOBAL);\n return this;\n }\n\n nonSensitive(): this {\n this.flags.add(Flags.NON_SENSITIVE);\n return this;\n }\n\n multiline(): this {\n this.flags.add(Flags.MULTILINE);\n return this;\n }\n\n dotAll(): this {\n this.flags.add(Flags.DOT_ALL);\n return this;\n }\n\n sticky(): this {\n this.flags.add(Flags.STICKY);\n return this;\n }\n\n unicodeChar(variant?: \"u\" | \"l\" | \"t\" | \"m\" | \"o\"): Base {\n this.flags.add(Flags.UNICODE);\n const validVariants = new Set([\"u\", \"l\", \"t\", \"m\", \"o\"] as const);\n\n if (variant !== undefined && !validVariants.has(variant)) {\n throw new Error(`Invalid Unicode letter variant: ${variant}`);\n }\n\n return this.add(`\\\\p{L${variant ?? \"\"}}`);\n }\n\n unicodeDigit(): Base {\n this.flags.add(Flags.UNICODE);\n return this.add(\"\\\\p{N}\");\n }\n\n unicodePunctuation(): Base {\n this.flags.add(Flags.UNICODE);\n return this.add(\"\\\\p{P}\");\n }\n\n unicodeSymbol(): Base {\n this.flags.add(Flags.UNICODE);\n return this.add(\"\\\\p{S}\");\n }\n\n repeat(count: number): Base {\n if (this.parts.length === 0) {\n throw new Error(\"No pattern to repeat\");\n }\n\n const lastPart = this.parts.pop();\n this.parts.push(`(${lastPart}){${count}}`);\n return this;\n }\n\n ipv4Octet(): Base {\n return this.add(\"(25[0-5]|2[0-4]\\\\d|1\\\\d\\\\d|[1-9]\\\\d|\\\\d)\");\n }\n\n protocol(): Base {\n return this.add(\"https?://\");\n }\n\n www(): Base {\n return this.add(\"(www\\\\.)?\");\n }\n\n tld(): Base {\n return this.add(\"(com|org|net)\");\n }\n\n path(): Base {\n return this.add(\"(/\\\\w+)*\");\n }\n\n private add(part: string): this {\n this.parts.push(part);\n return this;\n }\n\n toString(): string {\n return this.parts.join(\"\");\n }\n\n toRegExp(): RegExp {\n return new RegExp(this.toString(), [...this.flags].join(\"\"));\n }\n}\n\nfunction escapeLiteral(text: string): string {\n if (!escapeCache.has(text)) {\n escapeCache.set(text, text.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"));\n }\n return escapeCache.get(text)!;\n}\n\nconst createRegex = (): AtStart => new HumanRegex();\n\nconst Patterns = (() => {\n const createCachedPattern = (\n builder: () => PartialBut<HumanRegex, \"toRegExp\">\n ) => {\n const regex = builder().toRegExp();\n return () => new RegExp(regex.source, regex.flags);\n };\n\n return {\n email: createCachedPattern(() =>\n createRegex()\n .startAnchor()\n .word()\n .oneOrMore()\n .literal(\"@\")\n .word()\n .oneOrMore()\n .startGroup()\n .literal(\".\")\n .word()\n .oneOrMore()\n .endGroup()\n .zeroOrMore()\n .literal(\".\")\n .letter()\n .atLeast(2)\n .endAnchor()\n ),\n url: createCachedPattern(() =>\n createRegex()\n .startAnchor()\n .protocol()\n .www()\n .word()\n .oneOrMore()\n .literal(\".\")\n .tld()\n .path()\n .endAnchor()\n ),\n phoneInternational: createCachedPattern(() =>\n createRegex()\n .startAnchor()\n .literal(\"+\")\n .digit()\n .between(1, 3)\n .literal(\"-\")\n .digit()\n .between(3, 14)\n .endAnchor()\n ),\n };\n})();\n\nexport { createRegex, Patterns, Flags, Ranges, Quantifiers };\n","import { createRegex } from \"human-regex\";\n\nexport const metadataRegex = createRegex()\n .literal(\"---\").newline()\n .startCaptureGroup().anyCharacter().zeroOrMore().optional().endGroup()\n .newline().literal(\"---\")\n .dotAll().toRegExp();\n\nexport const scalingMetaValueRegex = (varName: string): RegExp => createRegex()\n .startAnchor()\n .literal(varName)\n .literal(\":\")\n .anyOf(\"\\\\t \").zeroOrMore()\n .startCaptureGroup()\n .startCaptureGroup()\n .notAnyOf(\",\\\\n\").oneOrMore()\n .endGroup()\n .startGroup()\n .literal(\",\")\n .whitespace().zeroOrMore()\n .startCaptureGroup()\n .anyCharacter().oneOrMore()\n .endGroup()\n .endGroup().optional()\n .endGroup()\n .endAnchor()\n .multiline()\n .toRegExp()\n\nconst nonWordChar = \"\\\\s@#~\\\\[\\\\]{(,;:!?\"\nconst nonWordCharStrict = \"\\\\s@#~\\\\[\\\\]{(,;:!?|\"\n\nexport const ingredientWithAlternativeRegex = createRegex()\n .literal(\"@\")\n .startNamedGroup(\"ingredientModifiers\")\n .anyOf(\"@\\\\-&?\").zeroOrMore()\n .endGroup().optional()\n .startNamedGroup(\"ingredientRecipeAnchor\")\n .literal(\"./\")\n .endGroup().optional()\n .startGroup()\n .startGroup()\n .startNamedGroup(\"mIngredientName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .startGroup()\n .whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore()\n .endGroup().oneOrMore()\n .endGroup()\n .positiveLookahead(\"\\\\s*(?:\\\\{[^\\\\}]*\\\\}|\\\\([^)]*\\\\))\")\n .endGroup()\n .or()\n .startNamedGroup(\"sIngredientName\")\n .notAnyOf(nonWordChar).zeroOrMore()\n .notAnyOf(\"\\\\.\"+nonWordChar)\n .endGroup()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"ingredientQuantityModifier\")\n .literal(\"=\").exactly(1)\n .endGroup().optional()\n .startNamedGroup(\"ingredientQuantity\")\n .startGroup()\n .notAnyOf(\"}|%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .notAnyOf(\"|}\").oneOrMore().lazy()\n .endGroup().optional()\n .startGroup()\n .literal(\"|\")\n .notAnyOf(\"}\").oneOrMore().lazy()\n .endGroup().zeroOrMore()\n .endGroup()\n .literal(\"}\")\n .endGroup().optional()\n .startGroup()\n .literal(\"(\")\n .startNamedGroup(\"ingredientPreparation\")\n .notAnyOf(\")\").oneOrMore().lazy()\n .endGroup()\n .literal(\")\")\n .endGroup().optional()\n .startGroup()\n .literal(\"[\")\n .startNamedGroup(\"ingredientNote\")\n .notAnyOf(\"\\\\]\").oneOrMore().lazy()\n .endGroup()\n .literal(\"]\")\n .endGroup().optional()\n .startNamedGroup(\"ingredientAlternative\")\n .startGroup()\n .literal(\"|\")\n .startGroup()\n .anyOf(\"@\\\\-&?\").zeroOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"./\")\n .endGroup().optional()\n .startGroup()\n .startGroup()\n .startGroup()\n .notAnyOf(nonWordChar).oneOrMore()\n .startGroup()\n .whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore()\n .endGroup().oneOrMore()\n .endGroup()\n .positiveLookahead(\"\\\\s*(?:\\\\{[^\\\\}]*\\\\}|\\\\([^)]*\\\\))\")\n .endGroup()\n .or()\n .startGroup()\n .notAnyOf(nonWordChar).oneOrMore()\n .endGroup()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startGroup()\n .literal(\"=\").exactly(1)\n .endGroup().optional()\n .startGroup()\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startGroup()\n .notAnyOf(\"}\").oneOrMore().lazy()\n .endGroup()\n .endGroup().optional()\n .literal(\"}\")\n .endGroup().optional()\n .startGroup()\n .literal(\"(\")\n .startGroup()\n .notAnyOf(\")\").oneOrMore().lazy()\n .endGroup()\n .literal(\")\")\n .endGroup().optional()\n .startGroup()\n .literal(\"[\")\n .startGroup()\n .notAnyOf(\"\\\\]\").oneOrMore().lazy()\n .endGroup()\n .literal(\"]\")\n .endGroup().optional()\n .endGroup().zeroOrMore()\n .endGroup()\n .toRegExp();\n\nexport const inlineIngredientAlternativesRegex = new RegExp(\"\\\\|\" + ingredientWithAlternativeRegex.source.slice(1))\n\nexport const quantityAlternativeRegex = createRegex()\n .startNamedGroup(\"ingredientQuantityValue\")\n .notAnyOf(\"}|%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"ingredientUnit\")\n .notAnyOf(\"|}\").oneOrMore()\n .endGroup()\n .endGroup().optional()\n .startGroup()\n .literal(\"|\")\n .startNamedGroup(\"ingredientAltQuantity\")\n .startGroup()\n .notAnyOf(\"}\").oneOrMore()\n .endGroup().zeroOrMore()\n .endGroup()\n .endGroup().optional()\n .toRegExp()\n \nexport const ingredientWithGroupKeyRegex = createRegex()\n .literal(\"@|\")\n .startNamedGroup(\"gIngredientGroupKey\")\n .notAnyOf(nonWordCharStrict).oneOrMore()\n .endGroup()\n .literal(\"|\")\n .startNamedGroup(\"gIngredientModifiers\")\n .anyOf(\"@\\\\-&?\").zeroOrMore()\n .endGroup().optional()\n .startNamedGroup(\"gIngredientRecipeAnchor\")\n .literal(\"./\")\n .endGroup().optional()\n .startGroup()\n .startGroup()\n .startNamedGroup(\"gmIngredientName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .startGroup()\n .whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore()\n .endGroup().oneOrMore()\n .endGroup()\n .positiveLookahead(\"\\\\s*(?:\\\\{[^\\\\}]*\\\\}|\\\\([^)]*\\\\))\")\n .endGroup()\n .or()\n .startNamedGroup(\"gsIngredientName\")\n .notAnyOf(nonWordChar).zeroOrMore()\n .notAnyOf(\"\\\\.\"+nonWordChar)\n .endGroup()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"gIngredientQuantityModifier\")\n .literal(\"=\").exactly(1)\n .endGroup().optional()\n .startNamedGroup(\"gIngredientQuantity\")\n .startGroup()\n .notAnyOf(\"}|%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .notAnyOf(\"|}\").oneOrMore().lazy()\n .endGroup().optional()\n .startGroup()\n .literal(\"|\")\n .notAnyOf(\"}\").oneOrMore().lazy()\n .endGroup().zeroOrMore()\n .endGroup()\n .literal(\"}\")\n .endGroup().optional()\n .startGroup()\n .literal(\"(\")\n .startNamedGroup(\"gIngredientPreparation\")\n .notAnyOf(\")\").oneOrMore().lazy()\n .endGroup()\n .literal(\")\")\n .endGroup().optional()\n .toRegExp()\n\nexport const ingredientAliasRegex = createRegex()\n .startAnchor()\n .startNamedGroup(\"ingredientListName\")\n .notAnyOf(\"|\").oneOrMore()\n .endGroup()\n .literal(\"|\")\n .startNamedGroup(\"ingredientDisplayName\")\n .notAnyOf(\"|\").oneOrMore()\n .endGroup()\n .endAnchor()\n .toRegExp();\n\nexport const cookwareRegex = createRegex()\n .literal(\"#\")\n .startNamedGroup(\"cookwareModifiers\")\n .anyOf(\"\\\\-&?\").zeroOrMore()\n .endGroup()\n .startGroup()\n .startGroup()\n .startNamedGroup(\"mCookwareName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .startGroup()\n .whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore()\n .endGroup().oneOrMore()\n .endGroup().positiveLookahead(\"\\\\s*(?:\\\\{[^\\\\}]*\\\\})\")\n .endGroup()\n .or()\n .startNamedGroup(\"sCookwareName\")\n .notAnyOf(nonWordChar).zeroOrMore()\n .notAnyOf(\"\\\\.\"+nonWordChar)\n .endGroup()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"cookwareQuantity\")\n .anyCharacter().zeroOrMore().lazy()\n .endGroup()\n .literal(\"}\")\n .endGroup().optional()\n .toRegExp();\n\nconst timerRegex = createRegex()\n .literal(\"~\")\n .startNamedGroup(\"timerName\")\n .anyCharacter().zeroOrMore().lazy()\n .endGroup()\n .literal(\"{\")\n .startNamedGroup(\"timerQuantity\")\n .anyCharacter().oneOrMore().lazy()\n .endGroup()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"timerUnit\")\n .anyCharacter().oneOrMore().lazy()\n .endGroup()\n .endGroup().optional()\n .literal(\"}\")\n .toRegExp()\n\nexport const tokensRegex = new RegExp(\n [\n ingredientWithGroupKeyRegex,\n ingredientWithAlternativeRegex,\n cookwareRegex,\n timerRegex,\n ]\n .map((r) => r.source)\n .join(\"|\"),\n \"gu\",\n);\n\nexport const commentRegex = createRegex()\n .literal(\"--\")\n .anyCharacter().zeroOrMore()\n .global()\n .toRegExp();\n\nexport const blockCommentRegex = createRegex()\n .literal(\"[-\")\n .anyCharacter().zeroOrMore().lazy()\n .literal(\"-]\")\n .whitespace().zeroOrMore()\n .global()\n .toRegExp();\n\nexport const shoppingListRegex = createRegex()\n .literal(\"[\")\n .startNamedGroup(\"name\")\n .anyCharacter().oneOrMore()\n .endGroup()\n .literal(\"]\")\n .newline()\n .startNamedGroup(\"items\")\n .anyCharacter().zeroOrMore().lazy()\n .endGroup()\n .startGroup()\n .newline().newline()\n .or()\n .endAnchor()\n .endGroup()\n .global()\n .toRegExp()\n\nexport const rangeRegex = createRegex()\n .startAnchor()\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".,/\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .literal(\"-\")\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".,/\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .endAnchor()\n .toRegExp()\n\nexport const numberLikeRegex = createRegex()\n .startAnchor()\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".,/\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .endAnchor()\n .toRegExp()\n\nexport const floatRegex = createRegex()\n .startAnchor()\n .digit().oneOrMore()\n .startGroup()\n .anyOf(\".\").exactly(1)\n .digit().oneOrMore()\n .endGroup().optional()\n .endAnchor()\n .toRegExp()","import type { UnitDefinition, UnitDefinitionLike } from \"../types\";\n\n// Base units: mass -> gram (g), volume -> milliliter (ml)\nexport const units: UnitDefinition[] = [\n // Mass (Metric)\n {\n name: \"g\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"gram\", \"grams\", \"grammes\"],\n toBase: 1,\n },\n {\n name: \"kg\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"kilogram\", \"kilograms\", \"kilogrammes\", \"kilos\", \"kilo\"],\n toBase: 1000,\n },\n // Mass (Imperial)\n {\n name: \"oz\",\n type: \"mass\",\n system: \"imperial\",\n aliases: [\"ounce\", \"ounces\"],\n toBase: 28.3495,\n },\n {\n name: \"lb\",\n type: \"mass\",\n system: \"imperial\",\n aliases: [\"pound\", \"pounds\"],\n toBase: 453.592,\n },\n\n // Volume (Metric)\n {\n name: \"ml\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"milliliter\", \"milliliters\", \"millilitre\", \"millilitres\", \"cc\"],\n toBase: 1,\n },\n {\n name: \"cl\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"centiliter\", \"centiliters\", \"centilitre\", \"centilitres\"],\n toBase: 10,\n },\n {\n name: \"dl\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"deciliter\", \"deciliters\", \"decilitre\", \"decilitres\"],\n toBase: 100,\n },\n {\n name: \"l\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"liter\", \"liters\", \"litre\", \"litres\"],\n toBase: 1000,\n },\n {\n name: \"tsp\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"teaspoon\", \"teaspoons\"],\n toBase: 5,\n },\n {\n name: \"tbsp\",\n type: \"volume\",\n system: \"metric\",\n aliases: [\"tablespoon\", \"tablespoons\"],\n toBase: 15,\n },\n\n // Volume (Imperial)\n {\n name: \"fl-oz\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"fluid ounce\", \"fluid ounces\"],\n toBase: 29.5735,\n },\n {\n name: \"cup\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"cups\"],\n toBase: 236.588,\n },\n {\n name: \"pint\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"pints\"],\n toBase: 473.176,\n },\n {\n name: \"quart\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"quarts\"],\n toBase: 946.353,\n },\n {\n name: \"gallon\",\n type: \"volume\",\n system: \"imperial\",\n aliases: [\"gallons\"],\n toBase: 3785.41,\n },\n\n // Count units (no conversion, but recognized as a type)\n {\n name: \"piece\",\n type: \"count\",\n system: \"metric\",\n aliases: [\"pieces\", \"pc\"],\n toBase: 1,\n },\n];\n\nconst unitMap = new Map<string, UnitDefinition>();\nfor (const unit of units) {\n unitMap.set(unit.name.toLowerCase(), unit);\n for (const alias of unit.aliases) {\n unitMap.set(alias.toLowerCase(), unit);\n }\n}\n\nexport function normalizeUnit(unit: string = \"\"): UnitDefinition | undefined {\n return unitMap.get(unit.toLowerCase().trim());\n}\n\nexport const NO_UNIT = \"__no-unit__\";\n\nexport function resolveUnit(\n name: string = NO_UNIT,\n integerProtected: boolean = false,\n): UnitDefinitionLike {\n const normalizedUnit = normalizeUnit(name);\n const resolvedUnit: UnitDefinitionLike = normalizedUnit\n ? { ...normalizedUnit, name }\n : { name, type: \"other\", system: \"none\" };\n return integerProtected\n ? { ...resolvedUnit, integerProtected: true }\n : resolvedUnit;\n}\n\nexport function isNoUnit(unit?: UnitDefinitionLike): boolean {\n if (!unit) return true;\n return resolveUnit(unit.name).name === NO_UNIT;\n}\n","import Big from \"big.js\";\nimport type { DecimalValue, FractionValue, FixedValue, Range } from \"../types\";\n\nfunction gcd(a: number, b: number): number {\n return b === 0 ? a : gcd(b, a % b);\n}\n\nexport function simplifyFraction(\n num: number,\n den: number,\n): DecimalValue | FractionValue {\n if (den === 0) {\n throw new Error(\"Denominator cannot be zero.\");\n }\n\n const commonDivisor = gcd(Math.abs(num), Math.abs(den));\n let simplifiedNum = num / commonDivisor;\n let simplifiedDen = den / commonDivisor;\n if (simplifiedDen < 0) {\n simplifiedNum = -simplifiedNum;\n simplifiedDen = -simplifiedDen;\n }\n\n if (simplifiedDen === 1) {\n return { type: \"decimal\", decimal: simplifiedNum };\n } else {\n return { type: \"fraction\", num: simplifiedNum, den: simplifiedDen };\n }\n}\n\nexport function getNumericValue(v: DecimalValue | FractionValue): number {\n if (v.type === \"decimal\") {\n return v.decimal;\n }\n return v.num / v.den;\n}\n\nexport function multiplyNumericValue(\n v: DecimalValue | FractionValue,\n factor: number | Big,\n): DecimalValue | FractionValue {\n if (v.type === \"decimal\") {\n return {\n type: \"decimal\",\n decimal: Big(v.decimal).times(factor).toNumber(),\n };\n }\n return simplifyFraction(Big(v.num).times(factor).toNumber(), v.den);\n}\n\nexport function addNumericValues(\n val1: DecimalValue | FractionValue,\n val2: DecimalValue | FractionValue,\n): DecimalValue | FractionValue {\n let num1: number;\n let den1: number;\n let num2: number;\n let den2: number;\n\n if (val1.type === \"decimal\") {\n num1 = val1.decimal;\n den1 = 1;\n } else {\n num1 = val1.num;\n den1 = val1.den;\n }\n\n if (val2.type === \"decimal\") {\n num2 = val2.decimal;\n den2 = 1;\n } else {\n num2 = val2.num;\n den2 = val2.den;\n }\n\n // Return 0 if both values are 0\n if (num1 === 0 && num2 === 0) {\n return { type: \"decimal\", decimal: 0 };\n }\n\n // We only return a fraction where both input values are fractions themselves or only one while the other is 0\n if (\n (val1.type === \"fraction\" && val2.type === \"fraction\") ||\n (val1.type === \"fraction\" &&\n val2.type === \"decimal\" &&\n val2.decimal === 0) ||\n (val2.type === \"fraction\" && val1.type === \"decimal\" && val1.decimal === 0)\n ) {\n const commonDen = den1 * den2;\n const sumNum = num1 * den2 + num2 * den1;\n return simplifyFraction(sumNum, commonDen);\n } else {\n return {\n type: \"decimal\",\n decimal: Big(num1).div(den1).add(Big(num2).div(den2)).toNumber(),\n };\n }\n}\n\nexport const toRoundedDecimal = (\n v: DecimalValue | FractionValue,\n): DecimalValue => {\n const value = v.type === \"decimal\" ? v.decimal : v.num / v.den;\n return { type: \"decimal\", decimal: Math.round(value * 1000) / 1000 };\n};\n\nexport function multiplyQuantityValue(\n value: FixedValue | Range,\n factor: number | Big,\n): FixedValue | Range {\n if (value.type === \"fixed\") {\n const newValue = multiplyNumericValue(\n value.value as DecimalValue | FractionValue,\n Big(factor),\n );\n if (\n factor === parseInt(factor.toString()) || // e.g. 2 === int\n Big(1).div(factor).toNumber() === parseInt(Big(1).div(factor).toString()) // e.g. 0.25 => 4 === int\n ) {\n // Preserve fractions\n return {\n type: \"fixed\",\n value: newValue,\n };\n }\n // We might multiply with big decimal number so rounding into decimal value\n return {\n type: \"fixed\",\n value: toRoundedDecimal(newValue),\n };\n }\n\n return {\n type: \"range\",\n min: multiplyNumericValue(value.min, factor),\n max: multiplyNumericValue(value.max, factor),\n };\n}\n\nexport function getAverageValue(q: FixedValue | Range): string | number {\n if (q.type === \"fixed\") {\n return q.value.type === \"text\" ? q.value.text : getNumericValue(q.value);\n } else {\n return (getNumericValue(q.min) + getNumericValue(q.max)) / 2;\n }\n}\n","import { IngredientFlag, CookwareFlag, NoProductMatchErrorCode } from \"./types\";\n\nexport class ReferencedItemCannotBeRedefinedError extends Error {\n constructor(\n item_type: \"ingredient\" | \"cookware\",\n item_name: string,\n new_modifier: IngredientFlag | CookwareFlag,\n ) {\n super(\n `The referenced ${item_type} \"${item_name}\" cannot be redefined as ${new_modifier}.\nYou can either remove the reference to create a new ${item_type} defined as ${new_modifier} or add the ${new_modifier} flag to the original definition of the ${item_type}`,\n );\n this.name = \"ReferencedItemCannotBeRedefinedError\";\n }\n}\n\n/**\n * Error thrown when trying to build a shopping cart without a product catalog\n * @category Errors\n */\nexport class NoProductCatalogForCartError extends Error {\n constructor() {\n super(\n `Cannot build a cart without a product catalog. Please set one using setProductCatalog()`,\n );\n this.name = \"NoProductCatalogForCartError\";\n }\n}\n\n/**\n * Error thrown when trying to build a shopping cart without a shopping list\n * @category Errors\n */\nexport class NoShoppingListForCartError extends Error {\n constructor() {\n super(\n `Cannot build a cart without a shopping list. Please set one using setShoppingList()`,\n );\n this.name = \"NoShoppingListForCartError\";\n }\n}\n\nexport class NoProductMatchError extends Error {\n code: NoProductMatchErrorCode;\n\n constructor(item_name: string, code: NoProductMatchErrorCode) {\n const messageMap: Record<NoProductMatchErrorCode, string> = {\n incompatibleUnits: `The units of the products in the catalogue are incompatible with ingredient ${item_name} in the shopping list.`,\n noProduct:\n \"No product was found linked to ingredient name ${item_name} in the shopping list\",\n textValue: `Ingredient ${item_name} has a text value as quantity and can therefore not be matched with any product in the catalogue.`,\n noQuantity: `Ingredient ${item_name} has no quantity and can therefore not be matched with any product in the catalogue.`,\n textValue_incompatibleUnits: `Multiple alternative quantities were provided for ingredient ${item_name} in the shopping list but they were either text values or no product in catalog were found to have compatible units`,\n };\n super(messageMap[code]);\n this.code = code;\n this.name = \"NoProductMatchError\";\n }\n}\n\nexport class InvalidProductCatalogFormat extends Error {\n constructor() {\n super(\"Invalid product catalog format.\");\n this.name = \"InvalidProductCatalogFormat\";\n }\n}\n\nexport class CannotAddTextValueError extends Error {\n constructor() {\n super(\"Cannot add a quantity with a text value.\");\n this.name = \"CannotAddTextValueError\";\n }\n}\n\nexport class IncompatibleUnitsError extends Error {\n constructor(unit1: string, unit2: string) {\n super(\n `Cannot add quantities with incompatible or unknown units: ${unit1} and ${unit2}`,\n );\n this.name = \"IncompatibleUnitsError\";\n }\n}\n\nexport class InvalidQuantityFormat extends Error {\n constructor(value: string) {\n super(`Invalid quantity format found in: ${value}`);\n this.name = \"InvalidQuantityFormat\";\n }\n}\n","import type {\n Group,\n OrGroup,\n AndGroup,\n QuantityWithUnitLike,\n DecimalValue,\n FractionValue,\n FixedValue,\n Range,\n} from \"../types\";\n\n// Helper type-checks (as before)\nexport function isGroup(x: QuantityWithUnitLike | Group): x is Group {\n return x && \"type\" in x;\n}\nexport function isOrGroup(x: QuantityWithUnitLike | Group): x is OrGroup {\n return isGroup(x) && x.type === \"or\";\n}\nexport function isAndGroup(x: QuantityWithUnitLike | Group): x is AndGroup {\n return isGroup(x) && x.type === \"and\";\n}\nexport function isQuantity(\n x: QuantityWithUnitLike | Group,\n): x is QuantityWithUnitLike {\n return x && typeof x === \"object\" && \"quantity\" in x;\n}\n\nfunction isNumericValueIntegerLike(v: DecimalValue | FractionValue): boolean {\n if (v.type === \"decimal\") return Number.isInteger(v.decimal);\n // fraction: integer-like when numerator divisible by denominator\n return v.num % v.den === 0;\n}\n\nexport function isValueIntegerLike(q: FixedValue | Range): boolean {\n if (q.type === \"fixed\") {\n if (q.value.type === \"text\") return false;\n return isNumericValueIntegerLike(q.value);\n }\n // Range: integer-like when both min and max are integer-like\n return isNumericValueIntegerLike(q.min) && isNumericValueIntegerLike(q.max);\n}\n","import type {\n FixedValue,\n Range,\n DecimalValue,\n FractionValue,\n Unit,\n UnitDefinition,\n QuantityWithPlainUnit,\n QuantityWithExtendedUnit,\n QuantityWithUnitDef,\n MaybeNestedGroup,\n} from \"../types\";\nimport {\n units,\n normalizeUnit,\n resolveUnit,\n isNoUnit,\n} from \"../units/definitions\";\nimport { addNumericValues, multiplyQuantityValue } from \"./numeric\";\nimport { CannotAddTextValueError, IncompatibleUnitsError } from \"../errors\";\nimport { isGroup, isOrGroup, isQuantity } from \"../utils/type_guards\";\n\n// `deNormalizeQuantity` is provided by `./math` and re-exported below.\n\nexport function extendAllUnits(\n q: QuantityWithPlainUnit | MaybeNestedGroup<QuantityWithPlainUnit>,\n): QuantityWithExtendedUnit | MaybeNestedGroup<QuantityWithExtendedUnit> {\n if (isGroup(q)) {\n return { ...q, entries: q.entries.map(extendAllUnits) };\n } else {\n const newQ: QuantityWithExtendedUnit = {\n quantity: q.quantity,\n };\n if (q.unit) {\n newQ.unit = { name: q.unit };\n }\n return newQ;\n }\n}\n\nexport function normalizeAllUnits(\n q: QuantityWithPlainUnit | MaybeNestedGroup<QuantityWithPlainUnit>,\n): QuantityWithUnitDef | MaybeNestedGroup<QuantityWithUnitDef> {\n if (isGroup(q)) {\n return { ...q, entries: q.entries.map(normalizeAllUnits) };\n } else {\n const newQ: QuantityWithUnitDef = {\n quantity: q.quantity,\n unit: resolveUnit(q.unit),\n };\n return newQ;\n }\n}\n\nexport const convertQuantityValue = (\n value: FixedValue | Range,\n def: UnitDefinition,\n targetDef: UnitDefinition,\n): FixedValue | Range => {\n if (def.name === targetDef.name) return value;\n\n const factor = def.toBase / targetDef.toBase;\n\n return multiplyQuantityValue(value, factor);\n};\n\n/**\n * Get the default / neutral quantity which can be provided to addQuantity\n * for it to return the other value as result\n *\n * @return zero\n */\nexport function getDefaultQuantityValue(): FixedValue {\n return { type: \"fixed\", value: { type: \"decimal\", decimal: 0 } };\n}\n\n/**\n * Adds two quantity values together.\n *\n * - Adding two {@link FixedValue}s returns a new {@link FixedValue}.\n * - Adding a {@link Range} to any value returns a {@link Range}.\n *\n * @param v1 - The first quantity value.\n * @param v2 - The second quantity value.\n * @returns A new quantity value representing the sum.\n */\nexport function addQuantityValues(v1: FixedValue, v2: FixedValue): FixedValue;\nexport function addQuantityValues(\n v1: FixedValue | Range,\n v2: FixedValue | Range,\n): Range;\n\nexport function addQuantityValues(\n v1: FixedValue | Range,\n v2: FixedValue | Range,\n): FixedValue | Range {\n if (\n (v1.type === \"fixed\" && v1.value.type === \"text\") ||\n (v2.type === \"fixed\" && v2.value.type === \"text\")\n ) {\n throw new CannotAddTextValueError();\n }\n\n if (v1.type === \"fixed\" && v2.type === \"fixed\") {\n const res = addNumericValues(\n v1.value as DecimalValue | FractionValue,\n v2.value as DecimalValue | FractionValue,\n );\n return { type: \"fixed\", value: res };\n }\n const r1 =\n v1.type === \"range\" ? v1 : { type: \"range\", min: v1.value, max: v1.value };\n const r2 =\n v2.type === \"range\" ? v2 : { type: \"range\", min: v2.value, max: v2.value };\n const newMin = addNumericValues(\n r1.min as DecimalValue | FractionValue,\n r2.min as DecimalValue | FractionValue,\n );\n const newMax = addNumericValues(\n r1.max as DecimalValue | FractionValue,\n r2.max as DecimalValue | FractionValue,\n );\n return { type: \"range\", min: newMin, max: newMax };\n}\n\n/**\n * Adds two quantities, returning the result in the most appropriate unit.\n */\nexport function addQuantities(\n q1: QuantityWithExtendedUnit,\n q2: QuantityWithExtendedUnit,\n): QuantityWithExtendedUnit {\n const v1 = q1.quantity;\n const v2 = q2.quantity;\n // Case 1: one of the two values is a text, we throw an error we can catch on the other end\n if (\n (v1.type === \"fixed\" && v1.value.type === \"text\") ||\n (v2.type === \"fixed\" && v2.value.type === \"text\")\n ) {\n throw new CannotAddTextValueError();\n }\n\n const unit1Def = normalizeUnit(q1.unit?.name);\n const unit2Def = normalizeUnit(q2.unit?.name);\n\n const addQuantityValuesAndSetUnit = (\n val1: FixedValue | Range,\n val2: FixedValue | Range,\n unit?: Unit,\n ): QuantityWithExtendedUnit => ({\n quantity: addQuantityValues(val1, val2),\n unit,\n });\n\n // Case 2: one of the two values doesn't have a unit, we preserve its value and consider its unit to be that of the other one\n // If at least one of the two units is \"\", this preserves it versus setting the resulting unit as undefined\n if (\n (q1.unit?.name === \"\" || q1.unit === undefined) &&\n q2.unit !== undefined\n ) {\n return addQuantityValuesAndSetUnit(v1, v2, q2.unit); // Prefer q2's unit\n }\n if (\n (q2.unit?.name === \"\" || q2.unit === undefined) &&\n q1.unit !== undefined\n ) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit); // Prefer q1's unit\n }\n\n // Case 3: the two quantities have the exact same unit\n if (\n (!q1.unit && !q2.unit) ||\n (q1.unit &&\n q2.unit &&\n q1.unit.name.toLowerCase() === q2.unit.name.toLowerCase())\n ) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit);\n }\n\n // Case 4: the two quantities have different units of known type\n if (unit1Def && unit2Def) {\n // Case 4.1: different unit type => we can't add quantities\n if (unit1Def.type !== unit2Def.type) {\n throw new IncompatibleUnitsError(\n `${unit1Def.type} (${q1.unit?.name})`,\n `${unit2Def.type} (${q2.unit?.name})`,\n );\n }\n\n let targetUnitDef: UnitDefinition;\n\n // Case 4.2: same unit type but different system => we convert to metric\n if (unit1Def.system !== unit2Def.system) {\n const metricUnitDef = unit1Def.system === \"metric\" ? unit1Def : unit2Def;\n targetUnitDef = units\n .filter((u) => u.type === metricUnitDef.type && u.system === \"metric\")\n .reduce((prev, current) =>\n prev.toBase > current.toBase ? prev : current,\n );\n }\n // Case 4.3: same unit type, same system but different unit => we use the biggest unit of the two\n else {\n targetUnitDef = unit1Def.toBase >= unit2Def.toBase ? unit1Def : unit2Def;\n }\n const convertedV1 = convertQuantityValue(v1, unit1Def, targetUnitDef);\n const convertedV2 = convertQuantityValue(v2, unit2Def, targetUnitDef);\n const targetUnit: Unit = { name: targetUnitDef.name };\n\n return addQuantityValuesAndSetUnit(convertedV1, convertedV2, targetUnit);\n }\n\n // Case 5: the two quantities have different units of unknown type\n throw new IncompatibleUnitsError(\n q1.unit?.name as string,\n q2.unit?.name as string,\n );\n}\n\nexport function toPlainUnit(\n quantity:\n | QuantityWithExtendedUnit\n | MaybeNestedGroup<QuantityWithExtendedUnit>,\n): QuantityWithPlainUnit | MaybeNestedGroup<QuantityWithPlainUnit> {\n if (isQuantity(quantity))\n return quantity.unit\n ? { ...quantity, unit: quantity.unit.name }\n : (quantity as QuantityWithPlainUnit);\n else {\n return {\n ...quantity,\n entries: quantity.entries.map(toPlainUnit),\n } as MaybeNestedGroup<QuantityWithPlainUnit>;\n }\n}\n\n// Convert plain unit to extended unit format for addEquivalentsAndSimplify\n// Overloads for precise return types\nexport function toExtendedUnit(\n q: QuantityWithPlainUnit,\n): QuantityWithExtendedUnit;\nexport function toExtendedUnit(\n q: MaybeNestedGroup<QuantityWithPlainUnit>,\n): MaybeNestedGroup<QuantityWithExtendedUnit>;\nexport function toExtendedUnit(\n q: QuantityWithPlainUnit | MaybeNestedGroup<QuantityWithPlainUnit>,\n): QuantityWithExtendedUnit | MaybeNestedGroup<QuantityWithExtendedUnit> {\n if (isQuantity(q)) {\n return q.unit\n ? { ...q, unit: { name: q.unit } }\n : (q as QuantityWithExtendedUnit);\n } else {\n return {\n ...q,\n entries: q.entries.map((entry) =>\n isQuantity(entry) ? toExtendedUnit(entry) : toExtendedUnit(entry),\n ),\n };\n }\n}\n\nexport function deNormalizeQuantity(\n q: QuantityWithUnitDef,\n): QuantityWithExtendedUnit {\n const result: QuantityWithExtendedUnit = {\n quantity: q.quantity,\n };\n if (!isNoUnit(q.unit)) {\n result.unit = { name: q.unit.name };\n }\n return result;\n}\n\n// Helper function to convert addEquivalentsAndSimplify result to Ingredient.quantities format\n// Returns either a QuantityWithPlainUnit (for simple/OR groups) or an IngredientQuantityAndGroup (for AND groups)\nexport const flattenPlainUnitGroup = (\n summed: QuantityWithPlainUnit | MaybeNestedGroup<QuantityWithPlainUnit>,\n): (\n | { groupQuantity: QuantityWithPlainUnit }\n | {\n type: \"and\";\n entries: QuantityWithPlainUnit[];\n equivalents?: QuantityWithPlainUnit[];\n }\n)[] => {\n if (isOrGroup(summed)) {\n // OR group: check if first entry is an AND group (nested OR-with-AND case from addEquivalentsAndSimplify)\n // This happens when we have incompatible integer-protected primaries with compatible equivalents\n // e.g., { type: \"or\", entries: [{ type: \"and\", entries: [large, small] }, cup] }\n const entries = summed.entries;\n const andGroupEntry = entries.find(\n (e): e is MaybeNestedGroup<QuantityWithPlainUnit> =>\n isGroup(e) && e.type === \"and\",\n );\n\n if (andGroupEntry) {\n // Nested OR-with-AND case: AND group of primaries + equivalents\n const andEntries: QuantityWithPlainUnit[] = [];\n for (const entry of andGroupEntry.entries) {\n if (isQuantity(entry)) {\n andEntries.push({\n quantity: entry.quantity,\n unit: entry.unit,\n });\n }\n }\n\n // The other entries in the OR group are the equivalents\n const equivalentsList: QuantityWithPlainUnit[] = entries\n .filter((e): e is QuantityWithPlainUnit => isQuantity(e))\n .map((e) => ({ quantity: e.quantity, unit: e.unit }));\n\n if (equivalentsList.length > 0) {\n return [\n {\n type: \"and\",\n entries: andEntries,\n equivalents: equivalentsList,\n },\n ];\n } else {\n // No equivalents: flatten to separate entries (shouldn't happen in this branch, but handle it)\n return andEntries.map((entry) => ({ groupQuantity: entry }));\n }\n }\n\n // Simple OR group: first entry is primary, rest are equivalents\n const simpleEntries = entries.filter((e): e is QuantityWithPlainUnit =>\n isQuantity(e),\n );\n /* v8 ignore else -- @preserve */\n if (simpleEntries.length > 0) {\n const result: QuantityWithPlainUnit = {\n quantity: simpleEntries[0]!.quantity,\n unit: simpleEntries[0]!.unit,\n };\n if (simpleEntries.length > 1) {\n result.equivalents = simpleEntries.slice(1);\n }\n return [{ groupQuantity: result }];\n }\n // Fallback: use first entry regardless\n else {\n const first = entries[0] as QuantityWithPlainUnit;\n return [\n { groupQuantity: { quantity: first.quantity, unit: first.unit } },\n ];\n }\n } else if (isGroup(summed)) {\n // AND group: check if entries have OR groups (equivalents that can be extracted)\n const andEntries: QuantityWithPlainUnit[] = [];\n const equivalentsList: QuantityWithPlainUnit[] = [];\n\n for (const entry of summed.entries) {\n if (isOrGroup(entry)) {\n // This entry has equivalents: first is primary, rest are equivalents\n const orEntries = entry.entries.filter(\n (e): e is QuantityWithPlainUnit => isQuantity(e),\n );\n if (orEntries.length > 0) {\n andEntries.push({\n quantity: orEntries[0]!.quantity,\n unit: orEntries[0]!.unit,\n });\n // Collect equivalents for later merging\n equivalentsList.push(...orEntries.slice(1));\n }\n } else if (isQuantity(entry)) {\n // Simple quantity, no equivalents\n andEntries.push({\n quantity: entry.quantity,\n unit: entry.unit,\n });\n }\n }\n\n // Build the AND group result\n // If there are no equivalents, flatten to separate groupQuantity entries (water case)\n // If there are equivalents, return an AND group with the summed equivalents (carrots case)\n if (equivalentsList.length === 0) {\n // No equivalents: flatten to separate entries\n return andEntries.map((entry) => ({ groupQuantity: entry }));\n }\n\n const result: {\n type: \"and\";\n entries: QuantityWithPlainUnit[];\n equivalents?: QuantityWithPlainUnit[];\n } = {\n type: \"and\",\n entries: andEntries,\n equivalents: equivalentsList,\n };\n\n return [result];\n } else {\n // Simple QuantityWithPlainUnit\n return [\n { groupQuantity: { quantity: summed.quantity, unit: summed.unit } },\n ];\n }\n};\n","import type {\n MetadataExtract,\n Metadata,\n FixedValue,\n Range,\n TextValue,\n DecimalValue,\n FractionValue,\n} from \"../types\";\nimport {\n metadataRegex,\n rangeRegex,\n numberLikeRegex,\n scalingMetaValueRegex,\n} from \"../regex\";\nimport { Section as SectionObject } from \"../classes/section\";\nimport type { Ingredient, Note, Step, Cookware } from \"../types\";\nimport { addQuantityValues } from \"../quantities/mutations\";\nimport {\n CannotAddTextValueError,\n ReferencedItemCannotBeRedefinedError,\n} from \"../errors\";\n\n/**\n * Pushes a pending note to the section content if it's not empty.\n * @param section - The current section object.\n * @param note - The note content.\n * @returns An empty string if the note was pushed, otherwise the original note.\n */\nexport function flushPendingNote(\n section: SectionObject,\n note: Note[\"note\"],\n): Note[\"note\"] {\n if (note.length > 0) {\n section.content.push({ type: \"note\", note });\n return \"\";\n }\n return note;\n}\n\n/**\n * Pushes pending step items and a pending note to the section content.\n * @param section - The current section object.\n * @param items - The list of step items. This array will be cleared.\n * @returns true if the items were pushed, otherwise false.\n */\nexport function flushPendingItems(\n section: SectionObject,\n items: Step[\"items\"],\n): boolean {\n if (items.length > 0) {\n section.content.push({ type: \"step\", items: [...items] });\n items.length = 0;\n return true;\n }\n return false;\n}\n\n/**\n * Finds an ingredient in the list (case-insensitively) and updates it, or adds it if not present.\n * This function mutates the `ingredients` array.\n * @param ingredients - The list of ingredients.\n * @param newIngredient - The ingredient to find or add.\n * @param isReference - Whether this is a reference ingredient (`&` modifier).\n * @returns The index of the ingredient in the list.\n * @returns An object containing the index of the ingredient and its quantity part in the list.\n */\nexport function findAndUpsertIngredient(\n ingredients: Ingredient[],\n newIngredient: Ingredient,\n isReference: boolean,\n): number {\n const { name } = newIngredient;\n\n if (isReference) {\n const indexFind = ingredients.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (indexFind === -1) {\n throw new Error(\n `Referenced ingredient \"${name}\" not found. A referenced ingredient must be declared before being referenced with '&'.`,\n );\n }\n\n // Ingredient already exists\n const existingIngredient = ingredients[indexFind]!;\n\n // Checking whether any provided flags are the same as the original ingredient\n // TODO: backport fix (check on array length) to v2\n if (!newIngredient.flags) {\n if (\n Array.isArray(existingIngredient.flags) &&\n existingIngredient.flags.length > 0\n ) {\n throw new ReferencedItemCannotBeRedefinedError(\n \"ingredient\",\n existingIngredient.name,\n existingIngredient.flags[0]!,\n );\n }\n } else {\n for (const flag of newIngredient.flags) {\n /* v8 ignore else -- @preserve */\n if (\n existingIngredient.flags === undefined ||\n !existingIngredient.flags.includes(flag)\n ) {\n throw new ReferencedItemCannotBeRedefinedError(\n \"ingredient\",\n existingIngredient.name,\n flag,\n );\n }\n }\n }\n\n return indexFind;\n }\n\n // Not a reference, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n}\n\nexport function findAndUpsertCookware(\n cookware: Cookware[],\n newCookware: Cookware,\n isReference: boolean,\n): number {\n const { name, quantity } = newCookware;\n\n if (isReference) {\n const index = cookware.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (index === -1) {\n throw new Error(\n `Referenced cookware \"${name}\" not found. A referenced cookware must be declared before being referenced with '&'.`,\n );\n }\n\n const existingCookware = cookware[index]!;\n\n // Checking whether any provided flags are the same as the original cookware\n // TODO: backport fix (if/else) + check on array length to v2\n if (!newCookware.flags) {\n if (\n Array.isArray(existingCookware.flags) &&\n existingCookware.flags.length > 0\n ) {\n throw new ReferencedItemCannotBeRedefinedError(\n \"cookware\",\n existingCookware.name,\n existingCookware.flags[0]!,\n );\n }\n } else {\n for (const flag of newCookware.flags) {\n /* v8 ignore else -- @preserve */\n if (\n existingCookware.flags === undefined ||\n !existingCookware.flags.includes(flag)\n ) {\n throw new ReferencedItemCannotBeRedefinedError(\n \"cookware\",\n existingCookware.name,\n flag,\n );\n }\n }\n }\n\n if (quantity !== undefined) {\n if (!existingCookware.quantity) {\n existingCookware.quantity = quantity;\n } else {\n try {\n existingCookware.quantity = addQuantityValues(\n existingCookware.quantity,\n quantity,\n );\n } catch (e) {\n /* v8 ignore else -- expliciting error type -- @preserve */\n if (e instanceof CannotAddTextValueError) {\n return cookware.push(newCookware) - 1;\n }\n }\n }\n }\n return index;\n }\n\n return cookware.push(newCookware) - 1;\n}\n\n// Parser when we know the input is either a number-like value\nexport const parseFixedValue = (\n input_str: string,\n): TextValue | DecimalValue | FractionValue => {\n if (!numberLikeRegex.test(input_str)) {\n return { type: \"text\", text: input_str };\n }\n\n // After this we know that s is either a fraction or a decimal value\n const s = input_str.trim().replace(\",\", \".\");\n\n // fraction\n if (s.includes(\"/\")) {\n const parts = s.split(\"/\");\n\n const num = Number(parts[0]);\n const den = Number(parts[1]);\n\n return { type: \"fraction\", num, den };\n }\n\n // decimal\n return { type: \"decimal\", decimal: Number(s) };\n};\n\nexport function stringifyQuantityValue(quantity: FixedValue | Range): string {\n if (quantity.type === \"fixed\") {\n return stringifyFixedValue(quantity);\n } else {\n return `${stringifyFixedValue({ type: \"fixed\", value: quantity.min })}-${stringifyFixedValue({ type: \"fixed\", value: quantity.max })}`;\n }\n}\n\nfunction stringifyFixedValue(quantity: FixedValue): string {\n if (quantity.value.type === \"fraction\")\n return `${quantity.value.num}/${quantity.value.den}`;\n else if (quantity.value.type === \"decimal\")\n return String(quantity.value.decimal);\n else return quantity.value.text;\n}\n\n// TODO: rename to parseQuantityValue\nexport function parseQuantityInput(input_str: string): FixedValue | Range {\n const clean_str = String(input_str).trim();\n\n if (rangeRegex.test(clean_str)) {\n const range_parts = clean_str.split(\"-\");\n // As we've tested for it, we know that we have Number-like Quantities to parse\n const min = parseFixedValue(range_parts[0]!.trim()) as\n | DecimalValue\n | FractionValue;\n const max = parseFixedValue(range_parts[1]!.trim()) as\n | DecimalValue\n | FractionValue;\n return { type: \"range\", min, max };\n }\n\n return { type: \"fixed\", value: parseFixedValue(clean_str) };\n}\n\nexport function parseSimpleMetaVar(content: string, varName: string) {\n const varMatch = content.match(\n new RegExp(`^${varName}:\\\\s*(.*(?:\\\\r?\\\\n\\\\s+.*)*)+`, \"m\"),\n );\n return varMatch\n ? varMatch[1]?.trim().replace(/\\s*\\r?\\n\\s+/g, \" \")\n : undefined;\n}\n\nexport function parseScalingMetaVar(\n content: string,\n varName: string,\n): [number, string] | undefined {\n const varMatch = content.match(scalingMetaValueRegex(varName));\n if (!varMatch) return undefined;\n if (isNaN(Number(varMatch[2]?.trim()))) {\n throw new Error(\"Scaling variables should be numbers\");\n }\n return [Number(varMatch[2]?.trim()), varMatch[1]!.trim()];\n}\n\nexport function parseListMetaVar(content: string, varName: string) {\n // Handle both inline and YAML-style tags\n const listMatch = content.match(\n new RegExp(\n `^${varName}:\\\\s*(?:\\\\[([^\\\\]]*)\\\\]|((?:\\\\r?\\\\n\\\\s*-\\\\s*.+)+))`,\n \"m\",\n ),\n );\n if (!listMatch) return undefined;\n\n /* v8 ignore else -- @preserve */\n if (listMatch[1] !== undefined) {\n // Inline list: tags: [one, two, three]\n return listMatch[1].split(\",\").map((tag) => tag.trim());\n } else if (listMatch[2]) {\n // YAML list:\n // tags:\n // - one\n // - two\n return listMatch[2]\n .split(\"\\n\")\n .filter((line) => line.trim() !== \"\")\n .map((line) => line.replace(/^\\s*-\\s*/, \"\").trim());\n }\n}\n\nexport function extractMetadata(content: string): MetadataExtract {\n const metadata: Metadata = {};\n let servings: number | undefined = undefined;\n\n // Is there front-matter at all?\n const metadataContent = content.match(metadataRegex)?.[2];\n if (!metadataContent) {\n return { metadata };\n }\n\n // String metadata variables\n for (const metaVar of [\n \"title\",\n \"source\",\n \"source.name\",\n \"source.url\",\n \"author\",\n \"source.author\",\n \"prep time\",\n \"time.prep\",\n \"cook time\",\n \"time.cook\",\n \"time required\",\n \"time\",\n \"duration\",\n \"locale\",\n \"introduction\",\n \"description\",\n \"course\",\n \"category\",\n \"diet\",\n \"cuisine\",\n \"difficulty\",\n \"image\",\n \"picture\",\n ] as (keyof Pick<\n Metadata,\n | \"title\"\n | \"source\"\n | \"source.name\"\n | \"source.url\"\n | \"author\"\n | \"source.author\"\n | \"prep time\"\n | \"time.prep\"\n | \"cook time\"\n | \"time.cook\"\n | \"time required\"\n | \"time\"\n | \"duration\"\n | \"locale\"\n | \"introduction\"\n | \"description\"\n | \"course\"\n | \"category\"\n | \"diet\"\n | \"cuisine\"\n | \"difficulty\"\n | \"image\"\n | \"picture\"\n >)[]) {\n const stringMetaValue = parseSimpleMetaVar(metadataContent, metaVar);\n if (stringMetaValue) metadata[metaVar] = stringMetaValue;\n }\n\n // String metadata variables\n for (const metaVar of [\"serves\", \"yield\", \"servings\"] as (keyof Pick<\n Metadata,\n \"servings\" | \"yield\" | \"serves\"\n >)[]) {\n const scalingMetaValue = parseScalingMetaVar(metadataContent, metaVar);\n if (scalingMetaValue && scalingMetaValue[1]) {\n metadata[metaVar] = scalingMetaValue[1];\n servings = scalingMetaValue[0];\n }\n }\n\n // List metadata variables\n for (const metaVar of [\"tags\", \"images\", \"pictures\"] as (keyof Pick<\n Metadata,\n \"tags\" | \"images\" | \"pictures\"\n >)[]) {\n const listMetaValue = parseListMetaVar(metadataContent, metaVar);\n if (listMetaValue) metadata[metaVar] = listMetaValue;\n }\n\n return { metadata, servings };\n}\n\nexport function isPositiveIntegerString(str: string): boolean {\n return /^\\d+$/.test(str);\n}\n\nexport function unionOfSets<T>(s1: Set<T>, s2: Set<T>): Set<T> {\n const result = new Set(s1);\n for (const item of s2) {\n result.add(item);\n }\n return result;\n}\n\n/**\n * Returns a canonical string key from sorted alternative indices for grouping quantities.\n * Used to determine if two ingredient items have the same alternatives and can be summed together.\n * @param alternatives - Array of alternative ingredient references\n * @returns A string of sorted indices (e.g., \"0,2,5\") or null if no alternatives\n */\nexport function getAlternativeSignature(\n alternatives: { index: number }[] | undefined,\n): string | null {\n if (!alternatives || alternatives.length === 0) return null;\n return alternatives\n .map((a) => a.index)\n .sort((a, b) => a - b)\n .join(\",\");\n}\n","import type { Step, Note } from \"../types\";\n\n/**\n * Represents a recipe section\n *\n * Wrapped as a _Class_ and not defined as a simple _Type_ to expose some useful helper\n * classes (e.g. {@link Section.isBlank | isBlank()})\n *\n * @category Types\n */\nexport class Section {\n /**\n * The name of the section. Can be an empty string for the default (first) section.\n * @defaultValue `\"\"`\n */\n name: string;\n /** An array of steps and notes that make up the content of the section. */\n content: (Step | Note)[] = [];\n\n /**\n * Creates an instance of Section.\n * @param name - The name of the section. Defaults to an empty string.\n */\n constructor(name: string = \"\") {\n this.name = name;\n }\n\n /**\n * Checks if the section is blank (has no name and no content).\n * Used during recipe parsing\n * @returns `true` if the section is blank, otherwise `false`.\n */\n isBlank(): boolean {\n return this.name === \"\" && this.content.length === 0;\n }\n}\n","import { isNoUnit } from \"../units/definitions\";\nimport type {\n QuantityWithPlainUnit,\n QuantityWithExtendedUnit,\n QuantityWithUnitDef,\n UnitDefinitionLike,\n FlatOrGroup,\n MaybeNestedOrGroup,\n FlatAndGroup,\n FlatGroup,\n MaybeNestedGroup,\n} from \"../types\";\nimport { resolveUnit } from \"../units/definitions\";\nimport { multiplyQuantityValue, getAverageValue } from \"./numeric\";\nimport Big from \"big.js\";\nimport {\n isGroup,\n isOrGroup,\n isQuantity,\n isValueIntegerLike,\n} from \"../utils/type_guards\";\nimport {\n getDefaultQuantityValue,\n addQuantities,\n deNormalizeQuantity,\n toPlainUnit,\n} from \"./mutations\";\nimport { getUnitRatio, getBaseUnitRatio } from \"../units/conversion\";\nimport {\n areUnitsCompatible,\n findCompatibleQuantityWithinList,\n findListWithCompatibleQuantity,\n} from \"../units/lookup\";\nimport { deepClone } from \"../utils/general\";\n\nexport function getEquivalentUnitsLists(\n ...quantities: (\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit>\n )[]\n): QuantityWithUnitDef[][] {\n const quantitiesCopy = deepClone(quantities);\n\n const OrGroups = (\n quantitiesCopy.filter(isOrGroup) as FlatOrGroup<QuantityWithExtendedUnit>[]\n ).filter((q) => q.entries.length > 1);\n\n const unitLists: QuantityWithUnitDef[][] = [];\n const normalizeOrGroup = (og: FlatOrGroup<QuantityWithExtendedUnit>) => ({\n ...og,\n entries: og.entries.map((q) => ({\n ...q,\n unit: resolveUnit(q.unit?.name, q.unit?.integerProtected),\n })),\n });\n\n function findLinkIndexForUnits(\n lists: QuantityWithUnitDef[][],\n unitsToCheck: (UnitDefinitionLike | undefined)[],\n ) {\n return lists.findIndex((l) => {\n const listItem = l.map((q) => resolveUnit(q.unit?.name));\n return unitsToCheck.some((u) =>\n listItem.some(\n (lu) =>\n lu.name === u?.name ||\n (lu.system === u?.system &&\n lu.type === u?.type &&\n lu.type !== \"other\"),\n ),\n );\n });\n }\n\n function mergeOrGroupIntoList(\n lists: QuantityWithUnitDef[][],\n idx: number,\n og: ReturnType<typeof normalizeOrGroup>,\n ) {\n let unitRatio: Big | undefined;\n const commonUnitList = lists[idx]!.reduce((acc, v) => {\n const normalizedV: QuantityWithUnitDef = {\n ...v,\n unit: resolveUnit(v.unit?.name, v.unit?.integerProtected),\n };\n\n const commonQuantity = og.entries.find(\n (q) => isQuantity(q) && areUnitsCompatible(q.unit, normalizedV.unit),\n );\n if (commonQuantity) {\n acc.push(normalizedV);\n unitRatio = getUnitRatio(normalizedV, commonQuantity);\n }\n return acc;\n }, [] as QuantityWithUnitDef[]);\n\n for (const newQ of og.entries) {\n if (commonUnitList.some((q) => areUnitsCompatible(q.unit, newQ.unit))) {\n continue;\n } else {\n const scaledQuantity = multiplyQuantityValue(newQ.quantity, unitRatio!);\n lists[idx]!.push({ ...newQ, quantity: scaledQuantity });\n }\n }\n }\n\n for (const orGroup of OrGroups) {\n const orGroupModified = normalizeOrGroup(orGroup);\n const units = orGroupModified.entries.map((q) => q.unit);\n const linkIndex = findLinkIndexForUnits(unitLists, units);\n if (linkIndex === -1) {\n unitLists.push(orGroupModified.entries);\n } else {\n mergeOrGroupIntoList(unitLists, linkIndex, orGroupModified);\n }\n }\n\n return unitLists;\n}\n\n/**\n * List sorting helper for equivalent units\n * @param list - list of quantities to sort\n * @returns sorted list of quantities with integerProtected units first, then no-unit, then the rest alphabetically\n */\nexport function sortUnitList(list: QuantityWithUnitDef[]) {\n if (!list || list.length <= 1) return list;\n const priorityList: QuantityWithUnitDef[] = [];\n const nonPriorityList: QuantityWithUnitDef[] = [];\n for (const q of list) {\n if (q.unit.integerProtected || q.unit.system === \"none\") {\n priorityList.push(q);\n } else {\n nonPriorityList.push(q);\n }\n }\n\n return priorityList\n .sort((a, b) => {\n const prefixA = a.unit.integerProtected ? \"___\" : \"\";\n const prefixB = b.unit.integerProtected ? \"___\" : \"\";\n return (prefixA + a.unit.name).localeCompare(prefixB + b.unit.name, \"en\");\n })\n .concat(nonPriorityList);\n}\n\nexport function reduceOrsToFirstEquivalent(\n unitList: QuantityWithUnitDef[][],\n quantities: (\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit>\n )[],\n): QuantityWithExtendedUnit[] {\n function reduceToQuantity(firstQuantity: QuantityWithExtendedUnit) {\n // Look for the global list of equivalent for this quantity unit;\n const equivalentList = sortUnitList(\n findListWithCompatibleQuantity(unitList, firstQuantity)!,\n );\n if (!equivalentList) return firstQuantity;\n // Find that first quantity in the OR\n const firstQuantityInList = findCompatibleQuantityWithinList(\n equivalentList,\n firstQuantity,\n )!;\n // Normalize the first quantity's unit\n const normalizedFirstQuantity: QuantityWithUnitDef = {\n ...firstQuantity,\n unit: resolveUnit(firstQuantity.unit?.name),\n };\n // Priority 1: the first quantity has an integer-protected unit\n if (firstQuantityInList.unit.integerProtected) {\n const resultQuantity: QuantityWithExtendedUnit = {\n quantity: firstQuantity.quantity,\n };\n /* v8 ignore else -- @preserve */\n if (!isNoUnit(normalizedFirstQuantity.unit)) {\n resultQuantity.unit = { name: normalizedFirstQuantity.unit.name };\n }\n return resultQuantity;\n } else {\n // Priority 2: the next integer-protected units in the equivalent list\n let nextProtected: number | undefined;\n const equivalentListTemp = [...equivalentList];\n while (nextProtected !== -1) {\n nextProtected = equivalentListTemp.findIndex(\n (eq) => eq.unit?.integerProtected,\n );\n // Ratio between the values in the OR group vs the ones used in the equivalent unit list\n if (nextProtected !== -1) {\n const unitRatio = getUnitRatio(\n equivalentListTemp[nextProtected]!,\n firstQuantityInList,\n );\n const nextProtectedQuantityValue = multiplyQuantityValue(\n firstQuantity.quantity,\n unitRatio,\n );\n if (isValueIntegerLike(nextProtectedQuantityValue)) {\n const nextProtectedQuantity: QuantityWithExtendedUnit = {\n quantity: nextProtectedQuantityValue,\n };\n /* v8 ignore else -- @preserve */\n if (!isNoUnit(equivalentListTemp[nextProtected]!.unit)) {\n nextProtectedQuantity.unit = {\n name: equivalentListTemp[nextProtected]!.unit.name,\n };\n }\n\n return nextProtectedQuantity;\n } else {\n equivalentListTemp.splice(nextProtected, 1);\n }\n }\n }\n\n // Priority 3: the first non-integer-Protected value of the list\n const firstNonIntegerProtected = equivalentListTemp.filter(\n (q) => !q.unit.integerProtected,\n )[0]!;\n const unitRatio = getUnitRatio(\n firstNonIntegerProtected,\n firstQuantityInList,\n ).times(getBaseUnitRatio(normalizedFirstQuantity, firstQuantityInList));\n const firstEqQuantity: QuantityWithExtendedUnit = {\n quantity:\n firstNonIntegerProtected.unit.name === firstQuantity.unit!.name\n ? firstQuantity.quantity\n : multiplyQuantityValue(firstQuantity.quantity, unitRatio),\n };\n if (!isNoUnit(firstNonIntegerProtected.unit)) {\n firstEqQuantity.unit = { name: firstNonIntegerProtected.unit.name };\n }\n return firstEqQuantity;\n }\n }\n return quantities.map((q) => {\n if (isQuantity(q)) return reduceToQuantity(q);\n // Now, q is necessarily an OR group\n // We normalize units and sort them to get integerProtected elements first, then no units, then the rest\n const qListModified = sortUnitList(\n q.entries.map((qq) => ({\n ...qq,\n unit: resolveUnit(qq.unit?.name, qq.unit?.integerProtected),\n })),\n );\n // We can simply use the first element\n return reduceToQuantity(qListModified[0]!);\n });\n}\n\nexport function addQuantitiesOrGroups(\n ...quantities: (\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit>\n )[]\n): {\n sum: QuantityWithUnitDef | FlatGroup<QuantityWithUnitDef>;\n unitsLists: QuantityWithUnitDef[][];\n} {\n if (quantities.length === 0)\n return {\n sum: {\n quantity: getDefaultQuantityValue(),\n unit: resolveUnit(),\n },\n unitsLists: [],\n };\n // This is purely theoretical and won't really happen in practice\n if (quantities.length === 1) {\n if (isQuantity(quantities[0]!))\n return {\n sum: {\n ...quantities[0],\n unit: resolveUnit(quantities[0].unit?.name),\n },\n unitsLists: [],\n };\n }\n // Step 1: find equivalents units\n const unitsLists = getEquivalentUnitsLists(...quantities);\n // Step 2: reduce the OR group to Quantities\n const reducedQuantities = reduceOrsToFirstEquivalent(unitsLists, quantities);\n // Step 3: calculate the sum\n const sum: QuantityWithUnitDef[] = [];\n for (const nextQ of reducedQuantities) {\n const existingQ = findCompatibleQuantityWithinList(sum, nextQ);\n if (existingQ === undefined) {\n sum.push({\n ...nextQ,\n unit: resolveUnit(nextQ.unit?.name),\n });\n } else {\n const sumQ = addQuantities(existingQ, nextQ);\n existingQ.quantity = sumQ.quantity;\n existingQ.unit = resolveUnit(sumQ.unit?.name);\n }\n }\n if (sum.length === 1) {\n return { sum: sum[0]!, unitsLists };\n }\n return { sum: { type: \"and\", entries: sum }, unitsLists };\n}\n\nexport function regroupQuantitiesAndExpandEquivalents(\n sum: QuantityWithUnitDef | FlatGroup<QuantityWithUnitDef>,\n unitsLists: QuantityWithUnitDef[][],\n): (QuantityWithExtendedUnit | MaybeNestedOrGroup<QuantityWithExtendedUnit>)[] {\n const sumQuantities = isGroup(sum) ? sum.entries : [sum];\n const result: (\n | QuantityWithExtendedUnit\n | MaybeNestedOrGroup<QuantityWithExtendedUnit>\n )[] = [];\n const processedQuantities = new Set<QuantityWithUnitDef>();\n\n for (const list of unitsLists) {\n const listCopy = deepClone(list);\n const main: QuantityWithUnitDef[] = [];\n const mainCandidates = sumQuantities.filter(\n (q) => !processedQuantities.has(q),\n );\n if (mainCandidates.length === 0) continue;\n\n mainCandidates.forEach((q) => {\n // If the sum contain a value from the unit list, we push it to the mains and remove it from the list\n const mainInList = findCompatibleQuantityWithinList(listCopy, q);\n /* v8 ignore else -- @preserve */\n if (mainInList !== undefined) {\n processedQuantities.add(q);\n main.push(q);\n listCopy.splice(listCopy.indexOf(mainInList), 1);\n }\n });\n\n // We sort the equivalent units and calculate the equivalent value for each of them\n const equivalents = sortUnitList(listCopy).map((equiv) => {\n const initialValue: QuantityWithExtendedUnit = {\n quantity: getDefaultQuantityValue(),\n };\n /* v8 ignore else -- @preserve */\n if (equiv.unit) {\n initialValue.unit = { name: equiv.unit.name };\n }\n return main.reduce((acc, v) => {\n const mainInList = findCompatibleQuantityWithinList(list, v)!;\n const newValue: QuantityWithExtendedUnit = {\n quantity: multiplyQuantityValue(\n v.quantity,\n Big(getAverageValue(equiv.quantity)).div(\n getAverageValue(mainInList.quantity),\n ),\n ),\n };\n if (equiv.unit && !isNoUnit(equiv.unit)) {\n newValue.unit = { name: equiv.unit.name };\n }\n return addQuantities(acc, newValue);\n }, initialValue);\n });\n\n if (main.length + equivalents.length > 1) {\n const resultMain:\n | QuantityWithExtendedUnit\n | FlatAndGroup<QuantityWithExtendedUnit> =\n main.length > 1\n ? {\n type: \"and\",\n entries: main.map(deNormalizeQuantity),\n }\n : deNormalizeQuantity(main[0]!);\n result.push({\n type: \"or\",\n entries: [resultMain, ...equivalents],\n });\n }\n // Processing a UnitList with only 1 quantity is purely theoretical and won't happen in practice\n else {\n result.push(deNormalizeQuantity(main[0]!));\n }\n }\n\n // We add at the end the lone quantities\n sumQuantities\n .filter((q) => !processedQuantities.has(q))\n .forEach((q) => result.push(deNormalizeQuantity(q)));\n\n return result;\n}\n\nexport function addEquivalentsAndSimplify(\n ...quantities: (\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit>\n )[]\n): QuantityWithPlainUnit | MaybeNestedGroup<QuantityWithPlainUnit> {\n if (quantities.length === 1) {\n return toPlainUnit(quantities[0]!);\n }\n // Step 1+2+3: find equivalents, reduce groups and add quantities\n const { sum, unitsLists } = addQuantitiesOrGroups(...quantities);\n // Step 4: regroup and expand equivalents per group\n const regrouped = regroupQuantitiesAndExpandEquivalents(sum, unitsLists);\n if (regrouped.length === 1) {\n return toPlainUnit(regrouped[0]!);\n } else {\n return { type: \"and\", entries: regrouped.map(toPlainUnit) };\n }\n}\n","import Big from \"big.js\";\nimport type { QuantityWithUnitDef } from \"../types\";\nimport { getAverageValue } from \"../quantities/numeric\";\n\nexport function getUnitRatio(q1: QuantityWithUnitDef, q2: QuantityWithUnitDef) {\n const q1Value = getAverageValue(q1.quantity);\n const q2Value = getAverageValue(q2.quantity);\n const factor =\n \"toBase\" in q1.unit && \"toBase\" in q2.unit\n ? q1.unit.toBase / q2.unit.toBase\n : 1;\n\n if (typeof q1Value !== \"number\" || typeof q2Value !== \"number\") {\n throw Error(\n \"One of both values is not a number, so a ratio cannot be computed\",\n );\n }\n return Big(q1Value).times(factor).div(q2Value);\n}\n\nexport function getBaseUnitRatio(\n q: QuantityWithUnitDef,\n qRef: QuantityWithUnitDef,\n) {\n if (\"toBase\" in q.unit && \"toBase\" in qRef.unit) {\n return q.unit.toBase / qRef.unit.toBase;\n } else {\n return 1;\n }\n}\n","import type {\n QuantityWithExtendedUnit,\n QuantityWithUnitDef,\n UnitDefinitionLike,\n} from \"../types\";\nimport { resolveUnit } from \"./definitions\";\n\nexport function areUnitsCompatible(\n u1: UnitDefinitionLike,\n u2: UnitDefinitionLike,\n): boolean {\n if (u1.name === u2.name) {\n return true;\n }\n if (u1.type !== \"other\" && u1.type === u2.type && u1.system === u2.system) {\n return true;\n }\n return false;\n}\n\nexport function findListWithCompatibleQuantity(\n list: QuantityWithUnitDef[][],\n quantity: QuantityWithExtendedUnit,\n) {\n const quantityWithUnitDef = {\n ...quantity,\n unit: resolveUnit(quantity.unit?.name),\n };\n return list.find((l) =>\n l.some((lq) => areUnitsCompatible(lq.unit, quantityWithUnitDef.unit)),\n );\n}\n\nexport function findCompatibleQuantityWithinList(\n list: QuantityWithUnitDef[],\n quantity: QuantityWithExtendedUnit,\n): QuantityWithUnitDef | undefined {\n const quantityWithUnitDef = {\n ...quantity,\n unit: resolveUnit(quantity.unit?.name),\n };\n return list.find(\n (q) =>\n q.unit.name === quantityWithUnitDef.unit.name ||\n (q.unit.type === quantityWithUnitDef.unit.type &&\n q.unit.type !== \"other\"),\n );\n}\n","const legacyDeepClone = <T>(v: T): T => {\n if (v === null || typeof v !== \"object\") {\n return v;\n }\n if (v instanceof Map) {\n return new Map(\n Array.from(v.entries()).map(([k, val]) => [\n legacyDeepClone(k),\n legacyDeepClone(val),\n ])\n ) as T;\n }\n if (v instanceof Set) {\n return new Set(Array.from(v).map((val: unknown) => legacyDeepClone(val))) as T;\n }\n if (v instanceof Date) {\n return new Date(v.getTime()) as T;\n }\n if (Array.isArray(v)) {\n return v.map((item: unknown) => legacyDeepClone(item)) as T;\n }\n const cloned = {} as Record<string, unknown>;\n for (const key of Object.keys(v)) {\n cloned[key] = legacyDeepClone((v as Record<string, unknown>)[key]);\n }\n return cloned as T;\n};\n\nexport const deepClone = <T>(v: T): T =>\n typeof structuredClone === \"function\"\n ? structuredClone(v)\n : legacyDeepClone(v);\n","import type {\n Metadata,\n Ingredient,\n IngredientExtras,\n IngredientItem,\n IngredientItemQuantity,\n Timer,\n Step,\n Note,\n Cookware,\n MetadataExtract,\n CookwareItem,\n IngredientFlag,\n CookwareFlag,\n RecipeChoices,\n RecipeAlternatives,\n IngredientAlternative,\n FlatOrGroup,\n QuantityWithExtendedUnit,\n ComputedIngredient,\n AlternativeIngredientRef,\n QuantityWithPlainUnit,\n IngredientQuantityGroup,\n IngredientQuantityAndGroup,\n} from \"../types\";\nimport { Section } from \"./section\";\nimport {\n tokensRegex,\n commentRegex,\n blockCommentRegex,\n metadataRegex,\n ingredientWithAlternativeRegex,\n ingredientWithGroupKeyRegex,\n ingredientAliasRegex,\n floatRegex,\n quantityAlternativeRegex,\n inlineIngredientAlternativesRegex,\n} from \"../regex\";\nimport {\n flushPendingItems,\n flushPendingNote,\n findAndUpsertIngredient,\n findAndUpsertCookware,\n parseQuantityInput,\n extractMetadata,\n unionOfSets,\n getAlternativeSignature,\n} from \"../utils/parser_helpers\";\nimport { addEquivalentsAndSimplify } from \"../quantities/alternatives\";\nimport { multiplyQuantityValue } from \"../quantities/numeric\";\nimport {\n toPlainUnit,\n toExtendedUnit,\n flattenPlainUnitGroup,\n} from \"../quantities/mutations\";\nimport Big from \"big.js\";\nimport { deepClone } from \"../utils/general\";\nimport { InvalidQuantityFormat } from \"../errors\";\n\n/**\n * Recipe parser.\n *\n * ## Usage\n *\n * You can either directly provide the recipe string when creating the instance\n * e.g. `const recipe = new Recipe('Add @eggs{3}')`, or create it first and then pass\n * the recipe string to the {@link Recipe.parse | parse()} method.\n *\n * Look at the [properties](#properties) to see how the recipe's properties are parsed.\n *\n * @category Classes\n *\n * @example\n * ```typescript\n * import { Recipe } from \"@tmlmt/cooklang-parser\";\n *\n * const recipeString = `\n * ---\n * title: Pancakes\n * tags: [breakfast, easy]\n * ---\n * Crack the @eggs{3} with @flour{100%g} and @milk{200%mL}\n *\n * Melt some @butter{50%g} in a #pan on medium heat.\n *\n * Cook for ~{5%minutes} on each side.\n * `\n * const recipe = new Recipe(recipeString);\n * ```\n */\nexport class Recipe {\n /**\n * The parsed recipe metadata.\n */\n metadata: Metadata = {};\n /**\n * The default or manual choice of alternative ingredients.\n * Contains the full context including alternatives list and active selection index.\n */\n choices: RecipeAlternatives = {\n ingredientItems: new Map(),\n ingredientGroups: new Map(),\n };\n /**\n * The parsed recipe ingredients.\n */\n ingredients: Ingredient[] = [];\n /**\n * The parsed recipe sections.\n */\n sections: Section[] = [];\n /**\n * The parsed recipe cookware.\n */\n cookware: Cookware[] = [];\n /**\n * The parsed recipe timers.\n */\n timers: Timer[] = [];\n /**\n * The parsed recipe servings. Used for scaling. Parsed from one of\n * {@link Metadata.servings}, {@link Metadata.yield} or {@link Metadata.serves}\n * metadata fields.\n *\n * @see {@link Recipe.scaleBy | scaleBy()} and {@link Recipe.scaleTo | scaleTo()} methods\n */\n servings?: number;\n\n /**\n * External storage for item count (not a property on instances).\n * Used for giving ID numbers to items during parsing.\n */\n private static itemCounts = new WeakMap<Recipe, number>();\n\n /**\n * Gets the current item count for this recipe.\n */\n private getItemCount(): number {\n return Recipe.itemCounts.get(this)!;\n }\n\n /**\n * Gets the current item count and increments it.\n */\n private getAndIncrementItemCount(): number {\n const current = this.getItemCount();\n Recipe.itemCounts.set(this, current + 1);\n return current;\n }\n\n /**\n * Creates a new Recipe instance.\n * @param content - The recipe content to parse.\n */\n constructor(content?: string) {\n Recipe.itemCounts.set(this, 0);\n if (content) {\n this.parse(content);\n }\n }\n\n private _parseQuantityRecursive(\n quantityRaw: string,\n ): QuantityWithExtendedUnit[] {\n let quantityMatch = quantityRaw.match(quantityAlternativeRegex);\n const quantities: QuantityWithExtendedUnit[] = [];\n while (quantityMatch?.groups) {\n const value = quantityMatch.groups.ingredientQuantityValue\n ? parseQuantityInput(quantityMatch.groups.ingredientQuantityValue)\n : undefined;\n const unit = quantityMatch.groups.ingredientUnit;\n if (value) {\n const newQuantity: QuantityWithExtendedUnit = { quantity: value };\n if (unit) {\n if (unit.startsWith(\"=\")) {\n newQuantity.unit = {\n name: unit.substring(1),\n integerProtected: true,\n };\n } else {\n newQuantity.unit = { name: unit };\n }\n }\n quantities.push(newQuantity);\n } else {\n throw new InvalidQuantityFormat(quantityRaw);\n }\n quantityMatch = quantityMatch.groups.ingredientAltQuantity\n ? quantityMatch.groups.ingredientAltQuantity.match(\n quantityAlternativeRegex,\n )\n : null;\n }\n return quantities;\n }\n\n private _parseIngredientWithAlternativeRecursive(\n ingredientMatchString: string,\n items: Step[\"items\"],\n ): void {\n const alternatives: IngredientAlternative[] = [];\n let testString = ingredientMatchString;\n while (true) {\n const match = testString.match(\n alternatives.length > 0\n ? inlineIngredientAlternativesRegex\n : ingredientWithAlternativeRegex,\n );\n if (!match?.groups) break;\n const groups = match.groups;\n\n // Use variables for readability\n // @<modifiers><name>{quantity%unit|altQuantities}(preparation)[note]|<altIngredients>\n let name = (groups.mIngredientName || groups.sIngredientName)!;\n\n // 1. We build up the different parts of the Ingredient object\n // Preparation\n const preparation = groups.ingredientPreparation;\n // Flags\n const modifiers = groups.ingredientModifiers;\n const reference = modifiers !== undefined && modifiers.includes(\"&\");\n const flags: IngredientFlag[] = [];\n if (modifiers !== undefined && modifiers.includes(\"?\")) {\n flags.push(\"optional\");\n }\n if (modifiers !== undefined && modifiers.includes(\"-\")) {\n flags.push(\"hidden\");\n }\n if (\n (modifiers !== undefined && modifiers.includes(\"@\")) ||\n groups.ingredientRecipeAnchor\n ) {\n flags.push(\"recipe\");\n }\n // Extras\n let extras: IngredientExtras | undefined = undefined;\n // -- if the ingredient is a recipe, we need to extract the name from the path given\n if (flags.includes(\"recipe\")) {\n extras = { path: `${name}.cook` };\n name = name.substring(name.lastIndexOf(\"/\") + 1);\n }\n // Distinguish name from display name / name alias\n const aliasMatch = name.match(ingredientAliasRegex);\n let listName, displayName: string;\n if (\n aliasMatch &&\n aliasMatch.groups!.ingredientListName!.trim().length > 0 &&\n aliasMatch.groups!.ingredientDisplayName!.trim().length > 0\n ) {\n listName = aliasMatch.groups!.ingredientListName!.trim();\n displayName = aliasMatch.groups!.ingredientDisplayName!.trim();\n } else {\n listName = name;\n displayName = name;\n }\n\n const newIngredient: Ingredient = {\n name: listName,\n };\n // Only add parameters if they are non null / non empty\n if (preparation) {\n newIngredient.preparation = preparation;\n }\n if (flags.length > 0) {\n newIngredient.flags = flags;\n }\n if (extras) {\n newIngredient.extras = extras;\n }\n\n const idxInList = findAndUpsertIngredient(\n this.ingredients,\n newIngredient,\n reference,\n );\n\n // 2. We build up the ingredient item\n // -- alternative quantities\n let itemQuantity: IngredientItemQuantity | undefined = undefined;\n if (groups.ingredientQuantity) {\n const parsedQuantities = this._parseQuantityRecursive(\n groups.ingredientQuantity,\n );\n const [primary, ...rest] = parsedQuantities;\n if (primary) {\n itemQuantity = {\n ...primary,\n scalable: groups.ingredientQuantityModifier !== \"=\",\n };\n if (rest.length > 0) {\n itemQuantity.equivalents = rest;\n }\n }\n }\n\n const alternative: IngredientAlternative = {\n index: idxInList,\n displayName,\n };\n // Only add itemQuantity and note if they exist\n const note = groups.ingredientNote?.trim();\n if (note) {\n alternative.note = note;\n }\n if (itemQuantity) {\n alternative.itemQuantity = itemQuantity;\n }\n alternatives.push(alternative);\n testString = groups.ingredientAlternative || \"\";\n }\n\n // Update alternatives list of all processed ingredients\n if (alternatives.length > 1) {\n const alternativesIndexes = alternatives.map((alt) => alt.index);\n for (const ingredientIndex of alternativesIndexes) {\n const ingredient = this.ingredients[ingredientIndex];\n // In practice, the ingredient will always be found\n /* v8 ignore else -- @preserve */\n if (ingredient) {\n if (!ingredient.alternatives) {\n ingredient.alternatives = new Set(\n alternativesIndexes.filter((index) => index !== ingredientIndex),\n );\n } else {\n ingredient.alternatives = unionOfSets(\n ingredient.alternatives,\n new Set(\n alternativesIndexes.filter(\n (index) => index !== ingredientIndex,\n ),\n ),\n );\n }\n }\n }\n }\n\n const id = `ingredient-item-${this.getAndIncrementItemCount()}`;\n\n // Finalize item\n const newItem: IngredientItem = {\n type: \"ingredient\",\n id,\n alternatives,\n };\n items.push(newItem);\n\n if (alternatives.length > 1) {\n this.choices.ingredientItems.set(id, alternatives);\n }\n }\n\n private _parseIngredientWithGroupKey(\n ingredientMatchString: string,\n items: Step[\"items\"],\n ): void {\n const match = ingredientMatchString.match(ingredientWithGroupKeyRegex);\n // This is a type guard to ensure match and match.groups are defined\n /* v8 ignore if -- @preserve */\n if (!match?.groups) return;\n const groups = match.groups;\n\n // Use variables for readability\n // @|<groupKey|<modifiers><name>{quantity%unit|altQuantities}(preparation)[note]\n const groupKey = groups.gIngredientGroupKey!;\n let name = (groups.gmIngredientName || groups.gsIngredientName)!;\n\n // 1. We build up the different parts of the Ingredient object\n // Preparation\n const preparation = groups.gIngredientPreparation;\n // Flags\n const modifiers = groups.gIngredientModifiers;\n const reference = modifiers !== undefined && modifiers.includes(\"&\");\n const flags: IngredientFlag[] = [];\n if (modifiers !== undefined && modifiers.includes(\"?\")) {\n flags.push(\"optional\");\n }\n if (modifiers !== undefined && modifiers.includes(\"-\")) {\n flags.push(\"hidden\");\n }\n if (\n (modifiers !== undefined && modifiers.includes(\"@\")) ||\n groups.gIngredientRecipeAnchor\n ) {\n flags.push(\"recipe\");\n }\n // Extras\n let extras: IngredientExtras | undefined = undefined;\n // -- if the ingredient is a recipe, we need to extract the name from the path given\n if (flags.includes(\"recipe\")) {\n extras = { path: `${name}.cook` };\n name = name.substring(name.lastIndexOf(\"/\") + 1);\n }\n // Distinguish name from display name / name alias\n const aliasMatch = name.match(ingredientAliasRegex);\n let listName, displayName: string;\n if (\n aliasMatch &&\n aliasMatch.groups!.ingredientListName!.trim().length > 0 &&\n aliasMatch.groups!.ingredientDisplayName!.trim().length > 0\n ) {\n listName = aliasMatch.groups!.ingredientListName!.trim();\n displayName = aliasMatch.groups!.ingredientDisplayName!.trim();\n } else {\n listName = name;\n displayName = name;\n }\n\n const newIngredient: Ingredient = {\n name: listName,\n };\n // Only add parameters if they are non null / non empty\n if (preparation) {\n newIngredient.preparation = preparation;\n }\n if (flags.length > 0) {\n newIngredient.flags = flags;\n }\n if (extras) {\n newIngredient.extras = extras;\n }\n\n const idxInList = findAndUpsertIngredient(\n this.ingredients,\n newIngredient,\n reference,\n );\n\n // 2. We build up the ingredient item\n // -- alternative quantities\n let itemQuantity: IngredientItemQuantity | undefined = undefined;\n if (groups.gIngredientQuantity) {\n const parsedQuantities = this._parseQuantityRecursive(\n groups.gIngredientQuantity,\n );\n const [primary, ...rest] = parsedQuantities;\n itemQuantity = {\n ...primary!, // there's necessarily a primary quantity as the match group was detected\n scalable: groups.gIngredientQuantityModifier !== \"=\",\n };\n if (rest.length > 0) {\n itemQuantity.equivalents = rest;\n }\n }\n\n const alternative: IngredientAlternative = {\n index: idxInList,\n displayName,\n };\n // Only add itemQuantity if it exists\n if (itemQuantity) {\n alternative.itemQuantity = itemQuantity;\n }\n\n const existingAlternatives = this.choices.ingredientGroups.get(groupKey);\n // For all alternative ingredients already processed for this group, add the new ingredient as alternative\n function upsertAlternativeToIngredient(\n ingredients: Ingredient[],\n ingredientIdx: number,\n newAlternativeIdx: number,\n ) {\n const ingredient = ingredients[ingredientIdx];\n // In practice, the ingredient will always be found\n /* v8 ignore else -- @preserve */\n if (ingredient) {\n if (ingredient.alternatives === undefined) {\n ingredient.alternatives = new Set([newAlternativeIdx]);\n } else {\n ingredient.alternatives.add(newAlternativeIdx);\n }\n }\n }\n if (existingAlternatives) {\n for (const alt of existingAlternatives) {\n upsertAlternativeToIngredient(this.ingredients, alt.index, idxInList);\n upsertAlternativeToIngredient(this.ingredients, idxInList, alt.index);\n }\n }\n const id = `ingredient-item-${this.getAndIncrementItemCount()}`;\n\n // Finalize item\n const newItem: IngredientItem = {\n type: \"ingredient\",\n id,\n group: groupKey,\n alternatives: [alternative],\n };\n items.push(newItem);\n\n // Populate or update choices\n const choiceAlternative = deepClone(alternative);\n choiceAlternative.itemId = id;\n const existingChoice = this.choices.ingredientGroups.get(groupKey);\n if (!existingChoice) {\n this.choices.ingredientGroups.set(groupKey, [choiceAlternative]);\n } else {\n existingChoice.push(choiceAlternative);\n }\n }\n\n /**\n * Populates the `quantities` property for each ingredient based on\n * how they appear in the recipe preparation. Only primary ingredients\n * get quantities populated. Primary ingredients get `usedAsPrimary: true` flag.\n *\n * For inline alternatives (e.g. `\\@a|b|c`), the first alternative is primary.\n * For grouped alternatives (e.g. `\\@|group|a`, `\\@|group|b`), the first item in the group is primary.\n *\n * Quantities are grouped by their alternative signature and summed using addEquivalentsAndSimplify.\n * @internal\n */\n private _populate_ingredient_quantities(): void {\n // Reset quantities and usedAsPrimary flag\n this.ingredients = this.ingredients.map((ing) => {\n if (ing.quantities) {\n delete ing.quantities;\n }\n if (ing.usedAsPrimary) {\n delete ing.usedAsPrimary;\n }\n return ing;\n });\n\n // Track which groups we've already seen (to identify first item in each group)\n const seenGroups = new Set<string>();\n\n // Type for accumulated alternative quantities (using extended units for summing)\n type AlternativeQuantitiesMap = Map<\n number,\n (QuantityWithExtendedUnit | FlatOrGroup<QuantityWithExtendedUnit>)[]\n >;\n\n // Nested map: ingredientIndex -> alternativeSignature -> { alternativeQuantities, quantities }\n // This groups quantities by their alternative signature for proper summing\n const ingredientGroups = new Map<\n number,\n Map<\n string | null,\n {\n alternativeQuantities: AlternativeQuantitiesMap;\n quantities: (\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit>\n )[];\n }\n >\n >();\n\n // Loop through all ingredient items in all sections\n for (const section of this.sections) {\n for (const step of section.content.filter(\n (item) => item.type === \"step\",\n )) {\n for (const item of step.items.filter(\n (item) => item.type === \"ingredient\",\n )) {\n // For grouped alternatives, only the first item in the group is primary\n const isGroupedItem = \"group\" in item && item.group !== undefined;\n const isFirstInGroup = isGroupedItem && !seenGroups.has(item.group!);\n if (isGroupedItem) {\n seenGroups.add(item.group!);\n }\n\n // Determine if this item's first alternative is a primary ingredient\n // - For non-grouped items: always primary (index 0)\n // - For grouped items: only primary if first in the group\n const isPrimary = !isGroupedItem || isFirstInGroup;\n\n // Only process the first alternative (primary ingredient) for quantities\n const alternative = item.alternatives[0] as IngredientAlternative;\n\n // Mark this ingredient as used as primary if applicable\n if (isPrimary) {\n const primaryIngredient = this.ingredients[alternative.index];\n /* v8 ignore else -- @preserve */\n if (primaryIngredient) {\n primaryIngredient.usedAsPrimary = true;\n }\n }\n\n // Only populate quantities for primary ingredients\n if (!isPrimary || !alternative.itemQuantity) continue;\n\n // Build the primary quantity with equivalents as an OrGroup (like calc_ingredient_quantities)\n const allQuantities: QuantityWithExtendedUnit[] = [\n {\n quantity: alternative.itemQuantity.quantity,\n unit: alternative.itemQuantity.unit,\n },\n ];\n if (alternative.itemQuantity.equivalents) {\n allQuantities.push(...alternative.itemQuantity.equivalents);\n }\n\n const quantityEntry:\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit> =\n allQuantities.length === 1\n ? allQuantities[0]!\n : { type: \"or\", entries: allQuantities };\n\n // Check if this ingredient item has alternatives (inline or grouped)\n const hasInlineAlternatives = item.alternatives.length > 1;\n const hasGroupedAlternatives =\n isGroupedItem && this.choices.ingredientGroups.has(item.group!);\n\n let alternativeRefs: AlternativeIngredientRef[] | undefined;\n\n if (hasInlineAlternatives) {\n // Build alternative refs for inline alternatives (e.g. @milk|almond milk|soy milk)\n alternativeRefs = [];\n for (let j = 1; j < item.alternatives.length; j++) {\n const otherAlt = item.alternatives[j] as IngredientAlternative;\n const newRef: AlternativeIngredientRef = {\n index: otherAlt.index,\n };\n if (otherAlt.itemQuantity) {\n // Build the alternativeQuantities with plain units\n const altQty: QuantityWithPlainUnit = {\n quantity: otherAlt.itemQuantity.quantity,\n };\n /* v8 ignore else -- @preserve */\n if (otherAlt.itemQuantity.unit) {\n altQty.unit = otherAlt.itemQuantity.unit.name;\n }\n if (otherAlt.itemQuantity.equivalents) {\n altQty.equivalents = otherAlt.itemQuantity.equivalents.map(\n (eq) => toPlainUnit(eq) as QuantityWithPlainUnit,\n );\n }\n newRef.alternativeQuantities = [altQty];\n }\n alternativeRefs.push(newRef);\n }\n } else if (hasGroupedAlternatives) {\n // Build alternative refs for grouped alternatives (e.g. @|group|milk, @|group|almond milk)\n const groupAlternatives = this.choices.ingredientGroups.get(\n item.group!,\n )!;\n // Skip the first one (that's the primary, which is this ingredient)\n alternativeRefs = [];\n for (let j = 1; j < groupAlternatives.length; j++) {\n const otherAlt = groupAlternatives[j] as IngredientAlternative;\n /* v8 ignore else -- @preserve */\n if (otherAlt.itemQuantity) {\n // Build the alternativeQuantities with plain units\n const altQty: QuantityWithPlainUnit = {\n quantity: otherAlt.itemQuantity.quantity,\n };\n if (otherAlt.itemQuantity.unit) {\n altQty.unit = otherAlt.itemQuantity.unit.name;\n }\n if (otherAlt.itemQuantity.equivalents) {\n altQty.equivalents = otherAlt.itemQuantity.equivalents.map(\n (eq) => toPlainUnit(eq) as QuantityWithPlainUnit,\n );\n }\n alternativeRefs.push({\n index: otherAlt.index,\n alternativeQuantities: [altQty],\n });\n }\n }\n if (alternativeRefs.length === 0) {\n alternativeRefs = undefined;\n }\n }\n\n // Get or create the map for this ingredient\n if (!ingredientGroups.has(alternative.index)) {\n ingredientGroups.set(alternative.index, new Map());\n }\n const groupsForIngredient = ingredientGroups.get(alternative.index)!;\n\n // Get the alternative signature for grouping\n // Include the group name to keep quantities from different choice groups separate\n const baseSignature = getAlternativeSignature(alternativeRefs);\n const signature = isGroupedItem\n ? `group:${item.group}|${baseSignature ?? \"\"}`\n : baseSignature;\n\n // Get or create the group for this signature\n if (!groupsForIngredient.has(signature)) {\n groupsForIngredient.set(signature, {\n alternativeQuantities: new Map<\n number,\n (\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit>\n )[]\n >(),\n quantities: [],\n });\n }\n const group = groupsForIngredient.get(signature)!;\n\n // Add the quantity to the group\n group.quantities.push(quantityEntry);\n\n // Also accumulate alternative quantities for summing\n if (alternativeRefs) {\n for (const ref of alternativeRefs) {\n // Always track the alternative index, even without quantity\n if (!group.alternativeQuantities.has(ref.index)) {\n group.alternativeQuantities.set(ref.index, []);\n }\n\n if (\n ref.alternativeQuantities &&\n ref.alternativeQuantities.length > 0\n ) {\n for (const altQty of ref.alternativeQuantities) {\n if (altQty.equivalents && altQty.equivalents.length > 0) {\n const entries: QuantityWithExtendedUnit[] = [\n toExtendedUnit({\n quantity: altQty.quantity,\n unit: altQty.unit,\n }),\n ...altQty.equivalents.map((eq) => toExtendedUnit(eq)),\n ];\n group.alternativeQuantities\n .get(ref.index)!\n .push({ type: \"or\", entries });\n } else {\n group.alternativeQuantities.get(ref.index)!.push(\n toExtendedUnit({\n quantity: altQty.quantity,\n unit: altQty.unit,\n }),\n );\n }\n }\n }\n }\n }\n }\n }\n }\n\n // Process each ingredient's groups and assign summed quantities\n for (const [index, groupsForIngredient] of ingredientGroups) {\n const ingredient = this.ingredients[index]!;\n\n const quantityGroups: (\n | IngredientQuantityGroup\n | IngredientQuantityAndGroup\n )[] = [];\n\n for (const [, group] of groupsForIngredient) {\n // Use addEquivalentsAndSimplify to sum all quantities in this group\n const summedGroupQuantity = addEquivalentsAndSimplify(\n ...group.quantities,\n );\n // Convert to proper format (IngredientQuantityGroup or IngredientQuantityAndGroup)\n const groupQuantities = flattenPlainUnitGroup(summedGroupQuantity);\n\n // Process alternatives - they need to be converted similarly\n let alternatives: AlternativeIngredientRef[] | undefined;\n if (group.alternativeQuantities.size > 0) {\n alternatives = [];\n for (const [altIndex, altQuantities] of group.alternativeQuantities) {\n const ref: AlternativeIngredientRef = { index: altIndex };\n if (altQuantities.length > 0) {\n // Sum the alternative quantities using addEquivalentsAndSimplify\n const summedAltQuantity = addEquivalentsAndSimplify(\n ...altQuantities,\n );\n // Convert to array of QuantityWithPlainUnit\n const flattenedAlt = flattenPlainUnitGroup(summedAltQuantity);\n // Extract quantities from the flattened result\n ref.alternativeQuantities = flattenedAlt.flatMap((item) => {\n if (\"groupQuantity\" in item) {\n return [item.groupQuantity];\n } else {\n // AND group: return entries (could also include equivalents if needed)\n return item.entries;\n }\n });\n }\n alternatives.push(ref);\n }\n }\n\n // Add quantity groups with alternatives\n for (const gq of groupQuantities) {\n if (\"type\" in gq && gq.type === \"and\") {\n // AND group\n const andGroup: IngredientQuantityAndGroup = {\n type: \"and\",\n entries: gq.entries,\n };\n if (gq.equivalents && gq.equivalents.length > 0) {\n andGroup.equivalents = gq.equivalents;\n }\n if (alternatives && alternatives.length > 0) {\n andGroup.alternatives = alternatives;\n }\n quantityGroups.push(andGroup);\n } else {\n // Simple group\n const quantityGroup: IngredientQuantityGroup =\n gq as IngredientQuantityGroup;\n if (alternatives && alternatives.length > 0) {\n quantityGroup.alternatives = alternatives;\n }\n quantityGroups.push(quantityGroup);\n }\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (quantityGroups.length > 0) {\n ingredient.quantities = quantityGroups;\n }\n }\n }\n\n /**\n * Calculates ingredient quantities based on the provided choices.\n * Returns a list of computed ingredients with their total quantities.\n *\n * @param choices - The recipe choices to apply when computing quantities.\n * If not provided, uses the default choices (first alternative for each item).\n * @returns An array of ComputedIngredient with quantityTotal calculated based on choices.\n */\n calc_ingredient_quantities(choices?: RecipeChoices): ComputedIngredient[] {\n // Use provided choices or derive default choices (index 0 for all)\n const effectiveChoices: RecipeChoices = choices || {\n ingredientItems: new Map(\n Array.from(this.choices.ingredientItems.keys()).map((k) => [k, 0]),\n ),\n ingredientGroups: new Map(\n Array.from(this.choices.ingredientGroups.keys()).map((k) => [k, 0]),\n ),\n };\n\n const ingredientQuantities = new Map<\n number,\n (QuantityWithExtendedUnit | FlatOrGroup<QuantityWithExtendedUnit>)[]\n >();\n\n // Track which ingredient indices are selected (either directly or as part of alternatives)\n const selectedIngredientIndices = new Set<number>();\n\n // Looping ingredient items\n for (const section of this.sections) {\n for (const step of section.content.filter(\n (item) => item.type === \"step\",\n )) {\n for (const item of step.items.filter(\n (item) => item.type === \"ingredient\",\n )) {\n for (let i = 0; i < item.alternatives.length; i++) {\n const alternative = item.alternatives[i] as IngredientAlternative;\n // Is the ingredient selected (potentially by default)\n const isAlternativeChoiceItem =\n effectiveChoices.ingredientItems?.get(item.id) === i;\n const alternativeChoiceGroupIdx = item.group\n ? effectiveChoices.ingredientGroups?.get(item.group)\n : undefined;\n const alternativeChoiceGroup = item.group\n ? this.choices.ingredientGroups.get(item.group)\n : undefined;\n const isAlternativeChoiceGroup =\n alternativeChoiceGroup && alternativeChoiceGroupIdx !== undefined\n ? alternativeChoiceGroup[alternativeChoiceGroupIdx]?.itemId ===\n item.id\n : false;\n\n // Determine if this ingredient is selected\n const isSelected =\n (!(\"group\" in item) &&\n (item.alternatives.length === 1 || isAlternativeChoiceItem)) ||\n isAlternativeChoiceGroup;\n\n if (isSelected) {\n selectedIngredientIndices.add(alternative.index);\n\n if (alternative.itemQuantity) {\n // Build equivalents: primary quantity + any additional equivalents\n const allQuantities: QuantityWithExtendedUnit[] = [\n {\n quantity: alternative.itemQuantity.quantity,\n unit: alternative.itemQuantity.unit,\n },\n ];\n if (alternative.itemQuantity.equivalents) {\n allQuantities.push(...alternative.itemQuantity.equivalents);\n }\n const equivalents:\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit> =\n allQuantities.length === 1\n ? allQuantities[0]!\n : {\n type: \"or\",\n entries: allQuantities,\n };\n ingredientQuantities.set(alternative.index, [\n ...(ingredientQuantities.get(alternative.index) || []),\n equivalents,\n ]);\n }\n }\n }\n }\n }\n }\n\n // Build computed ingredients - only include selected ingredients\n const computedIngredients: ComputedIngredient[] = [];\n for (let index = 0; index < this.ingredients.length; index++) {\n if (!selectedIngredientIndices.has(index)) continue;\n\n const ing = this.ingredients[index]!;\n const computed: ComputedIngredient = {\n name: ing.name,\n };\n if (ing.preparation) {\n computed.preparation = ing.preparation;\n }\n if (ing.flags) {\n computed.flags = ing.flags;\n }\n if (ing.extras) {\n computed.extras = ing.extras;\n }\n const quantities = ingredientQuantities.get(index);\n if (quantities && quantities.length > 0) {\n computed.quantityTotal = addEquivalentsAndSimplify(...quantities);\n }\n computedIngredients.push(computed);\n }\n\n return computedIngredients;\n }\n\n /**\n * Parses a recipe from a string.\n * @param content - The recipe content to parse.\n */\n parse(content: string) {\n // Remove noise\n const cleanContent = content\n .replace(metadataRegex, \"\")\n .replace(commentRegex, \"\")\n .replace(blockCommentRegex, \"\")\n .trim()\n .split(/\\r\\n?|\\n/);\n\n // Metadata\n const { metadata, servings }: MetadataExtract = extractMetadata(content);\n this.metadata = metadata;\n this.servings = servings;\n\n // Initializing utility variables and property bearers\n let blankLineBefore = true;\n let section: Section = new Section();\n const items: Step[\"items\"] = [];\n let note: Note[\"note\"] = \"\";\n let inNote = false;\n\n // We parse content line by line\n for (const line of cleanContent) {\n // A blank line triggers flushing pending stuff\n if (line.trim().length === 0) {\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n blankLineBefore = true;\n inNote = false;\n continue;\n }\n\n // New section\n if (line.startsWith(\"=\")) {\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n\n if (this.sections.length === 0 && section.isBlank()) {\n section.name = line.replace(/^=+|=+$/g, \"\").trim();\n } else {\n /* v8 ignore else -- @preserve */\n if (!section.isBlank()) {\n this.sections.push(section);\n }\n section = new Section(line.replace(/^=+|=+$/g, \"\").trim());\n }\n blankLineBefore = true;\n inNote = false;\n continue;\n }\n\n // New note\n if (blankLineBefore && line.startsWith(\">\")) {\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n note += line.substring(1).trim();\n inNote = true;\n blankLineBefore = false;\n continue;\n }\n\n // Continue note\n if (inNote) {\n if (line.startsWith(\">\")) {\n note += \" \" + line.substring(1).trim();\n } else {\n note += \" \" + line.trim();\n }\n blankLineBefore = false;\n continue;\n }\n note = flushPendingNote(section, note);\n\n // Detecting items\n let cursor = 0;\n for (const match of line.matchAll(tokensRegex)) {\n const idx = match.index;\n /* v8 ignore else -- @preserve */\n if (idx > cursor) {\n items.push({ type: \"text\", value: line.slice(cursor, idx) });\n }\n\n const groups = match.groups!;\n\n // Ingredient items with potential in-line alternatives\n if (groups.mIngredientName || groups.sIngredientName) {\n this._parseIngredientWithAlternativeRecursive(match[0], items);\n }\n // Ingredient items part of a group of alternative ingredients\n else if (groups.gmIngredientName || groups.gsIngredientName) {\n this._parseIngredientWithGroupKey(match[0], items);\n }\n // Cookware items\n else if (groups.mCookwareName || groups.sCookwareName) {\n const name = (groups.mCookwareName || groups.sCookwareName)!;\n const modifiers = groups.cookwareModifiers;\n const quantityRaw = groups.cookwareQuantity;\n const reference = modifiers !== undefined && modifiers.includes(\"&\");\n const flags: CookwareFlag[] = [];\n if (modifiers !== undefined && modifiers.includes(\"?\")) {\n flags.push(\"optional\");\n }\n if (modifiers !== undefined && modifiers.includes(\"-\")) {\n flags.push(\"hidden\");\n }\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n const newCookware: Cookware = {\n name,\n };\n if (quantity) {\n newCookware.quantity = quantity;\n }\n if (flags.length > 0) {\n newCookware.flags = flags;\n }\n\n // Add cookware in cookware list\n const idxInList = findAndUpsertCookware(\n this.cookware,\n newCookware,\n reference,\n );\n\n // Adding the item itself in the preparation\n const newItem: CookwareItem = {\n type: \"cookware\",\n index: idxInList,\n };\n if (quantity) {\n newItem.quantity = quantity;\n }\n items.push(newItem);\n }\n // Then it's necessarily a timer which was matched\n else {\n const durationStr = groups.timerQuantity!.trim();\n const unit = (groups.timerUnit || \"\").trim();\n if (!unit) {\n throw new Error(\"Timer missing unit\");\n }\n const name = groups.timerName || undefined;\n const duration = parseQuantityInput(durationStr);\n const timerObj: Timer = {\n name,\n duration,\n unit,\n };\n items.push({ type: \"timer\", index: this.timers.push(timerObj) - 1 });\n }\n\n cursor = idx + match[0].length;\n }\n\n if (cursor < line.length) {\n items.push({ type: \"text\", value: line.slice(cursor) });\n }\n\n blankLineBefore = false;\n }\n\n // End of content reached: pushing all temporarily saved elements\n flushPendingItems(section, items);\n note = flushPendingNote(section, note);\n if (!section.isBlank()) {\n this.sections.push(section);\n }\n\n this._populate_ingredient_quantities();\n }\n\n /**\n * Scales the recipe to a new number of servings. In practice, it calls\n * {@link Recipe.scaleBy | scaleBy} with a factor corresponding to the ratio between `newServings`\n * and the recipe's {@link Recipe.servings | servings} value.\n * @param newServings - The new number of servings.\n * @returns A new Recipe instance with the scaled ingredients.\n * @throws `Error` if the recipe does not contains an initial {@link Recipe.servings | servings} value\n */\n scaleTo(newServings: number): Recipe {\n const originalServings = this.getServings();\n\n if (originalServings === undefined || originalServings === 0) {\n throw new Error(\"Error scaling recipe: no initial servings value set\");\n }\n\n const factor = Big(newServings).div(originalServings);\n return this.scaleBy(factor);\n }\n\n /**\n * Scales the recipe by a factor.\n * @param factor - The factor to scale the recipe by. While integers can be passed as-is, it is recommended to pass fractions as\n * [Big](https://github.com/MikeMcl/big.js/) values, e.g. `Big(num).div(den)` in order to avoid undesirable floating point operation inaccuracies.\n * @returns A new Recipe instance with the scaled ingredients.\n */\n scaleBy(factor: number | Big): Recipe {\n const newRecipe = this.clone();\n\n const originalServings = newRecipe.getServings();\n\n if (originalServings === undefined || originalServings === 0) {\n throw new Error(\"Error scaling recipe: no initial servings value set\");\n }\n\n function scaleAlternativesBy(\n alternatives: IngredientAlternative[],\n factor: number | Big,\n ) {\n for (const alternative of alternatives) {\n if (alternative.itemQuantity) {\n const scaleFactor = alternative.itemQuantity.scalable\n ? Big(factor)\n : 1;\n // Scale the primary quantity\n if (\n alternative.itemQuantity.quantity.type !== \"fixed\" ||\n alternative.itemQuantity.quantity.value.type !== \"text\"\n ) {\n alternative.itemQuantity.quantity = multiplyQuantityValue(\n alternative.itemQuantity.quantity,\n scaleFactor,\n );\n }\n // Scale equivalents if any\n if (alternative.itemQuantity.equivalents) {\n alternative.itemQuantity.equivalents =\n alternative.itemQuantity.equivalents.map(\n (altQuantity: QuantityWithExtendedUnit) => {\n if (\n altQuantity.quantity.type === \"fixed\" &&\n altQuantity.quantity.value.type === \"text\"\n ) {\n return altQuantity;\n } else {\n return {\n ...altQuantity,\n quantity: multiplyQuantityValue(\n altQuantity.quantity,\n scaleFactor,\n ),\n };\n }\n },\n );\n }\n }\n }\n }\n\n // Scale IngredientItems\n for (const section of newRecipe.sections) {\n for (const step of section.content.filter(\n (item) => item.type === \"step\",\n )) {\n for (const item of step.items.filter(\n (item) => item.type === \"ingredient\",\n )) {\n scaleAlternativesBy(item.alternatives, factor);\n }\n }\n }\n\n // Scale Choices\n for (const alternatives of newRecipe.choices.ingredientGroups.values()) {\n scaleAlternativesBy(alternatives, factor);\n }\n for (const alternatives of newRecipe.choices.ingredientItems.values()) {\n scaleAlternativesBy(alternatives, factor);\n }\n\n newRecipe._populate_ingredient_quantities();\n\n newRecipe.servings = Big(originalServings).times(factor).toNumber();\n\n /* v8 ignore else -- @preserve */\n if (newRecipe.metadata.servings && this.metadata.servings) {\n if (\n floatRegex.test(String(this.metadata.servings).replace(\",\", \".\").trim())\n ) {\n const servingsValue = parseFloat(\n String(this.metadata.servings).replace(\",\", \".\"),\n );\n newRecipe.metadata.servings = String(\n Big(servingsValue).times(factor).toNumber(),\n );\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (newRecipe.metadata.yield && this.metadata.yield) {\n if (\n floatRegex.test(String(this.metadata.yield).replace(\",\", \".\").trim())\n ) {\n const yieldValue = parseFloat(\n String(this.metadata.yield).replace(\",\", \".\"),\n );\n newRecipe.metadata.yield = String(\n Big(yieldValue).times(factor).toNumber(),\n );\n }\n }\n\n /* v8 ignore else -- @preserve */\n if (newRecipe.metadata.serves && this.metadata.serves) {\n if (\n floatRegex.test(String(this.metadata.serves).replace(\",\", \".\").trim())\n ) {\n const servesValue = parseFloat(\n String(this.metadata.serves).replace(\",\", \".\"),\n );\n newRecipe.metadata.serves = String(\n Big(servesValue).times(factor).toNumber(),\n );\n }\n }\n\n return newRecipe;\n }\n\n /**\n * Gets the number of servings for the recipe.\n * @private\n * @returns The number of servings, or undefined if not set.\n */\n private getServings(): number | undefined {\n if (this.servings) {\n return this.servings;\n }\n return undefined;\n }\n\n /**\n * Clones the recipe.\n * @returns A new Recipe instance with the same properties.\n */\n clone(): Recipe {\n const newRecipe = new Recipe();\n newRecipe.choices = deepClone(this.choices);\n Recipe.itemCounts.set(newRecipe, this.getItemCount());\n // deep copy\n newRecipe.metadata = deepClone(this.metadata);\n newRecipe.ingredients = deepClone(this.ingredients);\n newRecipe.sections = this.sections.map((section) => {\n const newSection = new Section(section.name);\n newSection.content = deepClone(section.content);\n return newSection;\n });\n newRecipe.cookware = deepClone(this.cookware);\n newRecipe.timers = deepClone(this.timers);\n newRecipe.servings = this.servings;\n return newRecipe;\n }\n}\n","import { CategoryConfig } from \"./category_config\";\nimport { Recipe } from \"./recipe\";\nimport type {\n CategorizedIngredients,\n AddedRecipe,\n AddedIngredient,\n QuantityWithExtendedUnit,\n QuantityWithPlainUnit,\n MaybeNestedGroup,\n FlatOrGroup,\n AddedRecipeOptions,\n} from \"../types\";\nimport { addEquivalentsAndSimplify } from \"../quantities/alternatives\";\nimport { extendAllUnits } from \"../quantities/mutations\";\nimport { isAndGroup } from \"../utils/type_guards\";\n\n/**\n * Shopping List generator.\n *\n * ## Usage\n *\n * - Create a new ShoppingList instance with an optional category configuration (see {@link ShoppingList.\"constructor\" | constructor})\n * - Add recipes, scaling them as needed (see {@link ShoppingList.add_recipe | add_recipe()})\n * - Categorize the ingredients (see {@link ShoppingList.categorize | categorize()})\n *\n * @example\n *\n * ```typescript\n * import * as fs from \"fs\";\n * import { ShoppingList } from @tmlmt/cooklang-parser;\n *\n * const categoryConfig = fs.readFileSync(\"./myconfig.txt\", \"utf-8\")\n * const recipe1 = new Recipe(fs.readFileSync(\"./myrecipe.cook\", \"utf-8\"));\n * const shoppingList = new ShoppingList();\n * shoppingList.set_category_config(categoryConfig);\n * // Quantities are automatically calculated and ingredients categorized\n * // when adding a recipe\n * shoppingList.add_recipe(recipe1);\n * ```\n *\n * @category Classes\n */\nexport class ShoppingList {\n // TODO: backport type change\n /**\n * The ingredients in the shopping list.\n */\n ingredients: AddedIngredient[] = [];\n /**\n * The recipes in the shopping list.\n */\n recipes: AddedRecipe[] = [];\n /**\n * The category configuration for the shopping list.\n */\n category_config?: CategoryConfig;\n /**\n * The categorized ingredients in the shopping list.\n */\n categories?: CategorizedIngredients;\n\n /**\n * Creates a new ShoppingList instance\n * @param category_config_str - The category configuration to parse.\n */\n constructor(category_config_str?: string | CategoryConfig) {\n if (category_config_str) {\n this.set_category_config(category_config_str);\n }\n }\n\n private calculate_ingredients() {\n this.ingredients = [];\n\n const addIngredientQuantity = (\n name: string,\n quantityTotal:\n | QuantityWithPlainUnit\n | MaybeNestedGroup<QuantityWithPlainUnit>,\n ) => {\n const quantityTotalExtended = extendAllUnits(quantityTotal);\n const newQuantities = (\n isAndGroup(quantityTotalExtended)\n ? quantityTotalExtended.entries\n : [quantityTotalExtended]\n ) as (QuantityWithExtendedUnit | FlatOrGroup<QuantityWithExtendedUnit>)[];\n const existing = this.ingredients.find((i) => i.name === name);\n\n if (existing) {\n if (!existing.quantityTotal) {\n existing.quantityTotal = quantityTotal;\n return;\n }\n try {\n const existingQuantityTotalExtended = extendAllUnits(\n existing.quantityTotal,\n );\n const existingQuantities = (\n isAndGroup(existingQuantityTotalExtended)\n ? existingQuantityTotalExtended.entries\n : [existingQuantityTotalExtended]\n ) as (\n | QuantityWithExtendedUnit\n | FlatOrGroup<QuantityWithExtendedUnit>\n )[];\n existing.quantityTotal = addEquivalentsAndSimplify(\n ...existingQuantities,\n ...newQuantities,\n );\n return;\n } catch {\n // Incompatible\n }\n }\n\n this.ingredients.push({\n name,\n quantityTotal,\n });\n };\n\n for (const addedRecipe of this.recipes) {\n let scaledRecipe: Recipe;\n if (\"factor\" in addedRecipe) {\n const { recipe, factor } = addedRecipe;\n scaledRecipe = factor === 1 ? recipe : recipe.scaleBy(factor);\n } else {\n scaledRecipe = addedRecipe.recipe.scaleTo(addedRecipe.servings);\n }\n\n // Get computed ingredients with total quantities based on choices (or default)\n const computedIngredients = scaledRecipe.calc_ingredient_quantities(\n addedRecipe.choices,\n );\n\n for (const ingredient of computedIngredients) {\n // Do not add hidden ingredients to the shopping list\n if (ingredient.flags && ingredient.flags.includes(\"hidden\")) {\n continue;\n }\n\n if (ingredient.quantityTotal) {\n addIngredientQuantity(ingredient.name, ingredient.quantityTotal);\n } else if (!this.ingredients.some((i) => i.name === ingredient.name)) {\n this.ingredients.push({ name: ingredient.name });\n }\n }\n }\n }\n\n /**\n * Adds a recipe to the shopping list, then automatically\n * recalculates the quantities and recategorize the ingredients.\n * @param recipe - The recipe to add.\n * @param options - Options for adding the recipe.\n */\n add_recipe(recipe: Recipe, options: AddedRecipeOptions = {}): void {\n if (!options.scaling) {\n this.recipes.push({\n recipe,\n factor: options.scaling ?? 1,\n choices: options.choices,\n });\n } else {\n if (\"factor\" in options.scaling) {\n this.recipes.push({\n recipe,\n factor: options.scaling.factor,\n choices: options.choices,\n });\n } else {\n this.recipes.push({\n recipe,\n servings: options.scaling.servings,\n choices: options.choices,\n });\n }\n }\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Removes a recipe from the shopping list, then automatically\n * recalculates the quantities and recategorize the ingredients.s\n * @param index - The index of the recipe to remove.\n */\n remove_recipe(index: number) {\n if (index < 0 || index >= this.recipes.length) {\n throw new Error(\"Index out of bounds\");\n }\n this.recipes.splice(index, 1);\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Sets the category configuration for the shopping list\n * and automatically categorize current ingredients from the list.\n * @param config - The category configuration to parse.\n */\n set_category_config(config: string | CategoryConfig) {\n if (typeof config === \"string\")\n this.category_config = new CategoryConfig(config);\n else if (config instanceof CategoryConfig) this.category_config = config;\n else throw new Error(\"Invalid category configuration\");\n this.categorize();\n }\n\n /**\n * Categorizes the ingredients in the shopping list\n * Will use the category config if any, otherwise all ingredients will be placed in the \"other\" category\n */\n categorize() {\n if (!this.category_config) {\n this.categories = { other: this.ingredients };\n return;\n }\n\n const categories: CategorizedIngredients = { other: [] };\n for (const category of this.category_config.categories) {\n categories[category.name] = [];\n }\n\n for (const ingredient of this.ingredients) {\n let found = false;\n for (const category of this.category_config.categories) {\n for (const categoryIngredient of category.ingredients) {\n if (categoryIngredient.aliases.includes(ingredient.name)) {\n categories[category.name]!.push(ingredient);\n found = true;\n break;\n }\n }\n if (found) {\n break;\n }\n }\n if (!found) {\n categories.other!.push(ingredient);\n }\n }\n\n this.categories = categories;\n }\n}\n","import type {\n ProductOption,\n ProductSelection,\n AddedIngredient,\n CartContent,\n CartMatch,\n CartMisMatch,\n FixedNumericValue,\n Range,\n ProductOptionNormalized,\n ProductSizeNormalized,\n NoProductMatchErrorCode,\n FlatOrGroup,\n MaybeNestedGroup,\n QuantityWithUnitDef,\n} from \"../types\";\nimport { ProductCatalog } from \"./product_catalog\";\nimport { ShoppingList } from \"./shopping_list\";\nimport {\n NoProductCatalogForCartError,\n NoShoppingListForCartError,\n NoProductMatchError,\n} from \"../errors\";\nimport { resolveUnit } from \"../units/definitions\";\nimport { normalizeAllUnits } from \"../quantities/mutations\";\nimport { getNumericValue, multiplyQuantityValue } from \"../quantities/numeric\";\nimport { isAndGroup, isOrGroup } from \"../utils/type_guards\";\nimport { areUnitsCompatible } from \"../units/lookup\";\nimport { solve, type Model } from \"yalps\";\n\n/**\n * Options for the {@link ShoppingCart} constructor\n * @category Types\n */\nexport interface ShoppingCartOptions {\n /**\n * A product catalog to connect to the cart\n */\n catalog?: ProductCatalog;\n /**\n * A shopping list to connect to the cart\n */\n list?: ShoppingList;\n}\n\n/**\n * Key information about the {@link ShoppingCart}\n * @category Types\n */\nexport interface ShoppingCartSummary {\n /**\n * The total price of the cart\n */\n totalPrice: number;\n /**\n * The total number of items in the cart\n */\n totalItems: number;\n}\n\n/**\n * Shopping Cart Manager: a tool to find the best combination of products to buy (defined in a {@link ProductCatalog}) to satisfy a {@link ShoppingList}.\n *\n * @example\n * ```ts\n * const shoppingList = new ShoppingList();\n * const recipe = new Recipe(\"@flour{600%g}\");\n * shoppingList.add_recipe(recipe);\n *\n * const catalog = new ProductCatalog();\n * catalog.products = [\n * {\n * id: \"flour-1kg\",\n * productName: \"Flour (1kg)\",\n * ingredientName: \"flour\",\n * price: 10,\n * size: { type: \"fixed\", value: { type: \"decimal\", value: 1000 } },\n * unit: \"g\",\n * },\n * {\n * id: \"flour-500g\",\n * productName: \"Flour (500g)\",\n * ingredientName: \"flour\",\n * price: 6,\n * size: { type: \"fixed\", value: { type: \"decimal\", value: 500 } },\n * unit: \"g\",\n * },\n * ];\n *\n * const shoppingCart = new ShoppingCart({list: shoppingList, catalog}))\n * shoppingCart.buildCart();\n * ```\n *\n * @category Classes\n */\nexport class ShoppingCart {\n /**\n * The product catalog to use for matching products\n */\n productCatalog?: ProductCatalog;\n /**\n * The shopping list to build the cart from\n */\n shoppingList?: ShoppingList;\n /**\n * The content of the cart\n */\n cart: CartContent = [];\n /**\n * The ingredients that were successfully matched with products\n */\n match: CartMatch = [];\n /**\n * The ingredients that could not be matched with products\n */\n misMatch: CartMisMatch = [];\n /**\n * Key information about the shopping cart\n */\n summary: ShoppingCartSummary;\n\n /**\n * Creates a new ShoppingCart instance\n * @param options - {@link ShoppingCartOptions | Options} for the constructor\n */\n constructor(options?: ShoppingCartOptions) {\n if (options?.catalog) this.productCatalog = options.catalog;\n if (options?.list) this.shoppingList = options.list;\n this.summary = { totalPrice: 0, totalItems: 0 };\n }\n\n /**\n * Sets the product catalog to use for matching products\n * To use if a catalog was not provided at the creation of the instance\n * @param catalog - The {@link ProductCatalog} to set\n */\n setProductCatalog(catalog: ProductCatalog) {\n this.productCatalog = catalog;\n }\n\n // TODO: harmonize recipe name to use underscores\n /**\n * Sets the shopping list to build the cart from.\n * To use if a shopping list was not provided at the creation of the instance\n * @param list - The {@link ShoppingList} to set\n */\n setShoppingList(list: ShoppingList) {\n this.shoppingList = list;\n }\n\n /**\n * Builds the cart from the shopping list and product catalog\n * @remarks\n * - If a combination of product(s) is successfully found for a given ingredient, the latter will be listed in the {@link ShoppingCart.match | match} array\n * in addition to that combination being added to the {@link ShoppingCart.cart | cart}.\n * - Otherwise, the latter will be listed in the {@link ShoppingCart.misMatch | misMatch} array. Possible causes can be:\n * - No product is listed in the catalog for that ingredient\n * - The ingredient has no quantity, a text quantity\n * - The ingredient's quantity unit is incompatible with the units of the candidate products listed in the catalog\n * @throws {@link NoProductCatalogForCartError} if no product catalog is set\n * @throws {@link NoShoppingListForCartError} if no shopping list is set\n * @returns `true` if all ingredients in the shopping list have been matched to products in the catalog, or `false` otherwise\n */\n buildCart(): boolean {\n this.resetCart();\n\n if (this.productCatalog === undefined) {\n throw new NoProductCatalogForCartError();\n } else if (this.shoppingList === undefined) {\n throw new NoShoppingListForCartError();\n }\n\n for (const ingredient of this.shoppingList.ingredients) {\n const productOptions = this.getProductOptions(ingredient);\n try {\n const optimumMatch = this.getOptimumMatch(ingredient, productOptions);\n this.cart.push(...optimumMatch);\n this.match.push({ ingredient, selection: optimumMatch });\n } catch (error) {\n /* v8 ignore else -- @preserve */\n if (error instanceof NoProductMatchError) {\n this.misMatch.push({ ingredient, reason: error.code });\n }\n }\n }\n\n this.summarize();\n\n return this.misMatch.length > 0;\n }\n\n /**\n * Gets the product options for a given ingredient\n * @param ingredient - The ingredient to get the product options for\n * @returns An array of {@link ProductOption}\n */\n private getProductOptions(ingredient: AddedIngredient): ProductOption[] {\n // this function is only called in buildCart() which starts by checking that a product catalog is present\n return this.productCatalog!.products.filter(\n (product) =>\n product.ingredientName === ingredient.name ||\n product.ingredientAliases?.includes(ingredient.name),\n );\n }\n\n /**\n * Gets the optimum match for a given ingredient and product option\n * @param ingredient - The ingredient to match\n * @param options - The product options to choose from\n * @returns An array of {@link ProductSelection}\n * @throws {@link NoProductMatchError} if no match can be found\n */\n private getOptimumMatch(\n ingredient: AddedIngredient,\n options: ProductOption[],\n ): ProductSelection[] {\n // If there's no product option, return an empty match\n if (options.length === 0)\n throw new NoProductMatchError(ingredient.name, \"noProduct\");\n // If the ingredient has no quantity, we can't match any product\n if (!ingredient.quantityTotal)\n throw new NoProductMatchError(ingredient.name, \"noQuantity\");\n\n // Normalize options units and scale size to base\n const normalizedOptions: ProductOptionNormalized[] = options.map(\n (option) => ({\n ...option,\n sizes: option.sizes.map((s): ProductSizeNormalized => {\n const resolvedUnit = resolveUnit(s.unit);\n return {\n size:\n resolvedUnit && \"toBase\" in resolvedUnit\n ? (multiplyQuantityValue(\n s.size,\n resolvedUnit.toBase,\n ) as FixedNumericValue)\n : s.size,\n unit: resolvedUnit,\n };\n }),\n }),\n );\n const normalizedQuantityTotal = normalizeAllUnits(ingredient.quantityTotal);\n\n function getOptimumMatchForQuantityParts(\n normalizedQuantities:\n | QuantityWithUnitDef\n | MaybeNestedGroup<QuantityWithUnitDef>,\n normalizedOptions: ProductOptionNormalized[],\n selection: ProductSelection[] = [],\n ): ProductSelection[] {\n if (isAndGroup(normalizedQuantities)) {\n for (const q of normalizedQuantities.entries) {\n const result = getOptimumMatchForQuantityParts(\n q,\n normalizedOptions,\n selection,\n );\n selection.push(...result);\n }\n } else {\n const alternativeUnitsOfQuantity = isOrGroup(normalizedQuantities)\n ? (normalizedQuantities as FlatOrGroup<QuantityWithUnitDef>).entries\n : [normalizedQuantities];\n const solutions: ProductSelection[][] = [];\n const errors = new Set<NoProductMatchErrorCode>();\n for (const alternative of alternativeUnitsOfQuantity) {\n // At this stage, we're treating individual Quantities we should try to match\n if (\n alternative.quantity.type === \"fixed\" &&\n alternative.quantity.value.type === \"text\"\n ) {\n errors.add(\"textValue\");\n continue;\n }\n // At this stage, we know there is a numerical quantity\n // So we scale it to base in order to calculate the correct quantity\n const scaledQuantity = multiplyQuantityValue(\n alternative.quantity,\n \"toBase\" in alternative.unit ? alternative.unit.toBase : 1,\n ) as FixedNumericValue | Range;\n alternative.quantity = scaledQuantity;\n // Are there compatible product options for that specific unit alternative?\n // A product is compatible if ANY of its sizes has a compatible unit\n const matchOptions = normalizedOptions.filter((option) =>\n option.sizes.some((s) =>\n areUnitsCompatible(alternative.unit, s.unit),\n ),\n );\n if (matchOptions.length > 0) {\n // Helper to find the compatible size for a product option\n const findCompatibleSize = (\n option: ProductOptionNormalized,\n ): ProductSizeNormalized =>\n option.sizes.find((s) =>\n areUnitsCompatible(alternative.unit, s.unit),\n )!;\n\n // Simple minimization exercise if only one product option\n if (matchOptions.length == 1) {\n const matchedOption = matchOptions[0]!;\n const compatibleSize = findCompatibleSize(matchedOption);\n const product = options.find(\n (opt) => opt.id === matchedOption.id,\n )!;\n // FixedValue\n const targetQuantity =\n scaledQuantity.type === \"fixed\"\n ? scaledQuantity.value\n : scaledQuantity.min;\n const resQuantity = Math.ceil(\n getNumericValue(targetQuantity) /\n getNumericValue(compatibleSize.size.value),\n );\n solutions.push([\n {\n product,\n quantity: resQuantity,\n totalPrice: resQuantity * matchedOption.price,\n },\n ]);\n continue;\n }\n\n // More complex problem if there are several options\n const model: Model = {\n direction: \"minimize\",\n objective: \"price\",\n integers: true,\n constraints: {\n size: {\n min:\n scaledQuantity.type === \"fixed\"\n ? getNumericValue(scaledQuantity.value)\n : getNumericValue(scaledQuantity.min),\n },\n },\n variables: matchOptions.reduce(\n (acc, option) => {\n const compatibleSize = findCompatibleSize(option);\n acc[option.id] = {\n price: option.price,\n size: getNumericValue(compatibleSize.size.value),\n };\n return acc;\n },\n {} as Record<string, { price: number; size: number }>,\n ),\n };\n\n const solution = solve(model);\n solutions.push(\n solution.variables.map((variable) => {\n const resProductSelection = {\n product: options.find((option) => option.id === variable[0])!,\n quantity: variable[1],\n };\n return {\n ...resProductSelection,\n totalPrice:\n resProductSelection.quantity *\n resProductSelection.product.price,\n };\n }),\n );\n } else {\n errors.add(\"incompatibleUnits\");\n }\n }\n // All alternatives were checked\n if (solutions.length === 0) {\n throw new NoProductMatchError(\n ingredient.name,\n errors.size === 1\n ? (errors.values().next().value as NoProductMatchErrorCode)\n : \"textValue_incompatibleUnits\",\n );\n } else {\n // We return the cheapest solution among those found\n return solutions.sort(\n (a, b) =>\n a.reduce((acc, item) => acc + item.totalPrice, 0) -\n b.reduce((acc, item) => acc + item.totalPrice, 0),\n )[0]!;\n }\n }\n return selection;\n }\n\n return getOptimumMatchForQuantityParts(\n normalizedQuantityTotal,\n normalizedOptions,\n );\n }\n\n /**\n * Reset the cart's properties\n */\n private resetCart() {\n this.cart = [];\n this.match = [];\n this.misMatch = [];\n this.summary = { totalPrice: 0, totalItems: 0 };\n }\n\n /**\n * Calculate the cart's key info and store it in the cart's {@link ShoppingCart.summary | summary} property.\n * This function is automatically invoked by {@link ShoppingCart.buildCart | buildCart() } method.\n * @returns the total price and number of items in the cart\n */\n summarize(): ShoppingCartSummary {\n this.summary.totalPrice = this.cart.reduce(\n (acc, item) => acc + item.totalPrice,\n 0,\n );\n this.summary.totalItems = this.cart.length;\n return this.summary;\n }\n}\n"],"mappings":"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","names":["escapeCache","Map","Flags","GLOBAL","NON_SENSITIVE","MULTILINE","DOT_ALL","UNICODE","STICKY","Ranges","Object","freeze","digit","lowercaseLetter","uppercaseLetter","letter","alphanumeric","anyCharacter","Quantifiers","zeroOrMore","oneOrMore","optional","HumanRegex","constructor","this","parts","flags","Set","add","special","word","whitespace","nonWhitespace","text","has","set","replace","get","or","name","range","Error","chars","lazy","lastPart","pop","newline","pattern","hasSpecialCharacter","hasDigit","hasLetter","n","min","max","startGroup","startCaptureGroup","wordBoundary","nonWordBoundary","endGroup","startAnchor","endAnchor","global","nonSensitive","multiline","dotAll","sticky","variant","validVariants","unicodeDigit","unicodePunctuation","unicodeSymbol","count","length","push","ipv4Octet","protocol","www","tld","path","part","toString","join","toRegExp","RegExp","createRegex","Patterns","createCachedPattern","builder","regex","source","email","literal","atLeast","url","phoneInternational","between","r","a","Big","e","i","e","a","TOML","import_big","import_big","Big","units","a","unitRatio","Big","import_big","item","i","Big","factor","i","normalizedOptions","a"]}
|