@tmlmt/cooklang-parser 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -6
- package/dist/index.cjs +30 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -6
- package/dist/index.d.ts +10 -6
- package/dist/index.js +30 -18
- package/dist/index.js.map +1 -1
- package/package.json +23 -5
package/README.md
CHANGED
|
@@ -11,12 +11,23 @@ The documentation is available at [https://cooklang-parser.tmlmt.com](https://co
|
|
|
11
11
|
## Features
|
|
12
12
|
|
|
13
13
|
- **Cooklang Compliant:** Fully compliant with the Cooklang specifications.
|
|
14
|
-
- **
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
14
|
+
- **Recipe Parsing:** Parse Cooklang recipes to extract metadata, ingredients, cookware, timers, and steps. Several extensions on top of the original cooklang specifications:
|
|
15
|
+
- **Useful modifiers:** in line with the same in the canonical cooklang parser in Rust ([cooklang-rs](https://github.com/cooklang/cooklang-rs/blob/main/extensions.md))
|
|
16
|
+
- `@`: referenced recipe
|
|
17
|
+
- `&`: referenced ingredient (quantities will be added)
|
|
18
|
+
- `-`: hidden ingredient
|
|
19
|
+
- `?`: optional ingredient
|
|
20
|
+
- All but `@` also work with Cookware
|
|
21
|
+
- **Range values:**
|
|
22
|
+
- `@eggs{2-4}` will show eggs as an ingredient with a quantity range of 2 to 4
|
|
23
|
+
- `@water{1%L} and @&water{1/4-1/2%L}` will show water as an ingredient with a quantity range of 1.25 to 1.5 L
|
|
24
|
+
- Also works with Cookware and Timers
|
|
25
|
+
- **Cookware quantities:**
|
|
26
|
+
- Cookware can also be quantified (without any unit, e.g. `#bowls{2}`)
|
|
27
|
+
- Quantities will be added if cookware are referenced, e.g. `#&bowls{2}`
|
|
28
|
+
- **Ingredient aliases:**
|
|
29
|
+
- `@ingredientName|displayAlias{}` will add the ingredient as "ingredientName" in the ingredients list, but will display is as "displayAlias" in the preparation step.
|
|
30
|
+
- Also work with referencing etc., e.g. `Mix @wheat flour{100%g} with additional @&wheat flour|flour{50%g}` enables to get 150g of wheat flour in the ingredients list, and let you display "Mix wheat flour (100 g) with additional flour (50 g)" in your recipe renderer.
|
|
20
31
|
- **Recipe Scaling:** Scale recipes by a given factor.
|
|
21
32
|
- **Shopping Lists:** Generate shopping lists from one or more recipes.
|
|
22
33
|
- **Aisle Configuration:** Categorize shopping list ingredients based on a custom aisle configuration.
|
package/dist/index.cjs
CHANGED
|
@@ -316,6 +316,7 @@ var metadataRegex = d().literal("---").newline().startCaptureGroup().anyCharacte
|
|
|
316
316
|
var nonWordChar = "\\s@#~\\[\\]{(.,;:!?";
|
|
317
317
|
var multiwordIngredient = d().literal("@").startNamedGroup("mIngredientModifier").anyOf("@\\-&?").endGroup().optional().startNamedGroup("mIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").startGroup().literal("{").startNamedGroup("mIngredientQuantity").notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("mIngredientUnits").notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("mIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
318
318
|
var singleWordIngredient = d().literal("@").startNamedGroup("sIngredientModifier").anyOf("@\\-&?").endGroup().optional().startNamedGroup("sIngredientName").notAnyOf(nonWordChar).oneOrMore().endGroup().startGroup().literal("{").startNamedGroup("sIngredientQuantity").notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("sIngredientUnits").notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("sIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
319
|
+
var ingredientAliasRegex = d().startAnchor().startNamedGroup("ingredientListName").notAnyOf("|").oneOrMore().endGroup().literal("|").startNamedGroup("ingredientDisplayName").notAnyOf("|").oneOrMore().endGroup().endAnchor().toRegExp();
|
|
319
320
|
var multiwordCookware = d().literal("#").startNamedGroup("mCookwareModifier").anyOf("\\-&?").endGroup().optional().startNamedGroup("mCookwareName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\})").literal("{").startNamedGroup("mCookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").toRegExp();
|
|
320
321
|
var singleWordCookware = d().literal("#").startNamedGroup("sCookwareModifier").anyOf("\\-&?").endGroup().optional().startNamedGroup("sCookwareName").notAnyOf(nonWordChar).oneOrMore().endGroup().startGroup().literal("{").startNamedGroup("sCookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").endGroup().optional().toRegExp();
|
|
321
322
|
var timer = d().literal("~").startNamedGroup("timerName").anyCharacter().zeroOrMore().lazy().endGroup().literal("{").startNamedGroup("timerQuantity").anyCharacter().oneOrMore().lazy().endGroup().startGroup().literal("%").startNamedGroup("timerUnits").anyCharacter().oneOrMore().lazy().endGroup().endGroup().optional().literal("}").toRegExp();
|
|
@@ -536,12 +537,8 @@ function multiplyQuantityValue(value, factor) {
|
|
|
536
537
|
}
|
|
537
538
|
return {
|
|
538
539
|
type: "range",
|
|
539
|
-
min: toRoundedDecimal(
|
|
540
|
-
|
|
541
|
-
),
|
|
542
|
-
max: toRoundedDecimal(
|
|
543
|
-
multiplyNumericValue(value.max, factor)
|
|
544
|
-
)
|
|
540
|
+
min: toRoundedDecimal(multiplyNumericValue(value.min, factor)),
|
|
541
|
+
max: toRoundedDecimal(multiplyNumericValue(value.max, factor))
|
|
545
542
|
};
|
|
546
543
|
}
|
|
547
544
|
var convertQuantityValue = (value, def, targetDef) => {
|
|
@@ -623,14 +620,14 @@ function findOrPush(list, finder, creator) {
|
|
|
623
620
|
}
|
|
624
621
|
function flushPendingNote(section, note) {
|
|
625
622
|
if (note.length > 0) {
|
|
626
|
-
section.content.push({ note });
|
|
623
|
+
section.content.push({ type: "note", note });
|
|
627
624
|
return "";
|
|
628
625
|
}
|
|
629
626
|
return note;
|
|
630
627
|
}
|
|
631
628
|
function flushPendingItems(section, items) {
|
|
632
629
|
if (items.length > 0) {
|
|
633
|
-
section.content.push({ items: [...items] });
|
|
630
|
+
section.content.push({ type: "step", items: [...items] });
|
|
634
631
|
items.length = 0;
|
|
635
632
|
return true;
|
|
636
633
|
}
|
|
@@ -901,10 +898,19 @@ var Recipe = class _Recipe {
|
|
|
901
898
|
const reference = modifier === "&";
|
|
902
899
|
const isRecipe = modifier === "@";
|
|
903
900
|
const quantity = quantityRaw ? parseQuantityInput(quantityRaw) : void 0;
|
|
901
|
+
const aliasMatch = name.match(ingredientAliasRegex);
|
|
902
|
+
let listName, displayName;
|
|
903
|
+
if (aliasMatch && aliasMatch.groups.ingredientListName.trim().length > 0 && aliasMatch.groups.ingredientDisplayName.trim().length > 0) {
|
|
904
|
+
listName = aliasMatch.groups.ingredientListName.trim();
|
|
905
|
+
displayName = aliasMatch.groups.ingredientDisplayName.trim();
|
|
906
|
+
} else {
|
|
907
|
+
listName = name;
|
|
908
|
+
displayName = name;
|
|
909
|
+
}
|
|
904
910
|
const idxInList = findAndUpsertIngredient(
|
|
905
911
|
this.ingredients,
|
|
906
912
|
{
|
|
907
|
-
name,
|
|
913
|
+
name: listName,
|
|
908
914
|
quantity,
|
|
909
915
|
unit: units2,
|
|
910
916
|
optional,
|
|
@@ -916,13 +922,11 @@ var Recipe = class _Recipe {
|
|
|
916
922
|
);
|
|
917
923
|
const newItem = {
|
|
918
924
|
type: "ingredient",
|
|
919
|
-
value: idxInList
|
|
925
|
+
value: idxInList,
|
|
926
|
+
itemQuantity: quantity,
|
|
927
|
+
itemUnit: units2,
|
|
928
|
+
displayName
|
|
920
929
|
};
|
|
921
|
-
if (reference) {
|
|
922
|
-
newItem.partialQuantity = quantity;
|
|
923
|
-
newItem.partialUnit = units2;
|
|
924
|
-
newItem.partialPreparation = preparation;
|
|
925
|
-
}
|
|
926
930
|
items.push(newItem);
|
|
927
931
|
} else if (groups.mCookwareName || groups.sCookwareName) {
|
|
928
932
|
const name = groups.mCookwareName || groups.sCookwareName;
|
|
@@ -937,7 +941,11 @@ var Recipe = class _Recipe {
|
|
|
937
941
|
{ name, quantity, optional, hidden },
|
|
938
942
|
reference
|
|
939
943
|
);
|
|
940
|
-
items.push({
|
|
944
|
+
items.push({
|
|
945
|
+
type: "cookware",
|
|
946
|
+
value: idxInList,
|
|
947
|
+
itemQuantity: quantity
|
|
948
|
+
});
|
|
941
949
|
} else if (groups.timerQuantity !== void 0) {
|
|
942
950
|
const durationStr = groups.timerQuantity.trim();
|
|
943
951
|
const unit = (groups.timerUnits || "").trim();
|
|
@@ -1043,9 +1051,13 @@ var Recipe = class _Recipe {
|
|
|
1043
1051
|
clone() {
|
|
1044
1052
|
const newRecipe = new _Recipe();
|
|
1045
1053
|
newRecipe.metadata = JSON.parse(JSON.stringify(this.metadata));
|
|
1046
|
-
newRecipe.ingredients = JSON.parse(
|
|
1054
|
+
newRecipe.ingredients = JSON.parse(
|
|
1055
|
+
JSON.stringify(this.ingredients)
|
|
1056
|
+
);
|
|
1047
1057
|
newRecipe.sections = JSON.parse(JSON.stringify(this.sections));
|
|
1048
|
-
newRecipe.cookware = JSON.parse(
|
|
1058
|
+
newRecipe.cookware = JSON.parse(
|
|
1059
|
+
JSON.stringify(this.cookware)
|
|
1060
|
+
);
|
|
1049
1061
|
newRecipe.timers = JSON.parse(JSON.stringify(this.timers));
|
|
1050
1062
|
newRecipe.servings = this.servings;
|
|
1051
1063
|
return newRecipe;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/classes/aisle_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/parser_helpers.ts","../src/classes/recipe.ts","../src/classes/shopping_list.ts"],"sourcesContent":["import { AisleConfig } from \"./classes/aisle_config\";\nimport { Recipe } from \"./classes/recipe\";\nimport { ShoppingList } from \"./classes/shopping_list\";\nimport { Section } from \"./classes/section\";\n\nimport type {\n Metadata,\n MetadataExtract,\n Ingredient,\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 CategorizedIngredients,\n AddedRecipe,\n AisleIngredient,\n AisleCategory,\n} from \"./types\";\n\nexport {\n Recipe,\n ShoppingList,\n AisleConfig,\n Metadata,\n MetadataExtract,\n Ingredient,\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 CategorizedIngredients,\n AddedRecipe,\n AisleIngredient,\n AisleCategory,\n Section,\n};\n","import type { AisleCategory, AisleIngredient } from \"../types\";\n\n/**\n * Represents the aisle configuration for a shopping list.\n * @category Classes\n */\nexport class AisleConfig {\n /**\n * The categories of aisles.\n * @see {@link AisleCategory}\n */\n categories: AisleCategory[] = [];\n\n /**\n * Creates a new AisleConfig instance.\n * @param config - The aisle configuration to parse.\n */\n constructor(config?: string) {\n if (config) {\n this.parse(config);\n }\n }\n\n /**\n * Parses an aisle configuration from a string.\n * @param config - The aisle configuration to parse.\n */\n parse(config: string) {\n let currentCategory: AisleCategory | 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: AisleIngredient = {\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 * Wrapped as a Class and not as a simple type to expose some useful helper\n * classes (e.g. `isBlank()`)\n * @category Types\n */\nexport class Section {\n /** The name of the section. Can be an empty string for the default (first) section. */\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\nconst nonWordChar = \"\\\\s@#~\\\\[\\\\]{(.,;:!?\"\n\nconst multiwordIngredient = createRegex()\n .literal(\"@\")\n .startNamedGroup(\"mIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\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(\"mIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"mIngredientUnits\")\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(\"sIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\n .endGroup().optional()\n .startNamedGroup(\"sIngredientName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"sIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"sIngredientUnits\")\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\nconst multiwordCookware = createRegex()\n .literal(\"#\")\n .startNamedGroup(\"mCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"sCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"timerUnits\")\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 .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()","import type { FixedValue, Range, DecimalValue, FractionValue } from \"./types\";\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\"],\n toBase: 1,\n },\n {\n name: \"kg\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"kilogram\", \"kilograms\"],\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\"],\n toBase: 1,\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\"],\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: v.value * factor };\n }\n return simplifyFraction(v.num * factor, 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 // We only return a fraction where both input values are fractions themselves\n if (val1.type === \"fraction\" && val2.type === \"fraction\") {\n const commonDen = den1 * den2;\n const sumNum = num1 * den2 + num2 * den1;\n return simplifyFraction(sumNum, commonDen);\n } else {\n return { type: \"decimal\", value: num1 / den1 + num2 / den2 };\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 return {\n type: \"fixed\",\n value: toRoundedDecimal(\n multiplyNumericValue(\n value.value as DecimalValue | FractionValue,\n factor,\n ),\n ),\n };\n }\n\n return {\n type: \"range\",\n min: toRoundedDecimal(\n multiplyNumericValue(value.min as DecimalValue | FractionValue, factor),\n ),\n max: toRoundedDecimal(\n multiplyNumericValue(value.max as DecimalValue | FractionValue, factor),\n ),\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 * 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,\n ): Quantity => {\n if (val1.type === \"fixed\" && val2.type === \"fixed\") {\n const res = addNumericValues(\n val1.value as DecimalValue | FractionValue,\n val2.value as DecimalValue | FractionValue,\n );\n return { value: { type: \"fixed\", value: res }, unit };\n }\n const r1 =\n val1.type === \"range\"\n ? val1\n : { type: \"range\", min: val1.value, max: val1.value };\n const r2 =\n val2.type === \"range\"\n ? val2\n : { type: \"range\", min: val2.value, max: val2.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 { value: { type: \"range\", min: newMin, max: newMax }, unit };\n };\n\n // Case 2: one of the two values doesn't have a unit, we consider its value to be 0 and the unit to be that of the other one\n if (q1.unit === \"\" && unit2Def) {\n return addQuantityValuesAndSetUnit(v1, v2, q2.unit); // Prefer q2's unit\n }\n if (q2.unit === \"\" && unit1Def) {\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 (q1.unit.toLowerCase() === q2.unit.toLowerCase()) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit);\n }\n\n // Case 4: the two quantities do not have the same unit\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 throw new IncompatibleUnitsError(q1.unit, q2.unit);\n}\n","import type {\n MetadataExtract,\n Metadata,\n FixedValue,\n Range,\n TextValue,\n DecimalValue,\n FractionValue,\n} from \"./types\";\nimport { metadataRegex, rangeRegex, numberLikeRegex } from \"./regex\";\nimport { Section as SectionObject } from \"./classes/section\";\nimport type { Ingredient, Note, Step, Cookware } from \"./types\";\nimport {\n addQuantities,\n CannotAddTextValueError,\n IncompatibleUnitsError,\n Quantity,\n} from \"./units\";\n\n/**\n * Finds an item in a list or adds it if not present, then returns its index.\n * @param list The list to search in.\n * @param finder A predicate to find the item.\n * @param creator A function to create the item if not found.\n * @returns The index of the item in the list.\n */\nexport function findOrPush<T>(\n list: T[],\n finder: (elem: T) => boolean,\n creator: () => T,\n): number {\n let index = list.findIndex(finder);\n if (index === -1) {\n index = list.push(creator()) - 1;\n }\n return index;\n}\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({ 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({ 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 */\nexport function findAndUpsertIngredient(\n ingredients: Ingredient[],\n newIngredient: Ingredient,\n isReference: boolean,\n): number {\n const { name, quantity, unit } = newIngredient;\n\n // New ingredient\n if (isReference) {\n const index = ingredients.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (index === -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[index]!;\n if (quantity !== undefined) {\n const currentQuantity: Quantity = {\n value: existingIngredient.quantity ?? {\n type: \"fixed\",\n value: { type: \"decimal\", value: 0 },\n },\n unit: existingIngredient.unit ?? \"\",\n };\n const newQuantity = { value: quantity, unit: unit ?? \"\" };\n\n try {\n const total = addQuantities(currentQuantity, newQuantity);\n existingIngredient.quantity = total.value;\n existingIngredient.unit = total.unit || undefined;\n } catch (e) {\n if (\n e instanceof IncompatibleUnitsError ||\n e instanceof CannotAddTextValueError\n ) {\n // Addition not possible, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n }\n }\n }\n return index;\n }\n\n // Not a reference, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n}\n\nexport function findAndUpsertCookware(\n cookware: Cookware[],\n newCookware: Cookware,\n isReference: boolean,\n): number {\n const { name } = 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 return index;\n }\n\n return cookware.push(newCookware) - 1;\n}\n\n// Parser when we know the input is either a number-like value\nconst 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(content: string, varName: string) {\n const varMatch = content.match(\n new RegExp(`^${varName}:[\\\\t ]*(([^,\\\\n]*),? ?(?:.*)?)`, \"m\"),\n );\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 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 Metadata)[]) {\n const stringMetaValue: any = parseSimpleMetaVar(metadataContent, metaVar);\n if (stringMetaValue) metadata[metaVar] = stringMetaValue;\n }\n\n // String metadata variables\n for (const metaVar of [\"servings\", \"yield\", \"serves\"] as (keyof Metadata)[]) {\n const scalingMetaValue: any = 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 Metadata)[]) {\n const listMetaValue: any = parseListMetaVar(metadataContent, metaVar);\n if (listMetaValue) metadata[metaVar] = listMetaValue;\n }\n\n return { metadata, servings };\n}\n","import type {\n Metadata,\n Ingredient,\n IngredientItem,\n Timer,\n Step,\n Note,\n Cookware,\n MetadataExtract,\n} from \"../types\";\nimport { Section } from \"./section\";\nimport {\n tokensRegex,\n commentRegex,\n blockCommentRegex,\n metadataRegex,\n} from \"../regex\";\nimport {\n findOrPush,\n flushPendingItems,\n flushPendingNote,\n findAndUpsertIngredient,\n findAndUpsertCookware,\n parseQuantityInput,\n extractMetadata,\n} from \"../parser_helpers\";\nimport { multiplyQuantityValue } from \"../units\";\n\n/**\n * Represents a recipe.\n * @category Classes\n */\nexport class Recipe {\n /**\n * The recipe's metadata.\n * @see {@link Metadata}\n */\n metadata: Metadata = {};\n /**\n * The recipe's ingredients.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipe's sections.\n * @see {@link Section}\n */\n sections: Section[] = [];\n /**\n * The recipe's cookware.\n * @see {@link Cookware}\n */\n cookware: Cookware[] = [];\n /**\n * The recipe's timers.\n * @see {@link Timer}\n */\n timers: Timer[] = [];\n /**\n * The recipe's servings. Used for scaling\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 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 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 const name = (groups.mIngredientName || groups.sIngredientName)!;\n const quantityRaw =\n groups.mIngredientQuantity || groups.sIngredientQuantity;\n const units = groups.mIngredientUnits || groups.sIngredientUnits;\n const preparation =\n groups.mIngredientPreparation || groups.sIngredientPreparation;\n const modifier =\n groups.mIngredientModifier || groups.sIngredientModifier;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const isRecipe = modifier === \"@\";\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n\n const idxInList = findAndUpsertIngredient(\n this.ingredients,\n {\n name,\n quantity,\n unit: units,\n optional,\n hidden,\n preparation,\n isRecipe,\n },\n reference,\n );\n\n const newItem: IngredientItem = {\n type: \"ingredient\",\n value: idxInList,\n };\n if (reference) {\n newItem.partialQuantity = quantity;\n newItem.partialUnit = units;\n newItem.partialPreparation = preparation;\n }\n\n items.push(newItem);\n } else if (groups.mCookwareName || groups.sCookwareName) {\n const name = (groups.mCookwareName || groups.sCookwareName)!;\n const modifier = groups.mCookwareModifier || groups.sCookwareModifier;\n const quantityRaw =\n groups.mCookwareQuantity || groups.sCookwareQuantity;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n\n const idxInList = findAndUpsertCookware(\n this.cookware,\n { name, quantity, optional, hidden },\n reference,\n );\n items.push({ type: \"cookware\", value: idxInList });\n } else if (groups.timerQuantity !== undefined) {\n const durationStr = groups.timerQuantity.trim();\n const unit = (groups.timerUnits || \"\").trim();\n if (!unit) {\n throw new Error(\"Timer missing units\");\n }\n const name = groups.timerName || undefined;\n const duration = parseQuantityInput(durationStr);\n const timerObj: Timer = {\n name,\n duration,\n unit,\n };\n const idxInList = findOrPush(\n this.timers,\n (t) =>\n t.name === timerObj.name &&\n t.duration === timerObj.duration &&\n t.unit === timerObj.unit,\n () => timerObj,\n );\n items.push({ type: \"timer\", value: idxInList });\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.\n * @param newServings - The new number of servings.\n * @returns A new Recipe instance with the scaled ingredients.\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 if (\n ingredient.quantity &&\n !(\n ingredient.quantity.type === \"fixed\" &&\n ingredient.quantity.value.type === \"text\"\n )\n ) {\n ingredient.quantity = multiplyQuantityValue(\n ingredient.quantity,\n factor,\n );\n }\n return ingredient;\n })\n .filter((ingredient) => ingredient.quantity !== null);\n\n newRecipe.servings = originalServings * factor;\n\n if (newRecipe.metadata.servings && this.metadata.servings) {\n const servingsValue = parseFloat(this.metadata.servings);\n if (!isNaN(servingsValue)) {\n newRecipe.metadata.servings = String(servingsValue * factor);\n }\n }\n\n if (newRecipe.metadata.yield && this.metadata.yield) {\n const yieldValue = parseFloat(this.metadata.yield);\n if (!isNaN(yieldValue)) {\n newRecipe.metadata.yield = String(yieldValue * factor);\n }\n }\n\n if (newRecipe.metadata.serves && this.metadata.serves) {\n const servesValue = parseFloat(this.metadata.serves);\n if (!isNaN(servesValue)) {\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));\n newRecipe.ingredients = JSON.parse(JSON.stringify(this.ingredients));\n newRecipe.sections = JSON.parse(JSON.stringify(this.sections));\n newRecipe.cookware = JSON.parse(JSON.stringify(this.cookware));\n newRecipe.timers = JSON.parse(JSON.stringify(this.timers));\n newRecipe.servings = this.servings;\n return newRecipe;\n }\n}\n","import { AisleConfig } from \"./aisle_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 * Represents a shopping list.\n * @category Classes\n */\nexport class ShoppingList {\n /**\n * The ingredients in the shopping list.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipes in the shopping list.\n * @see {@link AddedRecipe}\n */\n recipes: AddedRecipe[] = [];\n /**\n * The aisle configuration for the shopping list.\n * @see {@link AisleConfig}\n */\n aisle_config?: AisleConfig;\n /**\n * The categorized ingredients in the shopping list.\n * @see {@link CategorizedIngredients}\n */\n categories?: CategorizedIngredients;\n\n /**\n * Creates a new ShoppingList instance.\n * @param aisle_config_str - The aisle configuration to parse.\n */\n constructor(aisle_config_str?: string) {\n if (aisle_config_str) {\n this.set_aisle_config(aisle_config_str);\n }\n }\n\n private calculate_ingredients() {\n this.ingredients = [];\n for (const { recipe, factor } of this.recipes) {\n const scaledRecipe = factor === 1 ? recipe : recipe.scaleBy(factor);\n for (const ingredient of scaledRecipe.ingredients) {\n if (ingredient.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) {\n if (existingIngredient.quantity && ingredient.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 if (ingredient.quantity) {\n existingIngredient.quantity = ingredient.quantity;\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.\n * @param recipe - The recipe to add.\n * @param factor - The factor to scale the recipe by.\n */\n add_recipe(recipe: Recipe, factor: number = 1) {\n this.recipes.push({ recipe, factor });\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Removes a recipe from the shopping list.\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 aisle configuration for the shopping list.\n * @param config - The aisle configuration to parse.\n */\n set_aisle_config(config: string) {\n this.aisle_config = new AisleConfig(config);\n this.categorize();\n }\n\n /**\n * Categorizes the ingredients in the shopping list\n * Will use the aisle config if any, otherwise all ingredients will be placed in the \"other\" category\n */\n categorize() {\n if (!this.aisle_config) {\n this.categories = { other: this.ingredients };\n return;\n }\n\n const categories: CategorizedIngredients = { other: [] };\n for (const category of this.aisle_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.aisle_config.categories) {\n for (const aisleIngredient of category.ingredients) {\n if (aisleIngredient.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","i","e","units","t","i"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/classes/aisle_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/parser_helpers.ts","../src/classes/recipe.ts","../src/classes/shopping_list.ts"],"sourcesContent":["import { AisleConfig } from \"./classes/aisle_config\";\nimport { Recipe } from \"./classes/recipe\";\nimport { ShoppingList } from \"./classes/shopping_list\";\nimport { Section } from \"./classes/section\";\n\nimport type {\n Metadata,\n MetadataExtract,\n Ingredient,\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 CategorizedIngredients,\n AddedRecipe,\n AisleIngredient,\n AisleCategory,\n} from \"./types\";\n\nexport {\n Recipe,\n ShoppingList,\n AisleConfig,\n Metadata,\n MetadataExtract,\n Ingredient,\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 CategorizedIngredients,\n AddedRecipe,\n AisleIngredient,\n AisleCategory,\n Section,\n};\n","import type { AisleCategory, AisleIngredient } from \"../types\";\n\n/**\n * Represents the aisle configuration for a shopping list.\n * @category Classes\n */\nexport class AisleConfig {\n /**\n * The categories of aisles.\n * @see {@link AisleCategory}\n */\n categories: AisleCategory[] = [];\n\n /**\n * Creates a new AisleConfig instance.\n * @param config - The aisle configuration to parse.\n */\n constructor(config?: string) {\n if (config) {\n this.parse(config);\n }\n }\n\n /**\n * Parses an aisle configuration from a string.\n * @param config - The aisle configuration to parse.\n */\n parse(config: string) {\n let currentCategory: AisleCategory | 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: AisleIngredient = {\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 * Wrapped as a Class and not as a simple type to expose some useful helper\n * classes (e.g. `isBlank()`)\n * @category Types\n */\nexport class Section {\n /** The name of the section. Can be an empty string for the default (first) section. */\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\nconst nonWordChar = \"\\\\s@#~\\\\[\\\\]{(.,;:!?\"\n\nconst multiwordIngredient = createRegex()\n .literal(\"@\")\n .startNamedGroup(\"mIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\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(\"mIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"mIngredientUnits\")\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(\"sIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\n .endGroup().optional()\n .startNamedGroup(\"sIngredientName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"sIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"sIngredientUnits\")\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(\"mCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"sCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"timerUnits\")\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 .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()","import type { FixedValue, Range, DecimalValue, FractionValue } from \"./types\";\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\"],\n toBase: 1,\n },\n {\n name: \"kg\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"kilogram\", \"kilograms\"],\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\"],\n toBase: 1,\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\"],\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: v.value * factor };\n }\n return simplifyFraction(v.num * factor, 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 // We only return a fraction where both input values are fractions themselves\n if (val1.type === \"fraction\" && val2.type === \"fraction\") {\n const commonDen = den1 * den2;\n const sumNum = num1 * den2 + num2 * den1;\n return simplifyFraction(sumNum, commonDen);\n } else {\n return { type: \"decimal\", value: num1 / den1 + num2 / den2 };\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 return {\n type: \"fixed\",\n value: toRoundedDecimal(\n multiplyNumericValue(\n value.value as DecimalValue | FractionValue,\n factor,\n ),\n ),\n };\n }\n\n return {\n type: \"range\",\n min: toRoundedDecimal(multiplyNumericValue(value.min, factor)),\n max: toRoundedDecimal(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 * 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,\n ): Quantity => {\n if (val1.type === \"fixed\" && val2.type === \"fixed\") {\n const res = addNumericValues(\n val1.value as DecimalValue | FractionValue,\n val2.value as DecimalValue | FractionValue,\n );\n return { value: { type: \"fixed\", value: res }, unit };\n }\n const r1 =\n val1.type === \"range\"\n ? val1\n : { type: \"range\", min: val1.value, max: val1.value };\n const r2 =\n val2.type === \"range\"\n ? val2\n : { type: \"range\", min: val2.value, max: val2.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 { value: { type: \"range\", min: newMin, max: newMax }, unit };\n };\n\n // Case 2: one of the two values doesn't have a unit, we consider its value to be 0 and the unit to be that of the other one\n if (q1.unit === \"\" && unit2Def) {\n return addQuantityValuesAndSetUnit(v1, v2, q2.unit); // Prefer q2's unit\n }\n if (q2.unit === \"\" && unit1Def) {\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 (q1.unit.toLowerCase() === q2.unit.toLowerCase()) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit);\n }\n\n // Case 4: the two quantities do not have the same unit\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 throw new IncompatibleUnitsError(q1.unit, q2.unit);\n}\n","import type {\n MetadataExtract,\n Metadata,\n FixedValue,\n Range,\n TextValue,\n DecimalValue,\n FractionValue,\n} from \"./types\";\nimport { metadataRegex, rangeRegex, numberLikeRegex } from \"./regex\";\nimport { Section as SectionObject } from \"./classes/section\";\nimport type { Ingredient, Note, Step, Cookware } from \"./types\";\nimport {\n addQuantities,\n CannotAddTextValueError,\n IncompatibleUnitsError,\n Quantity,\n} from \"./units\";\n\n/**\n * Finds an item in a list or adds it if not present, then returns its index.\n * @param list The list to search in.\n * @param finder A predicate to find the item.\n * @param creator A function to create the item if not found.\n * @returns The index of the item in the list.\n */\nexport function findOrPush<T>(\n list: T[],\n finder: (elem: T) => boolean,\n creator: () => T,\n): number {\n let index = list.findIndex(finder);\n if (index === -1) {\n index = list.push(creator()) - 1;\n }\n return index;\n}\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 */\nexport function findAndUpsertIngredient(\n ingredients: Ingredient[],\n newIngredient: Ingredient,\n isReference: boolean,\n): number {\n const { name, quantity, unit } = newIngredient;\n\n // New ingredient\n if (isReference) {\n const index = ingredients.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (index === -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[index]!;\n if (quantity !== undefined) {\n const currentQuantity: Quantity = {\n value: existingIngredient.quantity ?? {\n type: \"fixed\",\n value: { type: \"decimal\", value: 0 },\n },\n unit: existingIngredient.unit ?? \"\",\n };\n const newQuantity = { value: quantity, unit: unit ?? \"\" };\n\n try {\n const total = addQuantities(currentQuantity, newQuantity);\n existingIngredient.quantity = total.value;\n existingIngredient.unit = total.unit || undefined;\n } catch (e) {\n if (\n e instanceof IncompatibleUnitsError ||\n e instanceof CannotAddTextValueError\n ) {\n // Addition not possible, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n }\n }\n }\n return index;\n }\n\n // Not a reference, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n}\n\nexport function findAndUpsertCookware(\n cookware: Cookware[],\n newCookware: Cookware,\n isReference: boolean,\n): number {\n const { name } = 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 return index;\n }\n\n return cookware.push(newCookware) - 1;\n}\n\n// Parser when we know the input is either a number-like value\nconst 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(content: string, varName: string) {\n const varMatch = content.match(\n new RegExp(`^${varName}:[\\\\t ]*(([^,\\\\n]*),? ?(?:.*)?)`, \"m\"),\n );\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 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 Metadata)[]) {\n const stringMetaValue: any = parseSimpleMetaVar(metadataContent, metaVar);\n if (stringMetaValue) metadata[metaVar] = stringMetaValue;\n }\n\n // String metadata variables\n for (const metaVar of [\"servings\", \"yield\", \"serves\"] as (keyof Metadata)[]) {\n const scalingMetaValue: any = 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 Metadata)[]) {\n const listMetaValue: any = parseListMetaVar(metadataContent, metaVar);\n if (listMetaValue) metadata[metaVar] = listMetaValue;\n }\n\n return { metadata, servings };\n}\n","import type {\n Metadata,\n Ingredient,\n IngredientItem,\n Timer,\n Step,\n Note,\n Cookware,\n MetadataExtract,\n CookwareItem,\n} from \"../types\";\nimport { Section } from \"./section\";\nimport {\n tokensRegex,\n commentRegex,\n blockCommentRegex,\n metadataRegex,\n ingredientAliasRegex,\n} from \"../regex\";\nimport {\n findOrPush,\n flushPendingItems,\n flushPendingNote,\n findAndUpsertIngredient,\n findAndUpsertCookware,\n parseQuantityInput,\n extractMetadata,\n} from \"../parser_helpers\";\nimport { multiplyQuantityValue } from \"../units\";\n\n/**\n * Represents a recipe.\n * @category Classes\n */\nexport class Recipe {\n /**\n * The recipe's metadata.\n * @see {@link Metadata}\n */\n metadata: Metadata = {};\n /**\n * The recipe's ingredients.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipe's sections.\n * @see {@link Section}\n */\n sections: Section[] = [];\n /**\n * The recipe's cookware.\n * @see {@link Cookware}\n */\n cookware: Cookware[] = [];\n /**\n * The recipe's timers.\n * @see {@link Timer}\n */\n timers: Timer[] = [];\n /**\n * The recipe's servings. Used for scaling\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 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 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 const name = (groups.mIngredientName || groups.sIngredientName)!;\n const quantityRaw =\n groups.mIngredientQuantity || groups.sIngredientQuantity;\n const units = groups.mIngredientUnits || groups.sIngredientUnits;\n const preparation =\n groups.mIngredientPreparation || groups.sIngredientPreparation;\n const modifier =\n groups.mIngredientModifier || groups.sIngredientModifier;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const isRecipe = modifier === \"@\";\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 idxInList = findAndUpsertIngredient(\n this.ingredients,\n {\n name: listName,\n quantity,\n unit: units,\n optional,\n hidden,\n preparation,\n isRecipe,\n },\n reference,\n );\n\n const newItem: IngredientItem = {\n type: \"ingredient\",\n value: idxInList,\n itemQuantity: quantity,\n itemUnit: units,\n displayName,\n };\n\n items.push(newItem);\n } else if (groups.mCookwareName || groups.sCookwareName) {\n const name = (groups.mCookwareName || groups.sCookwareName)!;\n const modifier = groups.mCookwareModifier || groups.sCookwareModifier;\n const quantityRaw =\n groups.mCookwareQuantity || groups.sCookwareQuantity;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n\n const idxInList = findAndUpsertCookware(\n this.cookware,\n { name, quantity, optional, hidden },\n reference,\n );\n items.push({\n type: \"cookware\",\n value: idxInList,\n itemQuantity: quantity,\n } as CookwareItem);\n } else if (groups.timerQuantity !== undefined) {\n const durationStr = groups.timerQuantity.trim();\n const unit = (groups.timerUnits || \"\").trim();\n if (!unit) {\n throw new Error(\"Timer missing units\");\n }\n const name = groups.timerName || undefined;\n const duration = parseQuantityInput(durationStr);\n const timerObj: Timer = {\n name,\n duration,\n unit,\n };\n const idxInList = findOrPush(\n this.timers,\n (t) =>\n t.name === timerObj.name &&\n t.duration === timerObj.duration &&\n t.unit === timerObj.unit,\n () => timerObj,\n );\n items.push({ type: \"timer\", value: idxInList });\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.\n * @param newServings - The new number of servings.\n * @returns A new Recipe instance with the scaled ingredients.\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 if (\n ingredient.quantity &&\n !(\n ingredient.quantity.type === \"fixed\" &&\n ingredient.quantity.value.type === \"text\"\n )\n ) {\n ingredient.quantity = multiplyQuantityValue(\n ingredient.quantity,\n factor,\n );\n }\n return ingredient;\n })\n .filter((ingredient) => ingredient.quantity !== null);\n\n newRecipe.servings = originalServings * factor;\n\n if (newRecipe.metadata.servings && this.metadata.servings) {\n const servingsValue = parseFloat(this.metadata.servings);\n if (!isNaN(servingsValue)) {\n newRecipe.metadata.servings = String(servingsValue * factor);\n }\n }\n\n if (newRecipe.metadata.yield && this.metadata.yield) {\n const yieldValue = parseFloat(this.metadata.yield);\n if (!isNaN(yieldValue)) {\n newRecipe.metadata.yield = String(yieldValue * factor);\n }\n }\n\n if (newRecipe.metadata.serves && this.metadata.serves) {\n const servesValue = parseFloat(this.metadata.serves);\n if (!isNaN(servesValue)) {\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 { AisleConfig } from \"./aisle_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 * Represents a shopping list.\n * @category Classes\n */\nexport class ShoppingList {\n /**\n * The ingredients in the shopping list.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipes in the shopping list.\n * @see {@link AddedRecipe}\n */\n recipes: AddedRecipe[] = [];\n /**\n * The aisle configuration for the shopping list.\n * @see {@link AisleConfig}\n */\n aisle_config?: AisleConfig;\n /**\n * The categorized ingredients in the shopping list.\n * @see {@link CategorizedIngredients}\n */\n categories?: CategorizedIngredients;\n\n /**\n * Creates a new ShoppingList instance.\n * @param aisle_config_str - The aisle configuration to parse.\n */\n constructor(aisle_config_str?: string) {\n if (aisle_config_str) {\n this.set_aisle_config(aisle_config_str);\n }\n }\n\n private calculate_ingredients() {\n this.ingredients = [];\n for (const { recipe, factor } of this.recipes) {\n const scaledRecipe = factor === 1 ? recipe : recipe.scaleBy(factor);\n for (const ingredient of scaledRecipe.ingredients) {\n if (ingredient.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) {\n if (existingIngredient.quantity && ingredient.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 if (ingredient.quantity) {\n existingIngredient.quantity = ingredient.quantity;\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.\n * @param recipe - The recipe to add.\n * @param factor - The factor to scale the recipe by.\n */\n add_recipe(recipe: Recipe, factor: number = 1) {\n this.recipes.push({ recipe, factor });\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Removes a recipe from the shopping list.\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 aisle configuration for the shopping list.\n * @param config - The aisle configuration to parse.\n */\n set_aisle_config(config: string) {\n this.aisle_config = new AisleConfig(config);\n this.categorize();\n }\n\n /**\n * Categorizes the ingredients in the shopping list\n * Will use the aisle config if any, otherwise all ingredients will be placed in the \"other\" category\n */\n categorize() {\n if (!this.aisle_config) {\n this.categories = { other: this.ingredients };\n return;\n }\n\n const categories: CategorizedIngredients = { other: [] };\n for (const category of this.aisle_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.aisle_config.categories) {\n for (const aisleIngredient of category.ingredients) {\n if (aisleIngredient.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","i","e","units","t","i"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -264,12 +264,12 @@ interface IngredientItem {
|
|
|
264
264
|
type: "ingredient";
|
|
265
265
|
/** The value of the item. */
|
|
266
266
|
value: number;
|
|
267
|
-
/**
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
|
|
267
|
+
/** The alias/name of the ingredient as it should be displayed in the preparation */
|
|
268
|
+
displayName: string;
|
|
269
|
+
/** Quantity specific to this step item for this ingredient which may also be referenced elsewhere */
|
|
270
|
+
itemQuantity?: FixedValue | Range;
|
|
271
|
+
/** Unit specific to this step item for this ingredient which may also be referenced elsewhere */
|
|
272
|
+
itemUnit?: string;
|
|
273
273
|
}
|
|
274
274
|
/**
|
|
275
275
|
* Represents a cookware item in a recipe step.
|
|
@@ -280,6 +280,8 @@ interface CookwareItem {
|
|
|
280
280
|
type: "cookware";
|
|
281
281
|
/** The value of the item. */
|
|
282
282
|
value: number;
|
|
283
|
+
/** Quantity specific to this step item for this cookware which may also be referenced elsewhere */
|
|
284
|
+
itemQuantity?: FixedValue | Range;
|
|
283
285
|
}
|
|
284
286
|
/**
|
|
285
287
|
* Represents a timer item in a recipe step.
|
|
@@ -301,6 +303,7 @@ type Item = TextItem | IngredientItem | CookwareItem | TimerItem;
|
|
|
301
303
|
* @category Types
|
|
302
304
|
*/
|
|
303
305
|
interface Step {
|
|
306
|
+
type: "step";
|
|
304
307
|
/** The items in the step. */
|
|
305
308
|
items: Item[];
|
|
306
309
|
}
|
|
@@ -309,6 +312,7 @@ interface Step {
|
|
|
309
312
|
* @category Types
|
|
310
313
|
*/
|
|
311
314
|
interface Note {
|
|
315
|
+
type: "note";
|
|
312
316
|
/** The content of the note. */
|
|
313
317
|
note: string;
|
|
314
318
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -264,12 +264,12 @@ interface IngredientItem {
|
|
|
264
264
|
type: "ingredient";
|
|
265
265
|
/** The value of the item. */
|
|
266
266
|
value: number;
|
|
267
|
-
/**
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
|
|
267
|
+
/** The alias/name of the ingredient as it should be displayed in the preparation */
|
|
268
|
+
displayName: string;
|
|
269
|
+
/** Quantity specific to this step item for this ingredient which may also be referenced elsewhere */
|
|
270
|
+
itemQuantity?: FixedValue | Range;
|
|
271
|
+
/** Unit specific to this step item for this ingredient which may also be referenced elsewhere */
|
|
272
|
+
itemUnit?: string;
|
|
273
273
|
}
|
|
274
274
|
/**
|
|
275
275
|
* Represents a cookware item in a recipe step.
|
|
@@ -280,6 +280,8 @@ interface CookwareItem {
|
|
|
280
280
|
type: "cookware";
|
|
281
281
|
/** The value of the item. */
|
|
282
282
|
value: number;
|
|
283
|
+
/** Quantity specific to this step item for this cookware which may also be referenced elsewhere */
|
|
284
|
+
itemQuantity?: FixedValue | Range;
|
|
283
285
|
}
|
|
284
286
|
/**
|
|
285
287
|
* Represents a timer item in a recipe step.
|
|
@@ -301,6 +303,7 @@ type Item = TextItem | IngredientItem | CookwareItem | TimerItem;
|
|
|
301
303
|
* @category Types
|
|
302
304
|
*/
|
|
303
305
|
interface Step {
|
|
306
|
+
type: "step";
|
|
304
307
|
/** The items in the step. */
|
|
305
308
|
items: Item[];
|
|
306
309
|
}
|
|
@@ -309,6 +312,7 @@ interface Step {
|
|
|
309
312
|
* @category Types
|
|
310
313
|
*/
|
|
311
314
|
interface Note {
|
|
315
|
+
type: "note";
|
|
312
316
|
/** The content of the note. */
|
|
313
317
|
note: string;
|
|
314
318
|
}
|
package/dist/index.js
CHANGED
|
@@ -289,6 +289,7 @@ var metadataRegex = d().literal("---").newline().startCaptureGroup().anyCharacte
|
|
|
289
289
|
var nonWordChar = "\\s@#~\\[\\]{(.,;:!?";
|
|
290
290
|
var multiwordIngredient = d().literal("@").startNamedGroup("mIngredientModifier").anyOf("@\\-&?").endGroup().optional().startNamedGroup("mIngredientName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\}|\\([^)]*\\))").startGroup().literal("{").startNamedGroup("mIngredientQuantity").notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("mIngredientUnits").notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("mIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
291
291
|
var singleWordIngredient = d().literal("@").startNamedGroup("sIngredientModifier").anyOf("@\\-&?").endGroup().optional().startNamedGroup("sIngredientName").notAnyOf(nonWordChar).oneOrMore().endGroup().startGroup().literal("{").startNamedGroup("sIngredientQuantity").notAnyOf("}%").oneOrMore().endGroup().optional().startGroup().literal("%").startNamedGroup("sIngredientUnits").notAnyOf("}").oneOrMore().lazy().endGroup().endGroup().optional().literal("}").endGroup().optional().startGroup().literal("(").startNamedGroup("sIngredientPreparation").notAnyOf(")").oneOrMore().lazy().endGroup().literal(")").endGroup().optional().toRegExp();
|
|
292
|
+
var ingredientAliasRegex = d().startAnchor().startNamedGroup("ingredientListName").notAnyOf("|").oneOrMore().endGroup().literal("|").startNamedGroup("ingredientDisplayName").notAnyOf("|").oneOrMore().endGroup().endAnchor().toRegExp();
|
|
292
293
|
var multiwordCookware = d().literal("#").startNamedGroup("mCookwareModifier").anyOf("\\-&?").endGroup().optional().startNamedGroup("mCookwareName").notAnyOf(nonWordChar).oneOrMore().startGroup().whitespace().oneOrMore().notAnyOf(nonWordChar).oneOrMore().endGroup().oneOrMore().endGroup().positiveLookahead("\\s*(?:\\{[^\\}]*\\})").literal("{").startNamedGroup("mCookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").toRegExp();
|
|
293
294
|
var singleWordCookware = d().literal("#").startNamedGroup("sCookwareModifier").anyOf("\\-&?").endGroup().optional().startNamedGroup("sCookwareName").notAnyOf(nonWordChar).oneOrMore().endGroup().startGroup().literal("{").startNamedGroup("sCookwareQuantity").anyCharacter().zeroOrMore().lazy().endGroup().literal("}").endGroup().optional().toRegExp();
|
|
294
295
|
var timer = d().literal("~").startNamedGroup("timerName").anyCharacter().zeroOrMore().lazy().endGroup().literal("{").startNamedGroup("timerQuantity").anyCharacter().oneOrMore().lazy().endGroup().startGroup().literal("%").startNamedGroup("timerUnits").anyCharacter().oneOrMore().lazy().endGroup().endGroup().optional().literal("}").toRegExp();
|
|
@@ -509,12 +510,8 @@ function multiplyQuantityValue(value, factor) {
|
|
|
509
510
|
}
|
|
510
511
|
return {
|
|
511
512
|
type: "range",
|
|
512
|
-
min: toRoundedDecimal(
|
|
513
|
-
|
|
514
|
-
),
|
|
515
|
-
max: toRoundedDecimal(
|
|
516
|
-
multiplyNumericValue(value.max, factor)
|
|
517
|
-
)
|
|
513
|
+
min: toRoundedDecimal(multiplyNumericValue(value.min, factor)),
|
|
514
|
+
max: toRoundedDecimal(multiplyNumericValue(value.max, factor))
|
|
518
515
|
};
|
|
519
516
|
}
|
|
520
517
|
var convertQuantityValue = (value, def, targetDef) => {
|
|
@@ -596,14 +593,14 @@ function findOrPush(list, finder, creator) {
|
|
|
596
593
|
}
|
|
597
594
|
function flushPendingNote(section, note) {
|
|
598
595
|
if (note.length > 0) {
|
|
599
|
-
section.content.push({ note });
|
|
596
|
+
section.content.push({ type: "note", note });
|
|
600
597
|
return "";
|
|
601
598
|
}
|
|
602
599
|
return note;
|
|
603
600
|
}
|
|
604
601
|
function flushPendingItems(section, items) {
|
|
605
602
|
if (items.length > 0) {
|
|
606
|
-
section.content.push({ items: [...items] });
|
|
603
|
+
section.content.push({ type: "step", items: [...items] });
|
|
607
604
|
items.length = 0;
|
|
608
605
|
return true;
|
|
609
606
|
}
|
|
@@ -874,10 +871,19 @@ var Recipe = class _Recipe {
|
|
|
874
871
|
const reference = modifier === "&";
|
|
875
872
|
const isRecipe = modifier === "@";
|
|
876
873
|
const quantity = quantityRaw ? parseQuantityInput(quantityRaw) : void 0;
|
|
874
|
+
const aliasMatch = name.match(ingredientAliasRegex);
|
|
875
|
+
let listName, displayName;
|
|
876
|
+
if (aliasMatch && aliasMatch.groups.ingredientListName.trim().length > 0 && aliasMatch.groups.ingredientDisplayName.trim().length > 0) {
|
|
877
|
+
listName = aliasMatch.groups.ingredientListName.trim();
|
|
878
|
+
displayName = aliasMatch.groups.ingredientDisplayName.trim();
|
|
879
|
+
} else {
|
|
880
|
+
listName = name;
|
|
881
|
+
displayName = name;
|
|
882
|
+
}
|
|
877
883
|
const idxInList = findAndUpsertIngredient(
|
|
878
884
|
this.ingredients,
|
|
879
885
|
{
|
|
880
|
-
name,
|
|
886
|
+
name: listName,
|
|
881
887
|
quantity,
|
|
882
888
|
unit: units2,
|
|
883
889
|
optional,
|
|
@@ -889,13 +895,11 @@ var Recipe = class _Recipe {
|
|
|
889
895
|
);
|
|
890
896
|
const newItem = {
|
|
891
897
|
type: "ingredient",
|
|
892
|
-
value: idxInList
|
|
898
|
+
value: idxInList,
|
|
899
|
+
itemQuantity: quantity,
|
|
900
|
+
itemUnit: units2,
|
|
901
|
+
displayName
|
|
893
902
|
};
|
|
894
|
-
if (reference) {
|
|
895
|
-
newItem.partialQuantity = quantity;
|
|
896
|
-
newItem.partialUnit = units2;
|
|
897
|
-
newItem.partialPreparation = preparation;
|
|
898
|
-
}
|
|
899
903
|
items.push(newItem);
|
|
900
904
|
} else if (groups.mCookwareName || groups.sCookwareName) {
|
|
901
905
|
const name = groups.mCookwareName || groups.sCookwareName;
|
|
@@ -910,7 +914,11 @@ var Recipe = class _Recipe {
|
|
|
910
914
|
{ name, quantity, optional, hidden },
|
|
911
915
|
reference
|
|
912
916
|
);
|
|
913
|
-
items.push({
|
|
917
|
+
items.push({
|
|
918
|
+
type: "cookware",
|
|
919
|
+
value: idxInList,
|
|
920
|
+
itemQuantity: quantity
|
|
921
|
+
});
|
|
914
922
|
} else if (groups.timerQuantity !== void 0) {
|
|
915
923
|
const durationStr = groups.timerQuantity.trim();
|
|
916
924
|
const unit = (groups.timerUnits || "").trim();
|
|
@@ -1016,9 +1024,13 @@ var Recipe = class _Recipe {
|
|
|
1016
1024
|
clone() {
|
|
1017
1025
|
const newRecipe = new _Recipe();
|
|
1018
1026
|
newRecipe.metadata = JSON.parse(JSON.stringify(this.metadata));
|
|
1019
|
-
newRecipe.ingredients = JSON.parse(
|
|
1027
|
+
newRecipe.ingredients = JSON.parse(
|
|
1028
|
+
JSON.stringify(this.ingredients)
|
|
1029
|
+
);
|
|
1020
1030
|
newRecipe.sections = JSON.parse(JSON.stringify(this.sections));
|
|
1021
|
-
newRecipe.cookware = JSON.parse(
|
|
1031
|
+
newRecipe.cookware = JSON.parse(
|
|
1032
|
+
JSON.stringify(this.cookware)
|
|
1033
|
+
);
|
|
1022
1034
|
newRecipe.timers = JSON.parse(JSON.stringify(this.timers));
|
|
1023
1035
|
newRecipe.servings = this.servings;
|
|
1024
1036
|
return newRecipe;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/classes/aisle_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/parser_helpers.ts","../src/classes/recipe.ts","../src/classes/shopping_list.ts"],"sourcesContent":["import type { AisleCategory, AisleIngredient } from \"../types\";\n\n/**\n * Represents the aisle configuration for a shopping list.\n * @category Classes\n */\nexport class AisleConfig {\n /**\n * The categories of aisles.\n * @see {@link AisleCategory}\n */\n categories: AisleCategory[] = [];\n\n /**\n * Creates a new AisleConfig instance.\n * @param config - The aisle configuration to parse.\n */\n constructor(config?: string) {\n if (config) {\n this.parse(config);\n }\n }\n\n /**\n * Parses an aisle configuration from a string.\n * @param config - The aisle configuration to parse.\n */\n parse(config: string) {\n let currentCategory: AisleCategory | 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: AisleIngredient = {\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 * Wrapped as a Class and not as a simple type to expose some useful helper\n * classes (e.g. `isBlank()`)\n * @category Types\n */\nexport class Section {\n /** The name of the section. Can be an empty string for the default (first) section. */\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\nconst nonWordChar = \"\\\\s@#~\\\\[\\\\]{(.,;:!?\"\n\nconst multiwordIngredient = createRegex()\n .literal(\"@\")\n .startNamedGroup(\"mIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\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(\"mIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"mIngredientUnits\")\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(\"sIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\n .endGroup().optional()\n .startNamedGroup(\"sIngredientName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"sIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"sIngredientUnits\")\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\nconst multiwordCookware = createRegex()\n .literal(\"#\")\n .startNamedGroup(\"mCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"sCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"timerUnits\")\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 .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()","import type { FixedValue, Range, DecimalValue, FractionValue } from \"./types\";\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\"],\n toBase: 1,\n },\n {\n name: \"kg\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"kilogram\", \"kilograms\"],\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\"],\n toBase: 1,\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\"],\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: v.value * factor };\n }\n return simplifyFraction(v.num * factor, 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 // We only return a fraction where both input values are fractions themselves\n if (val1.type === \"fraction\" && val2.type === \"fraction\") {\n const commonDen = den1 * den2;\n const sumNum = num1 * den2 + num2 * den1;\n return simplifyFraction(sumNum, commonDen);\n } else {\n return { type: \"decimal\", value: num1 / den1 + num2 / den2 };\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 return {\n type: \"fixed\",\n value: toRoundedDecimal(\n multiplyNumericValue(\n value.value as DecimalValue | FractionValue,\n factor,\n ),\n ),\n };\n }\n\n return {\n type: \"range\",\n min: toRoundedDecimal(\n multiplyNumericValue(value.min as DecimalValue | FractionValue, factor),\n ),\n max: toRoundedDecimal(\n multiplyNumericValue(value.max as DecimalValue | FractionValue, factor),\n ),\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 * 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,\n ): Quantity => {\n if (val1.type === \"fixed\" && val2.type === \"fixed\") {\n const res = addNumericValues(\n val1.value as DecimalValue | FractionValue,\n val2.value as DecimalValue | FractionValue,\n );\n return { value: { type: \"fixed\", value: res }, unit };\n }\n const r1 =\n val1.type === \"range\"\n ? val1\n : { type: \"range\", min: val1.value, max: val1.value };\n const r2 =\n val2.type === \"range\"\n ? val2\n : { type: \"range\", min: val2.value, max: val2.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 { value: { type: \"range\", min: newMin, max: newMax }, unit };\n };\n\n // Case 2: one of the two values doesn't have a unit, we consider its value to be 0 and the unit to be that of the other one\n if (q1.unit === \"\" && unit2Def) {\n return addQuantityValuesAndSetUnit(v1, v2, q2.unit); // Prefer q2's unit\n }\n if (q2.unit === \"\" && unit1Def) {\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 (q1.unit.toLowerCase() === q2.unit.toLowerCase()) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit);\n }\n\n // Case 4: the two quantities do not have the same unit\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 throw new IncompatibleUnitsError(q1.unit, q2.unit);\n}\n","import type {\n MetadataExtract,\n Metadata,\n FixedValue,\n Range,\n TextValue,\n DecimalValue,\n FractionValue,\n} from \"./types\";\nimport { metadataRegex, rangeRegex, numberLikeRegex } from \"./regex\";\nimport { Section as SectionObject } from \"./classes/section\";\nimport type { Ingredient, Note, Step, Cookware } from \"./types\";\nimport {\n addQuantities,\n CannotAddTextValueError,\n IncompatibleUnitsError,\n Quantity,\n} from \"./units\";\n\n/**\n * Finds an item in a list or adds it if not present, then returns its index.\n * @param list The list to search in.\n * @param finder A predicate to find the item.\n * @param creator A function to create the item if not found.\n * @returns The index of the item in the list.\n */\nexport function findOrPush<T>(\n list: T[],\n finder: (elem: T) => boolean,\n creator: () => T,\n): number {\n let index = list.findIndex(finder);\n if (index === -1) {\n index = list.push(creator()) - 1;\n }\n return index;\n}\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({ 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({ 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 */\nexport function findAndUpsertIngredient(\n ingredients: Ingredient[],\n newIngredient: Ingredient,\n isReference: boolean,\n): number {\n const { name, quantity, unit } = newIngredient;\n\n // New ingredient\n if (isReference) {\n const index = ingredients.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (index === -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[index]!;\n if (quantity !== undefined) {\n const currentQuantity: Quantity = {\n value: existingIngredient.quantity ?? {\n type: \"fixed\",\n value: { type: \"decimal\", value: 0 },\n },\n unit: existingIngredient.unit ?? \"\",\n };\n const newQuantity = { value: quantity, unit: unit ?? \"\" };\n\n try {\n const total = addQuantities(currentQuantity, newQuantity);\n existingIngredient.quantity = total.value;\n existingIngredient.unit = total.unit || undefined;\n } catch (e) {\n if (\n e instanceof IncompatibleUnitsError ||\n e instanceof CannotAddTextValueError\n ) {\n // Addition not possible, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n }\n }\n }\n return index;\n }\n\n // Not a reference, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n}\n\nexport function findAndUpsertCookware(\n cookware: Cookware[],\n newCookware: Cookware,\n isReference: boolean,\n): number {\n const { name } = 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 return index;\n }\n\n return cookware.push(newCookware) - 1;\n}\n\n// Parser when we know the input is either a number-like value\nconst 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(content: string, varName: string) {\n const varMatch = content.match(\n new RegExp(`^${varName}:[\\\\t ]*(([^,\\\\n]*),? ?(?:.*)?)`, \"m\"),\n );\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 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 Metadata)[]) {\n const stringMetaValue: any = parseSimpleMetaVar(metadataContent, metaVar);\n if (stringMetaValue) metadata[metaVar] = stringMetaValue;\n }\n\n // String metadata variables\n for (const metaVar of [\"servings\", \"yield\", \"serves\"] as (keyof Metadata)[]) {\n const scalingMetaValue: any = 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 Metadata)[]) {\n const listMetaValue: any = parseListMetaVar(metadataContent, metaVar);\n if (listMetaValue) metadata[metaVar] = listMetaValue;\n }\n\n return { metadata, servings };\n}\n","import type {\n Metadata,\n Ingredient,\n IngredientItem,\n Timer,\n Step,\n Note,\n Cookware,\n MetadataExtract,\n} from \"../types\";\nimport { Section } from \"./section\";\nimport {\n tokensRegex,\n commentRegex,\n blockCommentRegex,\n metadataRegex,\n} from \"../regex\";\nimport {\n findOrPush,\n flushPendingItems,\n flushPendingNote,\n findAndUpsertIngredient,\n findAndUpsertCookware,\n parseQuantityInput,\n extractMetadata,\n} from \"../parser_helpers\";\nimport { multiplyQuantityValue } from \"../units\";\n\n/**\n * Represents a recipe.\n * @category Classes\n */\nexport class Recipe {\n /**\n * The recipe's metadata.\n * @see {@link Metadata}\n */\n metadata: Metadata = {};\n /**\n * The recipe's ingredients.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipe's sections.\n * @see {@link Section}\n */\n sections: Section[] = [];\n /**\n * The recipe's cookware.\n * @see {@link Cookware}\n */\n cookware: Cookware[] = [];\n /**\n * The recipe's timers.\n * @see {@link Timer}\n */\n timers: Timer[] = [];\n /**\n * The recipe's servings. Used for scaling\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 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 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 const name = (groups.mIngredientName || groups.sIngredientName)!;\n const quantityRaw =\n groups.mIngredientQuantity || groups.sIngredientQuantity;\n const units = groups.mIngredientUnits || groups.sIngredientUnits;\n const preparation =\n groups.mIngredientPreparation || groups.sIngredientPreparation;\n const modifier =\n groups.mIngredientModifier || groups.sIngredientModifier;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const isRecipe = modifier === \"@\";\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n\n const idxInList = findAndUpsertIngredient(\n this.ingredients,\n {\n name,\n quantity,\n unit: units,\n optional,\n hidden,\n preparation,\n isRecipe,\n },\n reference,\n );\n\n const newItem: IngredientItem = {\n type: \"ingredient\",\n value: idxInList,\n };\n if (reference) {\n newItem.partialQuantity = quantity;\n newItem.partialUnit = units;\n newItem.partialPreparation = preparation;\n }\n\n items.push(newItem);\n } else if (groups.mCookwareName || groups.sCookwareName) {\n const name = (groups.mCookwareName || groups.sCookwareName)!;\n const modifier = groups.mCookwareModifier || groups.sCookwareModifier;\n const quantityRaw =\n groups.mCookwareQuantity || groups.sCookwareQuantity;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n\n const idxInList = findAndUpsertCookware(\n this.cookware,\n { name, quantity, optional, hidden },\n reference,\n );\n items.push({ type: \"cookware\", value: idxInList });\n } else if (groups.timerQuantity !== undefined) {\n const durationStr = groups.timerQuantity.trim();\n const unit = (groups.timerUnits || \"\").trim();\n if (!unit) {\n throw new Error(\"Timer missing units\");\n }\n const name = groups.timerName || undefined;\n const duration = parseQuantityInput(durationStr);\n const timerObj: Timer = {\n name,\n duration,\n unit,\n };\n const idxInList = findOrPush(\n this.timers,\n (t) =>\n t.name === timerObj.name &&\n t.duration === timerObj.duration &&\n t.unit === timerObj.unit,\n () => timerObj,\n );\n items.push({ type: \"timer\", value: idxInList });\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.\n * @param newServings - The new number of servings.\n * @returns A new Recipe instance with the scaled ingredients.\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 if (\n ingredient.quantity &&\n !(\n ingredient.quantity.type === \"fixed\" &&\n ingredient.quantity.value.type === \"text\"\n )\n ) {\n ingredient.quantity = multiplyQuantityValue(\n ingredient.quantity,\n factor,\n );\n }\n return ingredient;\n })\n .filter((ingredient) => ingredient.quantity !== null);\n\n newRecipe.servings = originalServings * factor;\n\n if (newRecipe.metadata.servings && this.metadata.servings) {\n const servingsValue = parseFloat(this.metadata.servings);\n if (!isNaN(servingsValue)) {\n newRecipe.metadata.servings = String(servingsValue * factor);\n }\n }\n\n if (newRecipe.metadata.yield && this.metadata.yield) {\n const yieldValue = parseFloat(this.metadata.yield);\n if (!isNaN(yieldValue)) {\n newRecipe.metadata.yield = String(yieldValue * factor);\n }\n }\n\n if (newRecipe.metadata.serves && this.metadata.serves) {\n const servesValue = parseFloat(this.metadata.serves);\n if (!isNaN(servesValue)) {\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));\n newRecipe.ingredients = JSON.parse(JSON.stringify(this.ingredients));\n newRecipe.sections = JSON.parse(JSON.stringify(this.sections));\n newRecipe.cookware = JSON.parse(JSON.stringify(this.cookware));\n newRecipe.timers = JSON.parse(JSON.stringify(this.timers));\n newRecipe.servings = this.servings;\n return newRecipe;\n }\n}\n","import { AisleConfig } from \"./aisle_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 * Represents a shopping list.\n * @category Classes\n */\nexport class ShoppingList {\n /**\n * The ingredients in the shopping list.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipes in the shopping list.\n * @see {@link AddedRecipe}\n */\n recipes: AddedRecipe[] = [];\n /**\n * The aisle configuration for the shopping list.\n * @see {@link AisleConfig}\n */\n aisle_config?: AisleConfig;\n /**\n * The categorized ingredients in the shopping list.\n * @see {@link CategorizedIngredients}\n */\n categories?: CategorizedIngredients;\n\n /**\n * Creates a new ShoppingList instance.\n * @param aisle_config_str - The aisle configuration to parse.\n */\n constructor(aisle_config_str?: string) {\n if (aisle_config_str) {\n this.set_aisle_config(aisle_config_str);\n }\n }\n\n private calculate_ingredients() {\n this.ingredients = [];\n for (const { recipe, factor } of this.recipes) {\n const scaledRecipe = factor === 1 ? recipe : recipe.scaleBy(factor);\n for (const ingredient of scaledRecipe.ingredients) {\n if (ingredient.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) {\n if (existingIngredient.quantity && ingredient.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 if (ingredient.quantity) {\n existingIngredient.quantity = ingredient.quantity;\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.\n * @param recipe - The recipe to add.\n * @param factor - The factor to scale the recipe by.\n */\n add_recipe(recipe: Recipe, factor: number = 1) {\n this.recipes.push({ recipe, factor });\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Removes a recipe from the shopping list.\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 aisle configuration for the shopping list.\n * @param config - The aisle configuration to parse.\n */\n set_aisle_config(config: string) {\n this.aisle_config = new AisleConfig(config);\n this.categorize();\n }\n\n /**\n * Categorizes the ingredients in the shopping list\n * Will use the aisle config if any, otherwise all ingredients will be placed in the \"other\" category\n */\n categorize() {\n if (!this.aisle_config) {\n this.categories = { other: this.ingredients };\n return;\n }\n\n const categories: CategorizedIngredients = { other: [] };\n for (const category of this.aisle_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.aisle_config.categories) {\n for (const aisleIngredient of category.ingredients) {\n if (aisleIngredient.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","i","e","units","t","i"]}
|
|
1
|
+
{"version":3,"sources":["../src/classes/aisle_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/parser_helpers.ts","../src/classes/recipe.ts","../src/classes/shopping_list.ts"],"sourcesContent":["import type { AisleCategory, AisleIngredient } from \"../types\";\n\n/**\n * Represents the aisle configuration for a shopping list.\n * @category Classes\n */\nexport class AisleConfig {\n /**\n * The categories of aisles.\n * @see {@link AisleCategory}\n */\n categories: AisleCategory[] = [];\n\n /**\n * Creates a new AisleConfig instance.\n * @param config - The aisle configuration to parse.\n */\n constructor(config?: string) {\n if (config) {\n this.parse(config);\n }\n }\n\n /**\n * Parses an aisle configuration from a string.\n * @param config - The aisle configuration to parse.\n */\n parse(config: string) {\n let currentCategory: AisleCategory | 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: AisleIngredient = {\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 * Wrapped as a Class and not as a simple type to expose some useful helper\n * classes (e.g. `isBlank()`)\n * @category Types\n */\nexport class Section {\n /** The name of the section. Can be an empty string for the default (first) section. */\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\nconst nonWordChar = \"\\\\s@#~\\\\[\\\\]{(.,;:!?\"\n\nconst multiwordIngredient = createRegex()\n .literal(\"@\")\n .startNamedGroup(\"mIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\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(\"mIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"mIngredientUnits\")\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(\"sIngredientModifier\")\n .anyOf(\"@\\\\-&?\")\n .endGroup().optional()\n .startNamedGroup(\"sIngredientName\")\n .notAnyOf(nonWordChar).oneOrMore()\n .endGroup()\n .startGroup()\n .literal(\"{\")\n .startNamedGroup(\"sIngredientQuantity\")\n .notAnyOf(\"}%\").oneOrMore()\n .endGroup().optional()\n .startGroup()\n .literal(\"%\")\n .startNamedGroup(\"sIngredientUnits\")\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(\"mCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"sCookwareModifier\")\n .anyOf(\"\\\\-&?\")\n .endGroup().optional()\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(\"timerUnits\")\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 .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()","import type { FixedValue, Range, DecimalValue, FractionValue } from \"./types\";\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\"],\n toBase: 1,\n },\n {\n name: \"kg\",\n type: \"mass\",\n system: \"metric\",\n aliases: [\"kilogram\", \"kilograms\"],\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\"],\n toBase: 1,\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\"],\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: v.value * factor };\n }\n return simplifyFraction(v.num * factor, 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 // We only return a fraction where both input values are fractions themselves\n if (val1.type === \"fraction\" && val2.type === \"fraction\") {\n const commonDen = den1 * den2;\n const sumNum = num1 * den2 + num2 * den1;\n return simplifyFraction(sumNum, commonDen);\n } else {\n return { type: \"decimal\", value: num1 / den1 + num2 / den2 };\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 return {\n type: \"fixed\",\n value: toRoundedDecimal(\n multiplyNumericValue(\n value.value as DecimalValue | FractionValue,\n factor,\n ),\n ),\n };\n }\n\n return {\n type: \"range\",\n min: toRoundedDecimal(multiplyNumericValue(value.min, factor)),\n max: toRoundedDecimal(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 * 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,\n ): Quantity => {\n if (val1.type === \"fixed\" && val2.type === \"fixed\") {\n const res = addNumericValues(\n val1.value as DecimalValue | FractionValue,\n val2.value as DecimalValue | FractionValue,\n );\n return { value: { type: \"fixed\", value: res }, unit };\n }\n const r1 =\n val1.type === \"range\"\n ? val1\n : { type: \"range\", min: val1.value, max: val1.value };\n const r2 =\n val2.type === \"range\"\n ? val2\n : { type: \"range\", min: val2.value, max: val2.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 { value: { type: \"range\", min: newMin, max: newMax }, unit };\n };\n\n // Case 2: one of the two values doesn't have a unit, we consider its value to be 0 and the unit to be that of the other one\n if (q1.unit === \"\" && unit2Def) {\n return addQuantityValuesAndSetUnit(v1, v2, q2.unit); // Prefer q2's unit\n }\n if (q2.unit === \"\" && unit1Def) {\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 (q1.unit.toLowerCase() === q2.unit.toLowerCase()) {\n return addQuantityValuesAndSetUnit(v1, v2, q1.unit);\n }\n\n // Case 4: the two quantities do not have the same unit\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 throw new IncompatibleUnitsError(q1.unit, q2.unit);\n}\n","import type {\n MetadataExtract,\n Metadata,\n FixedValue,\n Range,\n TextValue,\n DecimalValue,\n FractionValue,\n} from \"./types\";\nimport { metadataRegex, rangeRegex, numberLikeRegex } from \"./regex\";\nimport { Section as SectionObject } from \"./classes/section\";\nimport type { Ingredient, Note, Step, Cookware } from \"./types\";\nimport {\n addQuantities,\n CannotAddTextValueError,\n IncompatibleUnitsError,\n Quantity,\n} from \"./units\";\n\n/**\n * Finds an item in a list or adds it if not present, then returns its index.\n * @param list The list to search in.\n * @param finder A predicate to find the item.\n * @param creator A function to create the item if not found.\n * @returns The index of the item in the list.\n */\nexport function findOrPush<T>(\n list: T[],\n finder: (elem: T) => boolean,\n creator: () => T,\n): number {\n let index = list.findIndex(finder);\n if (index === -1) {\n index = list.push(creator()) - 1;\n }\n return index;\n}\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 */\nexport function findAndUpsertIngredient(\n ingredients: Ingredient[],\n newIngredient: Ingredient,\n isReference: boolean,\n): number {\n const { name, quantity, unit } = newIngredient;\n\n // New ingredient\n if (isReference) {\n const index = ingredients.findIndex(\n (i) => i.name.toLowerCase() === name.toLowerCase(),\n );\n\n if (index === -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[index]!;\n if (quantity !== undefined) {\n const currentQuantity: Quantity = {\n value: existingIngredient.quantity ?? {\n type: \"fixed\",\n value: { type: \"decimal\", value: 0 },\n },\n unit: existingIngredient.unit ?? \"\",\n };\n const newQuantity = { value: quantity, unit: unit ?? \"\" };\n\n try {\n const total = addQuantities(currentQuantity, newQuantity);\n existingIngredient.quantity = total.value;\n existingIngredient.unit = total.unit || undefined;\n } catch (e) {\n if (\n e instanceof IncompatibleUnitsError ||\n e instanceof CannotAddTextValueError\n ) {\n // Addition not possible, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n }\n }\n }\n return index;\n }\n\n // Not a reference, so add as a new ingredient.\n return ingredients.push(newIngredient) - 1;\n}\n\nexport function findAndUpsertCookware(\n cookware: Cookware[],\n newCookware: Cookware,\n isReference: boolean,\n): number {\n const { name } = 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 return index;\n }\n\n return cookware.push(newCookware) - 1;\n}\n\n// Parser when we know the input is either a number-like value\nconst 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(content: string, varName: string) {\n const varMatch = content.match(\n new RegExp(`^${varName}:[\\\\t ]*(([^,\\\\n]*),? ?(?:.*)?)`, \"m\"),\n );\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 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 Metadata)[]) {\n const stringMetaValue: any = parseSimpleMetaVar(metadataContent, metaVar);\n if (stringMetaValue) metadata[metaVar] = stringMetaValue;\n }\n\n // String metadata variables\n for (const metaVar of [\"servings\", \"yield\", \"serves\"] as (keyof Metadata)[]) {\n const scalingMetaValue: any = 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 Metadata)[]) {\n const listMetaValue: any = parseListMetaVar(metadataContent, metaVar);\n if (listMetaValue) metadata[metaVar] = listMetaValue;\n }\n\n return { metadata, servings };\n}\n","import type {\n Metadata,\n Ingredient,\n IngredientItem,\n Timer,\n Step,\n Note,\n Cookware,\n MetadataExtract,\n CookwareItem,\n} from \"../types\";\nimport { Section } from \"./section\";\nimport {\n tokensRegex,\n commentRegex,\n blockCommentRegex,\n metadataRegex,\n ingredientAliasRegex,\n} from \"../regex\";\nimport {\n findOrPush,\n flushPendingItems,\n flushPendingNote,\n findAndUpsertIngredient,\n findAndUpsertCookware,\n parseQuantityInput,\n extractMetadata,\n} from \"../parser_helpers\";\nimport { multiplyQuantityValue } from \"../units\";\n\n/**\n * Represents a recipe.\n * @category Classes\n */\nexport class Recipe {\n /**\n * The recipe's metadata.\n * @see {@link Metadata}\n */\n metadata: Metadata = {};\n /**\n * The recipe's ingredients.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipe's sections.\n * @see {@link Section}\n */\n sections: Section[] = [];\n /**\n * The recipe's cookware.\n * @see {@link Cookware}\n */\n cookware: Cookware[] = [];\n /**\n * The recipe's timers.\n * @see {@link Timer}\n */\n timers: Timer[] = [];\n /**\n * The recipe's servings. Used for scaling\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 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 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 const name = (groups.mIngredientName || groups.sIngredientName)!;\n const quantityRaw =\n groups.mIngredientQuantity || groups.sIngredientQuantity;\n const units = groups.mIngredientUnits || groups.sIngredientUnits;\n const preparation =\n groups.mIngredientPreparation || groups.sIngredientPreparation;\n const modifier =\n groups.mIngredientModifier || groups.sIngredientModifier;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const isRecipe = modifier === \"@\";\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 idxInList = findAndUpsertIngredient(\n this.ingredients,\n {\n name: listName,\n quantity,\n unit: units,\n optional,\n hidden,\n preparation,\n isRecipe,\n },\n reference,\n );\n\n const newItem: IngredientItem = {\n type: \"ingredient\",\n value: idxInList,\n itemQuantity: quantity,\n itemUnit: units,\n displayName,\n };\n\n items.push(newItem);\n } else if (groups.mCookwareName || groups.sCookwareName) {\n const name = (groups.mCookwareName || groups.sCookwareName)!;\n const modifier = groups.mCookwareModifier || groups.sCookwareModifier;\n const quantityRaw =\n groups.mCookwareQuantity || groups.sCookwareQuantity;\n const optional = modifier === \"?\";\n const hidden = modifier === \"-\";\n const reference = modifier === \"&\";\n const quantity = quantityRaw\n ? parseQuantityInput(quantityRaw)\n : undefined;\n\n const idxInList = findAndUpsertCookware(\n this.cookware,\n { name, quantity, optional, hidden },\n reference,\n );\n items.push({\n type: \"cookware\",\n value: idxInList,\n itemQuantity: quantity,\n } as CookwareItem);\n } else if (groups.timerQuantity !== undefined) {\n const durationStr = groups.timerQuantity.trim();\n const unit = (groups.timerUnits || \"\").trim();\n if (!unit) {\n throw new Error(\"Timer missing units\");\n }\n const name = groups.timerName || undefined;\n const duration = parseQuantityInput(durationStr);\n const timerObj: Timer = {\n name,\n duration,\n unit,\n };\n const idxInList = findOrPush(\n this.timers,\n (t) =>\n t.name === timerObj.name &&\n t.duration === timerObj.duration &&\n t.unit === timerObj.unit,\n () => timerObj,\n );\n items.push({ type: \"timer\", value: idxInList });\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.\n * @param newServings - The new number of servings.\n * @returns A new Recipe instance with the scaled ingredients.\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 if (\n ingredient.quantity &&\n !(\n ingredient.quantity.type === \"fixed\" &&\n ingredient.quantity.value.type === \"text\"\n )\n ) {\n ingredient.quantity = multiplyQuantityValue(\n ingredient.quantity,\n factor,\n );\n }\n return ingredient;\n })\n .filter((ingredient) => ingredient.quantity !== null);\n\n newRecipe.servings = originalServings * factor;\n\n if (newRecipe.metadata.servings && this.metadata.servings) {\n const servingsValue = parseFloat(this.metadata.servings);\n if (!isNaN(servingsValue)) {\n newRecipe.metadata.servings = String(servingsValue * factor);\n }\n }\n\n if (newRecipe.metadata.yield && this.metadata.yield) {\n const yieldValue = parseFloat(this.metadata.yield);\n if (!isNaN(yieldValue)) {\n newRecipe.metadata.yield = String(yieldValue * factor);\n }\n }\n\n if (newRecipe.metadata.serves && this.metadata.serves) {\n const servesValue = parseFloat(this.metadata.serves);\n if (!isNaN(servesValue)) {\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 { AisleConfig } from \"./aisle_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 * Represents a shopping list.\n * @category Classes\n */\nexport class ShoppingList {\n /**\n * The ingredients in the shopping list.\n * @see {@link Ingredient}\n */\n ingredients: Ingredient[] = [];\n /**\n * The recipes in the shopping list.\n * @see {@link AddedRecipe}\n */\n recipes: AddedRecipe[] = [];\n /**\n * The aisle configuration for the shopping list.\n * @see {@link AisleConfig}\n */\n aisle_config?: AisleConfig;\n /**\n * The categorized ingredients in the shopping list.\n * @see {@link CategorizedIngredients}\n */\n categories?: CategorizedIngredients;\n\n /**\n * Creates a new ShoppingList instance.\n * @param aisle_config_str - The aisle configuration to parse.\n */\n constructor(aisle_config_str?: string) {\n if (aisle_config_str) {\n this.set_aisle_config(aisle_config_str);\n }\n }\n\n private calculate_ingredients() {\n this.ingredients = [];\n for (const { recipe, factor } of this.recipes) {\n const scaledRecipe = factor === 1 ? recipe : recipe.scaleBy(factor);\n for (const ingredient of scaledRecipe.ingredients) {\n if (ingredient.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) {\n if (existingIngredient.quantity && ingredient.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 if (ingredient.quantity) {\n existingIngredient.quantity = ingredient.quantity;\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.\n * @param recipe - The recipe to add.\n * @param factor - The factor to scale the recipe by.\n */\n add_recipe(recipe: Recipe, factor: number = 1) {\n this.recipes.push({ recipe, factor });\n this.calculate_ingredients();\n this.categorize();\n }\n\n /**\n * Removes a recipe from the shopping list.\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 aisle configuration for the shopping list.\n * @param config - The aisle configuration to parse.\n */\n set_aisle_config(config: string) {\n this.aisle_config = new AisleConfig(config);\n this.categorize();\n }\n\n /**\n * Categorizes the ingredients in the shopping list\n * Will use the aisle config if any, otherwise all ingredients will be placed in the \"other\" category\n */\n categorize() {\n if (!this.aisle_config) {\n this.categories = { other: this.ingredients };\n return;\n }\n\n const categories: CategorizedIngredients = { other: [] };\n for (const category of this.aisle_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.aisle_config.categories) {\n for (const aisleIngredient of category.ingredients) {\n if (aisleIngredient.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","i","e","units","t","i"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tmlmt/cooklang-parser",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Cooklang parsers and utilities",
|
|
5
5
|
"author": "Thomas Lamant <tom@tmlmt.com>",
|
|
6
6
|
"type": "module",
|
|
@@ -19,15 +19,16 @@
|
|
|
19
19
|
],
|
|
20
20
|
"sideEffects": false,
|
|
21
21
|
"devDependencies": {
|
|
22
|
+
"@eslint/compat": "1.3.2",
|
|
23
|
+
"@eslint/js": "9.34.0",
|
|
22
24
|
"@iconify-json/material-symbols": "1.2.33",
|
|
23
|
-
"@types/node": "
|
|
24
|
-
"@typescript-eslint/eslint-plugin": "8.42.0",
|
|
25
|
-
"@typescript-eslint/parser": "8.42.0",
|
|
25
|
+
"@types/node": "22.18.1",
|
|
26
26
|
"@vitest/coverage-v8": "3.2.4",
|
|
27
27
|
"@vitest/ui": "3.2.4",
|
|
28
28
|
"eslint": "9.34.0",
|
|
29
29
|
"eslint-config-prettier": "10.1.8",
|
|
30
30
|
"execa": "9.6.0",
|
|
31
|
+
"globals": "16.3.0",
|
|
31
32
|
"human-regex": "2.1.5",
|
|
32
33
|
"prettier": "3.6.2",
|
|
33
34
|
"rimraf": "6.0.1",
|
|
@@ -36,9 +37,26 @@
|
|
|
36
37
|
"typedoc-plugin-markdown": "4.8.1",
|
|
37
38
|
"typedoc-vitepress-theme": "1.1.2",
|
|
38
39
|
"typescript": "5.9.2",
|
|
40
|
+
"typescript-eslint": "8.42.0",
|
|
39
41
|
"vitepress": "2.0.0-alpha.12",
|
|
40
42
|
"vitest": "3.2.4"
|
|
41
43
|
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/tmlmt/cooklang-parser/issues",
|
|
46
|
+
"email": "tom@tmlmt.com"
|
|
47
|
+
},
|
|
48
|
+
"repository": {
|
|
49
|
+
"type": "git",
|
|
50
|
+
"url": "git+https://github.com/tmlmt/cooklang-parser.git"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://cooklang-parser.tmlmt.com",
|
|
53
|
+
"keywords": [
|
|
54
|
+
"cooklang",
|
|
55
|
+
"parser",
|
|
56
|
+
"typescript",
|
|
57
|
+
"cooking",
|
|
58
|
+
"recipes"
|
|
59
|
+
],
|
|
42
60
|
"license": "MIT",
|
|
43
61
|
"scripts": {
|
|
44
62
|
"clean": "rimraf dist",
|
|
@@ -47,7 +65,7 @@
|
|
|
47
65
|
"test:watch": "vitest watch",
|
|
48
66
|
"test:ui": "vitest --ui",
|
|
49
67
|
"test:coverage": "vitest run --coverage",
|
|
50
|
-
"lint": "eslint
|
|
68
|
+
"lint": "eslint",
|
|
51
69
|
"format": "prettier --write .",
|
|
52
70
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
53
71
|
"predocs:dev": "typedoc",
|