{"version":3,"file":"parse-ingredient.mjs","names":[],"sources":["../src/constants.ts","../src/unitLookup.ts","../src/convertUnit.ts","../src/parseIngredient.ts"],"sourcesContent":["import { numericRegex } from 'numeric-quantity';\nimport { ParseIngredientOptions, UnitOfMeasureDefinitions } from './types';\n\n// --- i18n Utilities ---\n\n/**\n * Escapes special regex characters in a string.\n */\nexport const escapeRegex = (str: string): string => str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\n/**\n * Builds a regex that matches any of the given patterns at the start of a string,\n * followed by whitespace. Strings are escaped and treated as literal prefixes.\n * RegExp patterns have their source extracted and combined.\n */\nexport const buildPrefixPatternRegex = (patterns: (string | RegExp)[]): RegExp | null => {\n  if (patterns.length === 0) return null;\n  const parts = patterns.map(p =>\n    p instanceof RegExp ? `(?:${p.source})` : `(?:${escapeRegex(p)})\\\\s`\n  );\n  return new RegExp(`^(?:${parts.join('|')})`, 'iu');\n};\n\n/**\n * Builds a regex source string for range separators (dashes and word separators).\n * Always includes dash characters (-, –, —), plus any custom word separators.\n */\nexport const buildRangeSeparatorSource = (words: (string | RegExp)[]): string => {\n  const wordParts = words.map(w =>\n    w instanceof RegExp ? `(?:${w.source})` : `(?:${escapeRegex(w)})`\n  );\n  // Always include dashes, then word separators followed by whitespace\n  return `(-|–|—|(?:${wordParts.join('|')})\\\\s)`;\n};\n\n/**\n * Builds a regex that matches range separators at the start of a string.\n */\nexport const buildRangeSeparatorRegex = (words: (string | RegExp)[]): RegExp =>\n  new RegExp(`^${buildRangeSeparatorSource(words)}`, 'iu');\n\n/**\n * Builds a regex that matches any of the given words or patterns at the start of a string.\n * Strings are matched as whole words followed by whitespace.\n * RegExp patterns are used as-is for more complex matching (e.g., French elisions).\n */\nexport const buildStripPrefixRegex = (patterns: (string | RegExp)[]): RegExp | null => {\n  if (patterns.length === 0) return null;\n  const parts = patterns.map(p =>\n    p instanceof RegExp ? `(?:${p.source})` : `(?:${escapeRegex(p)})\\\\s+`\n  );\n  return new RegExp(`^(?:${parts.join('|')})`, 'iu');\n};\n\n/**\n * Builds a regex that matches approximation/modifier prefixes at the start of\n * quantity expressions (e.g., \"about\", \"ca.\", \"bis zu\"), followed by optional whitespace.\n */\nexport const buildLeadingQuantityPrefixRegex = (patterns: (string | RegExp)[]): RegExp | null => {\n  if (patterns.length === 0) return null;\n  // Uses `\\s*` (optional whitespace) instead of `\\s+` (required whitespace, as in\n  // buildStripPrefixRegex) because quantity prefixes like \"ca.\" may appear directly\n  // adjacent to the number (e.g., \"ca.200g\").\n  const parts = patterns.map(p =>\n    p instanceof RegExp ? `(?:${p.source})\\\\s*` : `(?:${escapeRegex(p)})\\\\s*`\n  );\n  return new RegExp(`^(?:${parts.join('|')})`, 'iu');\n};\n\n/**\n * Builds a regex that matches any of the given words at the end of a string,\n * preceded by whitespace. Used for trailing quantity context like \"from\" or \"of\".\n */\nexport const buildTrailingContextRegex = (words: string[]): RegExp =>\n  new RegExp(`\\\\s+(?:${words.map(escapeRegex).join('|')})$`, 'iu');\n\n// --- Default i18n Values ---\n\n/**\n * Default group header prefixes (e.g., \"For the icing:\").\n */\nexport const defaultGroupHeaderPatterns = ['For'] as const;\n\n/**\n * Default range separator words (e.g., \"1 to 2\", \"1 or 2\").\n */\nexport const defaultRangeSeparators = ['or', 'to'] as const;\n\n/**\n * Default words to strip from the beginning of descriptions.\n */\nexport const defaultDescriptionStripPrefixes = ['of'] as const;\n\n/**\n * Default words that indicate trailing quantity context.\n */\nexport const defaultTrailingQuantityContext = ['from', 'of'] as const;\n/**\n * Default words/patterns that are stripped from the beginning of quantity expressions.\n */\nexport const defaultLeadingQuantityPrefixes = [] as const;\n\n/**\n * Default options for {@link parseIngredient}.\n */\nexport const defaultOptions: Required<ParseIngredientOptions> = {\n  additionalUOMs: {},\n  allowLeadingOf: false,\n  normalizeUOM: false,\n  ignoreUOMs: [],\n  decimalSeparator: '.',\n  groupHeaderPatterns: defaultGroupHeaderPatterns as unknown as string[],\n  rangeSeparators: defaultRangeSeparators as unknown as string[],\n  descriptionStripPrefixes: defaultDescriptionStripPrefixes as unknown as (string | RegExp)[],\n  trailingQuantityContext: defaultTrailingQuantityContext as unknown as string[],\n  leadingQuantityPrefixes: defaultLeadingQuantityPrefixes as unknown as (string | RegExp)[],\n  includeMeta: false,\n  partialUnitMatching: false,\n} as const;\n\n// --- Legacy Exports (for backward compatibility) ---\n\n/**\n * List of \"for\" equivalents.\n * @deprecated Use `defaultGroupHeaderPatterns` instead.\n */\nexport const fors: typeof defaultGroupHeaderPatterns = defaultGroupHeaderPatterns;\n\n/**\n * Regex to capture \"for\" equivalents.\n * @deprecated Build dynamically using `buildPrefixPatternRegex(options.groupHeaderPatterns)`.\n */\nexport const forsRegEx: RegExp = buildPrefixPatternRegex(\n  defaultGroupHeaderPatterns as unknown as string[]\n)!;\n\n/**\n * List of range separators.\n * @deprecated Use `defaultRangeSeparators` instead.\n */\nexport const rangeSeparatorWords: typeof defaultRangeSeparators = defaultRangeSeparators;\n\n/**\n * Regex to capture range separators.\n * @deprecated Build dynamically using `buildRangeSeparatorRegex(options.rangeSeparators)`.\n */\nexport const rangeSeparatorRegEx: RegExp = buildRangeSeparatorRegex(\n  defaultRangeSeparators as unknown as string[]\n);\n\n/**\n * Regex to capture the first word of a description to check if it's a unit of measure.\n */\nexport const firstWordRegEx: RegExp =\n  /^(fl(?:uid)?(?:\\s+|-)(?:oz|ounces?)|[\\p{L}\\p{N}_]+(?:[./-][\\p{L}\\p{N}_]+|\\([\\p{L}\\p{N}_]+\\))*[-.]?)(.+)?/iu;\n\nconst numericRegexAnywhere = numericRegex.source.replace('^', '').replace(/\\$$/, '');\n\n/**\n * Builds a regex to capture trailing quantity and unit of measure,\n * using the provided range separator words.\n */\nexport const buildTrailingQuantityRegex = (rangeSeparators: (string | RegExp)[]): RegExp => {\n  const rangeSeparatorSource = buildRangeSeparatorSource(rangeSeparators);\n  return new RegExp(\n    `(,|:|-|–|—|x|⨯)?\\\\s*((${numericRegexAnywhere})\\\\s*(${rangeSeparatorSource}))?\\\\s*(${numericRegexAnywhere})\\\\s*(fl(?:uid)?(?:\\\\s+|-)(?:oz|ounces?)|[\\\\p{L}\\\\p{N}_]+(?:[./-][\\\\p{L}\\\\p{N}_]+|\\\\([\\\\p{L}\\\\p{N}_]+\\\\))*[-.]?)?$`,\n    'iu'\n  );\n};\n\n/**\n * Regex to capture trailing quantity and unit of measure.\n * @deprecated Build dynamically using `buildTrailingQuantityRegex(options.rangeSeparators)`.\n */\nexport const trailingQuantityRegEx: RegExp = buildTrailingQuantityRegex(\n  defaultRangeSeparators as unknown as string[]\n);\n\n/**\n * List of \"of\" equivalents.\n * @deprecated Use `defaultDescriptionStripPrefixes` instead.\n */\nexport const ofs: typeof defaultDescriptionStripPrefixes = defaultDescriptionStripPrefixes;\n\n/**\n * Regex to capture \"of\" equivalents at the beginning of a string.\n * @deprecated Build dynamically using `buildStripPrefixRegex(options.descriptionStripPrefixes)`.\n */\nexport const ofRegEx: RegExp = buildStripPrefixRegex(\n  defaultDescriptionStripPrefixes as unknown as string[]\n)!;\n\n/**\n * List of \"from\" equivalents.\n * @deprecated Use `defaultTrailingQuantityContext` instead.\n */\nexport const froms: typeof defaultTrailingQuantityContext = defaultTrailingQuantityContext;\n\n/**\n * Regex to capture \"from\" equivalents at the end of a string.\n * @deprecated Build dynamically using `buildTrailingContextRegex(options.trailingQuantityContext)`.\n */\nexport const fromRegEx: RegExp = buildTrailingContextRegex(\n  defaultTrailingQuantityContext as unknown as string[]\n);\n\n/**\n * Default unit of measure specifications.\n */\nexport const unitsOfMeasure: UnitOfMeasureDefinitions = {\n  // Count units (no conversion factor)\n  bag: {\n    short: 'bag',\n    plural: 'bags',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  box: {\n    short: 'box',\n    plural: 'boxes',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  bunch: {\n    short: 'bunch',\n    plural: 'bunches',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  can: {\n    short: 'can',\n    plural: 'cans',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  carton: {\n    short: 'carton',\n    plural: 'cartons',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  clove: {\n    short: 'clove',\n    plural: 'cloves',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  container: {\n    short: 'container',\n    plural: 'containers',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  dozen: {\n    short: 'dz',\n    plural: 'dozen',\n    alternates: ['dz.'] satisfies string[],\n    type: 'count',\n  },\n  ear: {\n    short: 'ear',\n    plural: 'ears',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  head: {\n    short: 'head',\n    plural: 'heads',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  pack: {\n    short: 'pack',\n    plural: 'packs',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  package: {\n    short: 'pkg',\n    plural: 'packages',\n    alternates: ['pkg.', 'pkgs', 'pkgs.'] satisfies string[],\n    type: 'count',\n  },\n  piece: {\n    short: 'piece',\n    plural: 'pieces',\n    alternates: ['pc', 'pc.', 'pcs', 'pcs.'] satisfies string[],\n    type: 'count',\n  },\n  sprig: {\n    short: 'sprig',\n    plural: 'sprigs',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n  stick: {\n    short: 'stick',\n    plural: 'sticks',\n    alternates: [] satisfies string[],\n    type: 'count',\n  },\n\n  // Other units (no conversion factor)\n  large: {\n    short: 'lg',\n    plural: 'large',\n    alternates: ['lg', 'lg.'] satisfies string[],\n    type: 'other',\n  },\n  medium: {\n    short: 'md',\n    plural: 'medium',\n    alternates: ['med', 'med.', 'md.'] satisfies string[],\n    type: 'other',\n  },\n  small: {\n    short: 'sm',\n    plural: 'small',\n    alternates: ['sm.'] satisfies string[],\n    type: 'other',\n  },\n\n  // Length units (conversion factor in mm)\n  centimeter: {\n    short: 'cm',\n    plural: 'centimeters',\n    alternates: ['cm.'] satisfies string[],\n    type: 'length',\n    conversionFactor: 10,\n  },\n  foot: {\n    short: 'ft',\n    plural: 'feet',\n    alternates: ['ft.'] satisfies string[],\n    type: 'length',\n    conversionFactor: 304.8,\n  },\n  inch: {\n    short: 'in',\n    plural: 'inches',\n    alternates: ['in.'] satisfies string[],\n    type: 'length',\n    conversionFactor: 25.4,\n  },\n  meter: {\n    short: 'm',\n    plural: 'meters',\n    alternates: ['m.'] satisfies string[],\n    type: 'length',\n    conversionFactor: 1000,\n  },\n  millimeter: {\n    short: 'mm',\n    plural: 'millimeters',\n    alternates: ['mm.'] satisfies string[],\n    type: 'length',\n    conversionFactor: 1,\n  },\n  yard: {\n    short: 'yd',\n    plural: 'yards',\n    alternates: ['yd.', 'yds.'] satisfies string[],\n    type: 'length',\n    conversionFactor: 914.4,\n  },\n\n  // Mass units (conversion factor in g)\n  gram: {\n    short: 'g',\n    plural: 'grams',\n    alternates: ['g.'] satisfies string[],\n    type: 'mass',\n    conversionFactor: 1,\n  },\n  kilogram: {\n    short: 'kg',\n    plural: 'kilograms',\n    alternates: ['kg.'] satisfies string[],\n    type: 'mass',\n    conversionFactor: 1000,\n  },\n  milligram: {\n    short: 'mg',\n    plural: 'milligrams',\n    alternates: ['mg.'] satisfies string[],\n    type: 'mass',\n    conversionFactor: 0.001,\n  },\n  ounce: {\n    short: 'oz',\n    plural: 'ounces',\n    alternates: ['oz.'] satisfies string[],\n    type: 'mass',\n    conversionFactor: 28.349523,\n  },\n  pound: {\n    short: 'lb',\n    plural: 'pounds',\n    alternates: ['lb.', 'lbs', 'lbs.'] satisfies string[],\n    type: 'mass',\n    conversionFactor: 453.59237,\n  },\n\n  // Volume units (conversion factor in ml)\n  cup: {\n    short: 'c',\n    plural: 'cups',\n    alternates: ['c.', 'C'] satisfies string[],\n    type: 'volume',\n    conversionFactor: { us: 236.58824, imperial: 284.13063, metric: 250 },\n  },\n  deciliter: {\n    short: 'dl',\n    plural: 'deciliters',\n    alternates: ['dl.'] satisfies string[],\n    type: 'volume',\n    conversionFactor: 100,\n  },\n  'fluid ounce': {\n    short: 'fl oz',\n    plural: 'fluid ounces',\n    alternates: [\n      'fluidounce',\n      'floz',\n      'fl-oz',\n      'fluid-ounce',\n      'fluid-ounces',\n      'fluidounces',\n      'fl ounce',\n      'fl ounces',\n      'fl-ounce',\n      'fl-ounces',\n      'fluid oz',\n      'fluid-oz',\n    ] satisfies string[],\n    type: 'volume',\n    conversionFactor: { us: 29.57353, imperial: 28.413063 },\n  },\n  gallon: {\n    short: 'gal',\n    plural: 'gallons',\n    alternates: ['gal.'] satisfies string[],\n    type: 'volume',\n    conversionFactor: { us: 3785.4118, imperial: 4546.09 },\n  },\n  liter: {\n    short: 'l',\n    plural: 'liters',\n    alternates: ['l.'] satisfies string[],\n    type: 'volume',\n    conversionFactor: 1000,\n  },\n  milliliter: {\n    short: 'ml',\n    plural: 'milliliters',\n    alternates: ['mL', 'ml.', 'mL.'] satisfies string[],\n    type: 'volume',\n    conversionFactor: 1,\n  },\n  pint: {\n    short: 'pt',\n    plural: 'pints',\n    alternates: ['pt.'] satisfies string[],\n    type: 'volume',\n    conversionFactor: { us: 473.17647, imperial: 568.26125 },\n  },\n  quart: {\n    short: 'qt',\n    plural: 'quarts',\n    alternates: ['qt.', 'qts', 'qts.'] satisfies string[],\n    type: 'volume',\n    conversionFactor: { us: 946.35295, imperial: 1136.5225 },\n  },\n  tablespoon: {\n    short: 'tbsp',\n    plural: 'tablespoons',\n    alternates: ['tbsp.', 'T', 'Tbsp.', 'Tbsp', 'tablespoonful'] satisfies string[],\n    type: 'volume',\n    conversionFactor: { us: 14.786765, imperial: 15, metric: 15 },\n  },\n  teaspoon: {\n    short: 'tsp',\n    plural: 'teaspoons',\n    alternates: ['tsp.', 't', 'teaspoonful'] satisfies string[],\n    type: 'volume',\n    conversionFactor: { us: 4.9289216, imperial: 5, metric: 5 },\n  },\n\n  // Volume units without conversion factor (imprecise)\n  dash: {\n    short: 'dash',\n    plural: 'dashes',\n    alternates: [] satisfies string[],\n    type: 'volume',\n  },\n  drop: {\n    short: 'drop',\n    plural: 'drops',\n    alternates: [] satisfies string[],\n    type: 'volume',\n  },\n  pinch: {\n    short: 'pinch',\n    plural: 'pinches',\n    alternates: [] satisfies string[],\n    type: 'volume',\n  },\n} as const;\n","import { unitsOfMeasure } from './constants';\nimport { UnitOfMeasureDefinitions } from './types';\n\n/**\n * Result of building unit lookup maps.\n */\nexport interface UnitLookupMaps {\n  /** Case-sensitive map (exact matches only) */\n  caseSensitive: Map<string, string>;\n  /** Case-insensitive map (lowercase keys) */\n  caseInsensitive: Map<string, string>;\n}\n\n/**\n * Builds Maps for unit lookup. Returns both case-sensitive and case-insensitive maps.\n * The case-sensitive map should be checked first to handle cases like 'T' (tablespoon)\n * vs 't' (teaspoon).\n */\nexport const buildUnitLookupMaps = (\n  additionalUOMs: UnitOfMeasureDefinitions = {}\n): UnitLookupMaps => {\n  const caseSensitive = new Map<string, string>();\n  const caseInsensitive = new Map<string, string>();\n\n  // Helper to add versions to maps (first one wins for case-insensitive)\n  const addToMaps = (id: string, def: UnitOfMeasureDefinitions[string]) => {\n    const versions = [id, def.short, def.plural, ...def.alternates];\n    for (const version of versions) {\n      // For case-sensitive, later entries override (so additionalUOMs wins)\n      caseSensitive.set(version, id);\n      // For case-insensitive, later entries also override\n      caseInsensitive.set(version.toLowerCase(), id);\n    }\n  };\n\n  // Process default UOMs first\n  for (const [id, def] of Object.entries(unitsOfMeasure)) {\n    addToMaps(id, def);\n  }\n\n  // Process additionalUOMs second so they override defaults\n  for (const [id, def] of Object.entries(additionalUOMs)) {\n    addToMaps(id, def);\n  }\n\n  return { caseSensitive, caseInsensitive };\n};\n\n/**\n * Looks up a unit ID from the maps, trying case-sensitive first.\n */\nexport const lookupUnit = (unit: string, maps: UnitLookupMaps): string | null =>\n  maps.caseSensitive.get(unit) ?? maps.caseInsensitive.get(unit.toLowerCase()) ?? null;\n\n/**\n * Cached lookup maps for the default unitsOfMeasure (no additionalUOMs).\n * Lazily initialized on first use.\n */\nlet defaultLookupMaps: UnitLookupMaps | null = null;\n\n/**\n * Gets the default lookup maps, creating them if needed.\n */\nexport const getDefaultUnitLookupMaps = (): UnitLookupMaps =>\n  defaultLookupMaps ?? (defaultLookupMaps = buildUnitLookupMaps());\n\n/**\n * Collects all known UOM strings from the lookup maps, sorted longest-first.\n */\nexport const collectUOMStrings = (maps: UnitLookupMaps): string[] => {\n  const keys = [...maps.caseSensitive.keys()];\n  keys.sort((a, b) => b.length - a.length);\n  return keys;\n};\n","import { unitsOfMeasure } from './constants';\nimport {\n  MultiSystemConversionFactor,\n  ParseIngredientOptions,\n  UnitOfMeasureDefinitions,\n  UnitSystem,\n} from './types';\nimport { buildUnitLookupMaps, getDefaultUnitLookupMaps, lookupUnit } from './unitLookup';\n\nexport type IdentifyUnitOptions = Pick<ParseIngredientOptions, 'additionalUOMs' | 'ignoreUOMs'>;\n\n/**\n * Identifies a unit of measure from a string, returning the canonical unit ID.\n * Matches against the unit ID, short form, plural form, and all alternates.\n * Case-sensitive matches are tried first (e.g., 'T' = tablespoon, 't' = teaspoon),\n * then falls back to case-insensitive matching.\n *\n * @returns The canonical unit ID (e.g., 'cup'), or `null` if the unit is not recognized\n *          or is in the `ignoreUOMs` list.\n *\n * @example\n * ```ts\n * identifyUnit('cups') // 'cup'\n * identifyUnit('c') // 'cup'\n * identifyUnit('T') // 'tablespoon'\n * identifyUnit('t') // 'teaspoon'\n * identifyUnit('tbsp') // 'tablespoon'\n * identifyUnit('unknown') // null\n * identifyUnit('large', { ignoreUOMs: ['large'] }) // null\n * ```\n */\nexport const identifyUnit = (\n  /** The unit string to identify (e.g., 'cups', 'c', 'C', 'cup'). */\n  unit: string,\n  /** Options for unit identification. */\n  options: IdentifyUnitOptions = {}\n): string | null => {\n  const { additionalUOMs = {}, ignoreUOMs = [] } = options;\n\n  // Check if the unit should be ignored (case-insensitive)\n  if (ignoreUOMs.length > 0) {\n    const unitLC = unit.toLowerCase();\n    if (ignoreUOMs.some(ignored => ignored.toLowerCase() === unitLC)) {\n      return null;\n    }\n  }\n\n  const hasAdditionalUOMs = Object.keys(additionalUOMs).length > 0;\n  const maps = hasAdditionalUOMs ? buildUnitLookupMaps(additionalUOMs) : getDefaultUnitLookupMaps();\n\n  return lookupUnit(unit, maps);\n};\n\n/**\n * Options for {@link convertUnit}.\n */\nexport interface ConvertUnitOptions {\n  /**\n   * The measurement system to use when units have different US/Imperial conversion factors.\n   *\n   * @default 'us'\n   */\n  fromSystem?: UnitSystem;\n  /**\n   * The measurement system to use when units have different US/Imperial conversion factors.\n   *\n   * @default 'us'\n   */\n  toSystem?: UnitSystem;\n  /**\n   * Additional unit definitions to use for conversion.\n   * These are merged with the default {@link unitsOfMeasure}.\n   */\n  additionalUOMs?: UnitOfMeasureDefinitions;\n}\n\n/**\n * Gets the conversion factor for a unit, handling both single and multi-system factors.\n */\nconst getConversionFactor = (\n  factor: number | MultiSystemConversionFactor | undefined,\n  system: UnitSystem\n): number | null =>\n  factor === undefined ? null : typeof factor === 'number' ? factor : (factor[system] ?? null);\n\n/**\n * Converts a value from one unit to another.\n *\n * @returns The converted value, or `null` if conversion is not possible\n *          (incompatible types, missing conversion factors, or unknown units).\n *\n * @example\n * ```ts\n * convertUnit(1, 'cup', 'milliliter') // ~236.588 (US)\n * convertUnit(1, 'cup', 'milliliter', { fromSystem: 'imperial' }) // ~284.131\n * convertUnit(1, 'pound', 'gram') // ~453.592\n * convertUnit(1, 'cup', 'gram') // null (incompatible types)\n * ```\n */\nexport const convertUnit = (\n  /** The numeric value to convert. */\n  value: number,\n  /** The unit to convert from (unit ID, short, plural, or alternate spelling). */\n  fromUnit: string,\n  /** The unit to convert to (unit ID, short, plural, or alternate spelling). */\n  toUnit: string,\n  /** Conversion options. */\n  options: ConvertUnitOptions = {}\n): number | null => {\n  const { fromSystem = 'us', toSystem = 'us', additionalUOMs = {} } = options;\n  // Normalize system names to lowercase for case-insensitive matching\n  const normalizedFromSystem = fromSystem.toLowerCase() as UnitSystem;\n  const normalizedToSystem = toSystem.toLowerCase() as UnitSystem;\n  const mergedUOMs = { ...unitsOfMeasure, ...additionalUOMs };\n\n  const fromUnitID = identifyUnit(fromUnit, { additionalUOMs });\n  const toUnitID = identifyUnit(toUnit, { additionalUOMs });\n\n  // Unknown units\n  if (!fromUnitID || !toUnitID) {\n    return null;\n  }\n\n  // Same unit with same system - return value as-is\n  if (fromUnitID === toUnitID && normalizedFromSystem === normalizedToSystem) {\n    return value;\n  }\n\n  const fromDef = mergedUOMs[fromUnitID];\n  const toDef = mergedUOMs[toUnitID];\n\n  // Incompatible or missing types\n  if (!fromDef.type || !toDef.type || fromDef.type !== toDef.type) {\n    return null;\n  }\n\n  const fromFactor = getConversionFactor(fromDef.conversionFactor, normalizedFromSystem);\n  const toFactor = getConversionFactor(toDef.conversionFactor, normalizedToSystem);\n\n  // Missing conversion factors\n  if (fromFactor === null || toFactor === null) {\n    return null;\n  }\n\n  // Convert via base unit: value * fromFactor / toFactor\n  const result = (value * fromFactor) / toFactor;\n  return Math.round(result * 1e6) / 1e6;\n};\n","import { numericQuantity, NumericQuantityOptions } from 'numeric-quantity';\nimport {\n  buildLeadingQuantityPrefixRegex,\n  buildPrefixPatternRegex,\n  buildRangeSeparatorRegex,\n  buildStripPrefixRegex,\n  buildTrailingContextRegex,\n  buildTrailingQuantityRegex,\n  defaultOptions,\n  firstWordRegEx,\n} from './constants';\nimport { identifyUnit } from './convertUnit';\nimport type { Ingredient, ParseIngredientOptions } from './types';\nimport { buildUnitLookupMaps, collectUOMStrings, getDefaultUnitLookupMaps } from './unitLookup';\n\nconst newLineRegExp = /\\r?\\n/;\nconst nextWordRegExp = /^([\\p{L}\\p{N}_]+(?:[.-]?[\\p{L}\\p{N}_]+)*[-.]?)(?:\\s+|$)/iu;\n\n/**\n * Repeatedly strips configured quantity prefixes from the start of a string.\n */\nconst stripLeadingQuantityPrefixes = (text: string, prefixRegex: RegExp | null): string => {\n  if (!text || !prefixRegex) return text;\n  let out = text.trimStart();\n  while (out) {\n    const match = prefixRegex.exec(out);\n    // Break if no match or if the match is zero-length to prevent infinite loops\n    // (empty string is falsy, so `!match[0]` catches both null/undefined and \"\")\n    if (!match || !match[0]) break;\n    out = out.slice(match[0].length).trimStart();\n  }\n  return out;\n};\n\n/**\n * Parses a string or array of strings into an array of recipe ingredient objects\n */\nexport const parseIngredient = (\n  /**\n   * The ingredient list, as plain text or an array of strings.\n   */\n  ingredientText: string | string[],\n  /**\n   * Configuration options. Defaults to {@link defaultOptions}.\n   */\n  options: ParseIngredientOptions = defaultOptions\n): Ingredient[] => {\n  const opts = { ...defaultOptions, ...options };\n  const nqOpts: NumericQuantityOptions | undefined =\n    opts.decimalSeparator === ',' ? { decimalSeparator: ',' } : undefined;\n\n  // Pre-compute lowercase ignored UOMs for the trailing quantity bail-out check\n  const ignoredUOMsLC = opts.ignoreUOMs.map(u => u.toLowerCase());\n\n  // Build dynamic regexes from i18n options\n  const groupHeaderRegex = buildPrefixPatternRegex(opts.groupHeaderPatterns);\n  const rangeSeparatorRegex = buildRangeSeparatorRegex(opts.rangeSeparators);\n  const stripPrefixRegex = buildStripPrefixRegex(opts.descriptionStripPrefixes);\n  const trailingContextRegex = buildTrailingContextRegex(opts.trailingQuantityContext);\n  const trailingQuantityRegex = buildTrailingQuantityRegex(opts.rangeSeparators);\n  const leadingQuantityPrefixRegex = buildLeadingQuantityPrefixRegex(opts.leadingQuantityPrefixes);\n\n  const uomStrings = opts.partialUnitMatching\n    ? collectUOMStrings(\n        Object.keys(opts.additionalUOMs).length > 0\n          ? buildUnitLookupMaps(opts.additionalUOMs)\n          : getDefaultUnitLookupMaps()\n      )\n    : [];\n\n  const ingredientArray = (\n    Array.isArray(ingredientText) ? ingredientText : ingredientText.split(newLineRegExp)\n  )\n    .map((line, index) => ({ line: line.trim(), sourceIndex: index }))\n    .filter(({ line }) => Boolean(line));\n\n  return ingredientArray.map(({ line, sourceIndex }) => {\n    const lineToParse = stripLeadingQuantityPrefixes(line, leadingQuantityPrefixRegex);\n    const oIng: Ingredient = {\n      quantity: null,\n      quantity2: null,\n      unitOfMeasureID: null,\n      unitOfMeasure: null,\n      description: '',\n      isGroupHeader: false,\n    };\n\n    if (opts.includeMeta) {\n      oIng.meta = {\n        sourceText: line,\n        sourceIndex,\n      };\n    }\n\n    // Check if the line begins with either (1) at least one numeric character, or\n    // (2) a decimal separator followed by at least one numeric character.\n    if (\n      lineToParse &&\n      (!isNaN(numericQuantity(lineToParse[0], nqOpts)) ||\n        (lineToParse[0] === opts.decimalSeparator &&\n          !isNaN(numericQuantity(lineToParse.slice(0, 2), nqOpts))))\n    ) {\n      // See how many of the first seven characters constitute a single value. This will be `quantity`.\n      let lenNum = 6;\n      let nqResult = NaN;\n\n      while (lenNum > 0 && isNaN(nqResult)) {\n        nqResult = numericQuantity(lineToParse.substring(0, lenNum).trim(), nqOpts);\n\n        if (nqResult > -1) {\n          oIng.quantity = nqResult;\n          oIng.description = lineToParse.substring(lenNum).trim();\n        }\n\n        lenNum--;\n      }\n    } else {\n      // The first character is not numeric. First check for trailing quantity/uom.\n      const trailingQtyResult = trailingQuantityRegex.exec(lineToParse);\n      const trailingQtyMaybeUom = trailingQtyResult?.at(-1)?.toLowerCase();\n\n      if (trailingQtyMaybeUom && ignoredUOMsLC.includes(trailingQtyMaybeUom)) {\n        // Trailing quantity detected, but bailing out since the UOM should be ignored.\n        oIng.description = lineToParse;\n      } else if (trailingQtyResult) {\n        // Trailing quantity detected with missing or non-ignored UOM.\n        // Remove the quantity and unit of measure from the description.\n        oIng.description = lineToParse.replace(trailingQuantityRegex, '').trim();\n\n        // Trailing quantity/range.\n        const firstQty = trailingQtyResult[3];\n        const secondQty = trailingQtyResult[12];\n        if (!firstQty) {\n          oIng.quantity = numericQuantity(secondQty, nqOpts);\n        } else {\n          oIng.quantity = numericQuantity(firstQty, nqOpts);\n          oIng.quantity2 = numericQuantity(secondQty, nqOpts);\n        }\n\n        // Trailing unit of measure.\n        const uomRaw = trailingQtyResult.at(-1);\n        if (uomRaw) {\n          let uomID: string | null = null;\n          let finalUomRaw = uomRaw;\n\n          // Greedy: try multi-word unit first (prefer longer match over shorter one)\n          if (oIng.description) {\n            const descWords = oIng.description.trim().split(/\\s+/);\n            if (descWords.length >= 1) {\n              const lastDescWord = descWords[descWords.length - 1];\n              const twoWordUnit = lastDescWord + ' ' + uomRaw;\n              const twoWordID = identifyUnit(twoWordUnit, options);\n\n              if (twoWordID) {\n                uomID = twoWordID;\n                finalUomRaw = twoWordUnit;\n                // Remove the last word from description\n                oIng.description = descWords.slice(0, -1).join(' ');\n              }\n            }\n          }\n\n          // Fall back to single-word match\n          if (!uomID) {\n            uomID = identifyUnit(uomRaw, options);\n            finalUomRaw = uomRaw;\n          }\n\n          if (uomID) {\n            oIng.unitOfMeasureID = uomID;\n            oIng.unitOfMeasure = opts.normalizeUOM ? uomID : finalUomRaw;\n          } else if (oIng.description.match(trailingContextRegex)) {\n            oIng.description += ` ${uomRaw}`;\n          }\n        }\n      } else {\n        // The first character is not numeric, and no trailing quantity was detected,\n        // so the entire line is the description.\n        oIng.description = lineToParse;\n\n        // If the line ends with \":\" or matches a group header pattern, it is assumed to be a group header.\n        if (oIng.description.endsWith(':') || groupHeaderRegex?.test(oIng.description)) {\n          oIng.isGroupHeader = true;\n        }\n      }\n    }\n\n    // Now check the description for a `quantity2` at the beginning.\n    // First we look for a dash, emdash, endash, or word separator to\n    // indicate a range, then process the next seven characters just\n    // like we did for `quantity`.\n    const q2reMatch = rangeSeparatorRegex.exec(oIng.description);\n    if (q2reMatch) {\n      const q2reMatchLen = q2reMatch[1].length;\n      const q2Portion = stripLeadingQuantityPrefixes(\n        oIng.description.substring(q2reMatchLen).trim(),\n        leadingQuantityPrefixRegex\n      );\n\n      // Guard against empty string after prefix stripping (e.g., aggressive\n      // prefixes could strip content down to nothing)\n      if (q2Portion) {\n        const nqResultFirstChar = numericQuantity(q2Portion[0], nqOpts);\n\n        if (!isNaN(nqResultFirstChar)) {\n          let lenNum = 7;\n          let nqResult = NaN;\n\n          while (--lenNum > 0 && isNaN(nqResult)) {\n            nqResult = numericQuantity(q2Portion.substring(0, lenNum), nqOpts);\n\n            if (!isNaN(nqResult)) {\n              oIng.quantity2 = nqResult;\n              oIng.description = q2Portion.substring(lenNum).trim();\n            }\n          }\n        }\n      }\n    }\n\n    // Check for a known unit of measure\n    const firstWordREMatches = firstWordRegEx.exec(oIng.description);\n\n    if (firstWordREMatches) {\n      const firstWord = firstWordREMatches[1].replace(/\\s+/g, ' ');\n      const remainingDesc = (firstWordREMatches[2] ?? '').trim();\n      if (remainingDesc) {\n        let uomID = identifyUnit(firstWord, options);\n        let matchedUnit = firstWord;\n        let finalDesc = remainingDesc;\n\n        // Try multi-word unit combinations (greedy matching: prefer longer matches over shorter ones)\n        const nextWords = remainingDesc.match(nextWordRegExp);\n        if (nextWords) {\n          const twoWordCombo = firstWord + ' ' + nextWords[1];\n          const twoWordID = identifyUnit(twoWordCombo, options);\n\n          // If multi-word unit exists, prefer it over single-word match.\n          // When no quantity is present, require remaining description\n          // (consistent with single-word behavior: \"1 cup\" → description, not UOM).\n          const multiWordFinalDesc = remainingDesc.substring(nextWords[0].length).trim();\n          if (twoWordID && (oIng.quantity !== null || multiWordFinalDesc)) {\n            uomID = twoWordID;\n            matchedUnit = twoWordCombo;\n            finalDesc = multiWordFinalDesc;\n          }\n        }\n\n        if (uomID) {\n          oIng.unitOfMeasureID = uomID;\n          oIng.unitOfMeasure = opts.normalizeUOM ? uomID : matchedUnit;\n          oIng.description = finalDesc;\n        }\n      }\n    }\n\n    // Fallback: scan description for known UOM substrings (for CJK/spaceless text)\n    if (!oIng.unitOfMeasureID && opts.partialUnitMatching && oIng.description) {\n      const descLower = oIng.description.toLowerCase();\n      for (const uomStr of uomStrings) {\n        const idx = descLower.indexOf(uomStr.toLowerCase());\n        if (idx === -1) continue;\n\n        const matchedText = oIng.description.substring(idx, idx + uomStr.length);\n        const uomID = identifyUnit(matchedText, options);\n        if (!uomID) continue;\n\n        const before = oIng.description.substring(0, idx).trim();\n        const after = oIng.description.substring(idx + uomStr.length).trim();\n        const newDesc = [before, after].filter(Boolean).join(' ');\n\n        // Don't extract UOM if it would leave description empty\n        // (consistent with \"2 cup\" keeping \"cup\" as description, not UOM)\n        if (!newDesc) continue;\n\n        oIng.unitOfMeasureID = uomID;\n        oIng.unitOfMeasure = opts.normalizeUOM ? uomID : matchedText;\n        oIng.description = newDesc;\n        break;\n      }\n    }\n\n    if (!opts.allowLeadingOf && stripPrefixRegex && oIng.description.match(stripPrefixRegex)) {\n      oIng.description = oIng.description.replace(stripPrefixRegex, '');\n    }\n\n    return oIng;\n  });\n};\n"],"mappings":"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"}