picnic-mcp 0.4.0 → 1.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/README.md +64 -21
- package/dist/cli.js +52 -3
- package/dist/cli.js.map +1 -1
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +28 -0
- package/dist/errors.js.map +1 -1
- package/dist/format/deliveries.d.ts +3 -2
- package/dist/format/deliveries.js +5 -4
- package/dist/format/deliveries.js.map +1 -1
- package/dist/format/discover.d.ts +15 -0
- package/dist/format/discover.js +14 -0
- package/dist/format/discover.js.map +1 -0
- package/dist/format/product.d.ts +1 -1
- package/dist/format/product.js +1 -1
- package/dist/format/product.js.map +1 -1
- package/dist/format/products.d.ts +1 -1
- package/dist/format/products.js +1 -1
- package/dist/format/products.js.map +1 -1
- package/dist/format/recipes.d.ts +2 -1
- package/dist/format/recipes.js +1 -0
- package/dist/format/recipes.js.map +1 -1
- package/dist/http.d.ts +34 -0
- package/dist/http.js +113 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/recipe-link.d.ts +7 -0
- package/dist/recipe-link.js +55 -0
- package/dist/recipe-link.js.map +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/tools/cart.js +128 -24
- package/dist/tools/cart.js.map +1 -1
- package/dist/tools/deliveries.js +21 -10
- package/dist/tools/deliveries.js.map +1 -1
- package/dist/tools/discover.d.ts +3 -0
- package/dist/tools/discover.js +124 -0
- package/dist/tools/discover.js.map +1 -0
- package/dist/tools/groups.d.ts +2 -2
- package/dist/tools/groups.js +10 -5
- package/dist/tools/groups.js.map +1 -1
- package/dist/tools/recipes.js +57 -7
- package/dist/tools/recipes.js.map +1 -1
- package/dist/tools/result.d.ts +9 -3
- package/dist/tools/result.js +11 -4
- package/dist/tools/result.js.map +1 -1
- package/dist/tools/search-products.d.ts +1 -1
- package/dist/tools/search-products.js +82 -8
- package/dist/tools/search-products.js.map +1 -1
- package/dist/tools/slots.js +1 -1
- package/dist/tools/slots.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -1,30 +1,48 @@
|
|
|
1
1
|
# picnic-mcp
|
|
2
2
|
|
|
3
|
-
An [MCP](https://modelcontextprotocol.io) server for the online supermarket [Picnic](https://picnic.app). It lets a model search Picnic's products
|
|
3
|
+
An [MCP](https://modelcontextprotocol.io) server for the online supermarket [Picnic](https://picnic.app). It lets a model search Picnic's products, fill your shopping cart, pick a delivery slot, show promotions and the products you usually buy, and use recipes. It can't place an order.
|
|
4
4
|
|
|
5
|
-
Unofficial: not made or supported by Picnic. Built on [picnic-api](https://github.com/MRVDH/picnic-api).
|
|
5
|
+
Unofficial: not made or supported by Picnic. Built on [picnic-api](https://github.com/MRVDH/picnic-api). Tested with Dutch accounts; German and French accounts should work, but haven't been tested.
|
|
6
|
+
|
|
7
|
+
## Part of a set
|
|
8
|
+
|
|
9
|
+
picnic-api, picnic-web and picnic-mcp are developed and released together. When the Picnic app changes how it gets its data, picnic-api follows it and picnic-web and picnic-mcp are updated in the same change, so their latest versions always work together.
|
|
10
|
+
|
|
11
|
+
| Project | What it is |
|
|
12
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
13
|
+
| [picnic-api](https://github.com/MRVDH/picnic-api) [](https://www.npmjs.com/package/picnic-api) | Node.js library for Picnic's API: products, cart, delivery slots, deliveries and recipes. |
|
|
14
|
+
| [picnic-web](https://github.com/MRVDH/picnic-web) | Web interface for Picnic, to shop from your browser. Live at [picnic.maartenvandenhoven.com](http://picnic.maartenvandenhoven.com). |
|
|
15
|
+
| [picnic-mcp](https://github.com/MRVDH/picnic-mcp) [](https://www.npmjs.com/package/picnic-mcp) | MCP server that lets AI assistants search products, fill your cart and pick a delivery slot. |
|
|
6
16
|
|
|
7
17
|
## Tools
|
|
8
18
|
|
|
9
|
-
The tools come in groups. `cart`, `slots` and `
|
|
10
|
-
|
|
11
|
-
| Group | Tool | What it does
|
|
12
|
-
| ------------ | ------------------------- |
|
|
13
|
-
| `cart` | `search_products` | Searches products. Returns a `bestMatch` when one product fits (usually one you bought before), or 2 or 3 `choices` to ask you about.
|
|
14
|
-
| | `add_to_cart` | Adds
|
|
15
|
-
| | `
|
|
16
|
-
| | `
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
| `
|
|
20
|
-
| `
|
|
21
|
-
| `
|
|
22
|
-
| | `
|
|
23
|
-
|
|
|
24
|
-
|
|
|
19
|
+
The tools come in groups. `cart`, `slots`, `product` and `discover` are on by default; turn groups on or off with `PICNIC_MCP_TOOLS` (see Setup).
|
|
20
|
+
|
|
21
|
+
| Group | Tool | What it does |
|
|
22
|
+
| ------------ | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
|
+
| `cart` | `search_products` | Searches products. Returns a `bestMatch` when one product fits (usually one you bought before), or 2 or 3 `choices` to ask you about. |
|
|
24
|
+
| | `add_to_cart` | Adds one or more products by their ids. Returns the quantity of each in the cart, the products that failed, the cart total, and replacements when a product can't be delivered. |
|
|
25
|
+
| | `set_cart_quantity` | Sets how many of a product are in the cart; 0 removes it. |
|
|
26
|
+
| | `remove_from_cart` | Removes a product by its id. |
|
|
27
|
+
| | `get_cart` | Shows the cart: products, quantities, prices, total and delivery window, and replacements for products that can't be delivered. |
|
|
28
|
+
| `slots` | `get_delivery_slots` | Lists the delivery slots, two days at a time or for one date. |
|
|
29
|
+
| | `select_delivery_slot` | Chooses the delivery slot of the cart. |
|
|
30
|
+
| `product` | `get_product` | Shows a product's ingredients, allergens, nutrition, storage info, promotion and price. |
|
|
31
|
+
| `discover` | `get_promotions` | Lists this week's promotions per section (e.g. per category), all promotions of a section, or the products of one promotion. |
|
|
32
|
+
| | `get_favorites` | Lists the products you buy, per aisle, as the app's "Favorieten" tab does. For "add my usual groceries". |
|
|
33
|
+
| `deliveries` | `get_deliveries` | Shows upcoming deliveries (window, number of products, total, and the expected arrival while the driver is on the way) or the last past ones. Optionally the products, e.g. to order the same again. |
|
|
34
|
+
| `recipes` | `search_recipes` | Searches Picnic's recipes. |
|
|
35
|
+
| | `get_saved_recipes` | Lists the recipes you saved in the app. |
|
|
36
|
+
| | `get_recipe` | Shows a recipe's ingredients, allergens and steps. The recipe tools also take a recipe link shared from the Picnic app. |
|
|
37
|
+
| | `add_recipe_to_cart` | Adds a recipe's ingredients to the cart for a number of portions. |
|
|
38
|
+
| | `remove_recipe_from_cart` | Removes a recipe and its ingredients from the cart. |
|
|
39
|
+
| | `save_recipe` | Saves a recipe in the app. |
|
|
40
|
+
| | `unsave_recipe` | Removes a recipe from your saved recipes. |
|
|
25
41
|
|
|
26
42
|
Output is compact JSON with only what a model needs. It leaves out your name, address, payment details, order numbers and other account details. Keep in mind that whatever the tools return is sent to the model your MCP client uses.
|
|
27
43
|
|
|
44
|
+
When a search has no clear match and your MCP client can show a form ([elicitation](https://modelcontextprotocol.io/specification/latest/client/elicitation)), you pick the product in the client yourself, instead of the model asking you in the chat.
|
|
45
|
+
|
|
28
46
|
## Setup
|
|
29
47
|
|
|
30
48
|
Requires Node.js 20 or later.
|
|
@@ -39,7 +57,9 @@ It asks for your country, email and password, and the code Picnic texts you when
|
|
|
39
57
|
|
|
40
58
|
### 2. Add the server to your MCP client
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
**Claude Desktop and other MCPB clients:** download `picnic-mcp.mcpb` from the [latest release](https://github.com/MRVDH/picnic-mcp/releases/latest), open it, and fill in the auth key.
|
|
61
|
+
|
|
62
|
+
**Other clients** take a configuration like this:
|
|
43
63
|
|
|
44
64
|
```json
|
|
45
65
|
{
|
|
@@ -56,10 +76,33 @@ Most clients take a configuration like this:
|
|
|
56
76
|
}
|
|
57
77
|
```
|
|
58
78
|
|
|
59
|
-
`PICNIC_COUNTRY_CODE` is `NL`, `DE` or `FR` (default `NL`). To choose the tool groups, add `PICNIC_MCP_TOOLS` with a comma-separated list, e.g. `"cart,slots,product,deliveries,recipes"` (default `cart,slots,product`). The server runs over stdio.
|
|
79
|
+
`PICNIC_COUNTRY_CODE` is `NL`, `DE` or `FR` (default `NL`). To choose the tool groups, add `PICNIC_MCP_TOOLS` with a comma-separated list, e.g. `"cart,slots,product,discover,deliveries,recipes"` (default `cart,slots,product,discover`). Fewer tools help smaller models pick the right one. The server runs over stdio; see [HTTP](#http) to reach it over the network.
|
|
80
|
+
|
|
81
|
+
The server is also listed in the [MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.MRVDH/picnic-mcp`.
|
|
60
82
|
|
|
61
83
|
When the key expires, the tools say so; run `npx picnic-mcp login` again.
|
|
62
84
|
|
|
85
|
+
## HTTP
|
|
86
|
+
|
|
87
|
+
`picnic-mcp http` serves the same tools over HTTP at `/mcp`, for clients that connect to a URL. It needs a token that clients send as `Authorization: Bearer <token>`; generate one with:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
openssl rand -hex 32
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
PICNIC_AUTH_KEY=<auth key> PICNIC_MCP_HTTP_TOKEN=<token> npx picnic-mcp http
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
| Variable | |
|
|
98
|
+
| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
99
|
+
| `PICNIC_MCP_HTTP_TOKEN` | Required, at least 32 characters. Anyone with it can change your cart, so keep it secret. |
|
|
100
|
+
| `PICNIC_MCP_HTTP_HOST` | Address to listen on. Default `127.0.0.1`, so only this machine can connect. |
|
|
101
|
+
| `PICNIC_MCP_HTTP_PORT` | Default `3000`. |
|
|
102
|
+
| `PICNIC_MCP_HTTP_ALLOWED_HOSTS` | Host names allowed besides localhost, comma-separated, e.g. the name of a reverse proxy in front of it. Requests with another Host or Origin header are refused. |
|
|
103
|
+
|
|
104
|
+
The server doesn't do HTTPS. To use it from another machine, put it behind a reverse proxy with HTTPS, and add the proxy's host name to `PICNIC_MCP_HTTP_ALLOWED_HOSTS`.
|
|
105
|
+
|
|
63
106
|
## Use it in your own code
|
|
64
107
|
|
|
65
108
|
`createServer` returns the MCP server without reading environment variables or files, so you can run it in your own process and connect it to any MCP transport:
|
|
@@ -75,4 +118,4 @@ Pass `tools` to choose the tool groups (e.g. `tools: ["cart", "recipes"]`), `cli
|
|
|
75
118
|
|
|
76
119
|
### Product images
|
|
77
120
|
|
|
78
|
-
Results that list products (search, the cart,
|
|
121
|
+
Results that list products (search, the cart, added products, promotions, favorites, product details) carry the image URLs of those products in `_meta["picnic-mcp/images"]`, by product id. MCP clients don't pass `_meta` to the model, so the images cost no tokens; your own code can use them, e.g. to show product photos. The images don't need the auth key. The key is exported as `IMAGES_META_KEY`.
|
package/dist/cli.js
CHANGED
|
@@ -3,6 +3,7 @@ import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
|
3
3
|
import { createInterface } from "node:readline/promises";
|
|
4
4
|
import { Writable } from "node:stream";
|
|
5
5
|
import PicnicClient from "picnic-api";
|
|
6
|
+
import { MIN_TOKEN_LENGTH, startHttpServer } from "./http.js";
|
|
6
7
|
import { createServer } from "./server.js";
|
|
7
8
|
import { DEFAULT_TOOL_GROUPS, TOOL_GROUPS, parseToolGroups } from "./tools/groups.js";
|
|
8
9
|
import { COUNTRY_CODES } from "./types.js";
|
|
@@ -13,15 +14,23 @@ const USAGE = `Usage:
|
|
|
13
14
|
PICNIC_MCP_TOOLS tool groups, comma-separated, from
|
|
14
15
|
${TOOL_GROUPS.join(", ")}
|
|
15
16
|
(default ${DEFAULT_TOOL_GROUPS.join(",")})
|
|
17
|
+
picnic-mcp http Serve it over HTTP at /mcp instead, with the same variables and:
|
|
18
|
+
PICNIC_MCP_HTTP_TOKEN bearer token clients send, at least
|
|
19
|
+
${MIN_TOKEN_LENGTH} characters (required)
|
|
20
|
+
PICNIC_MCP_HTTP_HOST address to listen on (default 127.0.0.1)
|
|
21
|
+
PICNIC_MCP_HTTP_PORT port (default 3000)
|
|
22
|
+
PICNIC_MCP_HTTP_ALLOWED_HOSTS host names allowed besides localhost,
|
|
23
|
+
comma-separated, e.g. a reverse proxy's
|
|
16
24
|
picnic-mcp login Sign in to Picnic and print an auth key for PICNIC_AUTH_KEY.`;
|
|
17
25
|
function parseCountryCode(value) {
|
|
18
|
-
const code = (value
|
|
26
|
+
const code = (value?.trim() || "NL").toUpperCase();
|
|
19
27
|
if (!COUNTRY_CODES.includes(code)) {
|
|
20
28
|
throw new Error(`Unknown country code "${value}". Use one of ${COUNTRY_CODES.join(", ")}.`);
|
|
21
29
|
}
|
|
22
30
|
return code;
|
|
23
31
|
}
|
|
24
|
-
|
|
32
|
+
/** Reads the Picnic settings from the environment, or exits with a message. */
|
|
33
|
+
function picnicSettings() {
|
|
25
34
|
const authKey = process.env.PICNIC_AUTH_KEY?.trim();
|
|
26
35
|
if (!authKey) {
|
|
27
36
|
console.error("PICNIC_AUTH_KEY is not set. Get an auth key with `npx picnic-mcp login`.\n\n" + USAGE);
|
|
@@ -30,7 +39,45 @@ async function serve() {
|
|
|
30
39
|
const countryCode = parseCountryCode(process.env.PICNIC_COUNTRY_CODE);
|
|
31
40
|
const toolsEnv = process.env.PICNIC_MCP_TOOLS?.trim();
|
|
32
41
|
const tools = toolsEnv ? parseToolGroups(toolsEnv) : DEFAULT_TOOL_GROUPS;
|
|
33
|
-
|
|
42
|
+
return { authKey, countryCode, tools };
|
|
43
|
+
}
|
|
44
|
+
async function serve() {
|
|
45
|
+
const settings = picnicSettings();
|
|
46
|
+
serveStdio(() => createServer(settings));
|
|
47
|
+
}
|
|
48
|
+
async function serveHttp() {
|
|
49
|
+
const { authKey, countryCode, tools } = picnicSettings();
|
|
50
|
+
const token = process.env.PICNIC_MCP_HTTP_TOKEN?.trim() ?? "";
|
|
51
|
+
if (token.length < MIN_TOKEN_LENGTH) {
|
|
52
|
+
console.error(`PICNIC_MCP_HTTP_TOKEN must be set to a random value of at least ${MIN_TOKEN_LENGTH} characters, e.g. from \`openssl rand -hex 32\`. Clients send it as "Authorization: Bearer <token>".`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const host = process.env.PICNIC_MCP_HTTP_HOST?.trim() || "127.0.0.1";
|
|
56
|
+
const port = Number(process.env.PICNIC_MCP_HTTP_PORT?.trim() || 3000);
|
|
57
|
+
if (!Number.isInteger(port) || port < 0 || port > 65535) {
|
|
58
|
+
throw new Error(`PICNIC_MCP_HTTP_PORT must be a port number, not "${process.env.PICNIC_MCP_HTTP_PORT}".`);
|
|
59
|
+
}
|
|
60
|
+
const allowedHosts = (process.env.PICNIC_MCP_HTTP_ALLOWED_HOSTS ?? "")
|
|
61
|
+
.split(",")
|
|
62
|
+
.map((name) => name.trim())
|
|
63
|
+
.filter(Boolean);
|
|
64
|
+
// One Picnic client for all requests; each request gets its own MCP server.
|
|
65
|
+
const client = new PicnicClient({ authKey, countryCode });
|
|
66
|
+
const server = await startHttpServer({
|
|
67
|
+
token,
|
|
68
|
+
host,
|
|
69
|
+
port,
|
|
70
|
+
allowedHosts,
|
|
71
|
+
createServer: () => createServer({ client, countryCode, tools }),
|
|
72
|
+
onError: (error) => console.error("[picnic-mcp]", error),
|
|
73
|
+
});
|
|
74
|
+
console.error(`picnic-mcp listening on ${server.url}`);
|
|
75
|
+
if (!["127.0.0.1", "localhost", "::1"].includes(host)) {
|
|
76
|
+
console.error("This address can be reached from other machines, and the server doesn't use HTTPS. Put it behind an HTTPS reverse proxy.");
|
|
77
|
+
}
|
|
78
|
+
for (const signal of ["SIGINT", "SIGTERM"]) {
|
|
79
|
+
process.once(signal, () => void server.close().then(() => process.exit(0)));
|
|
80
|
+
}
|
|
34
81
|
}
|
|
35
82
|
/** Asks for the login details on the terminal and prints the auth key. Stores nothing. */
|
|
36
83
|
async function login() {
|
|
@@ -78,6 +125,8 @@ async function main() {
|
|
|
78
125
|
return serve();
|
|
79
126
|
if (command === "login")
|
|
80
127
|
return login();
|
|
128
|
+
if (command === "http")
|
|
129
|
+
return serveHttp();
|
|
81
130
|
console.error(command === "--help" || command === "-h" ? USAGE : `Unknown command "${command}".\n\n${USAGE}`);
|
|
82
131
|
process.exit(command === "--help" || command === "-h" ? 0 : 1);
|
|
83
132
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,MAAM,oCAAoC,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,YAAY,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACtF,OAAO,EAAE,aAAa,EAAoB,MAAM,YAAY,CAAC;AAE7D,MAAM,KAAK,GAAG;;;;;+CAKiC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC;wDACb,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -3,3 +3,17 @@
|
|
|
3
3
|
* they can be long HTML pages or contain account data.
|
|
4
4
|
*/
|
|
5
5
|
export declare function describeError(error: unknown): string;
|
|
6
|
+
/** Added to an error of a tool that changes the cart, when the change may have happened anyway. */
|
|
7
|
+
export declare const CART_MAY_HAVE_CHANGED = "The cart may have changed anyway: check get_cart before trying again.";
|
|
8
|
+
/**
|
|
9
|
+
* Whether an error is about the one product a request was for (it doesn't exist, Picnic refused
|
|
10
|
+
* it), so the next product can still be tried. Not for the auth key, rate limiting or an outage,
|
|
11
|
+
* which make every request fail.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isProductError(error: unknown): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a failed cart change may still have gone through: after a timeout, a network error or a
|
|
16
|
+
* server error the request may have reached Picnic. Not when Picnic rejected it (a 4xx, the auth
|
|
17
|
+
* key, rate limiting) or it failed before anything was sent.
|
|
18
|
+
*/
|
|
19
|
+
export declare function mayHaveChangedCart(error: unknown): boolean;
|
package/dist/errors.js
CHANGED
|
@@ -25,4 +25,32 @@ export function describeError(error) {
|
|
|
25
25
|
}
|
|
26
26
|
return "Could not reach Picnic. Try again later.";
|
|
27
27
|
}
|
|
28
|
+
/** Added to an error of a tool that changes the cart, when the change may have happened anyway. */
|
|
29
|
+
export const CART_MAY_HAVE_CHANGED = "The cart may have changed anyway: check get_cart before trying again.";
|
|
30
|
+
/**
|
|
31
|
+
* Whether an error is about the one product a request was for (it doesn't exist, Picnic refused
|
|
32
|
+
* it), so the next product can still be tried. Not for the auth key, rate limiting or an outage,
|
|
33
|
+
* which make every request fail.
|
|
34
|
+
*/
|
|
35
|
+
export function isProductError(error) {
|
|
36
|
+
if (PicnicClient.isProductNotFoundError(error))
|
|
37
|
+
return true;
|
|
38
|
+
if (!PicnicClient.isPicnicApiError(error) || /<html[\s>]/i.test(error.body))
|
|
39
|
+
return false;
|
|
40
|
+
return error.status >= 400 && error.status < 500 && ![401, 403, 429].includes(error.status);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Whether a failed cart change may still have gone through: after a timeout, a network error or a
|
|
44
|
+
* server error the request may have reached Picnic. Not when Picnic rejected it (a 4xx, the auth
|
|
45
|
+
* key, rate limiting) or it failed before anything was sent.
|
|
46
|
+
*/
|
|
47
|
+
export function mayHaveChangedCart(error) {
|
|
48
|
+
if (PicnicClient.isPicnicApiError(error))
|
|
49
|
+
return error.status >= 500 && !/<html[\s>]/i.test(error.body);
|
|
50
|
+
if (PicnicClient.isProductNotFoundError(error) ||
|
|
51
|
+
PicnicClient.isUnexpectedPageFormatError(error)) {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
28
56
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,YAAY,CAAC;AAEtC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,YAAY,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,sFAAsF;QACtF,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,OAAO,kEAAkE,CAAC;QAC5E,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACjD,OAAO,kHAAkH,CAAC;QAC5H,CAAC;QACD,OAAO,6BAA6B,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IAC7F,CAAC;IACD,IAAI,YAAY,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,WAAW,KAAK,CAAC,SAAS,oFAAoF,CAAC;IACxH,CAAC;IACD,IAAI,YAAY,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,uHAAuH,CAAC;IACjI,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5D,OAAO,2CAA2C,CAAC;IACrD,CAAC;IACD,OAAO,0CAA0C,CAAC;AACpD,CAAC"}
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,YAAY,CAAC;AAEtC;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,IAAI,YAAY,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QACzC,sFAAsF;QACtF,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACnC,OAAO,kEAAkE,CAAC;QAC5E,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YACjD,OAAO,kHAAkH,CAAC;QAC5H,CAAC;QACD,OAAO,6BAA6B,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC;IAC7F,CAAC;IACD,IAAI,YAAY,CAAC,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,WAAW,KAAK,CAAC,SAAS,oFAAoF,CAAC;IACxH,CAAC;IACD,IAAI,YAAY,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,uHAAuH,CAAC;IACjI,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;QAC5D,OAAO,2CAA2C,CAAC;IACrD,CAAC;IACD,OAAO,0CAA0C,CAAC;AACpD,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,MAAM,qBAAqB,GAChC,uEAAuE,CAAC;AAE1E;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,YAAY,CAAC,sBAAsB,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC5D,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1F,OAAO,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AAC9F,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,YAAY,CAAC,gBAAgB,CAAC,KAAK,CAAC;QACtC,OAAO,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChE,IACE,YAAY,CAAC,sBAAsB,CAAC,KAAK,CAAC;QAC1C,YAAY,CAAC,2BAA2B,CAAC,KAAK,CAAC,EAC/C,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { CountryCode, Delivery, DeliveryPosition, DeliverySummary } from "../types.js";
|
|
2
2
|
import { type CartLineOutput } from "./cart.js";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* A delivery as a tool shows it to a model. Delivery and order ids, payment details, the
|
|
5
5
|
* driver, the address and the route stay out.
|
|
6
6
|
*/
|
|
7
7
|
export type DeliveryOutput = {
|
|
8
8
|
/** The delivery window, e.g. "Thu 23 Oct, 18:00-19:00". */
|
|
9
9
|
window?: string;
|
|
10
|
-
|
|
10
|
+
/** Left out when unknown: Picnic's summary of a past delivery has 0. */
|
|
11
|
+
itemCount?: number;
|
|
11
12
|
total: string;
|
|
12
13
|
/** Set when the driver is on the way. */
|
|
13
14
|
onTheWay?: true;
|
|
@@ -3,15 +3,16 @@ import { formatPrice, formatTime, formatWindow } from "./prices.js";
|
|
|
3
3
|
export function formatDelivery(summary, position, delivery, countryCode) {
|
|
4
4
|
const window = formatWindow(summary.windowStart, summary.windowEnd);
|
|
5
5
|
const arrival = position?.inProgress ? expectedArrival(position) : null;
|
|
6
|
+
const items = delivery?.orders.flatMap((order) => order.items);
|
|
7
|
+
// Counted from the products when they're loaded, since the summary of a past delivery has 0.
|
|
8
|
+
const itemCount = items ? items.reduce((sum, item) => sum + item.quantity, 0) : summary.itemCount;
|
|
6
9
|
return {
|
|
7
10
|
...(window && { window }),
|
|
8
|
-
itemCount
|
|
11
|
+
...(itemCount > 0 && { itemCount }),
|
|
9
12
|
total: formatPrice(summary.totalPrice, countryCode),
|
|
10
13
|
...(position?.inProgress && { onTheWay: true }),
|
|
11
14
|
...(arrival && { expectedArrival: arrival }),
|
|
12
|
-
...(
|
|
13
|
-
products: delivery.orders.flatMap((order) => order.items.map((item) => formatCartLine(item, countryCode))),
|
|
14
|
-
}),
|
|
15
|
+
...(items && { products: items.map((item) => formatCartLine(item, countryCode)) }),
|
|
15
16
|
};
|
|
16
17
|
}
|
|
17
18
|
function expectedArrival(position) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/format/deliveries.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,cAAc,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"deliveries.js","sourceRoot":"","sources":["../../src/format/deliveries.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,cAAc,EAAE,MAAM,WAAW,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmBpE,MAAM,UAAU,cAAc,CAC5B,OAAwB,EACxB,QAAiC,EACjC,QAAyB,EACzB,WAAwB;IAExB,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IACpE,MAAM,OAAO,GAAG,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,MAAM,KAAK,GAAG,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC/D,6FAA6F;IAC7F,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC;IAClG,OAAO;QACL,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,EAAE,CAAC;QACzB,GAAG,CAAC,SAAS,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;QACnC,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,WAAW,CAAC;QACnD,GAAG,CAAC,QAAQ,EAAE,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC/C,GAAG,CAAC,OAAO,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,CAAC;QAC5C,GAAG,CAAC,KAAK,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC;KACnF,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,QAA0B;IACjD,IAAI,QAAQ,CAAC,cAAc,IAAI,QAAQ,CAAC,YAAY,EAAE,CAAC;QACrD,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;IACvF,CAAC;IACD,OAAO,QAAQ,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACzF,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CountryCode, ProductTile, PromotionGroup } from "../types.js";
|
|
2
|
+
import { type ProductOutput } from "./products.js";
|
|
3
|
+
/** A promotion on several products, as a tool shows it to a model. */
|
|
4
|
+
export type PromotionOutput = {
|
|
5
|
+
/** For get_promotions, to list its products. */
|
|
6
|
+
promotionId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
/** E.g. "Bv. sojayoghurt". */
|
|
9
|
+
example?: string;
|
|
10
|
+
/** E.g. "2 voor €3.99". */
|
|
11
|
+
label?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function formatPromotion(group: PromotionGroup): PromotionOutput;
|
|
14
|
+
/** Products from a promotions or favorites page; none of them is marked as bought before. */
|
|
15
|
+
export declare function formatTiles(tiles: ProductTile[], countryCode: CountryCode): ProductOutput[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { formatProduct } from "./products.js";
|
|
2
|
+
export function formatPromotion(group) {
|
|
3
|
+
return {
|
|
4
|
+
promotionId: group.id,
|
|
5
|
+
name: group.name,
|
|
6
|
+
...(group.example && { example: group.example }),
|
|
7
|
+
...(group.label && { label: group.label }),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
/** Products from a promotions or favorites page; none of them is marked as bought before. */
|
|
11
|
+
export function formatTiles(tiles, countryCode) {
|
|
12
|
+
return tiles.map((tile) => formatProduct({ tile, boughtBefore: false }, countryCode));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=discover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/format/discover.ts"],"names":[],"mappings":"AACA,OAAO,EAAsB,aAAa,EAAE,MAAM,eAAe,CAAC;AAalE,MAAM,UAAU,eAAe,CAAC,KAAqB;IACnD,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,EAAE;QACrB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;QAChD,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,WAAW,CAAC,KAAoB,EAAE,WAAwB;IACxE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,EAAE,WAAW,CAAC,CAAC,CAAC;AACxF,CAAC"}
|
package/dist/format/product.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CountryCode, Product } from "../types.js";
|
|
2
2
|
/** A product's details as a tool shows them to a model. Optional fields are left out when empty. */
|
|
3
3
|
export type ProductDetailsOutput = {
|
|
4
|
-
|
|
4
|
+
productId: string;
|
|
5
5
|
name: string;
|
|
6
6
|
brand?: string;
|
|
7
7
|
size?: string;
|
package/dist/format/product.js
CHANGED
|
@@ -13,7 +13,7 @@ export function formatProductDetails(product, countryCode) {
|
|
|
13
13
|
.filter((section) => section.content.trim() && lines(section.content.split("\n")) !== nutritionText)
|
|
14
14
|
.map((section) => ({ title: section.title, content: cut(section.content) }));
|
|
15
15
|
return {
|
|
16
|
-
|
|
16
|
+
productId: product.id,
|
|
17
17
|
name: product.name,
|
|
18
18
|
...(product.brand && { brand: product.brand }),
|
|
19
19
|
...(product.unitQuantity && { size: product.unitQuantity }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product.js","sourceRoot":"","sources":["../../src/format/product.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,4EAA4E;AAC5E,MAAM,QAAQ,GAAG,IAAI,CAAC;AAuBtB,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAClC,OAAgB,EAChB,WAAwB;IAExB,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,cAAc,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;SAC3E,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC1B,MAAM,CAAC,OAAO,CAAC,CAAC;IACnB,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAClD,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,CAC9D,CAAC;IACF,kFAAkF;IAClF,MAAM,aAAa,GAAG,KAAK,CACzB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACpC,GAAG,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,CAC1D,CACF,CAAC;IACF,MAAM,IAAI,GAAG,OAAO,CAAC,YAAY;SAC9B,MAAM,CACL,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,aAAa,CAC5F;SACA,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC;IAC/E,OAAO;QACL,
|
|
1
|
+
{"version":3,"file":"product.js","sourceRoot":"","sources":["../../src/format/product.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CountryCode, ProductTile } from "../types.js";
|
|
2
2
|
/** A product as a tool shows it to a model. Optional fields are left out when empty. */
|
|
3
3
|
export type ProductOutput = {
|
|
4
|
-
|
|
4
|
+
productId: string;
|
|
5
5
|
name: string;
|
|
6
6
|
brand?: string;
|
|
7
7
|
size?: string;
|
package/dist/format/products.js
CHANGED
|
@@ -7,7 +7,7 @@ export function formatProduct(candidate, countryCode) {
|
|
|
7
7
|
const { tile } = candidate;
|
|
8
8
|
const labels = tile.labels.map((label) => label.text).filter((text) => !!text);
|
|
9
9
|
return {
|
|
10
|
-
|
|
10
|
+
productId: tile.id,
|
|
11
11
|
name: tile.namePrefix ? `${tile.namePrefix} ${tile.name}` : tile.name,
|
|
12
12
|
...(tile.brand && { brand: tile.brand }),
|
|
13
13
|
...(tile.unitQuantity && { size: tile.unitQuantity }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"products.js","sourceRoot":"","sources":["../../src/format/products.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyB1C;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAoB,EAAE,WAAwB;IAC1E,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/F,OAAO;QACL,
|
|
1
|
+
{"version":3,"file":"products.js","sourceRoot":"","sources":["../../src/format/products.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAyB1C;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,SAAoB,EAAE,WAAwB;IAC1E,MAAM,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAC/F,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,EAAE;QAClB,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI;QACrE,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;QACxC,GAAG,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC;QACrD,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC;QAClD,GAAG,CAAC,IAAI,CAAC,aAAa,KAAK,IAAI;YAC7B,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,YAAY,IAAI;YACxC,QAAQ,EAAE,WAAW,CAAC,IAAI,CAAC,aAAa,EAAE,WAAW,CAAC;SACvD,CAAC;QACJ,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC;QACpD,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QACpC,GAAG,CAAC,SAAS,CAAC,YAAY,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,aAAa,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;KACjD,CAAC;AACJ,CAAC"}
|
package/dist/format/recipes.d.ts
CHANGED
|
@@ -36,5 +36,6 @@ export type RecipeOutput = {
|
|
|
36
36
|
ingredients: RecipeIngredientOutput[];
|
|
37
37
|
steps?: string[];
|
|
38
38
|
};
|
|
39
|
-
|
|
39
|
+
/** A recipe from search results or the saved recipes; both have these fields. */
|
|
40
|
+
export declare function formatRecipeSummary(recipe: Pick<SearchRecipe, "id" | "name" | "subtitle" | "tags">): RecipeSummaryOutput;
|
|
40
41
|
export declare function formatRecipe(recipe: Recipe, countryCode: CountryCode): RecipeOutput;
|
package/dist/format/recipes.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { formatPrice } from "./prices.js";
|
|
2
2
|
/** Steps are cut at this many characters each, to keep the output short. */
|
|
3
3
|
const MAX_STEP = 600;
|
|
4
|
+
/** A recipe from search results or the saved recipes; both have these fields. */
|
|
4
5
|
export function formatRecipeSummary(recipe) {
|
|
5
6
|
return {
|
|
6
7
|
recipeId: recipe.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../src/format/recipes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,4EAA4E;AAC5E,MAAM,QAAQ,GAAG,GAAG,CAAC;AAuCrB,MAAM,UAAU,mBAAmB,
|
|
1
|
+
{"version":3,"file":"recipes.js","sourceRoot":"","sources":["../../src/format/recipes.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,4EAA4E;AAC5E,MAAM,QAAQ,GAAG,GAAG,CAAC;AAuCrB,iFAAiF;AACjF,MAAM,UAAU,mBAAmB,CACjC,MAA+D;IAE/D,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,GAAG,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC;KACrD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,WAAwB;IACnE,OAAO;QACL,QAAQ,EAAE,MAAM,CAAC,EAAE;QACnB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,CAAC,MAAM,CAAC,kBAAkB,KAAK,IAAI,IAAI,EAAE,kBAAkB,EAAE,MAAM,CAAC,kBAAkB,EAAE,CAAC;QAC5F,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;QAC9D,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAC,SAAS,CAAC,UAAU,EAAE;QAC3F,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;YACnD,GAAG,CAAC,UAAU,CAAC,IAAI,IAAI,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC;YACjD,GAAG,CAAC,UAAU,CAAC,aAAa,IAAI,EAAE,SAAS,EAAE,UAAU,CAAC,aAAa,EAAE,CAAC;YACxE,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,GAAG,CAAC,UAAU,CAAC,UAAU,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU,EAAE,CAAC;YAC/D,GAAG,CAAC,UAAU,CAAC,KAAK,KAAK,IAAI,IAAI,EAAE,KAAK,EAAE,WAAW,CAAC,UAAU,CAAC,KAAK,EAAE,WAAW,CAAC,EAAE,CAAC;YACvF,KAAK,EAAE,UAAU,CAAC,OAAO;YACzB,GAAG,CAAC,UAAU,CAAC,IAAI,KAAK,UAAU,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;YACzD,GAAG,CAAC,CAAC,UAAU,CAAC,WAAW,IAAI;gBAC7B,WAAW,EAAE,UAAU,CAAC,iBAAiB,IAAI,eAAe;aAC7D,CAAC;SACH,CAAC,CAAC;QACH,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI;YAC7B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC/B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAC9D;SACF,CAAC;KACH,CAAC;AACJ,CAAC"}
|
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type McpServer } from "@modelcontextprotocol/server";
|
|
2
|
+
/** The only path the server answers on. */
|
|
3
|
+
export declare const MCP_PATH = "/mcp";
|
|
4
|
+
/** The shortest token the server accepts, so a weak token can't be guessed. */
|
|
5
|
+
export declare const MIN_TOKEN_LENGTH = 32;
|
|
6
|
+
export type HttpOptions = {
|
|
7
|
+
/** Clients send it as `Authorization: Bearer <token>`. At least 32 characters. */
|
|
8
|
+
token: string;
|
|
9
|
+
/** Host names allowed in the Host and Origin headers besides localhost, e.g. a reverse proxy's. */
|
|
10
|
+
allowedHosts?: string[];
|
|
11
|
+
/** Called once per request: the MCP server that handles it. */
|
|
12
|
+
createServer: () => McpServer;
|
|
13
|
+
onError?: (error: Error) => void;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The HTTP handler of the MCP endpoint, in fetch terms. Before a request reaches the MCP server it
|
|
17
|
+
* must be for `/mcp`, have an allowed Host and Origin (against DNS rebinding) and carry the token.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createHttpHandler(options: HttpOptions): {
|
|
20
|
+
fetch: (request: Request) => Promise<Response>;
|
|
21
|
+
close: () => Promise<void>;
|
|
22
|
+
};
|
|
23
|
+
export type HttpServerOptions = HttpOptions & {
|
|
24
|
+
host: string;
|
|
25
|
+
port: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Serves the MCP endpoint over HTTP with Node's own server. It doesn't do TLS: when it isn't on
|
|
29
|
+
* localhost, put it behind an HTTPS reverse proxy.
|
|
30
|
+
*/
|
|
31
|
+
export declare function startHttpServer(options: HttpServerOptions): Promise<{
|
|
32
|
+
url: string;
|
|
33
|
+
close: () => Promise<void>;
|
|
34
|
+
}>;
|