@emulates/edamam 0.0.0-stage → 3.0.0
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/CHANGELOG.md +19 -0
- package/DISCOVERY.md +55 -0
- package/README.md +126 -2
- package/SUPPORT.md +20 -0
- package/dist/chunk-2LVPG3CY.js +846 -0
- package/dist/chunk-2LVPG3CY.js.map +7 -0
- package/dist/chunk-QDWVQZH5.js +4560 -0
- package/dist/chunk-QDWVQZH5.js.map +7 -0
- package/dist/cli.js +19 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +1088 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +7 -0
- package/dist/server.d.ts +1490 -0
- package/dist/server.js +12 -0
- package/dist/server.js.map +7 -0
- package/openapi.yaml +1370 -0
- package/package.json +117 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Changelog — @emulates/edamam
|
|
2
|
+
|
|
3
|
+
## 3.0.0 (2026-10-07)
|
|
4
|
+
|
|
5
|
+
### ⚠️ Breaking changes
|
|
6
|
+
|
|
7
|
+
- point the repo at crvouga/emulates ([cdb5e53](https://github.com/crvouga/emulates/commit/cdb5e536ed8c1f52345e3984f089001c25fdb08a))
|
|
8
|
+
|
|
9
|
+
### Fixes and improvements
|
|
10
|
+
|
|
11
|
+
- format the emulates records query ([8698872](https://github.com/crvouga/emulates/commit/8698872509d06002be5a8fcdc85d79f8462320c3))
|
|
12
|
+
|
|
13
|
+
### Dependencies
|
|
14
|
+
|
|
15
|
+
- `@emulates/sqlite`
|
|
16
|
+
|
|
17
|
+
## 2.3.1 (2026-10-06)
|
|
18
|
+
|
|
19
|
+
Initial release.
|
package/DISCOVERY.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# @emulates/edamam discovery
|
|
2
|
+
|
|
3
|
+
This is the installed-package index for coding agents and tooling. All relative links resolve
|
|
4
|
+
inside `node_modules/@emulates/edamam/`; no repository checkout is needed to discover the emulator's
|
|
5
|
+
supported surface or documented behavior.
|
|
6
|
+
|
|
7
|
+
## Capability and behavior sources
|
|
8
|
+
|
|
9
|
+
| Question | Authoritative file | What it contains |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Behaviour and integration | [`README.md`](README.md) | Routes, state transitions, auth, webhooks, controls, presets and deliberate omissions. |
|
|
12
|
+
| Exact capabilities | [`SUPPORT.md`](SUPPORT.md) | Supported, unsupported and parity-covered operations or commands, including reasons for gaps. |
|
|
13
|
+
| Wire contract | [`openapi.yaml`](openapi.yaml) | Machine-readable paths, methods, schemas, responses and parity annotations. |
|
|
14
|
+
| Public API | [`dist/index.d.ts`](dist/index.d.ts) | The installed package's exact TypeScript exports and signatures. |
|
|
15
|
+
| Package metadata | [`package.json`](package.json) | Runtime/entry-point claims, vendor links, parity scope/tier and `emulates.discovery`. |
|
|
16
|
+
|
|
17
|
+
Read these together: the contract/capability matrix says *what* is available, while the README
|
|
18
|
+
defines stateful behavior, lifecycle rules, test controls, and intentional oracle differences.
|
|
19
|
+
If prose and an executable surface disagree, report a parity mismatch instead of adding a
|
|
20
|
+
consumer-side workaround.
|
|
21
|
+
|
|
22
|
+
## Parity and oracle
|
|
23
|
+
|
|
24
|
+
- Declared parity surface: **Food, nutrition, and recipes**.
|
|
25
|
+
- Parity tier: **cold** (the repository controls when live checks run).
|
|
26
|
+
- Oracle: **Live vendor API or sandbox**.
|
|
27
|
+
- Repository command: `bun run parity:service -- edamam`.
|
|
28
|
+
- Evidence model: Run from an Emulates checkout; credentials come only from .env.local or GitHub Actions secrets. Missing credentials exit 2.
|
|
29
|
+
|
|
30
|
+
The npm package contains evidence summaries and the exact contract, not credentials or the
|
|
31
|
+
repository-only parity harness. Self-parity/property and acceptance tests run in the Emulates
|
|
32
|
+
repository; live parity is an additional oracle check, not a substitute for the packaged matrix.
|
|
33
|
+
|
|
34
|
+
## Runtime introspection
|
|
35
|
+
|
|
36
|
+
- `GET /__admin/health`
|
|
37
|
+
- `GET /__admin`
|
|
38
|
+
- `GET /__admin/state`
|
|
39
|
+
- `GET /__admin/requests`
|
|
40
|
+
- `GET /__admin/metrics`
|
|
41
|
+
- `GET /__admin/faults/presets`
|
|
42
|
+
- `GET /__admin/ui`
|
|
43
|
+
|
|
44
|
+
For HTTP services, use `x-emulates-namespace` (or the documented credential/path carrier) so
|
|
45
|
+
parallel tests do not share state. Admin state, journal, metrics and fault-preset endpoints are
|
|
46
|
+
designed for assertions and diagnosis by consuming test suites.
|
|
47
|
+
|
|
48
|
+
## Report a mismatch or missing capability
|
|
49
|
+
|
|
50
|
+
Follow the [agent reporting contract](https://github.com/crvouga/emulates/blob/main/docs/REPORTING_ISSUES.md). Include package version,
|
|
51
|
+
operation/command, a minimal redacted request, actual emulator result, expected oracle result or vendor
|
|
52
|
+
documentation, and whether the mismatch appears in the matrix. Never include keys, tokens,
|
|
53
|
+
customer data, prompts, PHI, card data, or unredacted recordings.
|
|
54
|
+
|
|
55
|
+
Service key: `edamam`.
|
package/README.md
CHANGED
|
@@ -1,3 +1,127 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @emulates/edamam
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Part of [Emulates](https://github.com/crvouga/emulates): high-fidelity, in-process emulators for APIs and databases.
|
|
4
|
+
|
|
5
|
+
Stateful emulator of the **Edamam** APIs our apps call, answering from a built-in food and recipe
|
|
6
|
+
corpus: the Food Database v2 parser (text and UPC), nutrients and image recognition, Nutrition
|
|
7
|
+
Analysis (`nutrition-data`, `nutrition-details`), Recipe Search v2 (search with filters and
|
|
8
|
+
`_cont` paging, by URI, by id), the Meal Planner v1 `select`, and Shopping List v2. Nutrition
|
|
9
|
+
logging, barcode scans, photo logging, recipe search, meal plans and grocery lists then work in
|
|
10
|
+
tests without Edamam keys, quotas or network.
|
|
11
|
+
|
|
12
|
+
- Operation coverage: [SUPPORT.md](https://github.com/crvouga/emulates/blob/main/packages/service/edamam/SUPPORT.md)
|
|
13
|
+
- The contract (`openapi.yaml`) is hand-authored from Edamam's per-API docs and our consumers:
|
|
14
|
+
`metrics/adapters/outbound/edamam-nutrition.adapter.ts`,
|
|
15
|
+
`meal-planning/adapters/outbound/edamam-meal-planning.adapter.ts`, and the Python chat
|
|
16
|
+
service's `tools/nutrition/client.py`.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install -D @emulates/edamam
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with
|
|
25
|
+
`npx emulates-edamam serve`, `createServer` from `./server` (Node), or `createRuntime` with
|
|
26
|
+
any Fetch server.
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Both backend adapters hardcode `https://api.edamam.com` (seam: a base-URL env for
|
|
31
|
+
`EDAMAM_BASE_URL` / `EDAMAM_MEAL_BASE_URL`, and the Python client's `EDAMAM_BASE_URL`). Keys can be
|
|
32
|
+
any values (`EDAMAM_FOOD_APP_ID/KEY` or `EDAMAM_APP_ID/KEY`, `EDAMAM_MEAL_APP_ID/KEY`, the Python
|
|
33
|
+
client's `edamam_*` settings); without them our adapters report `unavailable` and never call out.
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx emulates-edamam serve --port 8824
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { createRuntime } from "@emulates/edamam"
|
|
41
|
+
|
|
42
|
+
const edamam = createRuntime()
|
|
43
|
+
const parsed = await edamam.fetch(
|
|
44
|
+
new Request(
|
|
45
|
+
"http://edamam.test/api/food-database/v2/parser?app_id=a&app_key=k&ingr=2%20large%20eggs&nutrition-type=logging",
|
|
46
|
+
),
|
|
47
|
+
)
|
|
48
|
+
// → {text, parsed: [{food: {foodId: "food_egg", label: "Egg", nutrients: {ENERC_KCAL: 143, …}},
|
|
49
|
+
// quantity: 2, measure: {uri: "…#Measure_large", label: "Large", weight: 50}}], hints: […]}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Routes
|
|
53
|
+
|
|
54
|
+
Every call takes `app_id` and `app_key` as query parameters (the meal planner and shopping list
|
|
55
|
+
also accept `Authorization: Basic app_id:app_key`, which our adapter sends).
|
|
56
|
+
|
|
57
|
+
| Route | Behaviour |
|
|
58
|
+
| --- | --- |
|
|
59
|
+
| `GET /api/food-database/v2/parser` | `ingr` is parsed into a leading quantity ("2", "1/2", "a", "two"), a measure the food has ("cup", "large", "slice", "g", "oz") and the best-matching food → `parsed[]`; every food sharing a word → `hints[]` with `measures`. Filters: `categoryLabel` (`food` drops meals), `category`, `health` (repeatable), `calories` (per 100 g). `upc` looks up a packaged food (unknown UPC 404). |
|
|
60
|
+
| `POST /api/food-database/v2/nutrients` | `{ingredients: [{quantity, measureURI, foodId}]}` → `calories`, `totalWeight`, `totalNutrients`, `totalDaily`, `dietLabels`, `healthLabels`, `ingredients[].parsed[]`; an unknown food or measure is 422. |
|
|
61
|
+
| `POST /api/food-database/nutrients-from-image?beta=true` | `{image: data URL or http URL}` → `{parsed: {food, quantity, measure}, recipe: {label, calories, totalNutrients}}`, deterministic per image (or pinned with `PUT /__admin/vision`). |
|
|
62
|
+
| `GET /api/nutrition-data?ingr=` | One ingredient line; unparsable is 422. |
|
|
63
|
+
| `POST /api/nutrition-details` | `{ingr: [...], title?, yield?}`; no lines is 422, any unparsable line 555. |
|
|
64
|
+
| `GET /api/recipes/v2` | `type` required; `q`, `health`, `diet`, `mealType`, `dishType`, `cuisineType`, `excluded` (repeatable), `calories` and `nutrients[CODE]` ranges per serving, `time`, `random`, `imageSize`. 20 per page; `_links.next.href` carries `_cont`. |
|
|
65
|
+
| `GET /api/recipes/v2/by-uri` | Up to 20 `uri` parameters; unknown URIs are skipped. |
|
|
66
|
+
| `GET /api/recipes/v2/{id}` | One recipe (the `_links.self` target). |
|
|
67
|
+
| `POST /api/meal-planner/v1/{app_id}/select` | `{size, plan: {accept, fit, exclude, sections: {Breakfast: {…}, …}}}` → `{status: OK \| INCOMPLETE, selection: [{sections: {<name>: {assigned, _links}}}]}`. Each section gets a recipe satisfying its and the plan's `accept` predicates (`health`, `meal`, `dish`), its per-serving `fit`, and `exclude`, varying by day; an unfillable section has no `assigned` and the status is `INCOMPLETE`. A plan without sections is 400. |
|
|
68
|
+
| `POST /api/shopping-list/v2` | `{entries: [{quantity, measure?: Measure_serving, item: recipe uri}]}` → ingredients aggregated per food in grams (`Measure_serving` scales by servings over the recipe's yield); `?shopping-cart=true&beta=true` adds `_links.shopping-cart`. |
|
|
69
|
+
|
|
70
|
+
Errors: Food Database and Nutrition Analysis answer `{status: "error", error, message}`;
|
|
71
|
+
Recipe Search, Meal Planner and Shopping List answer `[{errorCode, message, params}]`.
|
|
72
|
+
|
|
73
|
+
**Corpus** (`DEFAULT_FOODS`, `DEFAULT_RECIPES`): 18 foods with per-100 g nutrients and measures
|
|
74
|
+
(including a meal, two packaged products with UPCs `850000000012` and `850000000036`, and one
|
|
75
|
+
UPC `850000000029` with no nutrition data) and 8 recipes built from them, so recipe totals,
|
|
76
|
+
per-serving values and shopping lists agree with the food database.
|
|
77
|
+
|
|
78
|
+
### Admin (beyond the standard contract)
|
|
79
|
+
|
|
80
|
+
| Route | Effect |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `GET` / `POST /__admin/foods` | List foods, or add one (`{foodId, label, nutrients, measures: [{uri, label, weight}], upc?, brand?, category?, categoryLabel?, healthLabels?}`). |
|
|
83
|
+
| `GET` / `POST /__admin/recipes` | List recipes, or add one (`{id, label, yield, ingredients: [{foodId, quantity, measure, text}], mealType?, dishType?, cuisineType?, dietLabels?, healthLabels?, cautions?, totalTime?}`). |
|
|
84
|
+
| `PUT /__admin/vision` | `{foodId, quantity?, measure?}` pins what image recognition returns; `{notFound: true}` recognises nothing; `null` restores the default. |
|
|
85
|
+
| `GET/PUT /__admin/settings` | `{apps?: [{appId, appKey}], requireAccountUser?}`. |
|
|
86
|
+
|
|
87
|
+
Fault presets (`POST /__admin/faults {"preset": "<name>", "count"?: n}`): `rate_limited` (429),
|
|
88
|
+
`payment_required` (402), `unauthorized` (401), `server_error` (500), `parser_schema_drift`,
|
|
89
|
+
`recipe_quality` (555), `vision_not_found`, `meal_plan_incomplete`, `meal_plan_timeout`,
|
|
90
|
+
`slow` (12 s, past our 10 s timeouts), `connection_drop`.
|
|
91
|
+
|
|
92
|
+
### Namespaces
|
|
93
|
+
|
|
94
|
+
`x-emulates-namespace`, a `/__admin/ns/<name>` prefix on the base URL (works for the nutrition adapter
|
|
95
|
+
and the Python client, which concatenate paths; the meal adapter resolves paths with
|
|
96
|
+
`new URL(endpoint, base)`, which drops a prefix), or by application id:
|
|
97
|
+
`PUT /__admin/credentials {"credentials": {"<app_id>": "<namespace>"}}`.
|
|
98
|
+
|
|
99
|
+
### Deliberately not modelled
|
|
100
|
+
|
|
101
|
+
- Edamam's NLP and databases: the parser understands leading quantities, known measures and
|
|
102
|
+
corpus food names only; real food and recipe content needs a recorded corpus.
|
|
103
|
+
- Food Database autocomplete, brand search and `nutrients` qualifiers; Recipe Search user
|
|
104
|
+
recipes (`type=user`), `field=` projection and images other than the URL.
|
|
105
|
+
- Meal Planner plan-level `fit` beyond a day-calorie check, `mark` weighting, nested
|
|
106
|
+
sub-sections, and shopping-cart checkout pages.
|
|
107
|
+
|
|
108
|
+
## API
|
|
109
|
+
|
|
110
|
+
| Export | Kind | Description |
|
|
111
|
+
| --- | --- | --- |
|
|
112
|
+
| `EdamamAPI` | class | The in-process emulator: `fetch(request)`, `reset()`, `addFood(food)`, `addRecipe(seed)`, `recipes()`. Options: `sqlite`, `now`, `namespace`, `foods`, `recipes`, `settings`. |
|
|
113
|
+
| `createRuntime` | function | The emulator with the full service contract (health, admin, namespaces, credentials, presets). Options: `foods`, `recipes`, `settings`, `clock`, `seed`, `adminKey`, `onLog`, `sqlite`. |
|
|
114
|
+
| `EDAMAM_PRESETS` | object | Every named fault preset. |
|
|
115
|
+
| `EDAMAM_NAMESPACE` | string | The service name, `"edamam"`. |
|
|
116
|
+
| `ACCOUNT_USER_HEADER` | string | `edamam-account-user`. |
|
|
117
|
+
| `DEFAULT_FOODS`, `DEFAULT_RECIPES` | arrays | The built-in corpus. |
|
|
118
|
+
| `MEASURE_URI`, `RECIPE_URI`, `NUTRIENTS` | values | Edamam's measure and recipe URI prefixes, and the nutrient codes served. |
|
|
119
|
+
| `buildRecipe` | function | A recipe in Recipe Search v2 shape from a seed and the food corpus. |
|
|
120
|
+
| `parseLine` | function | The ingredient-line parser. |
|
|
121
|
+
| `perServing` | function | A recipe's per-serving value of a nutrient code. |
|
|
122
|
+
| `appIdCredential` | function | The `app_id` a request carries (how credentials map to namespaces). |
|
|
123
|
+
| `foodError`, `recipeErrors` | functions | Build each API family's error response. |
|
|
124
|
+
| `document`, `operationIds`, `supportedOperationIds` | values | The OpenAPI contract and its operation ids. |
|
|
125
|
+
| `createServer`, `serveTarget`, `DEFAULT_PORT` (`./server`) | Node | Serve over `node:http`; the `serve` CLI target (`--app`, `--require-account-user`); port 8824. |
|
|
126
|
+
|
|
127
|
+
Part of [Emulates](https://github.com/crvouga/emulates).
|
package/SUPPORT.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Edamam APIs (Emulates subset) — operation support
|
|
2
|
+
|
|
3
|
+
Generated from `openapi.yaml`; do not edit by hand.
|
|
4
|
+
|
|
5
|
+
- operations in spec: **10**
|
|
6
|
+
- supported by the emulator: **10**
|
|
7
|
+
- parity enabled: **10**
|
|
8
|
+
|
|
9
|
+
| operationId | route | emulator | parity | notes |
|
|
10
|
+
| --- | --- | --- | --- | --- |
|
|
11
|
+
| `FoodParser` | `GET /api/food-database/v2/parser` | ✅ supported | ✅ | |
|
|
12
|
+
| `FoodNutrients` | `POST /api/food-database/v2/nutrients` | ✅ supported | ✅ | |
|
|
13
|
+
| `FoodFromImage` | `POST /api/food-database/nutrients-from-image` | ✅ supported | ✅ | |
|
|
14
|
+
| `NutritionData` | `GET /api/nutrition-data` | ✅ supported | ✅ | |
|
|
15
|
+
| `NutritionDetails` | `POST /api/nutrition-details` | ✅ supported | ✅ | |
|
|
16
|
+
| `RecipeSearch` | `GET /api/recipes/v2` | ✅ supported | ✅ | |
|
|
17
|
+
| `RecipesByUri` | `GET /api/recipes/v2/by-uri` | ✅ supported | ✅ | |
|
|
18
|
+
| `RecipeById` | `GET /api/recipes/v2/{id}` | ✅ supported | ✅ | |
|
|
19
|
+
| `MealPlanSelect` | `POST /api/meal-planner/v1/{app_id}/select` | ✅ supported | ✅ | |
|
|
20
|
+
| `ShoppingList` | `POST /api/shopping-list/v2` | ✅ supported | ✅ | |
|