{"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  .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).oneOrMore()\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  .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).oneOrMore()\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  .whitespace().zeroOrMore()\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,\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,\n): FixedValue | Range {\n  if (value.type === \"fixed\") {\n    const newValue = multiplyNumericValue(\n      value.value as DecimalValue | FractionValue,\n      factor,\n    );\n    if (\n      factor === parseInt(factor.toString()) || // e.g. 2 === int\n      1 / factor === parseInt((1 / 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\";\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.substring(1).trim();\n        } else {\n          /* v8 ignore else -- @preserve */\n          if (!section.isBlank()) {\n            this.sections.push(section);\n          }\n          section = new Section(line.substring(1).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 = newServings / 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.\n   * @returns A new Recipe instance with the scaled ingredients.\n   */\n  scaleBy(factor: number): 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 ? 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 = originalServings * factor;\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(servingsValue * factor);\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(yieldValue * factor);\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(servesValue * factor);\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","i"]}