Data formats
The JSON shapes that flow between pipeline stages, and the YAML schema for the ingredient database. This page complements the per-package references — it’s the “what does the data actually look like” companion to their “what functions exist” content.
1. The AST (@gram-lang/parser)
Section titled “1. The AST (@gram-lang/parser)”For this source:
getAST() returns (annotated, loc offsets omitted for brevity — every node except RecipeAST itself carries one):
See parser.md for the full set of node interfaces and the ASTNodeType enum.
2. Compiled & analyzed recipes (@gram-lang/kitchen, @gram-lang/analyzer)
Section titled “2. Compiled & analyzed recipes (@gram-lang/kitchen, @gram-lang/analyzer)”compile() produces a CompilationResult; analyze() returns that same shape enriched with mass/nutrition fields (AnalyzedCompilationResult). Diffed below — analyzer-only fields are marked:
Note the compiler’s StepToken vocabulary inside content: plain narrative text is a bare string; ingredients/cookware/references share the Usage shape (no type field, identified by having an id); timers/temperatures/comments/declarations each carry their own lowercase type. This is intentionally distinct from the parser’s PascalCase ASTNodeType — it describes compiled output, not parsed input. See How to Build a Custom UI for a walkthrough of consuming this shape in a frontend framework.
3. Composed recipes (@gram-lang/modules)
Section titled “3. Composed recipes (@gram-lang/modules)”When a recipe imports sub-modules via @use, finalizeComposed() decorates the standard CompilationResult into a ComposedCompilationResult. It adds module metadata to both the root payload and individual spliced sections:
modules: Array of all imported module contributors (ModuleInfo), detailing local binding name, source URI, resolved scale factor, and execution mode ("inline"or"stocked").section.module: Present on any section spliced in from an import (ComposedSection), preserving traceability for UIs and custom renderers.
4. Ingredient database (YAML)
Section titled “4. Ingredient database (YAML)”The database passed to validateIngredientDatabase() / analyze() is a flat Record<string, IngredientData> keyed by ingredient slug. The gram CLI additionally accepts (and unwraps) an optional top-level ingredients: key, so both of these are valid .gram/ingredients.yaml files:
physical and nutrition are both optional — an entry with neither is still valid (it just contributes no mass/nutrition data, surfacing as missingMassIngredients / a MISSING_MACROS warning). Only name is required. nutrition.calories/protein/carbs/fat are required whenever nutrition is present; sugar/fiber/sodium/sat_fat/mono_fat/poly_fat/alcohol are all optional. Units matter and are not inferred: calories is kcal, sodium is milligrams, and every other nutrient is grams — all per 100 g of the raw ingredient. The set, its units and its display order come from @gram-lang/analyzer’s exported NUTRIENTS table, which every tool that reads or writes nutrition data derives from.