npm package: @recipe-scope/ingredient-converter
Description: Core logic for recipe ingredient quantity conversion and nutrition calculation
Author: recipe-scope
Latest version: 0.1.0

# @recipe-scope/ingredient-converter

Core logic for recipe ingredient quantity conversion and nutrition calculation.

## Installation

```bash
npm install @recipe-scope/ingredient-converter @recipe-scope/quantity-parser
```

## Usage

### Basic Ingredient Calculation

```typescript
import {
  IngredientCalculator,
  InMemorySearchProvider
} from '@recipe-scope/ingredient-converter';

// Set up food database (from @recipe-scope/food-data-ja or custom)
const foodDatabase = new Map([
  [6100, {
    foodId: 6100,
    normalizedName: '里芋',
    aliases: ['さといも'],
    foodType: 'countable',
    conversion: {
      unitWeights: { '個': 40 }
    }
  }]
]);

// Set up search provider with lookup maps
const provider = new InMemorySearchProvider(foodDatabase, {
  exactAliasMap: { '里芋': 6100 },
  hiraganaMap: { 'さといも': 6100 }
});

// Create calculator
const calculator = new IngredientCalculator(provider);

// Calculate scaled quantities
const result = await calculator.calculate(
  [{ name: '里芋', quantity: '2個' }],
  { originalServings: 2, targetServings: 4 }
);

console.log(result);
// [{ name: '里芋', quantity: '4 個', grams: 160 }]
```

### With @recipe-scope/food-data-ja

```typescript
import { IngredientCalculator, InMemorySearchProvider } from '@recipe-scope/ingredient-converter';
import { foodDatabase, exactAliasMap, foodNameHiraganaMap } from '@recipe-scope/food-data-ja';

const provider = new InMemorySearchProvider(foodDatabase, {
  exactAliasMap,
  hiraganaMap: foodNameHiraganaMap
});

const calculator = new IngredientCalculator(provider);

const result = await calculator.calculate(
  [
    { name: '砂糖', quantity: '大さじ2' },
    { name: '卵', quantity: '3個' }
  ],
  { originalServings: 2, targetServings: 4 }
);
```

### Nutrition Calculation

```typescript
import {
  NutritionCalculator,
  InMemorySearchProvider
} from '@recipe-scope/ingredient-converter';

// foodDatabase and provider setup as above...

const nutritionCalculator = new NutritionCalculator(provider);

const result = await nutritionCalculator.calculate(
  [
    { name: '卵', quantity: '2個' },
    { name: '砂糖', quantity: '大さじ1' }
  ],
  2, // servings
  foodCompositionData // Array of { foodId, foodName, enercKcal, prot, fat, ... }
);

console.log(result.nutrition);
// { totalEnercKcal: 150, totalProt: 12.5, ... }
```

### Custom Search Provider

Implement `FoodSearchProvider` for custom search logic (e.g., database, vector search):

```typescript
import type { FoodSearchProvider, SearchResult } from '@recipe-scope/ingredient-converter';

class D1SearchProvider implements FoodSearchProvider {
  constructor(private db: D1Database) {}

  async search(name: string): Promise<SearchResult | null> {
    const result = await this.db
      .prepare('SELECT * FROM foods WHERE name = ?')
      .bind(name)
      .first();

    if (result) {
      return {
        foodData: result as FoodDataEntry,
        method: 'exact'
      };
    }
    return null;
  }
}
```

## Features

- **Dependency Injection**: Use any search provider (in-memory, database, vector search)
- **Quantity Scaling**: Scale ingredient quantities between serving sizes
- **Gram Calculation**: Convert various units to grams
- **Nutrition Calculation**: Calculate per-serving nutrition from ingredients
- **Ambiguous Units**: Handle units like "少々", "適量"
- **Japanese Text**: Full support for Japanese ingredient names and units

## API

### IngredientCalculator

```typescript
class IngredientCalculator {
  constructor(searchProvider: FoodSearchProvider);

  parseQuantity(ingredientName: string, quantityStr: string): Promise<ParsedQuantity>;

  calculate(
    ingredients: Ingredient[],
    options: CalculateOptions
  ): Promise<CalculatedIngredient[]>;
}
```

### NutritionCalculator

```typescript
class NutritionCalculator {
  constructor(searchProvider: FoodSearchProvider);

  calculate(
    ingredients: Ingredient[],
    servings: number,
    foodCompositionData: FoodComposition[]
  ): Promise<NutritionCalculationResult>;
}
```

### InMemorySearchProvider

```typescript
class InMemorySearchProvider implements FoodSearchProvider {
  constructor(
    database: FoodDatabaseMap,
    config?: InMemorySearchProviderConfig
  );

  search(name: string): Promise<SearchResult | null>;
  searchBatch(names: string[]): Promise<Map<string, SearchResult | null>>;
  clearCache(): void;
}
```

## License

MIT