@estoreclub/api 0.0.0-stage → 0.1.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.
Files changed (58) hide show
  1. package/dist/bridgeClient.d.ts +10 -0
  2. package/dist/bridgeClient.js +10 -0
  3. package/dist/bridgeClient.js.map +7 -0
  4. package/dist/chunks/chunk-2554RPSS.js +45 -0
  5. package/dist/chunks/chunk-2554RPSS.js.map +7 -0
  6. package/dist/chunks/chunk-2BH4QPV2.js +27 -0
  7. package/dist/chunks/chunk-2BH4QPV2.js.map +7 -0
  8. package/dist/chunks/chunk-3KEEGSMP.js +86 -0
  9. package/dist/chunks/chunk-3KEEGSMP.js.map +7 -0
  10. package/dist/chunks/chunk-5DN7L6LX.js +861 -0
  11. package/dist/chunks/chunk-5DN7L6LX.js.map +7 -0
  12. package/dist/chunks/chunk-DUVULZ6N.js +111 -0
  13. package/dist/chunks/chunk-DUVULZ6N.js.map +7 -0
  14. package/dist/chunks/chunk-GJZ7EZEF.js +25 -0
  15. package/dist/chunks/chunk-GJZ7EZEF.js.map +7 -0
  16. package/dist/chunks/chunk-HFEQWXG6.js +24 -0
  17. package/dist/chunks/chunk-HFEQWXG6.js.map +7 -0
  18. package/dist/chunks/chunk-OFFTL6RC.js +24 -0
  19. package/dist/chunks/chunk-OFFTL6RC.js.map +7 -0
  20. package/dist/chunks/chunk-TSERNR4R.js +15 -0
  21. package/dist/chunks/chunk-TSERNR4R.js.map +7 -0
  22. package/dist/chunks/chunk-ZITPZDI7.js +280 -0
  23. package/dist/chunks/chunk-ZITPZDI7.js.map +7 -0
  24. package/dist/client.d.ts +407 -0
  25. package/dist/client.js +14 -0
  26. package/dist/client.js.map +7 -0
  27. package/dist/companyApiKey.d.ts +9 -0
  28. package/dist/companyApiKey.js +9 -0
  29. package/dist/companyApiKey.js.map +7 -0
  30. package/dist/customerToken.d.ts +18 -0
  31. package/dist/customerToken.js +11 -0
  32. package/dist/customerToken.js.map +7 -0
  33. package/dist/errors.d.ts +41 -0
  34. package/dist/errors.js +9 -0
  35. package/dist/errors.js.map +7 -0
  36. package/dist/index.d.ts +82 -0
  37. package/dist/index.js +166 -0
  38. package/dist/index.js.map +7 -0
  39. package/dist/language.d.ts +27 -0
  40. package/dist/language.js +11 -0
  41. package/dist/language.js.map +7 -0
  42. package/dist/params.d.ts +58 -0
  43. package/dist/params.js +27 -0
  44. package/dist/params.js.map +7 -0
  45. package/dist/requestCache.d.ts +46 -0
  46. package/dist/requestCache.js +9 -0
  47. package/dist/requestCache.js.map +7 -0
  48. package/dist/siteData.d.ts +237 -0
  49. package/dist/siteData.js +132 -0
  50. package/dist/siteData.js.map +7 -0
  51. package/dist/types.d.ts +2123 -0
  52. package/dist/types.js +1 -0
  53. package/dist/types.js.map +7 -0
  54. package/dist/visitorCountry.d.ts +25 -0
  55. package/dist/visitorCountry.js +15 -0
  56. package/dist/visitorCountry.js.map +7 -0
  57. package/package.json +64 -4
  58. package/README.md +0 -3
@@ -0,0 +1,861 @@
1
+ import {
2
+ memoGet
3
+ } from "./chunk-2BH4QPV2.js";
4
+ import {
5
+ normalCountry,
6
+ resolveVisitorCountry
7
+ } from "./chunk-2554RPSS.js";
8
+ import {
9
+ getCompanyApiKey
10
+ } from "./chunk-TSERNR4R.js";
11
+ import {
12
+ resolveCustomerToken
13
+ } from "./chunk-OFFTL6RC.js";
14
+ import {
15
+ PublicApiError,
16
+ refusalFactsOf
17
+ } from "./chunk-GJZ7EZEF.js";
18
+ import {
19
+ resolveLanguage
20
+ } from "./chunk-HFEQWXG6.js";
21
+ import {
22
+ flag,
23
+ idOrName,
24
+ include,
25
+ oneOf,
26
+ paging,
27
+ segment,
28
+ text,
29
+ trimSlash,
30
+ whole
31
+ } from "./chunk-3KEEGSMP.js";
32
+
33
+ // packages/api/src/client.ts
34
+ var COMMENT_MAX_LENGTH = 2e3;
35
+ var CATEGORY_INCLUDES = [
36
+ "translations",
37
+ "media",
38
+ "count"
39
+ ];
40
+ var PRODUCT_INCLUDES = [
41
+ "translations",
42
+ "media",
43
+ "categories",
44
+ "settings"
45
+ ];
46
+ var PRODUCT_SORTS = [
47
+ "custom",
48
+ "name",
49
+ "price",
50
+ "popularity",
51
+ "discount"
52
+ ];
53
+ var DEFAULT_TIMEOUT_MS = 2e3;
54
+ var STRIPE_TIMEOUT_MS = 15e3;
55
+ var MAX_QUANTITY = 99;
56
+ function createPublicApiClient(config) {
57
+ const call = requester(config);
58
+ const send = writer(config);
59
+ const companyRecord = companyResolver(config);
60
+ return {
61
+ getCompany: async () => strip(await companyRecord()),
62
+ getSiteInfo: async () => {
63
+ const company = await companyRecord();
64
+ return {
65
+ siteName: company.name,
66
+ defaultLanguage: company.defaultLanguage,
67
+ languages: company.languages ?? [],
68
+ currency: company.defaultCurrency,
69
+ countryCode: company.countryCode ?? null,
70
+ // Per request, not per company: asked of the render, never of the API.
71
+ visitorCountry: config.visitorCountry === null ? null : normalCountry(config.visitorCountry) ?? resolveVisitorCountry(),
72
+ currencySymbol: company.currencySymbol,
73
+ currencySymbolPosition: company.currencySymbolPosition,
74
+ taxRate: company.taxRate ?? 0,
75
+ taxInclusive: company.taxInclusive ?? true,
76
+ taxName: company.taxName ?? "Tax",
77
+ orderLocation: company.orderLocation ?? "optional",
78
+ orderTypes: company.orderTypes ?? ["pickup"],
79
+ reservationMode: company.reservationMode ?? "off",
80
+ reservationKinds: company.reservationKinds ?? [],
81
+ // Deprecated, for sites written before: no longer sent by the server.
82
+ tableReservations: (company.reservationMode ?? "off") !== "off",
83
+ customerSignIn: true,
84
+ // `apple` is false from a server that predates it.
85
+ customerSignInMethods: {
86
+ apple: false,
87
+ ...company.customerSignInMethods ?? { email: false, google: true }
88
+ },
89
+ customerAccess: company.customerAccess ?? "public",
90
+ reservationRequiresSignIn: company.reservationRequiresSignIn ?? false,
91
+ reservationOrder: company.reservationOrder ?? "off",
92
+ reservationPayment: company.reservationPayment ?? {
93
+ order: false,
94
+ deferred: false
95
+ },
96
+ orderRequiresSignIn: company.orderRequiresSignIn ?? false,
97
+ publicOrderTracking: company.publicOrderTracking ?? false,
98
+ paymentDiscounts: company.paymentDiscounts ?? {
99
+ online: null,
100
+ inStore: null
101
+ },
102
+ googleClientId: company.googleClientId ?? null,
103
+ googleSignInUrl: company.googleSignInUrl ?? null,
104
+ appleClientId: company.appleClientId ?? null,
105
+ appleSignInUrl: company.appleSignInUrl ?? null,
106
+ settings: company.settings
107
+ };
108
+ },
109
+ getCategories: async (params = {}) => {
110
+ const list = await call("product-categories", {
111
+ ...text("search", params.search),
112
+ ...include(params.include, CATEGORY_INCLUDES)
113
+ });
114
+ const limit = Number(params.limit);
115
+ if (!Array.isArray(list)) return [];
116
+ return Number.isFinite(limit) && limit > 0 ? list.slice(0, limit) : list;
117
+ },
118
+ getCategory: async (params) => (
119
+ // The name goes in the QUERY, not the path. It is whatever the shop typed — any
120
+ // language, any script, possibly a slash — and a path segment carries that only as
121
+ // percent-encoded UTF-8 that proxies, logs and humans all handle worse than a query
122
+ // parameter does.
123
+ call("product-categories/by-identifier", {
124
+ ...idOrName(params),
125
+ ...include(params.include, CATEGORY_INCLUDES)
126
+ })
127
+ ),
128
+ getProducts: async (params = {}) => {
129
+ const page = await call("products", {
130
+ ...paging(params),
131
+ ...text("categoryId", params.categoryId),
132
+ ...text("categoryName", params.categoryName),
133
+ ...text("search", params.search),
134
+ ...text("tags", params.tags),
135
+ ...flag("categoryEmpty", params.categoryEmpty),
136
+ ...oneOf("sortBy", params.sortBy, PRODUCT_SORTS),
137
+ ...oneOf("sortOrder", params.sortOrder, ["ASC", "DESC"]),
138
+ ...whole("popularityDays", params.popularityDays, 1, 365),
139
+ ...include(params.include, PRODUCT_INCLUDES)
140
+ });
141
+ return { ...page, data: (page?.data ?? []).map(priced) };
142
+ },
143
+ getProduct: async (params) => priced(
144
+ await call("products/by-identifier", idOrName(params))
145
+ ),
146
+ getProductSettings: async (params = {}) => {
147
+ const answer = await call("products/settings", flag("tagsOnly", params.tagsOnly));
148
+ return Array.isArray(answer?.settings) ? answer.settings : [];
149
+ },
150
+ listDictionaries: async () => {
151
+ const list = await call("dictionaries");
152
+ return Array.isArray(list) ? list : [];
153
+ },
154
+ getTerms: async (params = {}) => call(
155
+ "terms",
156
+ params.language ? { language: params.language } : {}
157
+ ),
158
+ getDiningTable: async ({ tableId }) => call(`tables/${segment(tableId, "tableId")}`),
159
+ verifyDiningTableCode: async ({ tableId, code }) => send("POST", `tables/${segment(tableId, "tableId")}/code`, {}, { code }),
160
+ getLocations: async () => {
161
+ const answer = await call("locations");
162
+ return Array.isArray(answer?.data) ? answer.data : [];
163
+ },
164
+ getCombos: async (params = {}) => {
165
+ const answer = await call("combos", {
166
+ ...params.categoryId ? { categoryId: segment(params.categoryId, "categoryId") } : {}
167
+ });
168
+ return Array.isArray(answer?.data) ? answer.data : [];
169
+ },
170
+ getDiscountRules: async () => {
171
+ const answer = await call(
172
+ "discount-rules"
173
+ );
174
+ return Array.isArray(answer?.data) ? answer.data : [];
175
+ },
176
+ getDictionary: async (params) => call("dictionaries/by-identifier", idOrName(params)),
177
+ getDictionaries: async (params) => params.identifiers.length === 0 ? [] : call("dictionaries/by-identifiers", {
178
+ identifiers: params.identifiers.join(",")
179
+ }),
180
+ lookupImages: async (params) => {
181
+ const urls = Array.from(new Set(params.urls.filter(Boolean))).slice(
182
+ 0,
183
+ 200
184
+ );
185
+ return urls.length === 0 ? [] : send(
186
+ "POST",
187
+ "images/lookup",
188
+ {},
189
+ { urls: urls.sort() }
190
+ );
191
+ },
192
+ getOrderTimes: async (params = {}) => call("checkout/times", {
193
+ ...params.locationId ? { locationId: segment(params.locationId, "locationId") } : {}
194
+ }),
195
+ getBookingSlots: async (params) => call("bookings/slots", {
196
+ locationId: segment(params.locationId, "locationId"),
197
+ date: params.date,
198
+ partySize: String(params.partySize),
199
+ ...params.seating ? { seating: params.seating } : {},
200
+ ...params.timeZone ? { timeZone: params.timeZone } : {},
201
+ ...params.tableId ? { tableId: segment(params.tableId, "tableId") } : {},
202
+ ...params.durationMinutes ? { durationMinutes: String(params.durationMinutes) } : {},
203
+ ...params.entryId ? { entryId: segment(params.entryId, "entryId") } : {},
204
+ ...params.entryIds?.length ? {
205
+ entryIds: params.entryIds.map((id) => segment(id, "entryIds")).join(",")
206
+ } : {},
207
+ ...params.productId ? { productId: segment(params.productId, "productId") } : {},
208
+ ...params.productIds?.length ? {
209
+ productIds: params.productIds.map((id) => segment(id, "productIds")).join(",")
210
+ } : {},
211
+ ...params.kind ? { kind: params.kind } : {}
212
+ }),
213
+ getBookingPlan: async (params) => call("bookings/plan", {
214
+ locationId: segment(params.locationId, "locationId"),
215
+ startsAt: params.startsAt,
216
+ partySize: String(params.partySize),
217
+ ...params.seating ? { seating: params.seating } : {},
218
+ ...params.durationMinutes ? { durationMinutes: String(params.durationMinutes) } : {},
219
+ ...params.kind ? { kind: params.kind } : {}
220
+ }),
221
+ createBooking: async ({ customerToken, ...params }) => send(
222
+ "POST",
223
+ "bookings",
224
+ {},
225
+ params,
226
+ void 0,
227
+ token(customerToken)
228
+ ),
229
+ requestSignInCode: async (params) => send(
230
+ "POST",
231
+ "customers/sign-in/code",
232
+ {},
233
+ { email: requiredEmail(params.email) }
234
+ ),
235
+ verifySignInCode: async (params) => send(
236
+ "POST",
237
+ "customers/sign-in/verify",
238
+ {},
239
+ {
240
+ email: requiredEmail(params.email),
241
+ code: signInCode(params.code)
242
+ }
243
+ ),
244
+ signInWithGoogle: async (params) => send(
245
+ "POST",
246
+ "customers/sign-in/google",
247
+ {},
248
+ { idToken: textOf(params.idToken, "idToken", 4096) }
249
+ ),
250
+ signInWithApple: async (params) => send(
251
+ "POST",
252
+ "customers/sign-in/apple",
253
+ {},
254
+ {
255
+ idToken: textOf(params.idToken, "idToken", 4096),
256
+ ...params.name?.trim() ? { name: textOf(params.name.trim(), "name", 200) } : {}
257
+ }
258
+ ),
259
+ refreshCustomerToken: async (params) => send(
260
+ "POST",
261
+ "customers/token/refresh",
262
+ {},
263
+ { refreshToken: textOf(params.refreshToken, "refreshToken", 128) }
264
+ ),
265
+ signOutCustomer: async (params) => send(
266
+ "POST",
267
+ "customers/sign-out",
268
+ {},
269
+ { refreshToken: textOf(params.refreshToken, "refreshToken", 128) }
270
+ ),
271
+ getCustomerAccount: async (params) => call(
272
+ "customers/me",
273
+ {},
274
+ void 0,
275
+ requiredToken(params.customerToken)
276
+ ),
277
+ getCustomerOrders: async ({ customerToken, page, limit }) => call(
278
+ "customers/me/orders",
279
+ historyQuery(page, limit),
280
+ void 0,
281
+ requiredToken(customerToken)
282
+ ),
283
+ getCustomerOrder: async ({ customerToken, orderId }) => call(
284
+ `customers/me/orders/${segment(orderId, "orderId")}`,
285
+ {},
286
+ void 0,
287
+ requiredToken(customerToken)
288
+ ),
289
+ getCustomerBookings: async ({ customerToken, scope, page, limit }) => call(
290
+ "customers/me/bookings",
291
+ {
292
+ ...historyQuery(page, limit),
293
+ ...oneOf("scope", scope, ["upcoming", "past", "all"])
294
+ },
295
+ void 0,
296
+ requiredToken(customerToken)
297
+ ),
298
+ cancelCustomerBooking: async ({ customerToken, bookingId }) => send(
299
+ "POST",
300
+ `customers/me/bookings/${segment(bookingId, "bookingId")}/cancel`,
301
+ {},
302
+ void 0,
303
+ void 0,
304
+ requiredToken(customerToken)
305
+ ),
306
+ setCustomerBookingPreorder: async ({ customerToken, bookingId, lines }) => send(
307
+ "PUT",
308
+ `customers/me/bookings/${segment(bookingId, "bookingId")}/preorder`,
309
+ {},
310
+ { lines },
311
+ void 0,
312
+ requiredToken(customerToken)
313
+ ),
314
+ getCustomerAddresses: async ({ customerToken }) => call(
315
+ "customers/me/addresses",
316
+ {},
317
+ void 0,
318
+ requiredToken(customerToken)
319
+ ),
320
+ addCustomerAddress: async ({
321
+ customerToken,
322
+ address,
323
+ details,
324
+ latitude,
325
+ longitude,
326
+ postcode
327
+ }) => send(
328
+ "POST",
329
+ "customers/me/addresses",
330
+ {},
331
+ {
332
+ address,
333
+ ...details !== void 0 ? { details } : {},
334
+ ...latitude !== void 0 ? { latitude } : {},
335
+ ...longitude !== void 0 ? { longitude } : {},
336
+ ...postcode !== void 0 ? { postcode } : {}
337
+ },
338
+ void 0,
339
+ requiredToken(customerToken)
340
+ ),
341
+ deleteCustomerAddress: async ({ customerToken, customerAddressId }) => send(
342
+ "DELETE",
343
+ `customers/me/addresses/${segment(customerAddressId, "customerAddressId")}`,
344
+ {},
345
+ void 0,
346
+ void 0,
347
+ requiredToken(customerToken)
348
+ ),
349
+ getCustomerPaymentMethods: async ({ customerToken }) => call(
350
+ "customers/me/payment-methods",
351
+ {},
352
+ void 0,
353
+ requiredToken(customerToken)
354
+ ),
355
+ deleteCustomerPaymentMethod: async ({ customerToken, paymentMethodId }) => send(
356
+ "DELETE",
357
+ `customers/me/payment-methods/${segment(paymentMethodId, "paymentMethodId")}`,
358
+ {},
359
+ void 0,
360
+ void 0,
361
+ requiredToken(customerToken)
362
+ ),
363
+ getCustomerFavourites: async ({ customerToken, ...params }) => {
364
+ const page = await call(
365
+ "customers/me/favourites",
366
+ {
367
+ ...paging(params),
368
+ ...text("categoryId", params.categoryId),
369
+ ...text("categoryName", params.categoryName),
370
+ ...text("search", params.search),
371
+ ...text("tags", params.tags),
372
+ ...oneOf("sortBy", params.sortBy, PRODUCT_SORTS),
373
+ ...oneOf("sortOrder", params.sortOrder, ["ASC", "DESC"]),
374
+ ...include(params.include, PRODUCT_INCLUDES)
375
+ },
376
+ void 0,
377
+ requiredToken(customerToken)
378
+ );
379
+ return { ...page, data: (page?.data ?? []).map(priced) };
380
+ },
381
+ getCustomerFavouriteIds: async ({ customerToken }) => call(
382
+ "customers/me/favourites/ids",
383
+ {},
384
+ void 0,
385
+ requiredToken(customerToken)
386
+ ),
387
+ addCustomerFavourite: async ({ customerToken, productId }) => send(
388
+ "PUT",
389
+ `customers/me/favourites/${segment(productId, "productId")}`,
390
+ {},
391
+ void 0,
392
+ void 0,
393
+ requiredToken(customerToken)
394
+ ),
395
+ removeCustomerFavourite: async ({ customerToken, productId }) => send(
396
+ "DELETE",
397
+ `customers/me/favourites/${segment(productId, "productId")}`,
398
+ {},
399
+ void 0,
400
+ void 0,
401
+ requiredToken(customerToken)
402
+ ),
403
+ updateCustomerProfile: async ({
404
+ customerToken,
405
+ name,
406
+ phone,
407
+ dateOfBirth,
408
+ acceptTerms,
409
+ healthConsent,
410
+ language
411
+ }) => send(
412
+ "PATCH",
413
+ "customers/me",
414
+ {},
415
+ {
416
+ ...name !== void 0 ? { name: name ?? "" } : {},
417
+ ...phone !== void 0 ? { phone: phone ?? "" } : {},
418
+ ...dateOfBirth !== void 0 ? { dateOfBirth: dateOfBirth ?? "" } : {},
419
+ ...acceptTerms === true ? { acceptTerms: true } : {},
420
+ ...typeof healthConsent === "boolean" ? { healthConsent } : {},
421
+ ...language !== void 0 ? { language: language ?? "" } : {}
422
+ },
423
+ void 0,
424
+ requiredToken(customerToken)
425
+ ),
426
+ getProductFeedback: async ({ productId, customerToken }) => call(
427
+ `products/${segment(productId, "productId")}/feedback`,
428
+ {},
429
+ void 0,
430
+ token(customerToken)
431
+ ),
432
+ getProductComments: async ({
433
+ productId,
434
+ parentId,
435
+ page,
436
+ limit,
437
+ customerToken
438
+ }) => call(
439
+ parentId ? `products/${segment(productId, "productId")}/comments/${segment(parentId, "parentId")}/replies` : `products/${segment(productId, "productId")}/comments`,
440
+ historyQuery(page, limit),
441
+ void 0,
442
+ token(customerToken)
443
+ ),
444
+ createProductComment: async ({
445
+ customerToken,
446
+ productId,
447
+ body,
448
+ parentId,
449
+ vote
450
+ }) => send(
451
+ "POST",
452
+ `products/${segment(productId, "productId")}/comments`,
453
+ {},
454
+ {
455
+ body: textOf(body, "body", COMMENT_MAX_LENGTH),
456
+ ...parentId ? { parentId: segment(parentId, "parentId") } : {},
457
+ // A vote rides on a root comment only - a reply is not a review.
458
+ ...vote != null && !parentId ? { vote: voteValue(vote, false) } : {}
459
+ },
460
+ void 0,
461
+ requiredToken(customerToken)
462
+ ),
463
+ deleteProductComment: async ({ customerToken, productId, commentId }) => send(
464
+ "DELETE",
465
+ `products/${segment(productId, "productId")}/comments/${segment(commentId, "commentId")}`,
466
+ {},
467
+ void 0,
468
+ void 0,
469
+ requiredToken(customerToken)
470
+ ),
471
+ rateProduct: async ({ customerToken, productId, value }) => send(
472
+ "PUT",
473
+ `products/${segment(productId, "productId")}/rating`,
474
+ {},
475
+ { value: voteValue(value, false) },
476
+ void 0,
477
+ requiredToken(customerToken)
478
+ ),
479
+ unrateProduct: async ({ customerToken, productId }) => send(
480
+ "DELETE",
481
+ `products/${segment(productId, "productId")}/rating`,
482
+ {},
483
+ void 0,
484
+ void 0,
485
+ requiredToken(customerToken)
486
+ ),
487
+ voteProductComment: async ({
488
+ customerToken,
489
+ productId,
490
+ commentId,
491
+ value
492
+ }) => send(
493
+ "PUT",
494
+ `products/${segment(productId, "productId")}/comments/${segment(commentId, "commentId")}/vote`,
495
+ {},
496
+ { value: voteValue(value, true) },
497
+ void 0,
498
+ requiredToken(customerToken)
499
+ ),
500
+ createCart: async () => send("POST", "cart"),
501
+ getCart: async (params) => call("cart", cart(params.cartId)),
502
+ addCartItem: async (params) => send(
503
+ "POST",
504
+ "cart/items",
505
+ cart(params.cartId),
506
+ params.comboId ? {
507
+ comboId: segment(params.comboId, "comboId"),
508
+ picks: (params.picks ?? []).map(pick),
509
+ quantity: quantity(params.quantity ?? 1, 1)
510
+ } : {
511
+ productId: segment(params.productId, "productId"),
512
+ quantity: quantity(params.quantity ?? 1, 1),
513
+ ...params.variantId ? { variantId: segment(params.variantId, "variantId") } : {},
514
+ variant: typeof params.variant === "string" ? params.variant : ""
515
+ }
516
+ ),
517
+ setCartItemQuantity: async (params) => send(
518
+ "PATCH",
519
+ `cart/items/${segment(params.cartItemId, "cartItemId")}`,
520
+ cart(params.cartId),
521
+ { quantity: quantity(params.quantity, 0) }
522
+ ),
523
+ removeCartItem: async (params) => send(
524
+ "DELETE",
525
+ `cart/items/${segment(params.cartItemId, "cartItemId")}`,
526
+ cart(params.cartId)
527
+ ),
528
+ setCartBooking: async (params) => send("PUT", "cart/booking", cart(params.cartId), {
529
+ bookingId: params.bookingId ? segment(params.bookingId, "bookingId") : null
530
+ }),
531
+ destroyCart: async (params) => {
532
+ await send("DELETE", "cart", cart(params.cartId));
533
+ },
534
+ getCheckout: async (params) => call(
535
+ `hosted-checkout/${segment(params.checkoutId, "checkoutId")}`,
536
+ {},
537
+ // Reads the session back from Stripe, so it waits on the same network the write does.
538
+ STRIPE_TIMEOUT_MS
539
+ ),
540
+ trackOrder: async (params) => call("orders/track", trackQuery(params)),
541
+ setCheckoutEmail: async (params) => {
542
+ const email = customerEmail(params.email).customerEmail;
543
+ if (!email) throw new Error("invalid email");
544
+ return send(
545
+ "PUT",
546
+ `hosted-checkout/${segment(params.checkoutId, "checkoutId")}/email`,
547
+ {},
548
+ { email },
549
+ STRIPE_TIMEOUT_MS
550
+ );
551
+ },
552
+ createCheckout: async (params) => send(
553
+ "POST",
554
+ "checkout",
555
+ {},
556
+ {
557
+ cartId: segment(params.cartId, "cartId"),
558
+ ...customerEmail(params.customerEmail),
559
+ ...params.customerPhone?.trim() ? { customerPhone: params.customerPhone.trim().slice(0, 32) } : {},
560
+ ...params.notes?.trim() ? { notes: params.notes.trim().slice(0, 500) } : {},
561
+ ...returnUrl("successUrl", params.successUrl),
562
+ ...returnUrl("cancelUrl", params.cancelUrl),
563
+ ...params.locationId ? { locationId: segment(params.locationId, "locationId") } : {},
564
+ ...params.orderType ? { orderType: params.orderType } : {},
565
+ ...params.scheduledFor ? { scheduledFor: params.scheduledFor } : {},
566
+ ...params.bookingId ? { bookingId: segment(params.bookingId, "bookingId") } : {},
567
+ ...params.tableId ? { tableId: segment(params.tableId, "tableId") } : {},
568
+ ...params.tableCode ? { tableCode: params.tableCode } : {},
569
+ ...params.allowTimeChoice === false ? { allowTimeChoice: false } : {},
570
+ ...params.allowOrderTypeChoice === false ? { allowOrderTypeChoice: false } : {},
571
+ ...params.allowPaymentMethodChoice === false ? { allowPaymentMethodChoice: false } : {},
572
+ ...params.paymentMethod ? { paymentMethod: params.paymentMethod } : {},
573
+ ...params.promoCode?.trim() ? { promoCode: params.promoCode.trim() } : {},
574
+ // The language the payment page will be written in, and the one the line names are
575
+ // snapshotted in. Defaulted to the language this client is reading in rather than
576
+ // left to each site to remember: a shopper who got this far in Russian has already
577
+ // been answered in Russian by every other call, and a checkout in English after that
578
+ // is a page that changed language at the till.
579
+ ...await checkoutLanguage(config, params.language)
580
+ },
581
+ STRIPE_TIMEOUT_MS,
582
+ token(params.customerToken)
583
+ )
584
+ };
585
+ function cart(cartId) {
586
+ return { cartId: segment(cartId, "cartId") };
587
+ }
588
+ function strip(company) {
589
+ const { apiKey: _key, ...rest } = company;
590
+ return rest;
591
+ }
592
+ }
593
+ function priced(product) {
594
+ const withVariants = product?.variants ? { ...product, variants: product.variants.map(pricedVariant) } : product;
595
+ return pricedPrice(withVariants);
596
+ }
597
+ function pricedVariant(variant) {
598
+ return pricedPrice(variant);
599
+ }
600
+ function pricedPrice(item) {
601
+ const raw = item?.price;
602
+ if (typeof raw === "number") return item;
603
+ const parsed = typeof raw === "string" ? Number(raw) : Number.NaN;
604
+ if (!Number.isFinite(parsed)) {
605
+ if (raw === void 0) return item;
606
+ const { price: _dropped, ...rest } = item;
607
+ return rest;
608
+ }
609
+ return { ...item, price: parsed };
610
+ }
611
+ function keyFor(config) {
612
+ const key = config.companyApiKey ?? getCompanyApiKey();
613
+ if (!key && !config.productSample) {
614
+ throw new PublicApiError("no key - supply companyApiKey");
615
+ }
616
+ return key ?? "";
617
+ }
618
+ function request(config, method, url, key, body, timeoutMs, language, customerToken) {
619
+ const once = () => fetchOnce(
620
+ config,
621
+ method,
622
+ url,
623
+ key,
624
+ body,
625
+ timeoutMs,
626
+ language,
627
+ customerToken
628
+ );
629
+ const isRead = method === "GET" || READ_POSTS.has(resourceOf(url));
630
+ if (isRead) {
631
+ const scope = `${key}\0${config.productSample ?? ""}\0${language ?? ""}\0${customerToken ?? ""}\0${method} ${url}${method === "GET" ? "" : `\0${JSON.stringify(body ?? null)}`}`;
632
+ return memoGet(scope, once);
633
+ }
634
+ return once();
635
+ }
636
+ var READ_POSTS = /* @__PURE__ */ new Set(["images/lookup"]);
637
+ function resourceOf(url) {
638
+ const path = url.split("?")[0];
639
+ const at = path.indexOf("/api/public/");
640
+ return at === -1 ? "" : path.slice(at + "/api/public/".length);
641
+ }
642
+ async function fetchOnce(config, method, url, key, body, timeoutMs, language, customerToken) {
643
+ const doFetch = config.fetchImpl ?? fetch;
644
+ const controller = new AbortController();
645
+ const timer = setTimeout(
646
+ () => controller.abort(),
647
+ Math.max(timeoutMs ?? 0, config.timeoutMs ?? DEFAULT_TIMEOUT_MS)
648
+ );
649
+ try {
650
+ const response = await doFetch(url, {
651
+ method,
652
+ headers: {
653
+ ...key ? { "x-company-api-key": key } : {},
654
+ accept: "application/json",
655
+ ...language ? { "x-language": language } : {},
656
+ // Who is signed in, for the calls that take a customer — see `customerToken`.
657
+ ...customerToken ? { "x-customer-token": customerToken } : {},
658
+ ...config.productSample ? { "x-product-sample": config.productSample } : {},
659
+ ...config.visitor ? {
660
+ "x-visitor-ip": config.visitor.ip,
661
+ "x-api-key": config.visitor.platformKey
662
+ } : {},
663
+ ...body === void 0 ? {} : { "content-type": "application/json" }
664
+ },
665
+ ...body === void 0 ? {} : { body: JSON.stringify(body) },
666
+ cache: config.cache ?? "no-store",
667
+ credentials: "include",
668
+ signal: controller.signal
669
+ });
670
+ if (!response.ok) {
671
+ const { detail, ...refusal } = await refusalOf(response);
672
+ throw new PublicApiError(
673
+ String(response.status),
674
+ response.status,
675
+ detail,
676
+ refusal
677
+ );
678
+ }
679
+ if (response.status === 204) return void 0;
680
+ return await response.json();
681
+ } finally {
682
+ clearTimeout(timer);
683
+ }
684
+ }
685
+ async function refusalOf(response) {
686
+ try {
687
+ const body = await response.json();
688
+ const message = body.message;
689
+ return {
690
+ ...refusalFactsOf(body),
691
+ detail: (Array.isArray(message) ? message[0] : message) ?? void 0
692
+ };
693
+ } catch {
694
+ return {};
695
+ }
696
+ }
697
+ function get(config, url, key) {
698
+ return request(config, "GET", url, key);
699
+ }
700
+ var COMPANY_TTL_MS = 3e4;
701
+ function companyResolver(config) {
702
+ let cachedKey = null;
703
+ let cached = null;
704
+ let cachedAt = 0;
705
+ return function companyRecord() {
706
+ const key = keyFor(config);
707
+ if (cached && cachedKey === key && Date.now() - cachedAt < COMPANY_TTL_MS) {
708
+ return cached;
709
+ }
710
+ const pending = get(
711
+ config,
712
+ `${trimSlash(config.baseUrl)}/api/public/company`,
713
+ key
714
+ );
715
+ pending.catch(() => {
716
+ if (cached === pending) {
717
+ cached = null;
718
+ cachedKey = null;
719
+ }
720
+ });
721
+ cached = pending;
722
+ cachedKey = key;
723
+ cachedAt = Date.now();
724
+ return pending;
725
+ };
726
+ }
727
+ function requester(config) {
728
+ return async function call(resource, query = {}, timeoutMs, customerToken) {
729
+ return dispatch(
730
+ config,
731
+ "GET",
732
+ resource,
733
+ query,
734
+ void 0,
735
+ timeoutMs,
736
+ customerToken
737
+ );
738
+ };
739
+ }
740
+ function writer(config) {
741
+ return async function send(method, resource, query = {}, body, timeoutMs, customerToken) {
742
+ return dispatch(
743
+ config,
744
+ method,
745
+ resource,
746
+ query,
747
+ body,
748
+ timeoutMs,
749
+ customerToken
750
+ );
751
+ };
752
+ }
753
+ async function dispatch(config, method, resource, query, body, timeoutMs, customerToken) {
754
+ const key = keyFor(config);
755
+ const language = config.language === null ? void 0 : config.language ?? await resolveLanguage();
756
+ const search = new URLSearchParams(query).toString();
757
+ const base = `${trimSlash(config.baseUrl)}/api/public/${resource}`;
758
+ return request(
759
+ config,
760
+ method,
761
+ search ? `${base}?${search}` : base,
762
+ key,
763
+ body,
764
+ timeoutMs,
765
+ language,
766
+ customerToken ?? (config.customerToken === null ? void 0 : config.customerToken ?? resolveCustomerToken())
767
+ );
768
+ }
769
+ async function checkoutLanguage(config, wanted) {
770
+ const asked = typeof wanted === "string" ? wanted.trim() : "";
771
+ const language = asked || config.language || (config.language === null ? void 0 : await resolveLanguage());
772
+ return language ? { language } : {};
773
+ }
774
+ function quantity(value, min) {
775
+ const parsed = Math.floor(Number(value));
776
+ if (!Number.isFinite(parsed) || parsed < min) {
777
+ throw new Error("invalid quantity");
778
+ }
779
+ return Math.min(parsed, MAX_QUANTITY);
780
+ }
781
+ function customerEmail(value) {
782
+ if (value === void 0 || value === null || value === "") return {};
783
+ const raw = typeof value === "string" ? value.trim() : "";
784
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(raw) || raw.length > 320) {
785
+ throw new Error("invalid customerEmail");
786
+ }
787
+ return { customerEmail: raw };
788
+ }
789
+ function trackQuery(params) {
790
+ const checkoutId = typeof params?.checkoutId === "string" ? params.checkoutId.trim() : "";
791
+ if (checkoutId) return { checkoutId: segment(checkoutId, "checkoutId") };
792
+ const code = typeof params?.code === "string" ? params.code.trim() : "";
793
+ if (!code || code.length > 16) throw new Error("invalid code");
794
+ let email;
795
+ try {
796
+ email = customerEmail(params?.email).customerEmail;
797
+ } catch {
798
+ email = void 0;
799
+ }
800
+ if (!email) throw new Error("invalid email");
801
+ return { code, email };
802
+ }
803
+ function pick(value) {
804
+ return {
805
+ slotId: segment(value?.slotId, "slotId"),
806
+ productId: segment(value?.productId, "productId"),
807
+ ...value?.variantId ? { variantId: segment(value.variantId, "variantId") } : {},
808
+ ...typeof value?.variant === "string" ? { variant: value.variant } : {}
809
+ };
810
+ }
811
+ function returnUrl(key, value) {
812
+ if (value === void 0 || value === null || value === "") return {};
813
+ if (typeof value !== "string" || !/^https?:\/\/\S+$/.test(value)) {
814
+ throw new Error(`invalid ${key}`);
815
+ }
816
+ return { [key]: value };
817
+ }
818
+ function token(value) {
819
+ if (value === void 0 || value === null || value === "") return void 0;
820
+ return requiredToken(value);
821
+ }
822
+ function requiredToken(value) {
823
+ if (typeof value !== "string" || !value.trim() || value.length > 4096 || /\s/.test(value.trim())) {
824
+ throw new Error("invalid customerToken");
825
+ }
826
+ return value.trim();
827
+ }
828
+ function requiredEmail(value) {
829
+ const email = customerEmail(value).customerEmail;
830
+ if (!email) throw new Error("invalid email");
831
+ return email;
832
+ }
833
+ function signInCode(value) {
834
+ const code = String(value ?? "").replace(/\s/g, "");
835
+ if (!/^\d{6}$/.test(code)) throw new Error("invalid code");
836
+ return code;
837
+ }
838
+ function textOf(value, name, max) {
839
+ if (typeof value !== "string" || !value.trim() || value.length > max) {
840
+ throw new Error(`invalid ${name}`);
841
+ }
842
+ return value.trim();
843
+ }
844
+ function voteValue(value, allowZero) {
845
+ const vote = Number(value);
846
+ if (!Number.isInteger(vote) || vote < -1 || vote > 10 || vote === 0 && !allowZero) {
847
+ throw new Error("invalid vote");
848
+ }
849
+ return vote;
850
+ }
851
+ function historyQuery(page, limit) {
852
+ return {
853
+ ...whole("page", page, 1, 1e5),
854
+ ...whole("limit", limit, 1, 50)
855
+ };
856
+ }
857
+
858
+ export {
859
+ createPublicApiClient
860
+ };
861
+ //# sourceMappingURL=chunk-5DN7L6LX.js.map