albert-heijn-mcp 1.7.2 → 1.9.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/README.md CHANGED
@@ -47,6 +47,8 @@ Ask in Dutch, English or any language your assistant speaks:
47
47
 
48
48
  > *"Plan three weeknight dinners around what's on bonus this week."*
49
49
 
50
+ > *"Put everything for the recipes in my AH meal planner on my shopping list."*
51
+
50
52
  **Save money**
51
53
 
52
54
  > *"Which products I usually buy are on bonus this week?"*
@@ -55,11 +57,19 @@ Ask in Dutch, English or any language your assistant speaks:
55
57
 
56
58
  > *"Is next week's bonus out yet? If not, when does it appear?"*
57
59
 
60
+ > *"Which of my personal offers are worth activating this week?"*
61
+
62
+ > *"How many koopzegels do I have, and what are they worth?"*
63
+
58
64
  > *"What's in the 2+1 gratis kaas deal?"*
59
65
 
60
66
  **Shop**
61
67
 
62
- > *"Put the products I've had delivered at least three times back on my list."*
68
+ > *"Put the products I buy most back on my list."*
69
+
70
+ > *"What's this product, and is it on bonus?"* (with a photo of its barcode)
71
+
72
+ > *"What's the cheapest cheese at AH right now?"*
63
73
 
64
74
  > *"Find organic, gluten-free pasta, cheapest first."*
65
75
 
@@ -354,7 +364,7 @@ Tools that return data also return it as [structured output](https://modelcontex
354
364
  |---|---|
355
365
  | `ah_login` | Log in: opens a login window on your computer, or returns AH's login link and completes the login with the code you paste back. |
356
366
  | `ah_logout` | Delete the stored tokens, to switch accounts or reset a session. |
357
- | `ah_get_member_profile` | Name, masked email, and bonus card number (last 4 digits). |
367
+ | `ah_get_member_profile` | Name, masked email, bonus card number (last 4 digits), and your savings: koopzegels (stamps, and what they're worth with interest) and Air Miles. |
358
368
 
359
369
  </details>
360
370
 
@@ -363,11 +373,14 @@ Tools that return data also return it as [structured output](https://modelcontex
363
373
 
364
374
  | Tool | Description |
365
375
  |---|---|
366
- | `ah_search_products` | Search one or more keywords at once; Dutch terms work best. Filter by `bonus=true` or by `filters` (organic, vegan, gluten_free and other diets, allergens and labels), and `sort` by price, what you buy most, or Nutri-Score. |
367
- | `ah_get_products` | Details for one or more products. `include_nutritional_info=true` adds the nutrition table. |
376
+ | `ah_search_products` | Search one or more keywords at once; Dutch terms work best. Filter by `bonus=true` or by `filters` (organic, vegan, gluten_free and other diets, allergens and labels), and `sort` by price, what you buy most, or Nutri-Score. `category_id` limits it to one category, or lists the category on its own. |
377
+ | `ah_get_products` | Details for one or more products, by product ID or by `barcodes` (the number under a barcode). `include_nutritional_info=true` adds the nutrition table. |
378
+ | `ah_get_categories` | AH's product categories and their subcategories, for `category_id` in `ah_search_products`. |
368
379
  | `ah_get_product_alternatives` | Similar products and substitutes AH suggests for a product. |
369
380
  | `ah_get_bonus_offers` | This week's bonus offers, or next week's with `period=next`. `previously_bought=true` limits them to products you bought before (AH's "Eerder gekocht"). Can filter by keyword. |
370
381
  | `ah_get_bonus_group_products` | The individual products behind a group deal such as "2+1 gratis". |
382
+ | `ah_get_personal_offers` | Your personal offers (Bonus Box): which are activated, which you can still activate, and how many you may activate this week. |
383
+ | `ah_activate_personal_offer` | Activate personal offers you choose. AH limits activations per week and they can't be undone. |
371
384
  | `ah_search_stores` | Nearby stores, by postal code or your own address. |
372
385
  | `ah_get_last_chance_items` | Vandaag-af markdowns in a store; the one nearest your address by default. |
373
386
 
@@ -378,8 +391,9 @@ Tools that return data also return it as [structured output](https://modelcontex
378
391
 
379
392
  | Tool | Description |
380
393
  |---|---|
381
- | `ah_search_recipes` | Search Allerhande recipes; Dutch terms work best. |
394
+ | `ah_search_recipes` | Search Allerhande recipes; Dutch terms work best. `filters` narrows by diet, course, cuisine, occasion, season or cooking method (e.g. `vegetarian`, `quick`, `italian`), and works without a query too; `sort` orders by relevance, newest or popular. |
382
395
  | `ah_get_recipe` | Ingredients, steps, and nutrition per serving. `servings` scales the ingredients. |
396
+ | `ah_get_meal_plan` | What's planned in the AH app's weekplanner for the next two weeks: recipes, products and notes. |
383
397
  | `ah_add_recipe_to_shopping_list` | Match a recipe's ingredients to products and add them to the list in one step. It prefers products on bonus (`prefer_bonus=false` turns that off); `skip` leaves out what you have, and `dry_run=true` previews the matches. |
384
398
 
385
399
  </details>
@@ -424,9 +438,9 @@ Choosing a delivery or pick-up slot in the AH app moves your shopping list into
424
438
  |---|---|
425
439
  | `ah_get_orders` | Upcoming delivery orders, or past ones with `past=true`. |
426
440
  | `ah_get_order_details` | Products in one order. |
427
- | `ah_get_frequent_items` | Your most-ordered products, counted over your delivery orders. |
441
+ | `ah_get_frequent_items` | The products you buy most, counted over your delivery orders and in-store receipts of the last few months (`months`, default 3). `source` limits it to `online` or `in_store`. |
428
442
  | `ah_get_receipts` | Recent in-store receipts (kassabonnen). |
429
- | `ah_get_receipt_details` | Items, discounts and payment for one receipt. |
443
+ | `ah_get_receipt_details` | Items, deposit, discounts, koopzegels bought and payment for one receipt, with its total. |
430
444
 
431
445
  </details>
432
446
 
@@ -434,8 +448,8 @@ Choosing a delivery or pick-up slot in the AH app moves your shopping list into
434
448
 
435
449
  - **Delivery orders can't be started through the API.** `ah_get_delivery_slots` lists the windows, but booking one, which starts the order, happens in the AH app or on ah.nl. While the order is active, AH doesn't serve the shopping list; the tools say so and point to the order tools.
436
450
  - **Ticking off shopping-list items isn't supported:** the API returns no usable item IDs.
437
- - **Bonus Box**, AH's personal weekly deals, is not available: its API is unknown.
438
- - **Limits per call:** at most 99 of a product, 50 items, and 100 characters for a free-text item or list name.
451
+ - **Recipes can't be planned in the meal planner:** `ah_get_meal_plan` reads it, but AH's API refuses to add recipes, so planning happens in the AH app.
452
+ - **Limits per call:** at most 99 of a product, 50 items, 100 characters for a free-text item or list name, and details for 20 products.
439
453
 
440
454
  ## Development
441
455
 
@@ -1,3 +1,4 @@
1
+ import { checkMutation } from "./client.js";
1
2
  import { fromGraphqlProduct, GRAPHQL_PRODUCT_FIELDS } from "./products.js";
2
3
  /** Published bonus periods, current week first. */
3
4
  export async function getBonusPeriods(c) {
@@ -50,4 +51,49 @@ export async function getBonusGroupProducts(c, groupId, period) {
50
51
  // Every product in a bonus group is on bonus, even when the price doesn't show it (e.g. "2+1 gratis").
51
52
  return data.bonusPromotions.flatMap((promo) => promo.products.map((p) => ({ ...fromGraphqlProduct(p), isBonus: true })));
52
53
  }
54
+ /** The member's personal offers of a bonus period, activated or still to activate. */
55
+ export async function getBonusBox(c, period) {
56
+ const query = `query BonusBox($start: String, $end: String) {
57
+ bonusPersonalPromotionBundles(validOn: $start) { maximumActivations }
58
+ bonusPromotions(filterSet: APP_BONUS_BOX, input: {periodStart: $start, periodEnd: $end, forcePromotionVisibility: true, states: [ACTIVATABLE, ACTIVATED]}) {
59
+ id hqId title category activationStatus productCount salesUnitSize storeOnly periodStart periodEnd
60
+ price { now { amount } was { amount } }
61
+ product { id title }
62
+ }
63
+ }`;
64
+ // The bundle only gives the activation limit: if it fails, the offers still come back without it.
65
+ const { data, errors } = await c.graphqlPartial(query, { start: period.start, end: period.end });
66
+ if (!data?.bonusPromotions) {
67
+ if (errors)
68
+ throw errors;
69
+ return { offers: [] };
70
+ }
71
+ return {
72
+ maxActivations: data.bonusPersonalPromotionBundles?.[0]?.maximumActivations ?? undefined,
73
+ offers: data.bonusPromotions.map((p) => ({
74
+ id: p.id,
75
+ hqId: p.hqId,
76
+ title: p.title,
77
+ category: p.category ?? undefined,
78
+ activated: p.activationStatus === "ACTIVATED",
79
+ product: p.productCount === 1 ? (p.product ?? undefined) : undefined,
80
+ productCount: p.productCount ?? undefined,
81
+ salesUnitSize: p.salesUnitSize ?? undefined,
82
+ priceWas: p.price?.was?.amount,
83
+ priceNow: p.price?.now?.amount,
84
+ storeOnly: Boolean(p.storeOnly),
85
+ periodStart: p.periodStart || period.start,
86
+ periodEnd: p.periodEnd || period.end,
87
+ })),
88
+ };
89
+ }
90
+ /** Activates a personal offer. AH has no way to undo this. */
91
+ export async function activatePersonalOffer(c, offer) {
92
+ const mutation = `mutation ActivatePersonalOffer($externalId: Int!, $startDate: String!) {
93
+ bonusActivatePersonalPromotion(externalId: $externalId, startDate: $startDate) { status message }
94
+ }`;
95
+ const data = await c.graphql(mutation, { externalId: offer.hqId, startDate: offer.periodStart });
96
+ const r = data.bonusActivatePersonalPromotion;
97
+ checkMutation(r ? { status: r.status ?? "", errorMessage: r.message } : undefined);
98
+ }
53
99
  //# sourceMappingURL=bonus.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bonus.js","sourceRoot":"","sources":["../../src/ahapi/bonus.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bonus.js","sourceRoot":"","sources":["../../src/ahapi/bonus.ts"],"names":[],"mappings":"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"}
@@ -62,9 +62,15 @@ export function wrapError(message, err) {
62
62
  }
63
63
  /** The errors array of a GraphQL response. */
64
64
  export class GraphQLError extends Error {
65
- constructor(messages) {
65
+ /** Top-level fields the errors belong to, where AH says. */
66
+ fields;
67
+ /** Some error came without a field, so it may belong to any. */
68
+ unattributed;
69
+ constructor(messages, fields = [], unattributed = false) {
66
70
  super(`AH GraphQL error: ${messages.join("; ")}`);
67
71
  this.name = "GraphQLError";
72
+ this.fields = fields;
73
+ this.unattributed = unattributed;
68
74
  }
69
75
  logText() {
70
76
  return "AH GraphQL error";
@@ -114,13 +120,27 @@ export class AhClient {
114
120
  }
115
121
  /** Runs a GraphQL operation; returns its data or throws GraphQLError. */
116
122
  async graphql(query, variables) {
123
+ const { data, errors } = await this.graphqlPartial(query, variables);
124
+ if (errors)
125
+ throw errors;
126
+ return data;
127
+ }
128
+ /**
129
+ * Runs a GraphQL operation and returns what it got: AH answers a field that fails with null and an
130
+ * error, and the other fields of the same request still carry data. For requests that batch
131
+ * independent lookups, where one failing entry shouldn't lose the rest.
132
+ */
133
+ async graphqlPartial(query, variables) {
117
134
  const res = await this.request("/graphql", {
118
135
  method: "POST",
119
136
  body: { query, variables: variables ?? {} },
120
137
  });
121
- if (res.errors?.length)
122
- throw new GraphQLError(res.errors.map((e) => e.message));
123
- return res.data;
138
+ return {
139
+ data: res.data ?? undefined,
140
+ errors: res.errors?.length
141
+ ? new GraphQLError(res.errors.map((e) => e.message), res.errors.flatMap((e) => (typeof e.path?.[0] === "string" ? [e.path[0]] : [])), res.errors.some((e) => typeof e.path?.[0] !== "string"))
142
+ : undefined,
143
+ };
124
144
  }
125
145
  }
126
146
  /** A mutation AH answered with a status other than SUCCESS. */
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ahapi/client.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAAE,aAAa,EAAgB,MAAM,WAAW,CAAC;AAExD,yEAAyE;AACzE,sEAAsE;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,cAAc,GAAG,MAAM,CAAC;AAC9B,MAAM,UAAU,GAAG,4DAA4D,CAAC;AAChF,MAAM,QAAQ,GAAG,mBAAmB,CAAC;AACrC,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B;;;GAGG;AACH,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC/B,8DAA8D;IAC9D,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;IACjE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED,8BAA8B;AAC9B,MAAM,OAAO,UAAW,SAAQ,KAAK;IAC1B,MAAM,CAAS;IAExB,uCAAuC;IACvC,YAAY,MAAc,EAAE,IAAY;QACtC,KAAK,CAAC,gBAAgB,MAAM,KAAK,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,OAAO;QACL,OAAO,gBAAgB,IAAI,CAAC,MAAM,EAAE,CAAC;IACvC,CAAC;CACF;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,GAAY,EAAE,MAAc;IACpD,OAAO,GAAG,YAAY,UAAU,IAAI,GAAG,CAAC,MAAM,KAAK,MAAM,CAAC;AAC5D,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,GAAY;IACzC,KAAK,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,YAAY,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;QAClD,IAAI,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IACrC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,qEAAqE;AACrE,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,0DAA0D;IACjD,OAAO,CAAS;IAEzB,YAAY,OAAe,EAAE,KAAc;QACzC,KAAK,CAAC,GAAG,OAAO,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1F,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,OAAO;QACL,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;IACzD,CAAC;CACF;AAED,wFAAwF;AACxF,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,GAAY;IACrD,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;AACxC,CAAC;AAED,8CAA8C;AAC9C,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,YAAY,QAAkB;QAC5B,KAAK,CAAC,qBAAqB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClD,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;IAED,OAAO;QACL,OAAO,kBAAkB,CAAC;IAC5B,CAAC;CACF;AASD,MAAM,OAAO,QAAQ;IACnB,sEAAsE;IAC7D,KAAK,CAAqB;IAEnC,YAAY,KAAc;QACxB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,iFAAiF;IACjF,KAAK,CAAC,OAAO,CAAI,IAAY,EAAE,OAAO,GAAmB,EAAE;QACzD,MAAM,OAAO,GAA2B;YACtC,YAAY,EAAE,UAAU;YACxB,eAAe,EAAE,WAAW;YAC5B,kBAAkB,EAAE,cAAc;YAClC,eAAe,EAAE,WAAW;YAC5B,MAAM,EAAE,kBAAkB;SAC3B,CAAC;QACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;QAC7E,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,CAAC,aAAa,GAAG,UAAU,IAAI,CAAC,KAAK,EAAE,CAAC;QAC/D,IAAI,OAAO,CAAC,OAAO;YAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAEjF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,GAAG,IAAI,EAAE;YACvC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,KAAK;YAC/B,OAAO;YACP,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;YAC3E,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;YACvC,mHAAmH;YACnH,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,GAAG,CAAC,EAAE;YAAE,MAAM,IAAI,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,IAAI;YAAE,OAAO,SAAc,CAAC;QACjC,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAM,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,iEAAiE;YACjE,MAAM,IAAI,KAAK,CAAC,6CAA6C,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;IAED,yEAAyE;IACzE,KAAK,CAAC,OAAO,CAAI,KAAa,EAAE,SAAmC;QACjE,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,OAAO,CAA+C,UAAU,EAAE;YACvF,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,IAAI,EAAE,EAAE;SAC5C,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM;YAAE,MAAM,IAAI,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;QACjF,OAAO,GAAG,CAAC,IAAS,CAAC;IACvB,CAAC;CACF;AAQD,+DAA+D;AAC/D,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAS;IAExB,YAAY,MAAkC;QAC5C,MAAM,MAAM,GAAG,MAAM,EAAE,MAAM,IAAI,WAAW,CAAC;QAC7C,KAAK,CAAC,oBAAoB,MAAM,MAAM,MAAM,EAAE,YAAY,IAAI,EAAE,EAAE,CAAC,CAAC;QACpE,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,OAAO;QACL,OAAO,oBAAoB,IAAI,CAAC,MAAM,GAAG,CAAC;IAC5C,CAAC;CACF;AAED,uCAAuC;AACvC,MAAM,UAAU,aAAa,CAAC,MAAkC;IAC9D,IAAI,MAAM,EAAE,MAAM,KAAK,SAAS;QAAE,MAAM,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;AACpE,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/ahapi/client.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { log } from "../log.js";
1
2
  /** "First Last", as AH shows it. */
2
3
  export function fullName(m) {
3
4
  return `${m.firstName} ${m.lastName}`.trim();
@@ -24,4 +25,43 @@ export async function getMember(c) {
24
25
  function isComplete(a) {
25
26
  return Boolean(a?.street && a.houseNumber && a.postalCode && a.city && a.countryCode);
26
27
  }
28
+ export async function getSavings(c) {
29
+ const query = `query Savings {
30
+ purchaseStampBalance {
31
+ points { totalPoints fullBooklets }
32
+ money { invested { amount } interest { amount } payout { amount } }
33
+ }
34
+ milesBalance { balance errorState }
35
+ }`;
36
+ // One balance failing leaves the other.
37
+ const { data, errors } = await c.graphqlPartial(query);
38
+ if (!data)
39
+ throw errors ?? new Error("AH returned no savings");
40
+ const k = data.purchaseStampBalance;
41
+ const m = data.milesBalance;
42
+ if (errors)
43
+ log.warn("some savings could not be fetched", { err: errors });
44
+ // A balance with an error of its own, at any depth, is unavailable or incomplete; an error AH gives no
45
+ // field for may be any balance's. A null balance without an error: the member just has none.
46
+ const failed = (field) => errors !== undefined && (errors.fields.includes(field) || errors.unattributed);
47
+ const unavailable = [];
48
+ if (failed("purchaseStampBalance"))
49
+ unavailable.push("koopzegels");
50
+ if (failed("milesBalance"))
51
+ unavailable.push("air_miles");
52
+ const koopzegels = {
53
+ stamps: k?.points?.totalPoints ?? undefined,
54
+ fullBooklets: k?.points?.fullBooklets ?? undefined,
55
+ invested: k?.money?.invested?.amount,
56
+ interest: k?.money?.interest?.amount,
57
+ payout: k?.money?.payout?.amount,
58
+ };
59
+ return {
60
+ // Absent, not empty, when AH sent no koopzegel figures at all.
61
+ koopzegels: Object.values(koopzegels).some((v) => v !== undefined) ? koopzegels : undefined,
62
+ // With an error state AH's balance (often 0) isn't the member's real balance.
63
+ airMiles: m ? { balance: m.errorState ? undefined : (m.balance ?? undefined), error: m.errorState ?? undefined } : undefined,
64
+ unavailable,
65
+ };
66
+ }
27
67
  //# sourceMappingURL=member.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"member.js","sourceRoot":"","sources":["../../src/ahapi/member.ts"],"names":[],"mappings":"AAsBA,oCAAoC;AACpC,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,GAAG,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,CAAW;IACzC,MAAM,KAAK,GAAG;;;;;;;EAOd,CAAC;IACD,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,OAAO,CAOlC,KAAK,CAAC,CAAC;IACV,OAAO;QACL,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,IAAI,EAAE;QAC9B,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE;QAC5B,KAAK,EAAE,CAAC,CAAC,YAAY,IAAI,EAAE;QAC3B,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE;QACvC,eAAe,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,IAAI,EAAE;QACrC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAA4C;IAC9D,OAAO,OAAO,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,UAAU,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,WAAW,CAAC,CAAC;AACxF,CAAC"}
1
+ {"version":3,"file":"member.js","sourceRoot":"","sources":["../../src/ahapi/member.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { hasStatus } from "./client.js";
1
2
  /**
2
3
  * Regular price, and the bonus price for products on bonus (else 0). Deals like
3
4
  * "2 voor 1.19" have no single bonus price: AH sends the regular price as both.
@@ -62,6 +63,8 @@ export async function searchProducts(c, query, size, opts = {}) {
62
63
  else if (filterBonus)
63
64
  fetch = Math.min(Math.max(size * 4, 40), MAX_PAGE);
64
65
  const params = new URLSearchParams({ query, page: "0", size: String(fetch), sortOn: opts.sort ?? "RELEVANCE" });
66
+ if (opts.categoryId)
67
+ params.set("taxonomyId", String(opts.categoryId));
65
68
  // Several properties go comma-separated, which AH combines with AND.
66
69
  if (properties.length)
67
70
  params.set("filters", `property=${properties.join(",")}`);
@@ -83,6 +86,8 @@ export async function getProduct(c, id) {
83
86
  const res = await c.request(`/mobile-services/product/detail/v4/fir/${id}`);
84
87
  return res.productCard;
85
88
  }
89
+ /** Ids per productsByIds call that keep the request URL a safe length. */
90
+ export const PRODUCT_IDS_BATCH = 50;
86
91
  /** Returns products in the order of ids. */
87
92
  export async function productsByIds(c, ids) {
88
93
  const params = new URLSearchParams({ sortOn: "INPUT_PRODUCT_IDS" });
@@ -100,4 +105,88 @@ export async function getNutrition(c, id) {
100
105
  const data = await c.graphql(query, { id });
101
106
  return data.product.tradeItem?.nutritions?.[0]?.nutrients;
102
107
  }
108
+ /** Most till ids convertPosIds takes in one call (one GraphQL request). */
109
+ export const POS_IDS_BATCH = 100;
110
+ /** Maps up to POS_IDS_BATCH receipt (till) product ids to webshop product ids; ids AH can't map are left out. */
111
+ export async function convertPosIds(c, posIds) {
112
+ const ids = [...new Set(posIds)].filter((id) => Number.isInteger(id) && id > 0);
113
+ if (ids.length > POS_IDS_BATCH)
114
+ throw new Error(`at most ${POS_IDS_BATCH} till ids per call`);
115
+ const out = new Map();
116
+ if (ids.length === 0)
117
+ return out;
118
+ // Ids are integers, so inlining them is safe.
119
+ const fields = ids.map((id, j) => `p${j}: productConvertId(sourceId: ${id})`).join(" ");
120
+ // An id AH fails on stays unmapped without losing the rest of the batch.
121
+ const { data, errors } = await c.graphqlPartial(`query ConvertPosIds { ${fields} }`);
122
+ if (!data)
123
+ throw errors ?? new Error("AH returned no product ids");
124
+ ids.forEach((id, j) => {
125
+ const webshopId = data[`p${j}`];
126
+ if (webshopId && webshopId > 0)
127
+ out.set(id, webshopId);
128
+ });
129
+ return out;
130
+ }
131
+ /**
132
+ * The code AH looks a barcode up by: its digits, a 12-digit UPC-A padded to the EAN-13 it is; undefined
133
+ * unless that is an EAN-8, EAN-13 or GTIN-14.
134
+ */
135
+ export function normalizeBarcode(barcode) {
136
+ // Only digits with spaces or dashes between them, as printed: other text ("x2") would change the number.
137
+ if (!/^[\d\s-]+$/.test(barcode.trim()))
138
+ return undefined;
139
+ const digits = barcode.replace(/\D/g, "");
140
+ // A 14-digit GTIN starting with 0 is the EAN-13 behind it: one form, so both share a lookup.
141
+ const code = digits.length === 12 ? `0${digits}` : digits.length === 14 && digits[0] === "0" ? digits.slice(1) : digits;
142
+ return [8, 13, 14].includes(code.length) ? code : undefined;
143
+ }
144
+ /**
145
+ * Whether a normalized barcode's last digit is its GS1 check digit; a digit misread from a photo fails this.
146
+ * 8-digit codes pass unchecked: a UPC-E has a different check digit than an EAN-8 of the same length.
147
+ */
148
+ export function hasValidCheckDigit(code) {
149
+ if (code.length === 8)
150
+ return true;
151
+ const digits = [...code].map(Number);
152
+ const check = digits.pop();
153
+ // From the right: weights 3, 1, 3, 1, ...
154
+ const sum = digits.reverse().reduce((acc, d, i) => acc + d * (i % 2 === 0 ? 3 : 1), 0);
155
+ return (10 - (sum % 10)) % 10 === check;
156
+ }
157
+ /** Webshop id of the product with this barcode (EAN/GTIN), or undefined if AH sells none. Throws on a malformed barcode. */
158
+ export async function productIdByBarcode(c, barcode) {
159
+ const code = normalizeBarcode(barcode);
160
+ if (!code)
161
+ throw new Error(`${barcode} is not a barcode number: barcodes have 8, 12, 13 or 14 digits`);
162
+ if (!hasValidCheckDigit(code))
163
+ throw new Error(`${barcode} is not a valid barcode (its check digit doesn't match): re-read the number`);
164
+ try {
165
+ const p = await c.request(`/mobile-services/product/search/v1/gtin/${code}`);
166
+ return p?.webshopId || undefined;
167
+ }
168
+ catch (err) {
169
+ // AH answers any number it doesn't know with 404.
170
+ if (hasStatus(err, 404))
171
+ return undefined;
172
+ throw err;
173
+ }
174
+ }
175
+ /** Top-level categories, or the subcategories of parentId. */
176
+ export async function getCategories(c, parentId) {
177
+ if (!parentId) {
178
+ const res = await c.request("/mobile-services/v1/product-shelves/categories");
179
+ return (res ?? []).map(({ id, name }) => ({ id, name }));
180
+ }
181
+ try {
182
+ const res = await c.request(`/mobile-services/v1/product-shelves/categories/${parentId}/sub-categories`);
183
+ return (res?.children ?? []).map(({ id, name }) => ({ id, name }));
184
+ }
185
+ catch (err) {
186
+ // A leaf category, or an id that isn't one.
187
+ if (hasStatus(err, 404))
188
+ return [];
189
+ throw err;
190
+ }
191
+ }
103
192
  //# sourceMappingURL=products.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"products.js","sourceRoot":"","sources":["../../src/ahapi/products.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"products.js","sourceRoot":"","sources":["../../src/ahapi/products.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,11 @@
1
- /** Most recent receipts, newest first. */
2
- export async function getReceipts(c, limit) {
3
- const query = `query Receipts($limit: Int!) {
4
- posReceiptsPage(pagination: {offset: 0, limit: $limit}) {
1
+ /** limit receipts starting at offset, newest first. */
2
+ export async function getReceipts(c, limit, offset = 0) {
3
+ const query = `query Receipts($offset: Int!, $limit: Int!) {
4
+ posReceiptsPage(pagination: {offset: $offset, limit: $limit}) {
5
5
  posReceipts { id dateTime totalAmount { amount } }
6
6
  }
7
7
  }`;
8
- const data = await c.graphql(query, { limit });
8
+ const data = await c.graphql(query, { offset, limit });
9
9
  return (data.posReceiptsPage.posReceipts ?? []).map((r) => ({
10
10
  id: r.id,
11
11
  dateTime: r.dateTime,
@@ -16,9 +16,11 @@ export async function getReceipt(c, id) {
16
16
  const query = `query Receipt($id: String!) {
17
17
  posReceiptDetails(id: $id) {
18
18
  id
19
- products { name quantity price { amount } amount { amount } }
19
+ products { name quantity price { amount } amount { amount } deposit { amount } }
20
20
  discounts { name amount { amount } }
21
+ stamps { quantity amount { amount } }
21
22
  payments { method amount { amount } }
23
+ total { amount }
22
24
  }
23
25
  }`;
24
26
  const { posReceiptDetails: d } = await c.graphql(query, { id });
@@ -29,9 +31,56 @@ export async function getReceipt(c, id) {
29
31
  quantity: p.quantity,
30
32
  unitPrice: p.price?.amount,
31
33
  total: p.amount?.amount ?? 0,
34
+ deposit: p.deposit?.amount || undefined,
32
35
  })),
33
36
  discounts: (d.discounts ?? []).map((x) => ({ name: x.name, amount: x.amount?.amount ?? 0 })),
37
+ stamps: d.stamps?.quantity || d.stamps?.amount?.amount
38
+ ? { quantity: d.stamps.quantity ?? 0, amount: d.stamps.amount?.amount ?? 0 }
39
+ : undefined,
34
40
  payments: (d.payments ?? []).map((x) => ({ method: x.method, amount: x.amount?.amount ?? 0 })),
41
+ total: d.total?.amount,
35
42
  };
36
43
  }
44
+ const RECEIPT_PAGE = 100;
45
+ /** Receipts, newest first, until the first one keep rejects; at most max. */
46
+ export async function getReceiptsWhile(c, keep, max) {
47
+ const out = [];
48
+ for (let offset = 0; out.length < max; offset += RECEIPT_PAGE) {
49
+ const size = Math.min(RECEIPT_PAGE, max - out.length);
50
+ const page = await getReceipts(c, size, offset);
51
+ for (const r of page) {
52
+ if (!keep(r))
53
+ return out;
54
+ out.push(r);
55
+ }
56
+ if (page.length < size)
57
+ break;
58
+ }
59
+ return out;
60
+ }
61
+ /** Most receipts getReceiptLines takes in one call (one GraphQL request). */
62
+ export const RECEIPT_LINES_BATCH = 25;
63
+ /**
64
+ * Product lines of up to RECEIPT_LINES_BATCH receipts, in the order of ids; undefined for a receipt AH
65
+ * could not return. Returned and voided lines (quantity 0 or less) are left out.
66
+ */
67
+ export async function getReceiptLines(c, ids) {
68
+ if (ids.length > RECEIPT_LINES_BATCH)
69
+ throw new Error(`at most ${RECEIPT_LINES_BATCH} receipts per call`);
70
+ if (ids.length === 0)
71
+ return [];
72
+ const vars = ids.map((_, j) => `$id${j}: String!`).join(", ");
73
+ const fields = ids.map((_, j) => `r${j}: posReceiptDetails(id: $id${j}) { products { id name quantity } }`).join("\n ");
74
+ const { data, errors } = await c.graphqlPartial(`query ReceiptLines(${vars}) {\n ${fields}\n}`, Object.fromEntries(ids.map((id, j) => [`id${j}`, id])));
75
+ if (!data)
76
+ throw errors ?? new Error("AH returned no receipt details");
77
+ return ids.map((_, j) => {
78
+ const receipt = data[`r${j}`];
79
+ if (!receipt)
80
+ return undefined;
81
+ return (receipt.products ?? [])
82
+ .filter((p) => (p.quantity ?? 1) > 0)
83
+ .map((p) => ({ posId: p.id && p.id > 0 ? p.id : undefined, name: p.name ?? "" }));
84
+ });
85
+ }
37
86
  //# sourceMappingURL=receipts.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"receipts.js","sourceRoot":"","sources":["../../src/ahapi/receipts.ts"],"names":[],"mappings":"AASA,0CAA0C;AAC1C,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,CAAW,EAAE,KAAa;IAC1D,MAAM,KAAK,GAAG;;;;EAId,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,OAAO,CAEzB,KAAK,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1D,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,KAAK,EAAE,CAAC,CAAC,WAAW,EAAE,MAAM,IAAI,CAAC;KAClC,CAAC,CAAC,CAAC;AACN,CAAC;AASD,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAW,EAAE,EAAU;IACtD,MAAM,KAAK,GAAG;;;;;;;EAOd,CAAC;IAED,MAAM,EAAE,iBAAiB,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC,OAAO,CAO7C,KAAK,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;IAClB,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,KAAK,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACpC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,QAAQ,EAAE,CAAC,CAAC,QAAQ;YACpB,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM;YAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;SAC7B,CAAC,CAAC;QACH,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;QAC5F,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC;KAC/F,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"receipts.js","sourceRoot":"","sources":["../../src/ahapi/receipts.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { log } from "../log.js";
1
2
  const AH_WEB = new URL("https://www.ah.nl/");
2
3
  export function recipeUrl(id, slug) {
3
4
  return new URL(`/allerhande/recept/R-R${id}/${encodeURIComponent(slug)}`, AH_WEB).href;
@@ -13,24 +14,26 @@ function decodeEntities(s) {
13
14
  return s.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (m, e) => {
14
15
  if (e[0] === "#") {
15
16
  const code = e[1] === "x" || e[1] === "X" ? parseInt(e.slice(2), 16) : parseInt(e.slice(1), 10);
16
- return Number.isFinite(code) ? String.fromCodePoint(code) : m;
17
+ // Beyond Unicode, fromCodePoint throws: leave such an entity as written.
18
+ return Number.isFinite(code) && code <= 0x10ffff ? String.fromCodePoint(code) : m;
17
19
  }
18
20
  return named[e.toLowerCase()] ?? m;
19
21
  });
20
22
  }
21
- export async function searchRecipes(c, text, size) {
22
- const query = `query RecipeSearch($query: RecipeSearchParams!) {
23
- recipeSearch(query: $query) {
23
+ /** Empty text lists all recipes matching the filters. */
24
+ export async function searchRecipes(c, text, size, opts) {
25
+ const query = `query RecipeSearch($searchText: String, $size: PageSize, $sortBy: RecipeSearchSortOption, $filters: [RecipeSearchQueryFilter!]) {
26
+ recipeSearchV2(searchText: $searchText, size: $size, sortBy: $sortBy, filters: $filters, recipeIds: []) {
27
+ correctedSearchTerm
24
28
  page { total }
25
29
  result { id title slug nutriScore courses time { cook oven wait } serving { number type } rating { average count } }
26
30
  }
27
31
  }`;
28
- const data = await c.graphql(query, {
29
- query: { searchText: text, size },
30
- });
32
+ const { recipeSearchV2: data } = await c.graphql(query, { searchText: text, size, sortBy: opts.sort, filters: opts.filters });
31
33
  return {
32
- total: data.recipeSearch.page?.total ?? 0,
33
- recipes: data.recipeSearch.result.map((r) => ({ ...r, title: decodeEntities(r.title) })),
34
+ total: data.page?.total ?? 0,
35
+ recipes: data.result.map((r) => ({ ...r, title: decodeEntities(r.title) })),
36
+ correctedText: data.correctedSearchTerm || undefined,
34
37
  };
35
38
  }
36
39
  /** servings scales the ingredient quantities; AH still reports the default in recipe.servings. */
@@ -60,4 +63,63 @@ export async function getRecipe(c, id, servings) {
60
63
  preparation: r.preparation && { steps: r.preparation.steps?.map(decodeEntities) },
61
64
  };
62
65
  }
66
+ /**
67
+ * The planner from today, one entry per day (AH returns two weeks), with what is planned on it.
68
+ * incomplete: AH failed on some days or entries, so the plan may be missing some.
69
+ */
70
+ export async function getMealPlan(c) {
71
+ const query = `query MealPlan {
72
+ recipeWeekPlannerOverview {
73
+ days {
74
+ date
75
+ items {
76
+ __typename
77
+ ... on WeekPlannerRecipeItem { recipeId recipeTitle recipe { slug } }
78
+ ... on WeekPlannerMemberRecipeItem { recipeId recipeTitle }
79
+ ... on WeekPlannerNoteItem { text }
80
+ ... on WeekPlannerProductItem { productId productTitle }
81
+ }
82
+ }
83
+ }
84
+ }`;
85
+ // An entry AH fails to resolve (e.g. an unpublished recipe) comes back null without losing the other days.
86
+ const { data, errors } = await c.graphqlPartial(query);
87
+ const days = data?.recipeWeekPlannerOverview?.days;
88
+ // With errors, no days, or days that all failed, is a failed fetch; an empty list is an empty plan.
89
+ if (!data || (errors && (!days || (days.length > 0 && !days.some(Boolean)))))
90
+ throw errors ?? new Error("AH returned no meal plan");
91
+ if (errors)
92
+ log.warn("some meal plan entries could not be fetched", { err: errors });
93
+ // undefined when AH sent no title.
94
+ const titleOf = (t) => (t ? decodeEntities(t) : undefined);
95
+ const toItem = (i) => {
96
+ if (!i)
97
+ return { kind: "other" };
98
+ switch (i.__typename) {
99
+ case "WeekPlannerRecipeItem":
100
+ if (i.recipeId) {
101
+ return { kind: "recipe", recipeId: i.recipeId, title: titleOf(i.recipeTitle), slug: i.recipe?.slug ?? undefined };
102
+ }
103
+ break;
104
+ case "WeekPlannerMemberRecipeItem":
105
+ if (i.recipeId)
106
+ return { kind: "own_recipe", recipeId: i.recipeId, title: titleOf(i.recipeTitle) };
107
+ break;
108
+ case "WeekPlannerProductItem":
109
+ if (i.productId)
110
+ return { kind: "product", productId: i.productId, title: titleOf(i.productTitle) };
111
+ break;
112
+ case "WeekPlannerNoteItem":
113
+ return { kind: "note", text: decodeEntities(i.text ?? "") };
114
+ }
115
+ // A kind this server doesn't know, or an entry missing its id: shown, not hidden.
116
+ const title = i.recipeTitle ?? i.productTitle ?? i.text;
117
+ return { kind: "other", title: title ? decodeEntities(title) : undefined };
118
+ };
119
+ // No planner yet (never used) is an empty plan; a day AH fails to resolve comes back null and is left out.
120
+ return {
121
+ days: (days ?? []).flatMap((d) => (d ? [{ date: d.date, items: (d.items ?? []).map(toItem) }] : [])),
122
+ incomplete: Boolean(errors),
123
+ };
124
+ }
63
125
  //# sourceMappingURL=recipes.js.map