@zappie/sdk 0.1.1 → 0.1.2

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 (188) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cart/errors.js +13 -0
  3. package/dist/cart/errors.js.map +7 -0
  4. package/dist/cart/index.js +768 -0
  5. package/dist/cart/index.js.map +7 -0
  6. package/dist/cart/item.js +12 -1
  7. package/dist/cart/item.js.map +7 -1
  8. package/dist/cart/preview.js +32 -0
  9. package/dist/cart/preview.js.map +7 -0
  10. package/dist/cart/price.js +25 -0
  11. package/dist/cart/price.js.map +7 -0
  12. package/dist/cart/quote-client.js +76 -0
  13. package/dist/cart/quote-client.js.map +7 -0
  14. package/dist/cart/quote-response.js +178 -0
  15. package/dist/cart/quote-response.js.map +7 -0
  16. package/dist/cart/reconcile.js +108 -0
  17. package/dist/cart/reconcile.js.map +7 -0
  18. package/dist/cart/source-menu.js +21 -0
  19. package/dist/cart/source-menu.js.map +7 -0
  20. package/dist/cart/storage.js +174 -0
  21. package/dist/cart/storage.js.map +7 -0
  22. package/dist/cart/types.js +1 -0
  23. package/dist/cart/types.js.map +7 -0
  24. package/dist/cart/validate.js +265 -0
  25. package/dist/cart/validate.js.map +7 -0
  26. package/dist/cart/wire.js +42 -0
  27. package/dist/cart/wire.js.map +7 -0
  28. package/dist/cart/working-set.js +80 -0
  29. package/dist/cart/working-set.js.map +7 -0
  30. package/dist/checkout/client.js +102 -0
  31. package/dist/checkout/client.js.map +7 -0
  32. package/dist/checkout/error.js +21 -0
  33. package/dist/checkout/error.js.map +7 -0
  34. package/dist/checkout/index.js +13 -1
  35. package/dist/checkout/index.js.map +7 -1
  36. package/dist/checkout/readiness.js +36 -0
  37. package/dist/checkout/readiness.js.map +7 -0
  38. package/dist/checkout/sdk.js +9 -0
  39. package/dist/checkout/sdk.js.map +7 -0
  40. package/dist/checkout/session.js +58 -0
  41. package/dist/checkout/session.js.map +7 -0
  42. package/dist/checkout/types.js +1 -0
  43. package/dist/checkout/types.js.map +7 -0
  44. package/dist/client.js +253 -0
  45. package/dist/client.js.map +7 -0
  46. package/dist/config/index.js +13 -1
  47. package/dist/config/index.js.map +7 -0
  48. package/dist/config.js +28 -0
  49. package/dist/config.js.map +7 -0
  50. package/dist/customer/client.js +149 -0
  51. package/dist/customer/client.js.map +7 -0
  52. package/dist/customer/error.js +20 -0
  53. package/dist/customer/error.js.map +7 -0
  54. package/dist/customer/index.js +8 -3
  55. package/dist/customer/index.js.map +7 -1
  56. package/dist/customer/navigation.js +45 -0
  57. package/dist/customer/navigation.js.map +7 -0
  58. package/dist/customer/react.js +67 -2
  59. package/dist/customer/react.js.map +7 -1
  60. package/dist/customer/request.js +37 -0
  61. package/dist/customer/request.js.map +7 -0
  62. package/dist/customer/sdk.js +6 -0
  63. package/dist/customer/sdk.js.map +7 -0
  64. package/dist/customer/types.js +1 -0
  65. package/dist/customer/types.js.map +7 -0
  66. package/dist/customer/watch-order.js +80 -0
  67. package/dist/customer/watch-order.js.map +7 -0
  68. package/dist/debug.js +78 -1
  69. package/dist/debug.js.map +7 -1
  70. package/dist/errors.js +69 -0
  71. package/dist/errors.js.map +7 -0
  72. package/dist/index.js +29 -1
  73. package/dist/index.js.map +7 -1
  74. package/dist/internal/abort.js +29 -0
  75. package/dist/internal/abort.js.map +7 -0
  76. package/dist/internal/cache.js +142 -0
  77. package/dist/internal/cache.js.map +7 -0
  78. package/dist/internal/customer-http.js +74 -0
  79. package/dist/internal/customer-http.js.map +7 -0
  80. package/dist/internal/digest.js +15 -0
  81. package/dist/internal/digest.js.map +7 -0
  82. package/dist/internal/emitter.js +27 -0
  83. package/dist/internal/emitter.js.map +7 -0
  84. package/dist/internal/money-math.js +30 -0
  85. package/dist/internal/money-math.js.map +7 -0
  86. package/dist/internal.js +9 -1
  87. package/dist/internal.js.map +7 -1
  88. package/dist/models.js +1 -0
  89. package/dist/models.js.map +7 -0
  90. package/dist/money.js +58 -0
  91. package/dist/money.js.map +7 -0
  92. package/dist/persist/catalogue.js +206 -2
  93. package/dist/persist/catalogue.js.map +7 -1
  94. package/dist/places/client.js +97 -0
  95. package/dist/places/client.js.map +7 -0
  96. package/dist/places/error.js +20 -0
  97. package/dist/places/error.js.map +7 -0
  98. package/dist/places/index.js +8 -1
  99. package/dist/places/index.js.map +7 -1
  100. package/dist/places/request.js +40 -0
  101. package/dist/places/request.js.map +7 -0
  102. package/dist/places/sdk.js +10 -0
  103. package/dist/places/sdk.js.map +7 -0
  104. package/dist/places/search.js +80 -0
  105. package/dist/places/search.js.map +7 -0
  106. package/dist/places/types.js +1 -0
  107. package/dist/places/types.js.map +7 -0
  108. package/dist/react/cart.js +30 -0
  109. package/dist/react/cart.js.map +7 -0
  110. package/dist/react/hooks.js +91 -0
  111. package/dist/react/hooks.js.map +7 -0
  112. package/dist/react/index.js +18 -1
  113. package/dist/react/index.js.map +7 -1
  114. package/dist/react/provider.js +34 -0
  115. package/dist/react/provider.js.map +7 -0
  116. package/dist/server/index.js +32 -2
  117. package/dist/server/index.js.map +7 -1
  118. package/dist/server/storefront-url/build-v1.js +75 -0
  119. package/dist/server/storefront-url/build-v1.js.map +7 -0
  120. package/dist/server/storefront-url/build-v2.js +79 -0
  121. package/dist/server/storefront-url/build-v2.js.map +7 -0
  122. package/dist/server/storefront-url/build.js +19 -0
  123. package/dist/server/storefront-url/build.js.map +7 -0
  124. package/dist/server/storefront-url/contract.js +25 -0
  125. package/dist/server/storefront-url/contract.js.map +7 -0
  126. package/dist/server/storefront-url/index.js +16 -0
  127. package/dist/server/storefront-url/index.js.map +7 -0
  128. package/dist/server/storefront-url/installation.js +58 -0
  129. package/dist/server/storefront-url/installation.js.map +7 -0
  130. package/dist/server/storefront-url/params.js +72 -0
  131. package/dist/server/storefront-url/params.js.map +7 -0
  132. package/dist/server/storefront-url/parse-v1.js +135 -0
  133. package/dist/server/storefront-url/parse-v1.js.map +7 -0
  134. package/dist/server/storefront-url/parse-v2.js +139 -0
  135. package/dist/server/storefront-url/parse-v2.js.map +7 -0
  136. package/dist/server/storefront-url/parse.js +9 -0
  137. package/dist/server/storefront-url/parse.js.map +7 -0
  138. package/dist/server/storefront-url/redirect.js +30 -0
  139. package/dist/server/storefront-url/redirect.js.map +7 -0
  140. package/dist/server/storefront-url/sign-in.js +17 -0
  141. package/dist/server/storefront-url/sign-in.js.map +7 -0
  142. package/dist/server/storefront-url/types.js +55 -0
  143. package/dist/server/storefront-url/types.js.map +7 -0
  144. package/dist/server/storefront-url/url.js +61 -0
  145. package/dist/server/storefront-url/url.js.map +7 -0
  146. package/dist/server/webhook.js +96 -0
  147. package/dist/server/webhook.js.map +7 -0
  148. package/dist/site-config/helpers.js +59 -0
  149. package/dist/site-config/helpers.js.map +7 -0
  150. package/dist/site-config/sections.js +105 -0
  151. package/dist/site-config/sections.js.map +7 -0
  152. package/dist/site-config/types.js +31 -0
  153. package/dist/site-config/types.js.map +7 -0
  154. package/dist/site-config/validate.js +165 -0
  155. package/dist/site-config/validate.js.map +7 -0
  156. package/dist/template/index.js +46 -3
  157. package/dist/template/index.js.map +7 -1
  158. package/dist/template/react.js +81 -2
  159. package/dist/template/react.js.map +7 -1
  160. package/dist/testing/index.js +451 -2
  161. package/dist/testing/index.js.map +7 -1
  162. package/dist/transport/category-page.js +21 -2
  163. package/dist/transport/category-page.js.map +7 -1
  164. package/dist/transport/mappers.js +326 -0
  165. package/dist/transport/mappers.js.map +7 -0
  166. package/dist/transport/rest.js +313 -0
  167. package/dist/transport/rest.js.map +7 -0
  168. package/dist/transport/search.js +32 -2
  169. package/dist/transport/search.js.map +7 -1
  170. package/dist/transport/selected-menu.js +79 -2
  171. package/dist/transport/selected-menu.js.map +7 -1
  172. package/dist/transport/store.js +120 -2
  173. package/dist/transport/store.js.map +7 -1
  174. package/dist/transport/types.js +1 -0
  175. package/dist/transport/types.js.map +7 -0
  176. package/dist/url/index.js +16 -2
  177. package/dist/url/index.js.map +7 -1
  178. package/dist/visits/index.js +62 -1
  179. package/dist/visits/index.js.map +7 -1
  180. package/dist/visits/page-views.js +103 -0
  181. package/dist/visits/page-views.js.map +7 -0
  182. package/package.json +3 -3
  183. package/dist/cart/quote.js +0 -1
  184. package/dist/cart/quote.js.map +0 -1
  185. package/dist/chunk-I55A5QXJ.js +0 -2
  186. package/dist/chunk-I55A5QXJ.js.map +0 -1
  187. package/dist/chunk-LFBQICYU.js +0 -2
  188. package/dist/chunk-LFBQICYU.js.map +0 -1
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cart/reconcile.ts"],
4
+ "sourcesContent": ["import { multiplyMoney } from \"../internal/money-math\";\nimport type { Branch, Menu } from \"../models\";\nimport type { Currency } from \"../money\";\nimport { format, money } from \"../money\";\nimport { estimateUnitPrice } from \"./price\";\nimport type { StoredLine } from \"./storage\";\nimport type { CartIssue, CartLine, CartLineInput } from \"./types\";\nimport {\n issue,\n normalizeInput,\n resolveVariant,\n validateLine,\n} from \"./validate\";\nexport function lineInput(line: CartLine): CartLineInput {\n return {\n itemId: line.itemId,\n ...(line.menuId !== undefined ? { menuId: line.menuId } : {}),\n quantity: line.quantity,\n choices: line.choices.map((c) => ({ ...c })),\n inputs: { ...line.inputs },\n ...(line.variantId ? { variantId: line.variantId } : {}),\n ...(line.note !== undefined ? { note: line.note } : {}),\n };\n}\nexport function freezeLine(line: CartLine): CartLine {\n return Object.freeze({\n ...line,\n choices: Object.freeze(line.choices.map((c) => Object.freeze({ ...c }))),\n inputs: Object.freeze({ ...line.inputs }),\n });\n}\nexport function reconcile(\n lines: readonly (CartLine | StoredLine)[],\n menu: Menu,\n branch: Branch,\n currency: Currency,\n previous: readonly CartIssue[] = [],\n) {\n const issues: CartIssue[] = [];\n let priced = true;\n const result = lines.map((old): CartLine => {\n const item = Object.hasOwn(menu.items, old.itemId)\n ? menu.items[old.itemId]\n : undefined;\n const raw = {\n ...old,\n choices: old.choices?.map((c) => ({ ...c })),\n inputs: { ...old.inputs },\n } as CartLineInput;\n const input = item ? normalizeInput(item, raw) : raw;\n issues.push(\n ...validateLine(item, input, { menu, branch }).map((i) => ({\n ...i,\n lineId: old.id,\n })),\n );\n const variantId =\n input.variantId ?? (item ? resolveVariant(item, input)?.id : undefined);\n let unitPrice = money(0, currency);\n let linePriced = true;\n try {\n if (!item) {\n throw new Error(\"Missing item\");\n }\n unitPrice = estimateUnitPrice(item, {\n choices: (input.choices ?? []).map((c) => ({\n ...c,\n quantity: c.quantity ?? 1,\n })),\n ...(variantId ? { variantId } : {}),\n });\n } catch {\n priced = false;\n linePriced = false;\n }\n if (\n old.unitPrice &&\n old.unitPrice.amountMinor !== unitPrice.amountMinor &&\n item &&\n linePriced\n ) {\n issues.push({\n ...issue(\n \"price_changed\",\n `${item.name} is now ${format.money(unitPrice)}`,\n [{ action: \"accept_price\" }, { action: \"remove_line\" }],\n ),\n severity: \"warning\",\n lineId: old.id,\n data: { previous: old.unitPrice, current: unitPrice },\n });\n }\n return freezeLine({\n id: old.id,\n itemId: old.itemId,\n ...(input.menuId !== undefined ? { menuId: input.menuId } : {}),\n name: item?.name ?? old.name,\n quantity: input.quantity ?? 1,\n choices: (input.choices ?? []).map((c) => ({\n ...c,\n quantity: c.quantity ?? 1,\n })),\n inputs: input.inputs ?? {},\n ...(variantId ? { variantId } : {}),\n ...(input.note !== undefined ? { note: input.note } : {}),\n unitPrice,\n lineSubtotal: multiplyMoney(unitPrice, input.quantity ?? 1),\n addedAt: old.addedAt,\n });\n });\n for (const pending of previous) {\n if (\n pending.code === \"price_changed\" &&\n result.some((l) => l.id === pending.lineId) &&\n !issues.some(\n (i) => i.code === pending.code && i.lineId === pending.lineId,\n )\n ) {\n issues.push(pending);\n }\n }\n return { lines: Object.freeze(result), issues, priced };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,21 @@
1
+ // src/cart/source-menu.ts
2
+ function resolveSourceMenu(menu, itemId, menuId) {
3
+ const categories = menu.categories.filter((c) => c.itemIds.includes(itemId));
4
+ const sources = menu.menus ?? [
5
+ { id: menu.id, categoryIds: menu.categories.map((c) => c.id) }
6
+ ];
7
+ const placements = menu.items[itemId]?.placements;
8
+ const candidates = sources.filter(
9
+ (source) => (!placements || placements.some(
10
+ (p) => p.menuId === source.id && source.categoryIds.includes(p.categoryId)
11
+ )) && source.categoryIds.some((id) => categories.some((c) => c.id === id))
12
+ );
13
+ if (menuId !== void 0) {
14
+ return typeof menuId === "string" && menuId.trim().length > 0 && menuId.length <= 256 && candidates.some((source) => source.id === menuId) ? menuId : void 0;
15
+ }
16
+ return candidates.length === 1 ? candidates[0].id : void 0;
17
+ }
18
+ export {
19
+ resolveSourceMenu
20
+ };
21
+ //# sourceMappingURL=source-menu.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cart/source-menu.ts"],
4
+ "sourcesContent": ["import type { Menu } from \"../models\";\n/** Never guess the first of several menus: their schedules may differ. */\nexport function resolveSourceMenu(\n menu: Menu,\n itemId: string,\n menuId?: string,\n): string | undefined {\n const categories = menu.categories.filter((c) => c.itemIds.includes(itemId));\n const sources = menu.menus ?? [\n { id: menu.id, categoryIds: menu.categories.map((c) => c.id) },\n ];\n const placements = menu.items[itemId]?.placements;\n const candidates = sources.filter(\n (source) =>\n (!placements ||\n placements.some(\n (p) =>\n p.menuId === source.id && source.categoryIds.includes(p.categoryId),\n )) &&\n source.categoryIds.some((id) => categories.some((c) => c.id === id)),\n );\n if (menuId !== undefined) {\n return typeof menuId === \"string\" &&\n menuId.trim().length > 0 &&\n menuId.length <= 256 &&\n candidates.some((source) => source.id === menuId)\n ? menuId\n : undefined;\n }\n return candidates.length === 1 ? candidates[0]!.id : undefined;\n}\n"],
5
+ "mappings": ";AAEO,SAAS,kBACd,MACA,QACA,QACoB;AACpB,QAAM,aAAa,KAAK,WAAW,OAAO,CAAC,MAAM,EAAE,QAAQ,SAAS,MAAM,CAAC;AAC3E,QAAM,UAAU,KAAK,SAAS;AAAA,IAC5B,EAAE,IAAI,KAAK,IAAI,aAAa,KAAK,WAAW,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE;AAAA,EAC/D;AACA,QAAM,aAAa,KAAK,MAAM,MAAM,GAAG;AACvC,QAAM,aAAa,QAAQ;AAAA,IACzB,CAAC,YACE,CAAC,cACA,WAAW;AAAA,MACT,CAAC,MACC,EAAE,WAAW,OAAO,MAAM,OAAO,YAAY,SAAS,EAAE,UAAU;AAAA,IACtE,MACF,OAAO,YAAY,KAAK,CAAC,OAAO,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AAAA,EACvE;AACA,MAAI,WAAW,QAAW;AACxB,WAAO,OAAO,WAAW,YACvB,OAAO,KAAK,EAAE,SAAS,KACvB,OAAO,UAAU,OACjB,WAAW,KAAK,CAAC,WAAW,OAAO,OAAO,MAAM,IAC9C,SACA;AAAA,EACN;AACA,SAAO,WAAW,WAAW,IAAI,WAAW,CAAC,EAAG,KAAK;AACvD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,174 @@
1
+ // src/cart/storage.ts
2
+ import { money } from "../money.js";
3
+ function storedMoney(value) {
4
+ try {
5
+ return value && money(value.amountMinor, value.currency);
6
+ } catch {
7
+ return void 0;
8
+ }
9
+ }
10
+ function parseCart(raw) {
11
+ const v = JSON.parse(raw);
12
+ if (!v || v.v !== 1 || !Array.isArray(v.lines) || v.lines.length > 100 || !Number.isFinite(v.updatedAt) || typeof v.menuVersion !== "string" || v.promoCode !== null && typeof v.promoCode !== "string") {
13
+ throw new Error("Invalid saved cart");
14
+ }
15
+ const ids = /* @__PURE__ */ new Set();
16
+ for (const line of v.lines) {
17
+ if (!line || typeof line.id !== "string" || ids.has(line.id) || typeof line.itemId !== "string" || typeof line.name !== "string" || !Number.isFinite(Date.parse(line.addedAt)) || !Number.isInteger(line.quantity) || line.quantity < 1 || line.quantity > 99 || line.note !== void 0 && (typeof line.note !== "string" || line.note.length > 280) || line.menuId !== void 0 && (typeof line.menuId !== "string" || !line.menuId.trim() || line.menuId.length > 256) || line.variantId !== void 0 && typeof line.variantId !== "string") {
18
+ throw new Error("Invalid saved cart line");
19
+ }
20
+ ids.add(line.id);
21
+ if (!Array.isArray(line.choices) || line.choices.length > 1e3 || line.choices.some(
22
+ (c) => !c || typeof c.groupId !== "string" || typeof c.choiceId !== "string" || !Number.isInteger(c.quantity) || c.quantity < 1 || c.quantity > 99
23
+ )) {
24
+ throw new Error("Invalid saved choices");
25
+ }
26
+ if (!line.inputs || typeof line.inputs !== "object" || Array.isArray(line.inputs) || Object.values(line.inputs).some((t) => typeof t !== "string")) {
27
+ throw new Error("Invalid saved inputs");
28
+ }
29
+ }
30
+ if (v.fulfilment !== null && !validFulfilment(v.fulfilment)) {
31
+ throw new Error("Invalid saved fulfilment");
32
+ }
33
+ return {
34
+ v: 1,
35
+ lines: v.lines.map((l) => {
36
+ const unitPrice = storedMoney(l.unitPrice);
37
+ return {
38
+ id: l.id,
39
+ itemId: l.itemId,
40
+ ...l.menuId !== void 0 ? { menuId: l.menuId } : {},
41
+ name: l.name,
42
+ addedAt: l.addedAt,
43
+ ...unitPrice ? { unitPrice } : {},
44
+ quantity: l.quantity,
45
+ choices: l.choices,
46
+ inputs: l.inputs,
47
+ ...l.variantId ? { variantId: l.variantId } : {},
48
+ ...l.note !== void 0 ? { note: l.note } : {}
49
+ };
50
+ }),
51
+ fulfilment: v.fulfilment,
52
+ promoCode: v.promoCode,
53
+ menuVersion: v.menuVersion,
54
+ updatedAt: v.updatedAt
55
+ };
56
+ }
57
+ function validFulfilment(f) {
58
+ if (!f || !["pickup", "delivery", "dine_in"].includes(f.type) || typeof f.branchId !== "string") {
59
+ return false;
60
+ }
61
+ if ("scheduledFor" in f && f.scheduledFor !== void 0 && !Number.isFinite(Date.parse(f.scheduledFor))) {
62
+ return false;
63
+ }
64
+ if (f.type === "delivery") {
65
+ const a = f.address;
66
+ if (!a || typeof a.line1 !== "string" || !a.line1.trim()) {
67
+ return false;
68
+ }
69
+ if ([a.line2, a.city, a.placeId, a.instructions].some(
70
+ (s) => s !== void 0 && typeof s !== "string"
71
+ )) {
72
+ return false;
73
+ }
74
+ if (a.coordinates && (!Number.isFinite(a.coordinates.lat) || !Number.isFinite(a.coordinates.lng) || Math.abs(a.coordinates.lat) > 90 || Math.abs(a.coordinates.lng) > 180)) {
75
+ return false;
76
+ }
77
+ }
78
+ return f.type !== "dine_in" || f.table === void 0 || typeof f.table === "string";
79
+ }
80
+ function createCartStorage(provided, warn) {
81
+ let storage;
82
+ let initialized = false;
83
+ let failed = false;
84
+ let warned = false;
85
+ const memory = /* @__PURE__ */ new Map();
86
+ const warning = (message) => {
87
+ if (!warned) {
88
+ warned = true;
89
+ warn(message);
90
+ }
91
+ };
92
+ const access = (fn, fallback) => {
93
+ try {
94
+ if (!initialized) {
95
+ initialized = true;
96
+ storage = provided ?? (typeof window === "undefined" ? void 0 : window.localStorage);
97
+ }
98
+ if (storage && !failed) {
99
+ return fn(storage);
100
+ }
101
+ } catch {
102
+ failed = true;
103
+ warning("Cart storage unavailable; using memory for this client");
104
+ }
105
+ return fallback();
106
+ };
107
+ return {
108
+ warning,
109
+ read(key) {
110
+ return access(
111
+ (s) => {
112
+ const value = s.getItem(key);
113
+ if (value !== null) {
114
+ memory.set(key, value);
115
+ } else {
116
+ memory.delete(key);
117
+ }
118
+ return value;
119
+ },
120
+ () => memory.get(key) ?? null
121
+ );
122
+ },
123
+ write(key, value) {
124
+ memory.set(key, value);
125
+ access(
126
+ (s) => s.setItem(key, value),
127
+ () => {
128
+ }
129
+ );
130
+ },
131
+ remove(key) {
132
+ memory.delete(key);
133
+ access(
134
+ (s) => s.removeItem(key),
135
+ () => {
136
+ }
137
+ );
138
+ },
139
+ accepts(area) {
140
+ return !failed && (!area || area === storage);
141
+ },
142
+ sweep(prefix, current, ttl) {
143
+ access(
144
+ (s) => {
145
+ const keys = Array.from(
146
+ { length: s.length ?? 0 },
147
+ (_, index) => s.key?.(index)
148
+ );
149
+ for (const key of keys) {
150
+ if (!key || key === current || !key.startsWith(prefix)) {
151
+ continue;
152
+ }
153
+ try {
154
+ const raw = s.getItem(key);
155
+ if (raw && Date.now() - parseCart(raw).updatedAt > ttl) {
156
+ s.removeItem(key);
157
+ }
158
+ } catch {
159
+ s.removeItem(key);
160
+ }
161
+ }
162
+ },
163
+ () => {
164
+ }
165
+ );
166
+ }
167
+ };
168
+ }
169
+ export {
170
+ createCartStorage,
171
+ parseCart,
172
+ validFulfilment
173
+ };
174
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cart/storage.ts"],
4
+ "sourcesContent": ["import type { StorageLike } from \"../config\";\nimport type { Money } from \"../money\";\nimport { money } from \"../money\";\nimport type { CartFulfilment, CartLineInput } from \"./types\";\nexport type StoredLine = CartLineInput & {\n id: string;\n name: string;\n addedAt: string;\n unitPrice?: Money;\n};\nexport type StoredCart = {\n v: 1;\n lines: StoredLine[];\n fulfilment: CartFulfilment | null;\n promoCode: string | null;\n menuVersion: string;\n updatedAt: number;\n};\nfunction storedMoney(value: Money | undefined): Money | undefined {\n try {\n return value && money(value.amountMinor, value.currency);\n } catch {\n return undefined;\n }\n}\nexport function parseCart(raw: string): StoredCart {\n const v = JSON.parse(raw) as StoredCart;\n if (\n !v ||\n v.v !== 1 ||\n !Array.isArray(v.lines) ||\n v.lines.length > 100 ||\n !Number.isFinite(v.updatedAt) ||\n typeof v.menuVersion !== \"string\" ||\n (v.promoCode !== null && typeof v.promoCode !== \"string\")\n ) {\n throw new Error(\"Invalid saved cart\");\n }\n const ids = new Set<string>();\n for (const line of v.lines) {\n if (\n !line ||\n typeof line.id !== \"string\" ||\n ids.has(line.id) ||\n typeof line.itemId !== \"string\" ||\n typeof line.name !== \"string\" ||\n !Number.isFinite(Date.parse(line.addedAt)) ||\n !Number.isInteger(line.quantity) ||\n line.quantity! < 1 ||\n line.quantity! > 99 ||\n (line.note !== undefined &&\n (typeof line.note !== \"string\" || line.note.length > 280)) ||\n (line.menuId !== undefined &&\n (typeof line.menuId !== \"string\" ||\n !line.menuId.trim() ||\n line.menuId.length > 256)) ||\n (line.variantId !== undefined && typeof line.variantId !== \"string\")\n ) {\n throw new Error(\"Invalid saved cart line\");\n }\n ids.add(line.id);\n if (\n !Array.isArray(line.choices) ||\n line.choices.length > 1000 ||\n line.choices.some(\n (c) =>\n !c ||\n typeof c.groupId !== \"string\" ||\n typeof c.choiceId !== \"string\" ||\n !Number.isInteger(c.quantity) ||\n c.quantity! < 1 ||\n c.quantity! > 99,\n )\n ) {\n throw new Error(\"Invalid saved choices\");\n }\n if (\n !line.inputs ||\n typeof line.inputs !== \"object\" ||\n Array.isArray(line.inputs) ||\n Object.values(line.inputs).some((t) => typeof t !== \"string\")\n ) {\n throw new Error(\"Invalid saved inputs\");\n }\n }\n if (v.fulfilment !== null && !validFulfilment(v.fulfilment)) {\n throw new Error(\"Invalid saved fulfilment\");\n }\n return {\n v: 1,\n lines: v.lines.map((l) => {\n const unitPrice = storedMoney(l.unitPrice);\n return {\n id: l.id,\n itemId: l.itemId,\n ...(l.menuId !== undefined ? { menuId: l.menuId } : {}),\n name: l.name,\n addedAt: l.addedAt,\n ...(unitPrice ? { unitPrice } : {}),\n quantity: l.quantity!,\n choices: l.choices!,\n inputs: l.inputs!,\n ...(l.variantId ? { variantId: l.variantId } : {}),\n ...(l.note !== undefined ? { note: l.note } : {}),\n };\n }),\n fulfilment: v.fulfilment,\n promoCode: v.promoCode,\n menuVersion: v.menuVersion,\n updatedAt: v.updatedAt,\n };\n}\nexport function validFulfilment(f: CartFulfilment): boolean {\n if (\n !f ||\n ![\"pickup\", \"delivery\", \"dine_in\"].includes(f.type) ||\n typeof f.branchId !== \"string\"\n ) {\n return false;\n }\n if (\n \"scheduledFor\" in f &&\n f.scheduledFor !== undefined &&\n !Number.isFinite(Date.parse(f.scheduledFor))\n ) {\n return false;\n }\n if (f.type === \"delivery\") {\n const a = f.address;\n if (!a || typeof a.line1 !== \"string\" || !a.line1.trim()) {\n return false;\n }\n if (\n [a.line2, a.city, a.placeId, a.instructions].some(\n (s) => s !== undefined && typeof s !== \"string\",\n )\n ) {\n return false;\n }\n if (\n a.coordinates &&\n (!Number.isFinite(a.coordinates.lat) ||\n !Number.isFinite(a.coordinates.lng) ||\n Math.abs(a.coordinates.lat) > 90 ||\n Math.abs(a.coordinates.lng) > 180)\n ) {\n return false;\n }\n }\n return (\n f.type !== \"dine_in\" || f.table === undefined || typeof f.table === \"string\"\n );\n}\n/** Lazy access: constructing a client or rendering it never reads browser storage. */\nexport function createCartStorage(\n provided: StorageLike | undefined,\n warn: (message: string) => void,\n) {\n let storage: StorageLike | undefined;\n let initialized = false;\n let failed = false;\n let warned = false;\n const memory = new Map<string, string>();\n const warning = (message: string) => {\n if (!warned) {\n warned = true;\n warn(message);\n }\n };\n const access = <T>(fn: (storage: StorageLike) => T, fallback: () => T): T => {\n try {\n if (!initialized) {\n initialized = true;\n storage =\n provided ??\n (typeof window === \"undefined\" ? undefined : window.localStorage);\n }\n if (storage && !failed) {\n return fn(storage);\n }\n } catch {\n failed = true;\n warning(\"Cart storage unavailable; using memory for this client\");\n }\n return fallback();\n };\n return {\n warning,\n read(key: string): string | null {\n return access(\n (s) => {\n const value = s.getItem(key);\n if (value !== null) {\n memory.set(key, value);\n } else {\n memory.delete(key);\n }\n return value;\n },\n () => memory.get(key) ?? null,\n );\n },\n write(key: string, value: string) {\n memory.set(key, value);\n access(\n (s) => s.setItem(key, value),\n () => {},\n );\n },\n remove(key: string) {\n memory.delete(key);\n access(\n (s) => s.removeItem(key),\n () => {},\n );\n },\n accepts(area: Storage | null) {\n return !failed && (!area || area === storage);\n },\n sweep(prefix: string, current: string, ttl: number) {\n access(\n (s) => {\n const keys = Array.from({ length: s.length ?? 0 }, (_, index) =>\n s.key?.(index),\n );\n for (const key of keys) {\n if (!key || key === current || !key.startsWith(prefix)) {\n continue;\n }\n try {\n const raw = s.getItem(key);\n if (raw && Date.now() - parseCart(raw).updatedAt > ttl) {\n s.removeItem(key);\n }\n } catch {\n // A malformed sibling belongs to this cart namespace and cannot be restored.\n s.removeItem(key);\n }\n }\n },\n () => {},\n );\n },\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,265 @@
1
+ // src/cart/validate.ts
2
+ import { resolveSourceMenu } from "./source-menu.js";
3
+ function issue(code, message, fixes = [], groupId) {
4
+ return {
5
+ code,
6
+ severity: "blocking",
7
+ message,
8
+ fixes,
9
+ source: "local",
10
+ ...groupId ? { groupId } : {}
11
+ };
12
+ }
13
+ function effectiveGroups(item, input) {
14
+ const selected = /* @__PURE__ */ new Map();
15
+ for (const choice of input.choices ?? []) {
16
+ const ids = selected.get(choice.groupId) ?? /* @__PURE__ */ new Set();
17
+ ids.add(choice.choiceId);
18
+ selected.set(choice.groupId, ids);
19
+ }
20
+ return item.optionGroups.map((group) => {
21
+ let required = group.required;
22
+ let { min, max } = group.selection;
23
+ for (const rule of group.conditions ?? []) {
24
+ const chosen = selected.get(rule.groupId) ?? /* @__PURE__ */ new Set();
25
+ const matches = rule.count ? rule.count.op === "eq" ? chosen.size === rule.count.value : rule.count.op === "gte" ? chosen.size >= rule.count.value : chosen.size <= rule.count.value : chosen.size > 0 && rule.choiceIds.length > 0 && (rule.match === "all" ? rule.choiceIds.every((id) => chosen.has(id)) : rule.choiceIds.some((id) => chosen.has(id)));
26
+ if (!matches) {
27
+ continue;
28
+ }
29
+ required = rule.setRequired ?? required;
30
+ min = rule.setMin ?? min;
31
+ if (rule.setMax != null) {
32
+ max = rule.setMax > 0 ? rule.setMax : void 0;
33
+ }
34
+ }
35
+ return {
36
+ ...group,
37
+ required,
38
+ selection: { min, ...max !== void 0 ? { max } : {} }
39
+ };
40
+ });
41
+ }
42
+ function normalizeInput(item, input) {
43
+ const inactive = new Set(
44
+ item.optionGroups.filter((g) => g.isAvailable === false).map((g) => g.id)
45
+ );
46
+ return {
47
+ ...input,
48
+ quantity: input.quantity ?? item.quantity.min,
49
+ choices: (input.choices ?? []).filter((c) => !inactive.has(c.groupId)).map((c) => ({ ...c, quantity: c.quantity ?? 1 })),
50
+ inputs: Object.fromEntries(
51
+ Object.entries(input.inputs ?? {}).filter(([id]) => !inactive.has(id))
52
+ )
53
+ };
54
+ }
55
+ function resolveVariant(item, input) {
56
+ const attrs = item.optionGroups.filter((g) => g.isAttribute);
57
+ const matches = item.variants.filter(
58
+ (variant) => (!input.variantId || variant.id === input.variantId) && attrs.every((group) => {
59
+ const selected = input.choices?.filter((c) => c.groupId === group.id) ?? [];
60
+ return selected.length === 1 && variant.attributeChoices[group.id] === selected[0].choiceId;
61
+ })
62
+ );
63
+ return matches.length === 1 ? matches[0] : void 0;
64
+ }
65
+ function validInput(kind, value) {
66
+ switch (kind) {
67
+ case "number":
68
+ return Number.isFinite(Number(value));
69
+ case "date":
70
+ return /^\d{4}-\d{2}-\d{2}$/.test(value) && Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value;
71
+ case "time":
72
+ return /^([01]\d|2[0-3]):[0-5]\d(:[0-5]\d)?$/.test(value);
73
+ case "toggle":
74
+ return ["true", "false"].includes(value.toLowerCase());
75
+ default:
76
+ return true;
77
+ }
78
+ }
79
+ function validateLine(item, raw, ctx) {
80
+ if (!item || item.id !== raw.itemId || !Object.hasOwn(ctx.menu.items, raw.itemId)) {
81
+ return [
82
+ issue("item_unavailable", "This item is no longer on the menu", [
83
+ { action: "remove_line" }
84
+ ])
85
+ ];
86
+ }
87
+ const issues = [];
88
+ if (!resolveSourceMenu(ctx.menu, raw.itemId, raw.menuId)) {
89
+ issues.push(
90
+ issue(
91
+ "item_unavailable",
92
+ raw.menuId === void 0 ? `${item.name} is on more than one menu. Remove it and add it again from the menu you want.` : `${item.name} is no longer on the menu it was added from.`,
93
+ [{ action: "remove_line" }]
94
+ )
95
+ );
96
+ }
97
+ if (!item.isAvailable) {
98
+ issues.push(
99
+ issue("item_unavailable", `${item.name} is unavailable`, [
100
+ { action: "remove_line" }
101
+ ])
102
+ );
103
+ }
104
+ const input = normalizeInput(item, raw);
105
+ const quantity = input.quantity;
106
+ const { min, step } = item.quantity;
107
+ const max = Math.min(item.quantity.max ?? 99, 99);
108
+ if (!Number.isInteger(quantity) || quantity < min || quantity > max || quantity % step !== 0) {
109
+ const lower = Math.ceil(min / step) * step;
110
+ const upper = Math.floor(max / step) * step;
111
+ const nearest = Math.max(
112
+ lower,
113
+ Math.min(
114
+ upper,
115
+ Math.round((Number.isFinite(quantity) ? quantity : min) / step) * step
116
+ )
117
+ );
118
+ issues.push(
119
+ issue(
120
+ "quantity_limit",
121
+ `Choose a valid quantity of ${item.name} (${min}\u2013${max}, step ${step})`,
122
+ lower <= upper ? [{ action: "set_quantity", quantity: nearest }] : []
123
+ )
124
+ );
125
+ }
126
+ if ((input.note?.length ?? 0) > 280) {
127
+ issues.push(issue("option_limit", "Notes must be at most 280 characters"));
128
+ }
129
+ for (const choice of input.choices ?? []) {
130
+ if (!item.optionGroups.some((g) => g.id === choice.groupId)) {
131
+ issues.push(
132
+ issue(
133
+ "option_unavailable",
134
+ "This option group no longer exists",
135
+ [{ action: "remove_line" }],
136
+ choice.groupId
137
+ )
138
+ );
139
+ }
140
+ }
141
+ for (const id of Object.keys(input.inputs ?? {})) {
142
+ if (!item.optionGroups.some((g) => g.id === id)) {
143
+ issues.push(
144
+ issue(
145
+ "option_unavailable",
146
+ "This input group no longer exists",
147
+ [{ action: "remove_line" }],
148
+ id
149
+ )
150
+ );
151
+ }
152
+ }
153
+ const groups = effectiveGroups(item, input);
154
+ const missing = groups.filter(
155
+ (g) => g.isAvailable !== false && g.required && !(input.choices ?? []).some((c) => c.groupId === g.id) && !(input.inputs?.[g.id] ?? "").trim()
156
+ );
157
+ if (missing.length) {
158
+ return [
159
+ ...issues,
160
+ ...missing.map(
161
+ (g) => issue(
162
+ "option_required",
163
+ `Choose ${g.name}`,
164
+ [{ action: "choose_option", groupId: g.id }],
165
+ g.id
166
+ )
167
+ )
168
+ ];
169
+ }
170
+ for (const group of groups) {
171
+ if (group.isAvailable === false) {
172
+ continue;
173
+ }
174
+ const choices = input.choices?.filter((c) => c.groupId === group.id) ?? [];
175
+ const text = input.inputs?.[group.id] ?? "";
176
+ const value = text.trim();
177
+ const hybrid = group.isInformational && group.kind === "select";
178
+ const isInput = group.kind !== "select" || hybrid && !group.choices.length;
179
+ const fix = [{ action: "choose_option", groupId: group.id }];
180
+ if (group.required && !choices.length && !value) {
181
+ issues.push(
182
+ issue("option_required", `Choose ${group.name}`, fix, group.id)
183
+ );
184
+ }
185
+ if (value && (!isInput && !hybrid || choices.length > 0)) {
186
+ issues.push(
187
+ issue(
188
+ "option_limit",
189
+ `Choose either an option or a response for ${group.name}`,
190
+ fix,
191
+ group.id
192
+ )
193
+ );
194
+ }
195
+ if ((isInput || hybrid) && (text.length > (group.input?.maxLength ?? 500) || value && !validInput(group.kind, value))) {
196
+ issues.push(
197
+ issue(
198
+ "option_limit",
199
+ `Enter a valid response for ${group.name}`,
200
+ fix,
201
+ group.id
202
+ )
203
+ );
204
+ }
205
+ const count = choices.reduce((sum, c) => sum + (c.quantity ?? 1), 0);
206
+ if (choices.length && (isInput || count < group.selection.min || count > (group.selection.max ?? Infinity) || new Set(choices.map((c) => c.choiceId)).size !== choices.length)) {
207
+ issues.push(
208
+ issue(
209
+ "option_limit",
210
+ `Check the number of options for ${group.name}`,
211
+ fix,
212
+ group.id
213
+ )
214
+ );
215
+ }
216
+ for (const selected of choices) {
217
+ const choice = group.choices.find((c) => c.id === selected.choiceId);
218
+ if (!choice?.isAvailable) {
219
+ issues.push(
220
+ issue(
221
+ "option_unavailable",
222
+ `An option in ${group.name} is unavailable`,
223
+ [
224
+ { action: "choose_other_option", groupId: group.id },
225
+ { action: "remove_line" }
226
+ ],
227
+ group.id
228
+ )
229
+ );
230
+ }
231
+ const n = selected.quantity ?? 1;
232
+ if (!Number.isInteger(n) || n < (choice?.quantity?.min ?? 1) || n > (choice?.quantity?.max ?? 99)) {
233
+ issues.push(
234
+ issue(
235
+ "option_limit",
236
+ `Check the option quantity for ${group.name}`,
237
+ fix,
238
+ group.id
239
+ )
240
+ );
241
+ }
242
+ }
243
+ }
244
+ const variant = resolveVariant(item, input);
245
+ if (!variant?.isAvailable) {
246
+ const groupId = item.optionGroups.find((g) => g.isAttribute)?.id;
247
+ issues.push(
248
+ issue(
249
+ "option_limit",
250
+ "Choose an available variant",
251
+ groupId ? [{ action: "choose_option", groupId }] : [{ action: "remove_line" }],
252
+ groupId
253
+ )
254
+ );
255
+ }
256
+ return issues;
257
+ }
258
+ export {
259
+ effectiveGroups,
260
+ issue,
261
+ normalizeInput,
262
+ resolveVariant,
263
+ validateLine
264
+ };
265
+ //# sourceMappingURL=validate.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cart/validate.ts"],
4
+ "sourcesContent": ["import type { Branch, Item, Menu, OptionGroup } from \"../models\";\nimport { resolveSourceMenu } from \"./source-menu\";\nimport type { CartFix, CartIssue, CartIssueCode, CartLineInput } from \"./types\";\nexport function issue(\n code: CartIssueCode,\n message: string,\n fixes: CartFix[] = [],\n groupId?: string,\n): CartIssue {\n return {\n code,\n severity: \"blocking\",\n message,\n fixes,\n source: \"local\",\n ...(groupId ? { groupId } : {}),\n };\n}\n/** Conditions override base rules in declaration order, exactly like resolveEffectiveAddons. */\nexport function effectiveGroups(\n item: Item,\n input: CartLineInput,\n): OptionGroup[] {\n const selected = new Map<string, Set<string>>();\n for (const choice of input.choices ?? []) {\n const ids = selected.get(choice.groupId) ?? new Set<string>();\n ids.add(choice.choiceId);\n selected.set(choice.groupId, ids);\n }\n return item.optionGroups.map((group) => {\n let required = group.required;\n let { min, max } = group.selection;\n for (const rule of group.conditions ?? []) {\n const chosen = selected.get(rule.groupId) ?? new Set<string>();\n const matches = rule.count\n ? rule.count.op === \"eq\"\n ? chosen.size === rule.count.value\n : rule.count.op === \"gte\"\n ? chosen.size >= rule.count.value\n : chosen.size <= rule.count.value\n : chosen.size > 0 &&\n rule.choiceIds.length > 0 &&\n (rule.match === \"all\"\n ? rule.choiceIds.every((id) => chosen.has(id))\n : rule.choiceIds.some((id) => chosen.has(id)));\n if (!matches) {\n continue;\n }\n required = rule.setRequired ?? required;\n min = rule.setMin ?? min;\n if (rule.setMax != null) {\n max = rule.setMax > 0 ? rule.setMax : undefined;\n }\n }\n return {\n ...group,\n required,\n selection: { min, ...(max !== undefined ? { max } : {}) },\n };\n });\n}\nexport function normalizeInput(\n item: Item,\n input: CartLineInput,\n): CartLineInput {\n const inactive = new Set(\n item.optionGroups.filter((g) => g.isAvailable === false).map((g) => g.id),\n );\n return {\n ...input,\n quantity: input.quantity ?? item.quantity.min,\n choices: (input.choices ?? [])\n .filter((c) => !inactive.has(c.groupId))\n .map((c) => ({ ...c, quantity: c.quantity ?? 1 })),\n inputs: Object.fromEntries(\n Object.entries(input.inputs ?? {}).filter(([id]) => !inactive.has(id)),\n ),\n };\n}\nexport function resolveVariant(item: Item, input: CartLineInput) {\n const attrs = item.optionGroups.filter((g) => g.isAttribute);\n const matches = item.variants.filter(\n (variant) =>\n (!input.variantId || variant.id === input.variantId) &&\n attrs.every((group) => {\n const selected =\n input.choices?.filter((c) => c.groupId === group.id) ?? [];\n return (\n selected.length === 1 &&\n variant.attributeChoices[group.id] === selected[0]!.choiceId\n );\n }),\n );\n return matches.length === 1 ? matches[0] : undefined;\n}\nfunction validInput(kind: OptionGroup[\"kind\"], value: string): boolean {\n switch (kind) {\n case \"number\":\n return Number.isFinite(Number(value));\n case \"date\":\n return (\n /^\\d{4}-\\d{2}-\\d{2}$/.test(value) &&\n Number.isFinite(Date.parse(value)) &&\n new Date(value).toISOString().slice(0, 10) === value\n );\n case \"time\":\n return /^([01]\\d|2[0-3]):[0-5]\\d(:[0-5]\\d)?$/.test(value);\n case \"toggle\":\n return [\"true\", \"false\"].includes(value.toLowerCase());\n default:\n return true;\n }\n}\n/**\n * Check a cart line against catalogue membership, availability, quantities and option rules.\n * Applies conditional group rules and checks that the selection resolves to an available variant.\n *\n * @param item - The current item, or undefined if it is no longer available.\n * @param raw - The shopper's proposed line.\n * @param ctx - The menu and branch context for validation.\n * @returns Local blocking issues with suggested fixes, or an empty array for a valid line.\n */\nexport function validateLine(\n item: Item | undefined,\n raw: CartLineInput,\n ctx: { menu: Menu; branch: Branch },\n): CartIssue[] {\n if (\n !item ||\n item.id !== raw.itemId ||\n !Object.hasOwn(ctx.menu.items, raw.itemId)\n ) {\n return [\n issue(\"item_unavailable\", \"This item is no longer on the menu\", [\n { action: \"remove_line\" },\n ]),\n ];\n }\n const issues: CartIssue[] = [];\n if (!resolveSourceMenu(ctx.menu, raw.itemId, raw.menuId)) {\n // Shoppers can't pick a menu from the cart, so the message names the fix.\n issues.push(\n issue(\n \"item_unavailable\",\n raw.menuId === undefined\n ? `${item.name} is on more than one menu. Remove it and add it again from the menu you want.`\n : `${item.name} is no longer on the menu it was added from.`,\n [{ action: \"remove_line\" }],\n ),\n );\n }\n if (!item.isAvailable) {\n issues.push(\n issue(\"item_unavailable\", `${item.name} is unavailable`, [\n { action: \"remove_line\" },\n ]),\n );\n }\n const input = normalizeInput(item, raw);\n const quantity = input.quantity!;\n const { min, step } = item.quantity;\n const max = Math.min(item.quantity.max ?? 99, 99);\n if (\n !Number.isInteger(quantity) ||\n quantity < min ||\n quantity > max ||\n quantity % step !== 0\n ) {\n const lower = Math.ceil(min / step) * step;\n const upper = Math.floor(max / step) * step;\n const nearest = Math.max(\n lower,\n Math.min(\n upper,\n Math.round((Number.isFinite(quantity) ? quantity : min) / step) * step,\n ),\n );\n issues.push(\n issue(\n \"quantity_limit\",\n `Choose a valid quantity of ${item.name} (${min}\u2013${max}, step ${step})`,\n lower <= upper ? [{ action: \"set_quantity\", quantity: nearest }] : [],\n ),\n );\n }\n if ((input.note?.length ?? 0) > 280) {\n issues.push(issue(\"option_limit\", \"Notes must be at most 280 characters\"));\n }\n for (const choice of input.choices ?? []) {\n if (!item.optionGroups.some((g) => g.id === choice.groupId)) {\n issues.push(\n issue(\n \"option_unavailable\",\n \"This option group no longer exists\",\n [{ action: \"remove_line\" }],\n choice.groupId,\n ),\n );\n }\n }\n for (const id of Object.keys(input.inputs ?? {})) {\n if (!item.optionGroups.some((g) => g.id === id)) {\n issues.push(\n issue(\n \"option_unavailable\",\n \"This input group no longer exists\",\n [{ action: \"remove_line\" }],\n id,\n ),\n );\n }\n }\n const groups = effectiveGroups(item, input);\n const missing = groups.filter(\n (g) =>\n g.isAvailable !== false &&\n g.required &&\n !(input.choices ?? []).some((c) => c.groupId === g.id) &&\n !(input.inputs?.[g.id] ?? \"\").trim(),\n );\n // The host reports missing required groups before checking counts of selected options.\n if (missing.length) {\n return [\n ...issues,\n ...missing.map((g) =>\n issue(\n \"option_required\",\n `Choose ${g.name}`,\n [{ action: \"choose_option\", groupId: g.id }],\n g.id,\n ),\n ),\n ];\n }\n for (const group of groups) {\n if (group.isAvailable === false) {\n continue;\n }\n const choices = input.choices?.filter((c) => c.groupId === group.id) ?? [];\n const text = input.inputs?.[group.id] ?? \"\";\n const value = text.trim();\n const hybrid = group.isInformational && group.kind === \"select\";\n const isInput =\n group.kind !== \"select\" || (hybrid && !group.choices.length);\n const fix: CartFix[] = [{ action: \"choose_option\", groupId: group.id }];\n if (group.required && !choices.length && !value) {\n issues.push(\n issue(\"option_required\", `Choose ${group.name}`, fix, group.id),\n );\n }\n if (value && ((!isInput && !hybrid) || choices.length > 0)) {\n issues.push(\n issue(\n \"option_limit\",\n `Choose either an option or a response for ${group.name}`,\n fix,\n group.id,\n ),\n );\n }\n if (\n (isInput || hybrid) &&\n (text.length > (group.input?.maxLength ?? 500) ||\n (value && !validInput(group.kind, value)))\n ) {\n issues.push(\n issue(\n \"option_limit\",\n `Enter a valid response for ${group.name}`,\n fix,\n group.id,\n ),\n );\n }\n const count = choices.reduce((sum, c) => sum + (c.quantity ?? 1), 0);\n if (\n choices.length &&\n (isInput ||\n count < group.selection.min ||\n count > (group.selection.max ?? Infinity) ||\n new Set(choices.map((c) => c.choiceId)).size !== choices.length)\n ) {\n issues.push(\n issue(\n \"option_limit\",\n `Check the number of options for ${group.name}`,\n fix,\n group.id,\n ),\n );\n }\n for (const selected of choices) {\n const choice = group.choices.find((c) => c.id === selected.choiceId);\n if (!choice?.isAvailable) {\n issues.push(\n issue(\n \"option_unavailable\",\n `An option in ${group.name} is unavailable`,\n [\n { action: \"choose_other_option\", groupId: group.id },\n { action: \"remove_line\" },\n ],\n group.id,\n ),\n );\n }\n const n = selected.quantity ?? 1;\n if (\n !Number.isInteger(n) ||\n n < (choice?.quantity?.min ?? 1) ||\n n > (choice?.quantity?.max ?? 99)\n ) {\n issues.push(\n issue(\n \"option_limit\",\n `Check the option quantity for ${group.name}`,\n fix,\n group.id,\n ),\n );\n }\n }\n }\n const variant = resolveVariant(item, input);\n if (!variant?.isAvailable) {\n const groupId = item.optionGroups.find((g) => g.isAttribute)?.id;\n issues.push(\n issue(\n \"option_limit\",\n \"Choose an available variant\",\n groupId\n ? [{ action: \"choose_option\", groupId }]\n : [{ action: \"remove_line\" }],\n groupId,\n ),\n );\n }\n return issues;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,42 @@
1
+ // src/cart/wire.ts
2
+ function wireLine(line) {
3
+ return {
4
+ line_id: line.id,
5
+ item_id: line.itemId,
6
+ ...line.menuId !== void 0 ? { menu_id: line.menuId } : {},
7
+ quantity: line.quantity,
8
+ ...line.variantId ? { variant_id: line.variantId } : {},
9
+ options: line.choices.map((c) => ({
10
+ group_id: c.groupId,
11
+ choice_id: c.choiceId,
12
+ quantity: c.quantity
13
+ })),
14
+ inputs: { ...line.inputs },
15
+ ...line.note !== void 0 ? { note: line.note } : {}
16
+ };
17
+ }
18
+ function wireFulfilment(f) {
19
+ return {
20
+ type: f.type,
21
+ ..."scheduledFor" in f && f.scheduledFor ? { scheduled_for: f.scheduledFor } : {},
22
+ ...f.type === "dine_in" && f.table ? { table: f.table } : {},
23
+ ...f.type === "delivery" ? {
24
+ address: {
25
+ line1: f.address.line1,
26
+ ...f.address.line2 ? { line2: f.address.line2 } : {},
27
+ ...f.address.city ? { city: f.address.city } : {},
28
+ ...f.address.placeId ? { place_id: f.address.placeId } : {},
29
+ ...f.address.instructions ? { instructions: f.address.instructions } : {},
30
+ ...f.address.coordinates ? {
31
+ lat: f.address.coordinates.lat,
32
+ lng: f.address.coordinates.lng
33
+ } : {}
34
+ }
35
+ } : {}
36
+ };
37
+ }
38
+ export {
39
+ wireFulfilment,
40
+ wireLine
41
+ };
42
+ //# sourceMappingURL=wire.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/cart/wire.ts"],
4
+ "sourcesContent": ["import type {\n CartFulfilment,\n CartLine,\n WireFulfilment,\n WireLine,\n} from \"./types\";\nexport function wireLine(line: CartLine): WireLine {\n return {\n line_id: line.id,\n item_id: line.itemId,\n ...(line.menuId !== undefined ? { menu_id: line.menuId } : {}),\n quantity: line.quantity,\n ...(line.variantId ? { variant_id: line.variantId } : {}),\n options: line.choices.map((c) => ({\n group_id: c.groupId,\n choice_id: c.choiceId,\n quantity: c.quantity,\n })),\n inputs: { ...line.inputs },\n ...(line.note !== undefined ? { note: line.note } : {}),\n };\n}\n/**\n * Convert a cart fulfilment selection to the API field names used by quotes and checkout.\n * Includes delivery address details, a schedule or a dine-in table when present.\n *\n * @param f - The shopper's selected fulfilment.\n * @returns Fulfilment fields without the branch ID. Checkout sessions accept pickup or delivery only.\n */\nexport function wireFulfilment(f: CartFulfilment): WireFulfilment {\n return {\n type: f.type,\n ...(\"scheduledFor\" in f && f.scheduledFor\n ? { scheduled_for: f.scheduledFor }\n : {}),\n ...(f.type === \"dine_in\" && f.table ? { table: f.table } : {}),\n ...(f.type === \"delivery\"\n ? {\n address: {\n line1: f.address.line1,\n ...(f.address.line2 ? { line2: f.address.line2 } : {}),\n ...(f.address.city ? { city: f.address.city } : {}),\n ...(f.address.placeId ? { place_id: f.address.placeId } : {}),\n ...(f.address.instructions\n ? { instructions: f.address.instructions }\n : {}),\n ...(f.address.coordinates\n ? {\n lat: f.address.coordinates.lat,\n lng: f.address.coordinates.lng,\n }\n : {}),\n },\n }\n : {}),\n };\n}\n"],
5
+ "mappings": ";AAMO,SAAS,SAAS,MAA0B;AACjD,SAAO;AAAA,IACL,SAAS,KAAK;AAAA,IACd,SAAS,KAAK;AAAA,IACd,GAAI,KAAK,WAAW,SAAY,EAAE,SAAS,KAAK,OAAO,IAAI,CAAC;AAAA,IAC5D,UAAU,KAAK;AAAA,IACf,GAAI,KAAK,YAAY,EAAE,YAAY,KAAK,UAAU,IAAI,CAAC;AAAA,IACvD,SAAS,KAAK,QAAQ,IAAI,CAAC,OAAO;AAAA,MAChC,UAAU,EAAE;AAAA,MACZ,WAAW,EAAE;AAAA,MACb,UAAU,EAAE;AAAA,IACd,EAAE;AAAA,IACF,QAAQ,EAAE,GAAG,KAAK,OAAO;AAAA,IACzB,GAAI,KAAK,SAAS,SAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,EACvD;AACF;AAQO,SAAS,eAAe,GAAmC;AAChE,SAAO;AAAA,IACL,MAAM,EAAE;AAAA,IACR,GAAI,kBAAkB,KAAK,EAAE,eACzB,EAAE,eAAe,EAAE,aAAa,IAChC,CAAC;AAAA,IACL,GAAI,EAAE,SAAS,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAAA,IAC5D,GAAI,EAAE,SAAS,aACX;AAAA,MACE,SAAS;AAAA,QACP,OAAO,EAAE,QAAQ;AAAA,QACjB,GAAI,EAAE,QAAQ,QAAQ,EAAE,OAAO,EAAE,QAAQ,MAAM,IAAI,CAAC;AAAA,QACpD,GAAI,EAAE,QAAQ,OAAO,EAAE,MAAM,EAAE,QAAQ,KAAK,IAAI,CAAC;AAAA,QACjD,GAAI,EAAE,QAAQ,UAAU,EAAE,UAAU,EAAE,QAAQ,QAAQ,IAAI,CAAC;AAAA,QAC3D,GAAI,EAAE,QAAQ,eACV,EAAE,cAAc,EAAE,QAAQ,aAAa,IACvC,CAAC;AAAA,QACL,GAAI,EAAE,QAAQ,cACV;AAAA,UACE,KAAK,EAAE,QAAQ,YAAY;AAAA,UAC3B,KAAK,EAAE,QAAQ,YAAY;AAAA,QAC7B,IACA,CAAC;AAAA,MACP;AAAA,IACF,IACA,CAAC;AAAA,EACP;AACF;",
6
+ "names": []
7
+ }