basketeer 0.2.0 → 0.3.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 +5 -0
- package/dist/client.d.ts +16 -1
- package/dist/client.js +35 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/models.d.ts +16 -0
- package/dist/nutrition.js +54 -8
- package/dist/operations.d.ts +3 -1
- package/dist/operations.js +4 -0
- package/dist/queries.d.ts +1 -0
- package/dist/queries.js +17 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -196,6 +196,11 @@ await client.orders.cancel(orders[0]!.orderNo);
|
|
|
196
196
|
// "Reorder my usual shop":
|
|
197
197
|
const last = await client.orders.lastFulfilled();
|
|
198
198
|
for (const it of last?.items ?? []) await client.basket.set(it.productId!, it.quantity, it.unit ?? "pcs");
|
|
199
|
+
|
|
200
|
+
// Completed orders, newest first, offset-paged (Tesco has no cursor or total).
|
|
201
|
+
// The result set is LIVE — dedupe by order.id and never persist nextOffset.
|
|
202
|
+
let page = await client.orders.history(); // { orders, nextOffset }
|
|
203
|
+
while (page.nextOffset !== null) page = await client.orders.history({ offset: page.nextOffset });
|
|
199
204
|
```
|
|
200
205
|
|
|
201
206
|
## MCP server (for AI agents)
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AuthBackend, Credentials } from "./auth/types.js";
|
|
2
|
-
import type { Basket, BasketUpdateResult, BookedSlot, NutritionFilter, NutritionSort, Order, Product, SearchPage, Session, Slot } from "./models.js";
|
|
2
|
+
import type { Basket, BasketUpdateResult, BookedSlot, NutritionFilter, NutritionSort, Order, OrderHistoryPage, Product, SearchPage, Session, Slot } from "./models.js";
|
|
3
3
|
import { type FulfilmentType, type OrderContext } from "./operations.js";
|
|
4
4
|
import type { TokenStore } from "./store/types.js";
|
|
5
5
|
export interface BasketeerOptions {
|
|
@@ -18,6 +18,8 @@ export interface BasketeerOptions {
|
|
|
18
18
|
}
|
|
19
19
|
/** Tesco's observed maximum number of aliased product fields in one operation. */
|
|
20
20
|
export declare const MAX_PRODUCT_BATCH_SIZE = 15;
|
|
21
|
+
/** Basketeer's policy cap on `orders.history` page size — not a discovered Tesco limit. */
|
|
22
|
+
export declare const MAX_HISTORY_PAGE_SIZE = 100;
|
|
21
23
|
/** A raw basket line input for the low-level `basket.update`. */
|
|
22
24
|
export interface BasketItemInput {
|
|
23
25
|
id: string;
|
|
@@ -128,6 +130,18 @@ export declare class Basketeer {
|
|
|
128
130
|
list: (opts?: {
|
|
129
131
|
contexts?: readonly OrderContext[];
|
|
130
132
|
}) => Promise<Order[]>;
|
|
133
|
+
/**
|
|
134
|
+
* One page of completed (previous) orders, newest first. `limit` defaults
|
|
135
|
+
* to 25 (max {@link MAX_HISTORY_PAGE_SIZE}). Offset pagination runs over a
|
|
136
|
+
* LIVE result set: follow `nextOffset` within a single traversal, dedupe
|
|
137
|
+
* by `Order.id`, and never persist an offset across runs — a new completed
|
|
138
|
+
* order shifts every later offset.
|
|
139
|
+
*/
|
|
140
|
+
history: (opts?: {
|
|
141
|
+
offset?: number;
|
|
142
|
+
limit?: number;
|
|
143
|
+
contexts?: readonly OrderContext[];
|
|
144
|
+
}) => Promise<OrderHistoryPage>;
|
|
131
145
|
/**
|
|
132
146
|
* Open an order for amendment. Returns an {@link Amendment} handle whose
|
|
133
147
|
* basket ops apply to THAT order; finish by checking out again, or call
|
|
@@ -144,6 +158,7 @@ export declare class Basketeer {
|
|
|
144
158
|
private cancelOrder;
|
|
145
159
|
private lastFulfilledOrder;
|
|
146
160
|
private listOrders;
|
|
161
|
+
private orderHistory;
|
|
147
162
|
private orderAction;
|
|
148
163
|
/**
|
|
149
164
|
* Prepare for payment. Returns the current basket and the URL where payment
|
package/dist/client.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { BasketeerError, ItemUnavailableError, LineRejectedError, NotFoundError, } from "./errors.js";
|
|
2
2
|
import { GraphQLTransport } from "./graphql.js";
|
|
3
3
|
import { filterByNutrition } from "./nutrition.js";
|
|
4
|
-
import { CHECKOUT_URL, FULFILMENT_TYPE, MFE, PENDING_ORDER_CONTEXTS, } from "./operations.js";
|
|
4
|
+
import { CHECKOUT_URL, FULFILMENT_TYPE, MFE, PENDING_ORDER_CONTEXTS, PREVIOUS_ORDER_CONTEXTS, } from "./operations.js";
|
|
5
5
|
import { isoDate, parseBasket, parseBookedSlot, parseOrder, parseProduct, parseProductNode, parseSlot, } from "./parsers.js";
|
|
6
|
-
import { AMEND_ORDER, CANCEL_AMEND, CANCEL_ORDER, COLLECTION_SLOTS, DELIVERY_SLOTS, FULFILMENT, GET_BASKET, GET_CATEGORY_PRODUCTS, GET_FAVOURITES, GET_LAST_FULFILLED_ORDER, GET_PRODUCT, GET_UPCOMING_ORDERS, getProductsQuery, SEARCH, UPDATE_BASKET, } from "./queries.js";
|
|
6
|
+
import { AMEND_ORDER, CANCEL_AMEND, CANCEL_ORDER, COLLECTION_SLOTS, DELIVERY_SLOTS, FULFILMENT, GET_BASKET, GET_CATEGORY_PRODUCTS, GET_FAVOURITES, GET_LAST_FULFILLED_ORDER, GET_ORDER_HISTORY, GET_PRODUCT, GET_UPCOMING_ORDERS, getProductsQuery, SEARCH, UPDATE_BASKET, } from "./queries.js";
|
|
7
7
|
import { SessionManager } from "./session-manager.js";
|
|
8
8
|
/** Tesco's observed maximum number of aliased product fields in one operation. */
|
|
9
9
|
export const MAX_PRODUCT_BATCH_SIZE = 15;
|
|
10
|
+
/** Basketeer's policy cap on `orders.history` page size — not a discovered Tesco limit. */
|
|
11
|
+
export const MAX_HISTORY_PAGE_SIZE = 100;
|
|
10
12
|
/** Guard for numeric SDK inputs. Throws `RangeError` for non-integer or out-of-range values. */
|
|
11
13
|
function reqInt(v, name, min) {
|
|
12
14
|
if (v === undefined)
|
|
@@ -267,6 +269,14 @@ export class Basketeer {
|
|
|
267
269
|
orders = {
|
|
268
270
|
/** List upcoming (pending) orders with their items, slot, and amend window. */
|
|
269
271
|
list: (opts = {}) => this.listOrders(opts),
|
|
272
|
+
/**
|
|
273
|
+
* One page of completed (previous) orders, newest first. `limit` defaults
|
|
274
|
+
* to 25 (max {@link MAX_HISTORY_PAGE_SIZE}). Offset pagination runs over a
|
|
275
|
+
* LIVE result set: follow `nextOffset` within a single traversal, dedupe
|
|
276
|
+
* by `Order.id`, and never persist an offset across runs — a new completed
|
|
277
|
+
* order shifts every later offset.
|
|
278
|
+
*/
|
|
279
|
+
history: (opts = {}) => this.orderHistory(opts),
|
|
270
280
|
/**
|
|
271
281
|
* Open an order for amendment. Returns an {@link Amendment} handle whose
|
|
272
282
|
* basket ops apply to THAT order; finish by checking out again, or call
|
|
@@ -317,6 +327,29 @@ export class Basketeer {
|
|
|
317
327
|
});
|
|
318
328
|
return (data.orderSearch?.orders ?? []).map(parseOrder);
|
|
319
329
|
}
|
|
330
|
+
async orderHistory(opts) {
|
|
331
|
+
// Never forward surprising values: Tesco treats count: 0 as UNBOUNDED and
|
|
332
|
+
// returns an empty (terminal-looking) page for a negative offset.
|
|
333
|
+
reqInt(opts.offset, "offset", 0);
|
|
334
|
+
reqInt(opts.limit, "limit", 1);
|
|
335
|
+
if (opts.limit !== undefined && opts.limit > MAX_HISTORY_PAGE_SIZE)
|
|
336
|
+
throw new RangeError(`limit must be an integer <= ${MAX_HISTORY_PAGE_SIZE}`);
|
|
337
|
+
const offset = opts.offset ?? 0;
|
|
338
|
+
const limit = opts.limit ?? 25;
|
|
339
|
+
const data = await this.transport.execute({
|
|
340
|
+
operationName: "GetOrderHistory",
|
|
341
|
+
query: GET_ORDER_HISTORY,
|
|
342
|
+
variables: { orderContexts: opts.contexts ?? PREVIOUS_ORDER_CONTEXTS, count: limit, offset },
|
|
343
|
+
mfeName: MFE.orders,
|
|
344
|
+
});
|
|
345
|
+
// Strict, unlike list(): a short page is this API's only terminal signal,
|
|
346
|
+
// so a malformed response must not masquerade as one.
|
|
347
|
+
const raw = data.orderSearch?.orders;
|
|
348
|
+
if (!Array.isArray(raw))
|
|
349
|
+
throw new BasketeerError("GetOrderHistory returned no orderSearch.orders array");
|
|
350
|
+
const orders = raw.map(parseOrder);
|
|
351
|
+
return { orders, nextOffset: orders.length < limit ? null : offset + orders.length };
|
|
352
|
+
}
|
|
320
353
|
async orderAction(query, operationName, orderNo) {
|
|
321
354
|
await this.transport.execute({
|
|
322
355
|
operationName,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { keepCookie, sessionFromCookies } from "./auth/harvest.js";
|
|
2
2
|
export type { AuthBackend, Credentials, OtpProvider } from "./auth/types.js";
|
|
3
3
|
export type { Amendment, BasketeerOptions, BasketItemInput } from "./client.js";
|
|
4
|
-
export { Basketeer, MAX_PRODUCT_BATCH_SIZE } from "./client.js";
|
|
4
|
+
export { Basketeer, MAX_HISTORY_PAGE_SIZE, MAX_PRODUCT_BATCH_SIZE } from "./client.js";
|
|
5
5
|
export { ApiKeyError, AuthExpiredError, BasketeerError, GraphQLRequestError, ItemUnavailableError, LineRejectedError, NotFoundError, RateLimitedError, } from "./errors.js";
|
|
6
6
|
export type { ImageSize } from "./images.js";
|
|
7
7
|
export { resizeImageUrl } from "./images.js";
|
|
8
|
-
export type { Basket, BasketLine, BasketUpdateResult, BookedSlot, CatchWeightOption, MacroFilterKey, Macros, Micronutrient, Nutrition, NutritionBasis, NutritionFilter, NutritionSort, Order, OrderAddress, OrderItem, OrderSlot, PackSize, Price, Product, ProductQuantityRules, Promotion, Range, SearchPage, SearchResult, Session, Slot, } from "./models.js";
|
|
8
|
+
export type { Basket, BasketLine, BasketUpdateResult, BookedSlot, CatchWeightOption, MacroFilterKey, Macros, Micronutrient, Nutrition, NutritionBasis, NutritionFilter, NutritionSort, Order, OrderAddress, OrderHistoryPage, OrderItem, OrderSlot, PackSize, Price, Product, ProductQuantityRules, Promotion, Range, SearchPage, SearchResult, Session, Slot, } from "./models.js";
|
|
9
9
|
export { filterByNutrition, parseNutrition } from "./nutrition.js";
|
|
10
10
|
export type { FulfilmentType, OrderContext } from "./operations.js";
|
|
11
|
-
export { CHECKOUT_URL, categoryFacet, ENDPOINT, FULFILMENT_TYPE, PENDING_ORDER_CONTEXTS, PUBLIC_API_KEY, } from "./operations.js";
|
|
11
|
+
export { CHECKOUT_URL, categoryFacet, ENDPOINT, FULFILMENT_TYPE, PENDING_ORDER_CONTEXTS, PREVIOUS_ORDER_CONTEXTS, PUBLIC_API_KEY, } from "./operations.js";
|
|
12
12
|
export { FileTokenStore } from "./store/file-store.js";
|
|
13
13
|
export { MemoryTokenStore } from "./store/memory-store.js";
|
|
14
14
|
export type { TokenStore } from "./store/types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// Public API surface. Everything exported here is part of the semver contract;
|
|
2
2
|
// internal modules (transport, queries, parsers, session-manager) are not.
|
|
3
3
|
export { keepCookie, sessionFromCookies } from "./auth/harvest.js";
|
|
4
|
-
export { Basketeer, MAX_PRODUCT_BATCH_SIZE } from "./client.js";
|
|
4
|
+
export { Basketeer, MAX_HISTORY_PAGE_SIZE, MAX_PRODUCT_BATCH_SIZE } from "./client.js";
|
|
5
5
|
// Errors
|
|
6
6
|
export { ApiKeyError, AuthExpiredError, BasketeerError, GraphQLRequestError, ItemUnavailableError, LineRejectedError, NotFoundError, RateLimitedError, } from "./errors.js";
|
|
7
7
|
export { resizeImageUrl } from "./images.js";
|
|
8
8
|
// Nutrition
|
|
9
9
|
export { filterByNutrition, parseNutrition } from "./nutrition.js";
|
|
10
10
|
// Config & helpers
|
|
11
|
-
export { CHECKOUT_URL, categoryFacet, ENDPOINT, FULFILMENT_TYPE, PENDING_ORDER_CONTEXTS, PUBLIC_API_KEY, } from "./operations.js";
|
|
11
|
+
export { CHECKOUT_URL, categoryFacet, ENDPOINT, FULFILMENT_TYPE, PENDING_ORDER_CONTEXTS, PREVIOUS_ORDER_CONTEXTS, PUBLIC_API_KEY, } from "./operations.js";
|
|
12
12
|
// Storage seam
|
|
13
13
|
export { FileTokenStore } from "./store/file-store.js";
|
|
14
14
|
export { MemoryTokenStore } from "./store/memory-store.js";
|
package/dist/models.d.ts
CHANGED
|
@@ -176,6 +176,22 @@ export interface Order {
|
|
|
176
176
|
items: OrderItem[];
|
|
177
177
|
raw: unknown;
|
|
178
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* One page of completed-order history. Pagination is offset-based over a
|
|
181
|
+
* LIVE, newest-first result set — a new completed order shifts every later
|
|
182
|
+
* offset — so keep `nextOffset` only for the duration of one traversal and
|
|
183
|
+
* deduplicate by `Order.id`.
|
|
184
|
+
*/
|
|
185
|
+
export interface OrderHistoryPage {
|
|
186
|
+
orders: Order[];
|
|
187
|
+
/**
|
|
188
|
+
* Offset for the next page, or null when this page was short (terminal).
|
|
189
|
+
* Non-null only means "another request is needed to know" — Tesco supplies
|
|
190
|
+
* no authoritative has-next-page signal, so when the total is an exact
|
|
191
|
+
* multiple of `limit` the final page is empty.
|
|
192
|
+
*/
|
|
193
|
+
nextOffset: number | null;
|
|
194
|
+
}
|
|
179
195
|
/**
|
|
180
196
|
* An authenticated session. For the browser-minted hybrid (v1), `accessToken`
|
|
181
197
|
* is the harvested `OAuth.AccessToken` bearer and `customerUuid` is the `UUID`
|
package/dist/nutrition.js
CHANGED
|
@@ -40,18 +40,43 @@ function basisFromHeader(v) {
|
|
|
40
40
|
}
|
|
41
41
|
const MACRO_LABELS = {
|
|
42
42
|
fat: "fat",
|
|
43
|
+
"total fat": "fat",
|
|
43
44
|
saturates: "saturates",
|
|
44
45
|
"of which saturates": "saturates",
|
|
46
|
+
"saturated fat": "saturates",
|
|
47
|
+
"of which saturated fat": "saturates",
|
|
45
48
|
carbohydrate: "carbs",
|
|
49
|
+
carbohydrates: "carbs",
|
|
46
50
|
"available carbohydrate": "carbs",
|
|
47
51
|
"total carbohydrate": "carbs",
|
|
48
52
|
sugars: "sugars",
|
|
49
53
|
"of which sugars": "sugars",
|
|
54
|
+
"total sugars": "sugars",
|
|
50
55
|
fibre: "fibre",
|
|
51
56
|
protein: "protein",
|
|
52
57
|
salt: "salt",
|
|
53
58
|
"salt equivalent": "salt",
|
|
54
59
|
};
|
|
60
|
+
/** Trailing unit in a row name: "Fat (g)", "Energy (kcal)", "Calcium (mg)". */
|
|
61
|
+
const UNIT_IN_NAME = /\s*\(\s*(kj|kcal|g|mg|µg|μg)\s*\)\s*$/i;
|
|
62
|
+
/** Drop a leading dash or unbalanced paren: "- Saturates" / "(of which saturates". */
|
|
63
|
+
function cleanName(name) {
|
|
64
|
+
return name
|
|
65
|
+
.trim()
|
|
66
|
+
.replace(/^-\s*(?=[a-z])/i, "")
|
|
67
|
+
.replace(/^\((?![^(]*\))\s*/, "");
|
|
68
|
+
}
|
|
69
|
+
/** Lookup key for a row label: "(of which: sugars*)" / "Protein g" -> "of which sugars" / "protein". */
|
|
70
|
+
function labelKey(name) {
|
|
71
|
+
return name
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.replace(/[*†:,]+/g, " ")
|
|
74
|
+
.trim()
|
|
75
|
+
.replace(/(\s+|\s*\()(g|mg|µg|μg)\)?$/, "")
|
|
76
|
+
.replace(/^\((.*)\)$/, "$1")
|
|
77
|
+
.replace(/\s+/g, " ")
|
|
78
|
+
.trim();
|
|
79
|
+
}
|
|
55
80
|
function isFootnote(name, value) {
|
|
56
81
|
const n = name.trim();
|
|
57
82
|
if (n.startsWith("*"))
|
|
@@ -62,7 +87,7 @@ function isFootnote(name, value) {
|
|
|
62
87
|
return true;
|
|
63
88
|
if (/reference intake|nutrient reference value/i.test(n))
|
|
64
89
|
return true;
|
|
65
|
-
const known = !!MACRO_LABELS[n
|
|
90
|
+
const known = !!MACRO_LABELS[labelKey(n)] || /^energy/i.test(n);
|
|
66
91
|
if (!known && (value === "-" || value === null || value === ""))
|
|
67
92
|
return true;
|
|
68
93
|
return false;
|
|
@@ -76,7 +101,7 @@ export function parseNutrition(rows) {
|
|
|
76
101
|
let expectKcalNext = false;
|
|
77
102
|
for (const r of rows) {
|
|
78
103
|
const row = (r ?? {});
|
|
79
|
-
const name = typeof row.name === "string" ? row.name
|
|
104
|
+
const name = typeof row.name === "string" ? cleanName(row.name) : "";
|
|
80
105
|
// Normalize a decimal comma so the energy kJ/kcal regexes (and num/nrvOf) see "1.5", not "1".
|
|
81
106
|
const value1 = typeof row.value1 === "string" ? deComma(row.value1.trim()) : null;
|
|
82
107
|
if (!name)
|
|
@@ -96,9 +121,18 @@ export function parseNutrition(rows) {
|
|
|
96
121
|
basis = basisFromHeader(value1);
|
|
97
122
|
continue;
|
|
98
123
|
}
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
124
|
+
// Unit in the row name with a bare-number value: "Fat (g)" -> "3.0", "Energy (kcal)" -> "60".
|
|
125
|
+
const unitInName = name.match(UNIT_IN_NAME)?.[1]?.toLowerCase() ?? null;
|
|
126
|
+
const baseName = unitInName ? name.replace(UNIT_IN_NAME, "").trim() : name;
|
|
127
|
+
// Energy: unit in the name, inline "486kJ / 115kcal", or split "257 kJ/"
|
|
128
|
+
if (/^energy/i.test(baseName)) {
|
|
129
|
+
if (value1 && unitInName && !/kj|kcal/i.test(value1)) {
|
|
130
|
+
if (unitInName === "kcal")
|
|
131
|
+
macros.energyKcal = num(value1);
|
|
132
|
+
if (unitInName === "kj")
|
|
133
|
+
macros.energyKj = num(value1);
|
|
134
|
+
}
|
|
135
|
+
else if (value1) {
|
|
102
136
|
const kj = value1.match(/(\d+(?:\.\d+)?)\s*kj/i);
|
|
103
137
|
const kcal = value1.match(/(\d+(?:\.\d+)?)\s*kcal/i);
|
|
104
138
|
if (kj)
|
|
@@ -110,16 +144,28 @@ export function parseNutrition(rows) {
|
|
|
110
144
|
}
|
|
111
145
|
continue;
|
|
112
146
|
}
|
|
113
|
-
|
|
147
|
+
// NRV on its own row named "-": "Calcium" => "124mg", then "-" => "(15.5% RI*)".
|
|
148
|
+
if (name === "-") {
|
|
149
|
+
const prev = micros[micros.length - 1];
|
|
150
|
+
if (prev && prev.nrvPercent == null && value1)
|
|
151
|
+
prev.nrvPercent = nrvOf(value1);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (isFootnote(baseName, value1))
|
|
114
155
|
continue;
|
|
115
|
-
const key = MACRO_LABELS[
|
|
156
|
+
const key = MACRO_LABELS[labelKey(baseName)];
|
|
116
157
|
if (key) {
|
|
117
158
|
macros[key] = num(value1);
|
|
118
159
|
continue;
|
|
119
160
|
}
|
|
120
161
|
// Anything else with a value is a micronutrient.
|
|
121
162
|
if (value1) {
|
|
122
|
-
micros.push({
|
|
163
|
+
micros.push({
|
|
164
|
+
name: baseName,
|
|
165
|
+
amount: num(value1),
|
|
166
|
+
unit: unitOf(value1) ?? unitInName,
|
|
167
|
+
nrvPercent: nrvOf(value1),
|
|
168
|
+
});
|
|
123
169
|
}
|
|
124
170
|
}
|
|
125
171
|
return { basis, macros, micros, raw: rows };
|
package/dist/operations.d.ts
CHANGED
|
@@ -23,13 +23,15 @@ export declare const MFE: {
|
|
|
23
23
|
readonly orders: "mfe-orders";
|
|
24
24
|
readonly favourites: "mfe-favourites";
|
|
25
25
|
};
|
|
26
|
-
/** One context for
|
|
26
|
+
/** One context for the order queries — an order type plus the statuses to include. */
|
|
27
27
|
export interface OrderContext {
|
|
28
28
|
type: string;
|
|
29
29
|
statuses: readonly string[];
|
|
30
30
|
}
|
|
31
31
|
/** Default contexts for listing upcoming (pending) orders across order types. */
|
|
32
32
|
export declare const PENDING_ORDER_CONTEXTS: readonly OrderContext[];
|
|
33
|
+
/** Default contexts for `orders.history` — completed (previous) grocery orders. */
|
|
34
|
+
export declare const PREVIOUS_ORDER_CONTEXTS: readonly OrderContext[];
|
|
33
35
|
/** Fulfilment method types accepted by the slot ops. */
|
|
34
36
|
export declare const FULFILMENT_TYPE: {
|
|
35
37
|
readonly delivery: "DELIVERY_VAN";
|
package/dist/operations.js
CHANGED
|
@@ -29,6 +29,10 @@ export const PENDING_ORDER_CONTEXTS = [
|
|
|
29
29
|
{ type: "MARKETPLACE", statuses: ["Pending"] },
|
|
30
30
|
{ type: "FNF", statuses: ["Pending"] },
|
|
31
31
|
];
|
|
32
|
+
/** Default contexts for `orders.history` — completed (previous) grocery orders. */
|
|
33
|
+
export const PREVIOUS_ORDER_CONTEXTS = [
|
|
34
|
+
{ type: "GROCERY", statuses: ["Previous"] },
|
|
35
|
+
];
|
|
32
36
|
/** Fulfilment method types accepted by the slot ops. */
|
|
33
37
|
export const FULFILMENT_TYPE = {
|
|
34
38
|
delivery: "DELIVERY_VAN",
|
package/dist/queries.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export declare const GET_FAVOURITES: string;
|
|
|
11
11
|
export declare const GET_BASKET: string;
|
|
12
12
|
export declare const UPDATE_BASKET: string;
|
|
13
13
|
export declare const GET_UPCOMING_ORDERS: string;
|
|
14
|
+
export declare const GET_ORDER_HISTORY: string;
|
|
14
15
|
export declare const AMEND_ORDER: string;
|
|
15
16
|
export declare const CANCEL_ORDER: string;
|
|
16
17
|
export declare const CANCEL_AMEND: string;
|
package/dist/queries.js
CHANGED
|
@@ -144,10 +144,7 @@ mutation UpdateBasket($items: [BasketLineItemInputType], $orderId: ID) {
|
|
|
144
144
|
}
|
|
145
145
|
}`.trim();
|
|
146
146
|
// ---- orders ----------------------------------------------------------------
|
|
147
|
-
|
|
148
|
-
query GetUpcomingOrders($orderContexts: [OrderContextType]) {
|
|
149
|
-
orderSearch(orderContexts: $orderContexts) {
|
|
150
|
-
orders {
|
|
147
|
+
const ORDER_FIELDS = `
|
|
151
148
|
id
|
|
152
149
|
orderNo
|
|
153
150
|
status
|
|
@@ -159,7 +156,22 @@ query GetUpcomingOrders($orderContexts: [OrderContextType]) {
|
|
|
159
156
|
paymentMode
|
|
160
157
|
slot { id start end charge }
|
|
161
158
|
address { name city addressLine1 postcode }
|
|
162
|
-
items { id quantity unit weight product { id title } }
|
|
159
|
+
items { id quantity unit weight product { id title } }`;
|
|
160
|
+
export const GET_UPCOMING_ORDERS = `
|
|
161
|
+
query GetUpcomingOrders($orderContexts: [OrderContextType]) {
|
|
162
|
+
orderSearch(orderContexts: $orderContexts) {
|
|
163
|
+
orders {${ORDER_FIELDS}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}`.trim();
|
|
167
|
+
// Offset pagination over a LIVE, newest-first result set. Tesco's orderSearch
|
|
168
|
+
// exposes only count/offset — no cursor, total, or page metadata — so terminal
|
|
169
|
+
// state is inferred from a short page. The schema accepts `page` but ignores
|
|
170
|
+
// it; never send it.
|
|
171
|
+
export const GET_ORDER_HISTORY = `
|
|
172
|
+
query GetOrderHistory($orderContexts: [OrderContextType], $count: Int, $offset: Int) {
|
|
173
|
+
orderSearch(orderContexts: $orderContexts, count: $count, offset: $offset) {
|
|
174
|
+
orders {${ORDER_FIELDS}
|
|
163
175
|
}
|
|
164
176
|
}
|
|
165
177
|
}`.trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "basketeer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Typed, pure-HTTP TypeScript SDK for your own Tesco grocery account — search, basket, delivery slots, and orders over plain fetch. Browser only for login + payment.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|