@zappie/sdk 0.1.6 → 0.1.8

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 (68) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +47 -1
  3. package/dist/cart/branch.d.ts +2 -2
  4. package/dist/cart/item.d.ts +3 -3
  5. package/dist/cart/quote-response.js +6 -1
  6. package/dist/cart/quote-response.js.map +2 -2
  7. package/dist/cart/quote.d.ts +3 -3
  8. package/dist/{client-Bm2cCYea.d.ts → client-DzUauU-b.d.ts} +2 -2
  9. package/dist/debug.d.ts +3 -3
  10. package/dist/debug.js +1 -1
  11. package/dist/index.d.ts +6 -6
  12. package/dist/index.js +1 -1
  13. package/dist/integrations/index.d.ts +9 -6
  14. package/dist/integrations/index.js +1 -0
  15. package/dist/integrations/index.js.map +2 -2
  16. package/dist/internal.d.ts +3 -3
  17. package/dist/{models-Rg5kQUkv.d.ts → models-D__QbqEd.d.ts} +14 -1
  18. package/dist/persist/catalogue.d.ts +3 -3
  19. package/dist/places/index.d.ts +6 -2
  20. package/dist/pricing/index.d.ts +116 -0
  21. package/dist/pricing/index.js +30 -0
  22. package/dist/pricing/index.js.map +7 -0
  23. package/dist/react/index.d.ts +3 -3
  24. package/dist/server/index.d.ts +7 -4
  25. package/dist/server/storefront-url/build-v1.js +8 -17
  26. package/dist/server/storefront-url/build-v1.js.map +2 -2
  27. package/dist/server/storefront-url/build-v2.js +13 -41
  28. package/dist/server/storefront-url/build-v2.js.map +2 -2
  29. package/dist/server/storefront-url/canonical.js +36 -0
  30. package/dist/server/storefront-url/canonical.js.map +7 -0
  31. package/dist/server/storefront-url/contract.js +2 -2
  32. package/dist/server/storefront-url/contract.js.map +2 -2
  33. package/dist/server/storefront-url/params.js +35 -18
  34. package/dist/server/storefront-url/params.js.map +2 -2
  35. package/dist/server/storefront-url/parse-v1.js +15 -12
  36. package/dist/server/storefront-url/parse-v1.js.map +2 -2
  37. package/dist/server/storefront-url/parse-v2.js +17 -79
  38. package/dist/server/storefront-url/parse-v2.js.map +2 -2
  39. package/dist/server/storefront-url/public-id.js +8 -0
  40. package/dist/server/storefront-url/public-id.js.map +7 -0
  41. package/dist/server/storefront-url/redirect.js +20 -6
  42. package/dist/server/storefront-url/redirect.js.map +2 -2
  43. package/dist/server/storefront-url/sign-in.js +2 -1
  44. package/dist/server/storefront-url/sign-in.js.map +2 -2
  45. package/dist/server/storefront-url/types.js +1 -0
  46. package/dist/server/storefront-url/types.js.map +2 -2
  47. package/dist/server/storefront-url/url.js +34 -23
  48. package/dist/server/storefront-url/url.js.map +2 -2
  49. package/dist/testing/index.d.ts +3 -3
  50. package/dist/transport/category-page.d.ts +2 -2
  51. package/dist/transport/category-page.js +6 -2
  52. package/dist/transport/category-page.js.map +2 -2
  53. package/dist/transport/presentation.d.ts +7 -0
  54. package/dist/transport/presentation.js +68 -0
  55. package/dist/transport/presentation.js.map +7 -0
  56. package/dist/transport/rest.js +15 -3
  57. package/dist/transport/rest.js.map +2 -2
  58. package/dist/transport/search.d.ts +3 -3
  59. package/dist/transport/search.js +6 -1
  60. package/dist/transport/search.js.map +2 -2
  61. package/dist/transport/selected-menu.d.ts +3 -3
  62. package/dist/transport/selected-menu.js +7 -2
  63. package/dist/transport/selected-menu.js.map +2 -2
  64. package/dist/transport/store.d.ts +1 -2
  65. package/dist/transport/store.js.map +1 -1
  66. package/dist/{types-CuDvfW9g.d.ts → types-gQXU2gEA.d.ts} +3 -1
  67. package/dist/url/index.d.ts +4 -1
  68. package/package.json +6 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/server/storefront-url/parse-v2.ts"],
4
- "sourcesContent": ["import { CART_MODAL, CHECKOUT_MODAL, SIGN_IN_MODALS } from \"./contract\";\nimport { normalizedPath, segment } from \"./installation\";\nimport { parseV1 } from \"./parse-v1\";\nimport { signInFromOverlay } from \"./sign-in\";\nimport type {\n StorefrontPageState,\n StorefrontRoute,\n StorefrontUrlInput,\n StorefrontUrlOptions,\n} from \"./types\";\nimport { ITEM_MODAL_PREFIX, inputUrl, pageState } from \"./url\";\n\nconst branch = (url: URL) =>\n url.searchParams.has(\"branch\")\n ? { branch: url.searchParams.get(\"branch\") ?? \"\" }\n : {};\n\nconst canonical = (\n url: URL,\n state: StorefrontPageState,\n business: string,\n): StorefrontRoute | null => {\n const pathname = normalizedPath(url.pathname);\n const parts = pathname.split(\"/\").filter(Boolean);\n const scoped = branch(url);\n if (pathname === \"/\") {\n return { type: \"order-menu\", business, ...scoped, state };\n }\n if (parts[0] === \"item\" && parts[1] && parts.length === 2) {\n return {\n type: \"item\",\n business,\n ...scoped,\n item: segment(parts, 1),\n state,\n };\n }\n if (pathname === \"/cart\" || pathname === \"/checkout\") {\n return {\n type: pathname === \"/cart\" ? \"cart\" : \"checkout\",\n business,\n ...scoped,\n state,\n };\n }\n if (pathname === \"/checkout/return\") {\n const session = url.searchParams.get(\"session\");\n return {\n type: \"checkout-return\",\n business,\n ...scoped,\n ...(session !== null ? { session } : {}),\n state,\n };\n }\n if (pathname === \"/sign-in\") {\n const returnTo = url.searchParams.get(\"return\");\n return {\n type: \"sign-in\",\n business,\n ...scoped,\n ...(returnTo !== null ? { returnTo } : {}),\n state,\n };\n }\n if (parts[0] === \"pages\" && parts[1] && parts.length === 2) {\n return {\n type: \"content-page\",\n business,\n ...scoped,\n slug: segment(parts, 1),\n state,\n };\n }\n if (parts[0] === \"orders\" && parts.length <= 2) {\n return {\n type: \"profile\",\n page: \"orders\",\n ...(parts[1] ? { order: segment(parts, 1) } : {}),\n state,\n };\n }\n if (parts[0] === \"account\" && parts.length <= 2) {\n const pages = {\n \"payment-methods\": \"payment-methods\",\n transactions: \"transactions\",\n \"store-credit\": \"add-store-credit\",\n } as const;\n if (parts.length === 1) {\n return { type: \"profile\", page: \"edit\", state };\n }\n const page = parts[1];\n if (page && Object.hasOwn(pages, page)) {\n return {\n type: \"profile\",\n page: pages[page as keyof typeof pages],\n state,\n };\n }\n }\n return null;\n};\n\nconst overlay = (\n route: StorefrontRoute,\n installation: NonNullable<StorefrontUrlOptions[\"installation\"]>,\n): StorefrontRoute => {\n if (route.type !== \"order-menu\") {\n return route;\n }\n const { authModalId, modalId } = route.state;\n if (authModalId && SIGN_IN_MODALS.has(authModalId)) {\n return signInFromOverlay(route, installation);\n }\n if (modalId?.startsWith(ITEM_MODAL_PREFIX)) {\n return {\n type: \"item\",\n business: route.business,\n ...(route.branch !== undefined ? { branch: route.branch } : {}),\n item: modalId.slice(ITEM_MODAL_PREFIX.length),\n state: route.state,\n };\n }\n if (modalId === CART_MODAL || modalId === CHECKOUT_MODAL) {\n return {\n type: modalId === CART_MODAL ? \"cart\" : \"checkout\",\n business: route.business,\n ...(route.branch !== undefined ? { branch: route.branch } : {}),\n state: route.state,\n };\n }\n return route;\n};\n\nexport const parseV2 = (\n input: StorefrontUrlInput,\n options: StorefrontUrlOptions,\n): StorefrontRoute => {\n try {\n const url = inputUrl(input);\n const state = pageState(url);\n const installation = options.installation;\n if (!installation) {\n return parseV1(input, options);\n }\n const route = canonical(url, state, installation.business);\n if (route) {\n return route.type === \"order-menu\" ? overlay(route, installation) : route;\n }\n return overlay(\n parseV1(input, { ...options, contract: \"v1\" }),\n installation,\n );\n } catch {\n return { type: \"unknown\", pathname: \"\", state: { query: {} } };\n }\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { canonicalRoute } from \"./canonical\";\nimport { normalizedPath, segment } from \"./installation\";\nimport { parseV1 } from \"./parse-v1\";\nimport type {\n StorefrontInstallation,\n StorefrontPageState,\n StorefrontRoute,\n StorefrontUrlInput,\n StorefrontUrlOptions,\n} from \"./types\";\nimport { inputUrl, pageState } from \"./url\";\n\nconst canonical = (\n url: URL,\n state: StorefrontPageState,\n business: string,\n): StorefrontRoute | null => {\n const pathname = normalizedPath(url.pathname);\n const parts = pathname.split(\"/\").filter(Boolean);\n const branch = url.searchParams.get(\"branch\");\n const common = { business, ...(branch !== null ? { branch } : {}), state };\n if (pathname === \"/\") {\n return { type: \"order-menu\", ...common };\n }\n if (parts[0] === \"item\" && parts[1] && parts.length === 2) {\n return {\n type: \"item\",\n ...common,\n item: segment(parts, 1),\n };\n }\n if (pathname === \"/cart\" || pathname === \"/checkout\") {\n return {\n type: pathname === \"/cart\" ? \"cart\" : \"checkout\",\n ...common,\n };\n }\n if (pathname === \"/checkout/return\" || pathname === \"/sign-in\") {\n const signIn = pathname === \"/sign-in\";\n const value = url.searchParams.get(signIn ? \"return\" : \"session\");\n return {\n type: signIn ? \"sign-in\" : \"checkout-return\",\n ...common,\n ...(value !== null ? { [signIn ? \"returnTo\" : \"session\"]: value } : {}),\n };\n }\n if (parts[0] === \"pages\" && parts[1] && parts.length === 2) {\n return {\n type: \"content-page\",\n ...common,\n slug: segment(parts, 1),\n };\n }\n if (parts[0] === \"account\" && parts.length <= 2) {\n const pages = {\n \"payment-methods\": \"payment-methods\",\n transactions: \"transactions\",\n \"store-credit\": \"add-store-credit\",\n } as const;\n if (parts.length === 1) {\n return { type: \"profile\", page: \"edit\", state };\n }\n const page = parts[1];\n if (page && Object.hasOwn(pages, page)) {\n return {\n type: \"profile\",\n page: pages[page as keyof typeof pages],\n state,\n };\n }\n }\n return null;\n};\n\nexport const parseV2 = (\n input: StorefrontUrlInput,\n options: StorefrontUrlOptions & { installation: StorefrontInstallation },\n): StorefrontRoute => {\n try {\n const url = inputUrl(input);\n const state = pageState(url);\n const installation = options.installation;\n const route = canonical(url, state, installation.business);\n return canonicalRoute(route ?? parseV1(input, options), installation);\n } catch {\n return { type: \"unknown\", pathname: \"\", state: { query: {} } };\n }\n};\n"],
5
+ "mappings": ";AAAA,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB,eAAe;AACxC,SAAS,eAAe;AAQxB,SAAS,UAAU,iBAAiB;AAEpC,IAAM,YAAY,CAChB,KACA,OACA,aAC2B;AAC3B,QAAM,WAAW,eAAe,IAAI,QAAQ;AAC5C,QAAM,QAAQ,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AAChD,QAAM,SAAS,IAAI,aAAa,IAAI,QAAQ;AAC5C,QAAM,SAAS,EAAE,UAAU,GAAI,WAAW,OAAO,EAAE,OAAO,IAAI,CAAC,GAAI,MAAM;AACzE,MAAI,aAAa,KAAK;AACpB,WAAO,EAAE,MAAM,cAAc,GAAG,OAAO;AAAA,EACzC;AACA,MAAI,MAAM,CAAC,MAAM,UAAU,MAAM,CAAC,KAAK,MAAM,WAAW,GAAG;AACzD,WAAO;AAAA,MACL,MAAM;AAAA,MACN,GAAG;AAAA,MACH,MAAM,QAAQ,OAAO,CAAC;AAAA,IACxB;AAAA,EACF;AACA,MAAI,aAAa,WAAW,aAAa,aAAa;AACpD,WAAO;AAAA,MACL,MAAM,aAAa,UAAU,SAAS;AAAA,MACtC,GAAG;AAAA,IACL;AAAA,EACF;AACA,MAAI,aAAa,sBAAsB,aAAa,YAAY;AAC9D,UAAM,SAAS,aAAa;AAC5B,UAAM,QAAQ,IAAI,aAAa,IAAI,SAAS,WAAW,SAAS;AAChE,WAAO;AAAA,MACL,MAAM,SAAS,YAAY;AAAA,MAC3B,GAAG;AAAA,MACH,GAAI,UAAU,OAAO,EAAE,CAAC,SAAS,aAAa,SAAS,GAAG,MAAM,IAAI,CAAC;AAAA,IACvE;AAAA,EACF;AACA,MAAI,MAAM,CAAC,MAAM,WAAW,MAAM,CAAC,KAAK,MAAM,WAAW,GAAG;AAC1D,WAAO;AAAA,MACL,MAAM;AAAA,MACN,GAAG;AAAA,MACH,MAAM,QAAQ,OAAO,CAAC;AAAA,IACxB;AAAA,EACF;AACA,MAAI,MAAM,CAAC,MAAM,aAAa,MAAM,UAAU,GAAG;AAC/C,UAAM,QAAQ;AAAA,MACZ,mBAAmB;AAAA,MACnB,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB;AACA,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO,EAAE,MAAM,WAAW,MAAM,QAAQ,MAAM;AAAA,IAChD;AACA,UAAM,OAAO,MAAM,CAAC;AACpB,QAAI,QAAQ,OAAO,OAAO,OAAO,IAAI,GAAG;AACtC,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,MAAM,IAA0B;AAAA,QACtC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,UAAU,CACrB,OACA,YACoB;AACpB,MAAI;AACF,UAAM,MAAM,SAAS,KAAK;AAC1B,UAAM,QAAQ,UAAU,GAAG;AAC3B,UAAM,eAAe,QAAQ;AAC7B,UAAM,QAAQ,UAAU,KAAK,OAAO,aAAa,QAAQ;AACzD,WAAO,eAAe,SAAS,QAAQ,OAAO,OAAO,GAAG,YAAY;AAAA,EACtE,QAAQ;AACN,WAAO,EAAE,MAAM,WAAW,UAAU,IAAI,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,EAC/D;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,8 @@
1
+ // src/server/storefront-url/public-id.ts
2
+ var publicId = (value, prefix) => value.startsWith(`${prefix}_`) ? value : `${prefix}_${value}`;
3
+ var categoryHash = (category) => category ? `#c-${encodeURIComponent(publicId(category, "cat"))}` : "";
4
+ export {
5
+ categoryHash,
6
+ publicId
7
+ };
8
+ //# sourceMappingURL=public-id.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/server/storefront-url/public-id.ts"],
4
+ "sourcesContent": ["export const publicId = (value: string, prefix: string) =>\n value.startsWith(`${prefix}_`) ? value : `${prefix}_${value}`;\n\nexport const categoryHash = (category?: string) =>\n category ? `#c-${encodeURIComponent(publicId(category, \"cat\"))}` : \"\";\n"],
5
+ "mappings": ";AAAO,IAAM,WAAW,CAAC,OAAe,WACtC,MAAM,WAAW,GAAG,MAAM,GAAG,IAAI,QAAQ,GAAG,MAAM,IAAI,KAAK;AAEtD,IAAM,eAAe,CAAC,aAC3B,WAAW,MAAM,mBAAmB,SAAS,UAAU,KAAK,CAAC,CAAC,KAAK;",
6
+ "names": []
7
+ }
@@ -3,11 +3,10 @@ import { buildStorefrontUrl } from "./build.js";
3
3
  import { usesV2 } from "./contract.js";
4
4
  import { normalizedPath } from "./installation.js";
5
5
  import { parseStorefrontUrl } from "./parse.js";
6
- import { inputUrl } from "./url.js";
7
- var comparable = (value) => {
8
- const url = inputUrl(value);
6
+ import { inputUrl, isAttributionKey } from "./url.js";
7
+ var comparable = (url) => {
9
8
  url.searchParams.sort();
10
- return `${normalizedPath(url.pathname)}${url.search}`;
9
+ return `${normalizedPath(url.pathname)}${url.search}${url.hash}`;
11
10
  };
12
11
  var storefrontRedirect = (input, options = {}) => {
13
12
  if (!usesV2(options)) {
@@ -18,8 +17,23 @@ var storefrontRedirect = (input, options = {}) => {
18
17
  if (route.type === "unknown") {
19
18
  return null;
20
19
  }
21
- const target = buildStorefrontUrl(route, options);
22
- return comparable(inputUrl(input)) === comparable(target) ? null : target;
20
+ const targetUrl = inputUrl(buildStorefrontUrl(route, options));
21
+ const original = inputUrl(input);
22
+ for (const key of [...targetUrl.searchParams.keys()]) {
23
+ if (isAttributionKey(key)) {
24
+ targetUrl.searchParams.delete(key);
25
+ }
26
+ }
27
+ original.searchParams.forEach((value, key) => {
28
+ if (isAttributionKey(key)) {
29
+ targetUrl.searchParams.append(key, value);
30
+ }
31
+ });
32
+ const path = comparable(targetUrl);
33
+ if (comparable(original) === path) {
34
+ return null;
35
+ }
36
+ return options.origin ? targetUrl.href : path;
23
37
  } catch {
24
38
  return null;
25
39
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/server/storefront-url/redirect.ts"],
4
- "sourcesContent": ["import { buildStorefrontUrl } from \"./build\";\nimport { usesV2 } from \"./contract\";\nimport { normalizedPath } from \"./installation\";\nimport { parseStorefrontUrl } from \"./parse\";\nimport type { StorefrontUrlInput, StorefrontUrlOptions } from \"./types\";\nimport { inputUrl } from \"./url\";\n\nconst comparable = (value: string | URL) => {\n const url = inputUrl(value);\n url.searchParams.sort();\n return `${normalizedPath(url.pathname)}${url.search}`;\n};\n\n/**\n * Find the canonical v2 URL for a recognized storefront route.\n * Compares normalized paths and sorted query parameters to avoid unnecessary redirects.\n *\n * @returns A redirect target, or null for v1, an unknown route, a parse/build failure or an equivalent URL.\n */\nexport const storefrontRedirect = (\n input: StorefrontUrlInput,\n options: StorefrontUrlOptions = {},\n): string | null => {\n if (!usesV2(options)) {\n return null;\n }\n try {\n const route = parseStorefrontUrl(input, options);\n if (route.type === \"unknown\") {\n return null;\n }\n const target = buildStorefrontUrl(route, options);\n return comparable(inputUrl(input)) === comparable(target) ? null : target;\n } catch {\n return null;\n }\n};\n"],
5
- "mappings": ";AAAA,SAAS,0BAA0B;AACnC,SAAS,cAAc;AACvB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AAEnC,SAAS,gBAAgB;AAEzB,IAAM,aAAa,CAAC,UAAwB;AAC1C,QAAM,MAAM,SAAS,KAAK;AAC1B,MAAI,aAAa,KAAK;AACtB,SAAO,GAAG,eAAe,IAAI,QAAQ,CAAC,GAAG,IAAI,MAAM;AACrD;AAQO,IAAM,qBAAqB,CAChC,OACA,UAAgC,CAAC,MACf;AAClB,MAAI,CAAC,OAAO,OAAO,GAAG;AACpB,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,QAAQ,mBAAmB,OAAO,OAAO;AAC/C,QAAI,MAAM,SAAS,WAAW;AAC5B,aAAO;AAAA,IACT;AACA,UAAM,SAAS,mBAAmB,OAAO,OAAO;AAChD,WAAO,WAAW,SAAS,KAAK,CAAC,MAAM,WAAW,MAAM,IAAI,OAAO;AAAA,EACrE,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
4
+ "sourcesContent": ["import { buildStorefrontUrl } from \"./build\";\nimport { usesV2 } from \"./contract\";\nimport { normalizedPath } from \"./installation\";\nimport { parseStorefrontUrl } from \"./parse\";\nimport type { StorefrontUrlInput, StorefrontUrlOptions } from \"./types\";\nimport { inputUrl, isAttributionKey } from \"./url\";\n\nconst comparable = (url: URL) => {\n url.searchParams.sort();\n return `${normalizedPath(url.pathname)}${url.search}${url.hash}`;\n};\n\n/**\n * Find the canonical v2 URL for a recognized storefront route.\n * Compares normalized paths and sorted query parameters to avoid unnecessary redirects.\n *\n * Callers choose the HTTP status when serving the redirect.\n * Zappie templates use temporary redirects (302) for now.\n *\n * @returns A redirect target, or null for v1, an unknown route, a parse/build failure or an equivalent URL.\n */\nexport const storefrontRedirect = (\n input: StorefrontUrlInput,\n options: StorefrontUrlOptions = {},\n): string | null => {\n if (!usesV2(options)) {\n return null;\n }\n try {\n const route = parseStorefrontUrl(input, options);\n if (route.type === \"unknown\") {\n return null;\n }\n const targetUrl = inputUrl(buildStorefrontUrl(route, options));\n const original = inputUrl(input);\n for (const key of [...targetUrl.searchParams.keys()]) {\n if (isAttributionKey(key)) {\n targetUrl.searchParams.delete(key);\n }\n }\n original.searchParams.forEach((value, key) => {\n if (isAttributionKey(key)) {\n targetUrl.searchParams.append(key, value);\n }\n });\n const path = comparable(targetUrl);\n if (comparable(original) === path) {\n return null;\n }\n return options.origin ? targetUrl.href : path;\n } catch {\n return null;\n }\n};\n"],
5
+ "mappings": ";AAAA,SAAS,0BAA0B;AACnC,SAAS,cAAc;AACvB,SAAS,sBAAsB;AAC/B,SAAS,0BAA0B;AAEnC,SAAS,UAAU,wBAAwB;AAE3C,IAAM,aAAa,CAAC,QAAa;AAC/B,MAAI,aAAa,KAAK;AACtB,SAAO,GAAG,eAAe,IAAI,QAAQ,CAAC,GAAG,IAAI,MAAM,GAAG,IAAI,IAAI;AAChE;AAWO,IAAM,qBAAqB,CAChC,OACA,UAAgC,CAAC,MACf;AAClB,MAAI,CAAC,OAAO,OAAO,GAAG;AACpB,WAAO;AAAA,EACT;AACA,MAAI;AACF,UAAM,QAAQ,mBAAmB,OAAO,OAAO;AAC/C,QAAI,MAAM,SAAS,WAAW;AAC5B,aAAO;AAAA,IACT;AACA,UAAM,YAAY,SAAS,mBAAmB,OAAO,OAAO,CAAC;AAC7D,UAAM,WAAW,SAAS,KAAK;AAC/B,eAAW,OAAO,CAAC,GAAG,UAAU,aAAa,KAAK,CAAC,GAAG;AACpD,UAAI,iBAAiB,GAAG,GAAG;AACzB,kBAAU,aAAa,OAAO,GAAG;AAAA,MACnC;AAAA,IACF;AACA,aAAS,aAAa,QAAQ,CAAC,OAAO,QAAQ;AAC5C,UAAI,iBAAiB,GAAG,GAAG;AACzB,kBAAU,aAAa,OAAO,KAAK,KAAK;AAAA,MAC1C;AAAA,IACF,CAAC;AACD,UAAM,OAAO,WAAW,SAAS;AACjC,QAAI,WAAW,QAAQ,MAAM,MAAM;AACjC,aAAO;AAAA,IACT;AACA,WAAO,QAAQ,SAAS,UAAU,OAAO;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  // src/server/storefront-url/sign-in.ts
2
2
  import { v2Params } from "./params.js";
3
+ import { categoryHash } from "./public-id.js";
3
4
  var signInFromOverlay = (route, installation) => {
4
5
  const params = v2Params(route, installation);
5
6
  params.delete("auth_modal_id");
@@ -8,7 +9,7 @@ var signInFromOverlay = (route, installation) => {
8
9
  type: "sign-in",
9
10
  business: route.business,
10
11
  state: { query: {} },
11
- returnTo: `/${search ? `?${search}` : ""}`
12
+ returnTo: `/${search ? `?${search}` : ""}${categoryHash(route.state.categoryId)}`
12
13
  };
13
14
  };
14
15
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/server/storefront-url/sign-in.ts"],
4
- "sourcesContent": ["import { v2Params } from \"./params\";\nimport type { StorefrontInstallation, StorefrontRoute } from \"./types\";\n\nexport const signInFromOverlay = (\n route: Extract<StorefrontRoute, { type: \"order-menu\" }>,\n installation: StorefrontInstallation,\n): Extract<StorefrontRoute, { type: \"sign-in\" }> => {\n const params = v2Params(route, installation);\n params.delete(\"auth_modal_id\");\n const search = params.toString();\n return {\n type: \"sign-in\",\n business: route.business,\n state: { query: {} },\n returnTo: `/${search ? `?${search}` : \"\"}`,\n };\n};\n"],
5
- "mappings": ";AAAA,SAAS,gBAAgB;AAGlB,IAAM,oBAAoB,CAC/B,OACA,iBACkD;AAClD,QAAM,SAAS,SAAS,OAAO,YAAY;AAC3C,SAAO,OAAO,eAAe;AAC7B,QAAM,SAAS,OAAO,SAAS;AAC/B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,MAAM;AAAA,IAChB,OAAO,EAAE,OAAO,CAAC,EAAE;AAAA,IACnB,UAAU,IAAI,SAAS,IAAI,MAAM,KAAK,EAAE;AAAA,EAC1C;AACF;",
4
+ "sourcesContent": ["import { v2Params } from \"./params\";\nimport { categoryHash } from \"./public-id\";\nimport type { StorefrontInstallation, StorefrontRoute } from \"./types\";\n\nexport const signInFromOverlay = (\n route: Extract<StorefrontRoute, { type: \"order-menu\" }>,\n installation: StorefrontInstallation,\n): Extract<StorefrontRoute, { type: \"sign-in\" }> => {\n const params = v2Params(route, installation);\n params.delete(\"auth_modal_id\");\n const search = params.toString();\n return {\n type: \"sign-in\",\n business: route.business,\n state: { query: {} },\n returnTo: `/${search ? `?${search}` : \"\"}${categoryHash(route.state.categoryId)}`,\n };\n};\n"],
5
+ "mappings": ";AAAA,SAAS,gBAAgB;AACzB,SAAS,oBAAoB;AAGtB,IAAM,oBAAoB,CAC/B,OACA,iBACkD;AAClD,QAAM,SAAS,SAAS,OAAO,YAAY;AAC3C,SAAO,OAAO,eAAe;AAC7B,QAAM,SAAS,OAAO,SAAS;AAC/B,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU,MAAM;AAAA,IAChB,OAAO,EAAE,OAAO,CAAC,EAAE;AAAA,IACnB,UAAU,IAAI,SAAS,IAAI,MAAM,KAAK,EAAE,GAAG,aAAa,MAAM,MAAM,UAAU,CAAC;AAAA,EACjF;AACF;",
6
6
  "names": []
7
7
  }
@@ -45,6 +45,7 @@ var STOREFRONT_V1_QUERY_PARAMETERS = [
45
45
  ];
46
46
  var STOREFRONT_QUERY_PARAMETERS = [
47
47
  ...STOREFRONT_V1_QUERY_PARAMETERS,
48
+ "menu",
48
49
  "return",
49
50
  "session"
50
51
  ];
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/server/storefront-url/types.ts"],
4
- "sourcesContent": ["/**\n * A URL string, URL object or Request you can parse as a storefront route.\n */\nexport type StorefrontUrlInput = string | URL | Request;\n\n/**\n * The business and optional default branch bound to an installed storefront.\n */\nexport type StorefrontInstallation = {\n business: string;\n defaultBranch?: string;\n};\n\n/**\n * URL contract, installation and origin settings for parsing and building storefront routes.\n * An installation selects v2 unless you explicitly request v1.\n */\nexport type StorefrontUrlOptions = {\n contract?: \"v1\" | \"v2\";\n installation?: StorefrontInstallation;\n origin?: string;\n};\n\n/** Query keys that URL contract v1 keeps. */\nexport const STOREFRONT_V1_QUERY_PARAMETERS = [\n \"ref\",\n \"utm_source\",\n \"payment_id\",\n \"context\",\n \"prefetch\",\n \"business_tag\",\n \"pay_point_id\",\n \"dine_in_order_id\",\n \"no_context\",\n \"payment_context_id\",\n \"ws\",\n \"preview\",\n \"device\",\n \"auto_scroll\",\n \"pickup\",\n \"branch_closed\",\n \"order_id\",\n \"modal_id\",\n \"lightbox\",\n \"menu_id\",\n \"payment_method\",\n \"beacon_id\",\n \"menu_group_id\",\n \"category_id\",\n \"menu_layout\",\n \"can_schedule\",\n \"refresh\",\n \"success\",\n \"grant\",\n \"portal\",\n \"can_download_receipt\",\n \"auth_modal_id\",\n \"experimental_otp\",\n \"branch\",\n \"business\",\n \"env\",\n \"switched_branch\",\n \"from\",\n \"template\",\n \"catalog_rendering\",\n \"catalogue_search\",\n \"catalogue_categories\",\n] as const;\n\n/**\n * Recognized storefront query keys, including the v1 keys and checkout return and session keys.\n */\nexport const STOREFRONT_QUERY_PARAMETERS = [\n ...STOREFRONT_V1_QUERY_PARAMETERS,\n \"return\",\n \"session\",\n] as const;\n\n/**\n * Readonly query parameter names and string values carried by storefront page state.\n */\nexport type StorefrontQuery = Readonly<Record<string, string>>;\n\n/**\n * Query parameters and named UI selections carried alongside a storefront route.\n */\nexport type StorefrontPageState = {\n query: StorefrontQuery;\n menuId?: string;\n categoryId?: string;\n menuLayout?: string;\n mode?: \"delivery\" | \"pickup\";\n modalId?: string;\n authModalId?: string;\n paymentId?: string;\n lightbox?: string;\n catalogueSearch?: string;\n catalogueCategories?: string;\n};\n\nexport type WithState = { state: StorefrontPageState };\n\n/**\n * A parsed storefront destination with its route identifiers and page state.\n * Unrecognized paths use the unknown variant.\n */\nexport type StorefrontRoute =\n | ({\n type: \"order-menu\";\n business: string;\n branch?: string;\n cataloguePath?: boolean;\n } & WithState)\n | ({\n type: \"item\";\n business: string;\n branch?: string;\n item: string;\n } & WithState)\n | ({ type: \"cart\"; business: string; branch?: string } & WithState)\n | ({ type: \"checkout\"; business: string; branch?: string } & WithState)\n | ({\n type: \"checkout-return\";\n business: string;\n branch?: string;\n session?: string;\n } & WithState)\n | ({\n type: \"sign-in\";\n business: string;\n branch?: string;\n returnTo?: string;\n } & WithState)\n | ({\n type: \"content-page\";\n business: string;\n slug: string;\n branch?: string;\n } & WithState)\n | ({ type: \"dine-in\"; order: string } & WithState)\n | ({ type: \"dine-in-menus\" } & WithState)\n | ({ type: \"self-service-menus\" } & WithState)\n | ({ type: \"pay-point\"; payPoint: string } & WithState)\n | ({ type: \"beacon\"; beacon: string } & WithState)\n | ({ type: \"link\"; link: string } & WithState)\n | ({ type: \"collect-payment\" } & WithState)\n | ({ type: \"reservation\" } & WithState)\n | ({ type: \"pay-session\"; session: string } & WithState)\n | ({\n type: \"payment-step\";\n payment: string;\n step: \"payment\" | \"successful\" | \"failed\" | \"refunded\";\n } & WithState)\n | ({\n type: \"payment-card\";\n transaction: string;\n } & WithState)\n | ({\n type: \"profile\";\n page:\n | \"add-store-credit\"\n | \"edit\"\n | \"orders\"\n | \"payment-methods\"\n | \"transactions\";\n order?: string;\n } & WithState)\n | ({ type: \"explore\"; home?: boolean } & WithState)\n | ({ type: \"unknown\"; pathname: string } & WithState);\n"],
5
- "mappings": ";AAwBO,IAAM,iCAAiC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKO,IAAM,8BAA8B;AAAA,EACzC,GAAG;AAAA,EACH;AAAA,EACA;AACF;",
4
+ "sourcesContent": ["/**\n * A URL string, URL object or Request you can parse as a storefront route.\n */\nexport type StorefrontUrlInput = string | URL | Request;\n\n/**\n * The business and optional default branch bound to an installed storefront.\n */\nexport type StorefrontInstallation = {\n business: string;\n defaultBranch?: string;\n};\n\n/**\n * URL contract, installation and origin settings for parsing and building storefront routes.\n * An installation selects v2 unless you explicitly request v1.\n */\nexport type StorefrontUrlOptions = {\n contract?: \"v1\" | \"v2\";\n installation?: StorefrontInstallation;\n origin?: string;\n};\n\n/** Query keys that URL contract v1 keeps. */\nexport const STOREFRONT_V1_QUERY_PARAMETERS = [\n \"ref\",\n \"utm_source\",\n \"payment_id\",\n \"context\",\n \"prefetch\",\n \"business_tag\",\n \"pay_point_id\",\n \"dine_in_order_id\",\n \"no_context\",\n \"payment_context_id\",\n \"ws\",\n \"preview\",\n \"device\",\n \"auto_scroll\",\n \"pickup\",\n \"branch_closed\",\n \"order_id\",\n \"modal_id\",\n \"lightbox\",\n \"menu_id\",\n \"payment_method\",\n \"beacon_id\",\n \"menu_group_id\",\n \"category_id\",\n \"menu_layout\",\n \"can_schedule\",\n \"refresh\",\n \"success\",\n \"grant\",\n \"portal\",\n \"can_download_receipt\",\n \"auth_modal_id\",\n \"experimental_otp\",\n \"branch\",\n \"business\",\n \"env\",\n \"switched_branch\",\n \"from\",\n \"template\",\n \"catalog_rendering\",\n \"catalogue_search\",\n \"catalogue_categories\",\n] as const;\n\n/**\n * Recognized storefront query keys, including the v1 keys and checkout return and session keys.\n */\nexport const STOREFRONT_QUERY_PARAMETERS = [\n ...STOREFRONT_V1_QUERY_PARAMETERS,\n \"menu\",\n \"return\",\n \"session\",\n] as const;\n\n/**\n * Readonly query parameter names and string values carried by storefront page state.\n */\nexport type StorefrontQuery = Readonly<Record<string, string>>;\n\n/**\n * Query parameters and named UI selections carried alongside a storefront route.\n */\nexport type StorefrontPageState = {\n query: StorefrontQuery;\n menuId?: string;\n categoryId?: string;\n menuLayout?: string;\n mode?: \"delivery\" | \"pickup\";\n modalId?: string;\n authModalId?: string;\n paymentId?: string;\n lightbox?: string;\n catalogueSearch?: string;\n catalogueCategories?: string;\n};\n\nexport type WithState = { state: StorefrontPageState };\n\n/**\n * A parsed storefront destination with its route identifiers and page state.\n * Unrecognized paths use the unknown variant.\n */\nexport type StorefrontRoute =\n | ({\n type: \"order-menu\";\n business: string;\n branch?: string;\n cataloguePath?: boolean;\n } & WithState)\n | ({\n type: \"item\";\n business: string;\n branch?: string;\n item: string;\n } & WithState)\n | ({ type: \"cart\"; business: string; branch?: string } & WithState)\n | ({ type: \"checkout\"; business: string; branch?: string } & WithState)\n | ({\n type: \"checkout-return\";\n business: string;\n branch?: string;\n session?: string;\n } & WithState)\n | ({\n type: \"sign-in\";\n business: string;\n branch?: string;\n returnTo?: string;\n } & WithState)\n | ({\n type: \"content-page\";\n business: string;\n slug: string;\n branch?: string;\n } & WithState)\n | ({ type: \"dine-in\"; order: string } & WithState)\n | ({ type: \"dine-in-menus\" } & WithState)\n | ({ type: \"self-service-menus\" } & WithState)\n | ({ type: \"pay-point\"; payPoint: string } & WithState)\n | ({ type: \"beacon\"; beacon: string } & WithState)\n | ({ type: \"link\"; link: string } & WithState)\n | ({ type: \"collect-payment\" } & WithState)\n | ({ type: \"reservation\" } & WithState)\n | ({ type: \"pay-session\"; session: string } & WithState)\n | ({\n type: \"payment-step\";\n payment: string;\n step: \"payment\" | \"successful\" | \"failed\" | \"refunded\";\n } & WithState)\n | ({\n type: \"payment-card\";\n transaction: string;\n } & WithState)\n | ({\n type: \"profile\";\n page:\n | \"add-store-credit\"\n | \"edit\"\n | \"orders\"\n | \"payment-methods\"\n | \"transactions\";\n order?: string;\n } & WithState)\n | ({ type: \"explore\"; home?: boolean } & WithState)\n | ({ type: \"unknown\"; pathname: string } & WithState);\n"],
5
+ "mappings": ";AAwBO,IAAM,iCAAiC;AAAA,EAC5C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAKO,IAAM,8BAA8B;AAAA,EACzC,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AACF;",
6
6
  "names": []
7
7
  }
@@ -20,41 +20,52 @@ var inputUrl = (input) => {
20
20
  const value = typeof input === "string" ? input : input.url;
21
21
  return new URL(value, FALLBACK_ORIGIN);
22
22
  };
23
+ var isAttributionKey = (key) => /^utm_|^(ref|\w*c..?id|.?braid|li_fat_id)$/.test(key);
23
24
  var queryRecord = (params, contract) => {
24
- const query = {};
25
- params.forEach((value, key) => {
26
- const allowed = contract === "v2" ? STOREFRONT_QUERY_PARAMETERS : STOREFRONT_V1_QUERY_PARAMETERS;
27
- if (allowed.includes(key)) {
28
- query[key] = value;
29
- }
30
- });
31
- return query;
25
+ const allowed = contract === "v2" ? STOREFRONT_QUERY_PARAMETERS : STOREFRONT_V1_QUERY_PARAMETERS;
26
+ return Object.fromEntries(
27
+ [...params].filter(
28
+ ([key]) => allowed.includes(key) || isAttributionKey(key)
29
+ )
30
+ );
31
+ };
32
+ var STATE_FIELDS = {
33
+ menu_id: "menuId",
34
+ category_id: "categoryId",
35
+ menu_layout: "menuLayout",
36
+ modal_id: "modalId",
37
+ auth_modal_id: "authModalId",
38
+ payment_id: "paymentId",
39
+ lightbox: "lightbox",
40
+ catalogue_search: "catalogueSearch",
41
+ catalogue_categories: "catalogueCategories"
32
42
  };
33
43
  var pageState = (url, contract = "v2") => {
34
44
  const query = queryRecord(url.searchParams, contract);
45
+ const state = { query };
46
+ for (const [key, field] of Object.entries(STATE_FIELDS)) {
47
+ const value = url.searchParams.get(key);
48
+ if (value !== null) {
49
+ state[field] = value;
50
+ }
51
+ }
35
52
  const pickup = url.searchParams.get("pickup");
36
- return {
37
- query,
38
- ...url.searchParams.has("menu_id") ? { menuId: url.searchParams.get("menu_id") ?? "" } : {},
39
- ...url.searchParams.has("category_id") ? { categoryId: url.searchParams.get("category_id") ?? "" } : {},
40
- ...url.searchParams.has("menu_layout") ? { menuLayout: url.searchParams.get("menu_layout") ?? "" } : {},
41
- ...pickup === "true" ? { mode: "pickup" } : pickup === "false" ? { mode: "delivery" } : {},
42
- ...url.searchParams.has("modal_id") ? { modalId: url.searchParams.get("modal_id") ?? "" } : {},
43
- ...url.searchParams.has("auth_modal_id") ? { authModalId: url.searchParams.get("auth_modal_id") ?? "" } : {},
44
- ...url.searchParams.has("payment_id") ? { paymentId: url.searchParams.get("payment_id") ?? "" } : {},
45
- ...url.searchParams.has("lightbox") ? { lightbox: url.searchParams.get("lightbox") ?? "" } : {},
46
- ...url.searchParams.has("catalogue_search") ? { catalogueSearch: url.searchParams.get("catalogue_search") ?? "" } : {},
47
- ...url.searchParams.has("catalogue_categories") ? {
48
- catalogueCategories: url.searchParams.get("catalogue_categories") ?? ""
49
- } : {}
50
- };
53
+ if (pickup === "true" || pickup === "false") {
54
+ state.mode = pickup === "true" ? "pickup" : "delivery";
55
+ }
56
+ if (state.categoryId === void 0 && contract === "v2" && url.hash.startsWith("#c-")) {
57
+ state.categoryId = decode(url.hash.slice(3));
58
+ }
59
+ return state;
51
60
  };
52
61
  export {
53
62
  FALLBACK_ORIGIN,
54
63
  ITEM_MODAL_PREFIX,
64
+ STATE_FIELDS,
55
65
  decode,
56
66
  encode,
57
67
  inputUrl,
68
+ isAttributionKey,
58
69
  pageState,
59
70
  queryRecord
60
71
  };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/server/storefront-url/url.ts"],
4
- "sourcesContent": ["import {\n STOREFRONT_QUERY_PARAMETERS,\n STOREFRONT_V1_QUERY_PARAMETERS,\n type StorefrontPageState,\n type StorefrontUrlInput,\n} from \"./types\";\n\nexport const FALLBACK_ORIGIN = \"https://storefront.invalid\";\nexport const ITEM_MODAL_PREFIX = \"product_\";\n\nexport const decode = (value: string) => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n};\n\nexport const encode = (value: string) => encodeURIComponent(value);\n\nexport const inputUrl = (input: StorefrontUrlInput) => {\n if (input instanceof URL) {\n return new URL(input.toString());\n }\n const value = typeof input === \"string\" ? input : input.url;\n return new URL(value, FALLBACK_ORIGIN);\n};\n\nexport const queryRecord = (params: URLSearchParams, contract: \"v1\" | \"v2\") => {\n const query: Record<string, string> = {};\n params.forEach((value, key) => {\n const allowed: readonly string[] =\n contract === \"v2\"\n ? STOREFRONT_QUERY_PARAMETERS\n : STOREFRONT_V1_QUERY_PARAMETERS;\n if (allowed.includes(key)) {\n query[key] = value;\n }\n });\n return query;\n};\n\nexport const pageState = (\n url: URL,\n contract: \"v1\" | \"v2\" = \"v2\",\n): StorefrontPageState => {\n const query = queryRecord(url.searchParams, contract);\n const pickup = url.searchParams.get(\"pickup\");\n return {\n query,\n ...(url.searchParams.has(\"menu_id\")\n ? { menuId: url.searchParams.get(\"menu_id\") ?? \"\" }\n : {}),\n ...(url.searchParams.has(\"category_id\")\n ? { categoryId: url.searchParams.get(\"category_id\") ?? \"\" }\n : {}),\n ...(url.searchParams.has(\"menu_layout\")\n ? { menuLayout: url.searchParams.get(\"menu_layout\") ?? \"\" }\n : {}),\n ...(pickup === \"true\"\n ? { mode: \"pickup\" as const }\n : pickup === \"false\"\n ? { mode: \"delivery\" as const }\n : {}),\n ...(url.searchParams.has(\"modal_id\")\n ? { modalId: url.searchParams.get(\"modal_id\") ?? \"\" }\n : {}),\n ...(url.searchParams.has(\"auth_modal_id\")\n ? { authModalId: url.searchParams.get(\"auth_modal_id\") ?? \"\" }\n : {}),\n ...(url.searchParams.has(\"payment_id\")\n ? { paymentId: url.searchParams.get(\"payment_id\") ?? \"\" }\n : {}),\n ...(url.searchParams.has(\"lightbox\")\n ? { lightbox: url.searchParams.get(\"lightbox\") ?? \"\" }\n : {}),\n ...(url.searchParams.has(\"catalogue_search\")\n ? { catalogueSearch: url.searchParams.get(\"catalogue_search\") ?? \"\" }\n : {}),\n ...(url.searchParams.has(\"catalogue_categories\")\n ? {\n catalogueCategories:\n url.searchParams.get(\"catalogue_categories\") ?? \"\",\n }\n : {}),\n };\n};\n"],
5
- "mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAEA,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAE1B,IAAM,SAAS,CAAC,UAAkB;AACvC,MAAI;AACF,WAAO,mBAAmB,KAAK;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,SAAS,CAAC,UAAkB,mBAAmB,KAAK;AAE1D,IAAM,WAAW,CAAC,UAA8B;AACrD,MAAI,iBAAiB,KAAK;AACxB,WAAO,IAAI,IAAI,MAAM,SAAS,CAAC;AAAA,EACjC;AACA,QAAM,QAAQ,OAAO,UAAU,WAAW,QAAQ,MAAM;AACxD,SAAO,IAAI,IAAI,OAAO,eAAe;AACvC;AAEO,IAAM,cAAc,CAAC,QAAyB,aAA0B;AAC7E,QAAM,QAAgC,CAAC;AACvC,SAAO,QAAQ,CAAC,OAAO,QAAQ;AAC7B,UAAM,UACJ,aAAa,OACT,8BACA;AACN,QAAI,QAAQ,SAAS,GAAG,GAAG;AACzB,YAAM,GAAG,IAAI;AAAA,IACf;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEO,IAAM,YAAY,CACvB,KACA,WAAwB,SACA;AACxB,QAAM,QAAQ,YAAY,IAAI,cAAc,QAAQ;AACpD,QAAM,SAAS,IAAI,aAAa,IAAI,QAAQ;AAC5C,SAAO;AAAA,IACL;AAAA,IACA,GAAI,IAAI,aAAa,IAAI,SAAS,IAC9B,EAAE,QAAQ,IAAI,aAAa,IAAI,SAAS,KAAK,GAAG,IAChD,CAAC;AAAA,IACL,GAAI,IAAI,aAAa,IAAI,aAAa,IAClC,EAAE,YAAY,IAAI,aAAa,IAAI,aAAa,KAAK,GAAG,IACxD,CAAC;AAAA,IACL,GAAI,IAAI,aAAa,IAAI,aAAa,IAClC,EAAE,YAAY,IAAI,aAAa,IAAI,aAAa,KAAK,GAAG,IACxD,CAAC;AAAA,IACL,GAAI,WAAW,SACX,EAAE,MAAM,SAAkB,IAC1B,WAAW,UACT,EAAE,MAAM,WAAoB,IAC5B,CAAC;AAAA,IACP,GAAI,IAAI,aAAa,IAAI,UAAU,IAC/B,EAAE,SAAS,IAAI,aAAa,IAAI,UAAU,KAAK,GAAG,IAClD,CAAC;AAAA,IACL,GAAI,IAAI,aAAa,IAAI,eAAe,IACpC,EAAE,aAAa,IAAI,aAAa,IAAI,eAAe,KAAK,GAAG,IAC3D,CAAC;AAAA,IACL,GAAI,IAAI,aAAa,IAAI,YAAY,IACjC,EAAE,WAAW,IAAI,aAAa,IAAI,YAAY,KAAK,GAAG,IACtD,CAAC;AAAA,IACL,GAAI,IAAI,aAAa,IAAI,UAAU,IAC/B,EAAE,UAAU,IAAI,aAAa,IAAI,UAAU,KAAK,GAAG,IACnD,CAAC;AAAA,IACL,GAAI,IAAI,aAAa,IAAI,kBAAkB,IACvC,EAAE,iBAAiB,IAAI,aAAa,IAAI,kBAAkB,KAAK,GAAG,IAClE,CAAC;AAAA,IACL,GAAI,IAAI,aAAa,IAAI,sBAAsB,IAC3C;AAAA,MACE,qBACE,IAAI,aAAa,IAAI,sBAAsB,KAAK;AAAA,IACpD,IACA,CAAC;AAAA,EACP;AACF;",
4
+ "sourcesContent": ["import {\n STOREFRONT_QUERY_PARAMETERS,\n STOREFRONT_V1_QUERY_PARAMETERS,\n type StorefrontPageState,\n type StorefrontUrlInput,\n} from \"./types\";\n\nexport const FALLBACK_ORIGIN = \"https://storefront.invalid\";\nexport const ITEM_MODAL_PREFIX = \"product_\";\n\nexport const decode = (value: string) => {\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n};\n\nexport const encode = (value: string) => encodeURIComponent(value);\n\nexport const inputUrl = (input: StorefrontUrlInput) => {\n if (input instanceof URL) {\n return new URL(input.toString());\n }\n const value = typeof input === \"string\" ? input : input.url;\n return new URL(value, FALLBACK_ORIGIN);\n};\n\nexport const isAttributionKey = (key: string) =>\n /^utm_|^(ref|\\w*c..?id|.?braid|li_fat_id)$/.test(key);\n\nexport const queryRecord = (params: URLSearchParams, contract: \"v1\" | \"v2\") => {\n const allowed: readonly string[] =\n contract === \"v2\"\n ? STOREFRONT_QUERY_PARAMETERS\n : STOREFRONT_V1_QUERY_PARAMETERS;\n return Object.fromEntries(\n [...params].filter(\n ([key]) => allowed.includes(key) || isAttributionKey(key),\n ),\n );\n};\n\nexport const STATE_FIELDS = {\n menu_id: \"menuId\",\n category_id: \"categoryId\",\n menu_layout: \"menuLayout\",\n modal_id: \"modalId\",\n auth_modal_id: \"authModalId\",\n payment_id: \"paymentId\",\n lightbox: \"lightbox\",\n catalogue_search: \"catalogueSearch\",\n catalogue_categories: \"catalogueCategories\",\n} as const;\n\nexport const pageState = (\n url: URL,\n contract: \"v1\" | \"v2\" = \"v2\",\n): StorefrontPageState => {\n const query = queryRecord(url.searchParams, contract);\n const state: StorefrontPageState = { query };\n for (const [key, field] of Object.entries(STATE_FIELDS)) {\n const value = url.searchParams.get(key);\n if (value !== null) {\n state[field] = value;\n }\n }\n const pickup = url.searchParams.get(\"pickup\");\n if (pickup === \"true\" || pickup === \"false\") {\n state.mode = pickup === \"true\" ? \"pickup\" : \"delivery\";\n }\n if (\n state.categoryId === undefined &&\n contract === \"v2\" &&\n url.hash.startsWith(\"#c-\")\n ) {\n state.categoryId = decode(url.hash.slice(3));\n }\n return state;\n};\n"],
5
+ "mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,OAGK;AAEA,IAAM,kBAAkB;AACxB,IAAM,oBAAoB;AAE1B,IAAM,SAAS,CAAC,UAAkB;AACvC,MAAI;AACF,WAAO,mBAAmB,KAAK;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,IAAM,SAAS,CAAC,UAAkB,mBAAmB,KAAK;AAE1D,IAAM,WAAW,CAAC,UAA8B;AACrD,MAAI,iBAAiB,KAAK;AACxB,WAAO,IAAI,IAAI,MAAM,SAAS,CAAC;AAAA,EACjC;AACA,QAAM,QAAQ,OAAO,UAAU,WAAW,QAAQ,MAAM;AACxD,SAAO,IAAI,IAAI,OAAO,eAAe;AACvC;AAEO,IAAM,mBAAmB,CAAC,QAC/B,4CAA4C,KAAK,GAAG;AAE/C,IAAM,cAAc,CAAC,QAAyB,aAA0B;AAC7E,QAAM,UACJ,aAAa,OACT,8BACA;AACN,SAAO,OAAO;AAAA,IACZ,CAAC,GAAG,MAAM,EAAE;AAAA,MACV,CAAC,CAAC,GAAG,MAAM,QAAQ,SAAS,GAAG,KAAK,iBAAiB,GAAG;AAAA,IAC1D;AAAA,EACF;AACF;AAEO,IAAM,eAAe;AAAA,EAC1B,SAAS;AAAA,EACT,aAAa;AAAA,EACb,aAAa;AAAA,EACb,UAAU;AAAA,EACV,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,sBAAsB;AACxB;AAEO,IAAM,YAAY,CACvB,KACA,WAAwB,SACA;AACxB,QAAM,QAAQ,YAAY,IAAI,cAAc,QAAQ;AACpD,QAAM,QAA6B,EAAE,MAAM;AAC3C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AACvD,UAAM,QAAQ,IAAI,aAAa,IAAI,GAAG;AACtC,QAAI,UAAU,MAAM;AAClB,YAAM,KAAK,IAAI;AAAA,IACjB;AAAA,EACF;AACA,QAAM,SAAS,IAAI,aAAa,IAAI,QAAQ;AAC5C,MAAI,WAAW,UAAU,WAAW,SAAS;AAC3C,UAAM,OAAO,WAAW,SAAS,WAAW;AAAA,EAC9C;AACA,MACE,MAAM,eAAe,UACrB,aAAa,QACb,IAAI,KAAK,WAAW,KAAK,GACzB;AACA,UAAM,aAAa,OAAO,IAAI,KAAK,MAAM,CAAC,CAAC;AAAA,EAC7C;AACA,SAAO;AACT;",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
- import { T as Transport, e as ZappieErrorCode, S as StorageLike, c as StoreResource, M as MenuResource, d as ZappieEvents, C as Cache } from '../client-Bm2cCYea.js';
2
- import { S as Store, a as Menu, F as Formatters } from '../models-Rg5kQUkv.js';
1
+ import { T as Transport, e as ZappieErrorCode, S as StorageLike, c as StoreResource, M as MenuResource, d as ZappieEvents, C as Cache } from '../client-DzUauU-b.js';
2
+ import { S as Store, a as Menu, F as Formatters } from '../models-D__QbqEd.js';
3
3
  export { VERSION } from '../index.js';
4
- import { d as CartModule, b as CartLineInput } from '../types-CuDvfW9g.js';
4
+ import { d as CartModule, b as CartLineInput } from '../types-gQXU2gEA.js';
5
5
 
6
6
  /** Store and branch menus supplied to the deterministic mock transport. */
7
7
  type MockFixtures = {
@@ -1,4 +1,4 @@
1
- import { a as Menu, C as CategoryPage } from '../models-Rg5kQUkv.js';
1
+ import { a as Menu, C as CategoryPage } from '../models-D__QbqEd.js';
2
2
 
3
3
  /**
4
4
  * Validates and maps one category page. Only progressive catalogues read
@@ -12,6 +12,6 @@ declare function categoryPage(raw: Record<string, unknown>, ctx: {
12
12
  menuId: string;
13
13
  categoryId: string;
14
14
  cursor?: string;
15
- }, mapMenu: (value: unknown, branchId: string) => Menu, invalid: () => Error): CategoryPage;
15
+ }, mapMenu: (value: unknown, branchId: string) => Menu, invalid: () => Error): Promise<CategoryPage>;
16
16
 
17
17
  export { categoryPage };
@@ -1,6 +1,10 @@
1
1
  // src/transport/category-page.ts
2
- function categoryPage(raw, ctx, mapMenu, invalid) {
3
- const menu = mapMenu(raw, ctx.branchId);
2
+ async function categoryPage(raw, ctx, mapMenu, invalid) {
3
+ const menu = (await import("./presentation.js")).presentMenu(
4
+ mapMenu(raw, ctx.branchId),
5
+ raw,
6
+ invalid
7
+ );
4
8
  if (raw.next_cursor != null && (typeof raw.next_cursor !== "string" || !raw.next_cursor || raw.next_cursor === ctx.cursor) || menu.categories.some((c) => c.id !== ctx.categoryId) || Object.keys(menu.items).some(
5
9
  (id) => !menu.categories.some((c) => c.itemIds.includes(id))
6
10
  ) || !menu.menus?.some(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/transport/category-page.ts"],
4
- "sourcesContent": ["import type { CategoryPage, Menu } from \"../models\";\n\n/**\n * Validates and maps one category page. Only progressive catalogues read\n * pages, so this loads on the first `menu.page()` call and stays out of the\n * core bundle, like the quote client (PRD 03 \u00A74). The mapper and error come\n * in as arguments: importing them would split the core's shared code into a\n * separate chunk, which costs the core more than this module saves.\n */\nexport function categoryPage(\n raw: Record<string, unknown>,\n ctx: {\n branchId: string;\n menuId: string;\n categoryId: string;\n cursor?: string;\n },\n mapMenu: (value: unknown, branchId: string) => Menu,\n invalid: () => Error,\n): CategoryPage {\n const menu = mapMenu(raw, ctx.branchId);\n if (\n (raw.next_cursor != null &&\n (typeof raw.next_cursor !== \"string\" ||\n !raw.next_cursor ||\n raw.next_cursor === ctx.cursor)) ||\n menu.categories.some((c) => c.id !== ctx.categoryId) ||\n Object.keys(menu.items).some(\n (id) => !menu.categories.some((c) => c.itemIds.includes(id)),\n ) ||\n !menu.menus?.some(\n (m) => m.id === ctx.menuId && m.categoryIds.includes(ctx.categoryId),\n )\n ) {\n throw invalid();\n }\n return {\n menu: { ...menu, completeness: \"partial\" },\n menuId: ctx.menuId,\n categoryId: ctx.categoryId,\n ...(typeof raw.next_cursor === \"string\" && raw.next_cursor\n ? { nextCursor: raw.next_cursor }\n : {}),\n };\n}\n"],
5
- "mappings": ";AASO,SAAS,aACd,KACA,KAMA,SACA,SACc;AACd,QAAM,OAAO,QAAQ,KAAK,IAAI,QAAQ;AACtC,MACG,IAAI,eAAe,SACjB,OAAO,IAAI,gBAAgB,YAC1B,CAAC,IAAI,eACL,IAAI,gBAAgB,IAAI,WAC5B,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,UAAU,KACnD,OAAO,KAAK,KAAK,KAAK,EAAE;AAAA,IACtB,CAAC,OAAO,CAAC,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,QAAQ,SAAS,EAAE,CAAC;AAAA,EAC7D,KACA,CAAC,KAAK,OAAO;AAAA,IACX,CAAC,MAAM,EAAE,OAAO,IAAI,UAAU,EAAE,YAAY,SAAS,IAAI,UAAU;AAAA,EACrE,GACA;AACA,UAAM,QAAQ;AAAA,EAChB;AACA,SAAO;AAAA,IACL,MAAM,EAAE,GAAG,MAAM,cAAc,UAAU;AAAA,IACzC,QAAQ,IAAI;AAAA,IACZ,YAAY,IAAI;AAAA,IAChB,GAAI,OAAO,IAAI,gBAAgB,YAAY,IAAI,cAC3C,EAAE,YAAY,IAAI,YAAY,IAC9B,CAAC;AAAA,EACP;AACF;",
4
+ "sourcesContent": ["import type { CategoryPage, Menu } from \"../models\";\n\n/**\n * Validates and maps one category page. Only progressive catalogues read\n * pages, so this loads on the first `menu.page()` call and stays out of the\n * core bundle, like the quote client (PRD 03 \u00A74). The mapper and error come\n * in as arguments: importing them would split the core's shared code into a\n * separate chunk, which costs the core more than this module saves.\n */\nexport async function categoryPage(\n raw: Record<string, unknown>,\n ctx: {\n branchId: string;\n menuId: string;\n categoryId: string;\n cursor?: string;\n },\n mapMenu: (value: unknown, branchId: string) => Menu,\n invalid: () => Error,\n): Promise<CategoryPage> {\n const menu = (await import(\"./presentation\")).presentMenu(\n mapMenu(raw, ctx.branchId),\n raw,\n invalid,\n );\n if (\n (raw.next_cursor != null &&\n (typeof raw.next_cursor !== \"string\" ||\n !raw.next_cursor ||\n raw.next_cursor === ctx.cursor)) ||\n menu.categories.some((c) => c.id !== ctx.categoryId) ||\n Object.keys(menu.items).some(\n (id) => !menu.categories.some((c) => c.itemIds.includes(id)),\n ) ||\n !menu.menus?.some(\n (m) => m.id === ctx.menuId && m.categoryIds.includes(ctx.categoryId),\n )\n ) {\n throw invalid();\n }\n return {\n menu: { ...menu, completeness: \"partial\" },\n menuId: ctx.menuId,\n categoryId: ctx.categoryId,\n ...(typeof raw.next_cursor === \"string\" && raw.next_cursor\n ? { nextCursor: raw.next_cursor }\n : {}),\n };\n}\n"],
5
+ "mappings": ";AASA,eAAsB,aACpB,KACA,KAMA,SACA,SACuB;AACvB,QAAM,QAAQ,MAAM,OAAO,mBAAgB,GAAG;AAAA,IAC5C,QAAQ,KAAK,IAAI,QAAQ;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AACA,MACG,IAAI,eAAe,SACjB,OAAO,IAAI,gBAAgB,YAC1B,CAAC,IAAI,eACL,IAAI,gBAAgB,IAAI,WAC5B,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,UAAU,KACnD,OAAO,KAAK,KAAK,KAAK,EAAE;AAAA,IACtB,CAAC,OAAO,CAAC,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,QAAQ,SAAS,EAAE,CAAC;AAAA,EAC7D,KACA,CAAC,KAAK,OAAO;AAAA,IACX,CAAC,MAAM,EAAE,OAAO,IAAI,UAAU,EAAE,YAAY,SAAS,IAAI,UAAU;AAAA,EACrE,GACA;AACA,UAAM,QAAQ;AAAA,EAChB;AACA,SAAO;AAAA,IACL,MAAM,EAAE,GAAG,MAAM,cAAc,UAAU;AAAA,IACzC,QAAQ,IAAI;AAAA,IACZ,YAAY,IAAI;AAAA,IAChB,GAAI,OAAO,IAAI,gBAAgB,YAAY,IAAI,cAC3C,EAAE,YAAY,IAAI,YAAY,IAC9B,CAAC;AAAA,EACP;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,7 @@
1
+ import { a as Menu, S as Store } from '../models-D__QbqEd.js';
2
+
3
+ type Invalid = () => Error;
4
+ declare function presentStore(store: Store, value: unknown, invalid: Invalid): Store;
5
+ declare function presentMenu(menu: Menu, value: unknown, invalid: Invalid): Menu;
6
+
7
+ export { presentMenu, presentStore };
@@ -0,0 +1,68 @@
1
+ // src/transport/presentation.ts
2
+ var object = (value, invalid) => {
3
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4
+ throw invalid();
5
+ }
6
+ return value;
7
+ };
8
+ var array = (value, invalid) => {
9
+ if (!Array.isArray(value)) {
10
+ throw invalid();
11
+ }
12
+ return value;
13
+ };
14
+ var string = (value, invalid) => {
15
+ if (typeof value !== "string") {
16
+ throw invalid();
17
+ }
18
+ return value;
19
+ };
20
+ function presentStore(store, value, invalid) {
21
+ const source = value;
22
+ source.branches.forEach((raw, index) => {
23
+ const branch = store.branches[index];
24
+ if (raw.order_ahead_days !== void 0) {
25
+ if (!Number.isSafeInteger(raw.order_ahead_days) || Number(raw.order_ahead_days) < 0) {
26
+ throw invalid();
27
+ }
28
+ branch.orderAheadDays = raw.order_ahead_days;
29
+ }
30
+ if (raw.promos !== void 0) {
31
+ branch.promos = array(raw.promos, invalid).map((value2) => {
32
+ const promo = object(value2, invalid);
33
+ return {
34
+ id: string(promo.id, invalid),
35
+ name: string(promo.name, invalid),
36
+ description: string(promo.description, invalid)
37
+ };
38
+ });
39
+ }
40
+ });
41
+ return store;
42
+ }
43
+ function presentMenu(menu, value, invalid) {
44
+ value.menus.forEach((raw, index) => {
45
+ const section = menu.menus[index];
46
+ if (raw.layout !== void 0) {
47
+ if (!["LIST", "GRID", "CARD"].includes(raw.layout)) {
48
+ throw invalid();
49
+ }
50
+ section.layout = raw.layout;
51
+ }
52
+ if (raw.is_active !== void 0) {
53
+ if (typeof raw.is_active !== "boolean") {
54
+ throw invalid();
55
+ }
56
+ section.isActive = raw.is_active;
57
+ }
58
+ if (raw.info_banner_html !== void 0) {
59
+ section.infoBannerHtml = string(raw.info_banner_html, invalid);
60
+ }
61
+ });
62
+ return menu;
63
+ }
64
+ export {
65
+ presentMenu,
66
+ presentStore
67
+ };
68
+ //# sourceMappingURL=presentation.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/transport/presentation.ts"],
4
+ "sourcesContent": ["import type { Menu, Store } from \"../models\";\n\ntype Invalid = () => Error;\ntype Wire = Record<string, unknown>;\n\nconst object = (value: unknown, invalid: Invalid) => {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw invalid();\n }\n return value as Wire;\n};\nconst array = (value: unknown, invalid: Invalid) => {\n if (!Array.isArray(value)) {\n throw invalid();\n }\n return value;\n};\nconst string = (value: unknown, invalid: Invalid) => {\n if (typeof value !== \"string\") {\n throw invalid();\n }\n return value;\n};\n\nexport function presentStore(store: Store, value: unknown, invalid: Invalid) {\n const source = value as Wire;\n (source.branches as Wire[]).forEach((raw, index) => {\n const branch = store.branches[index]!;\n if (raw.order_ahead_days !== undefined) {\n if (\n !Number.isSafeInteger(raw.order_ahead_days) ||\n Number(raw.order_ahead_days) < 0\n ) {\n throw invalid();\n }\n branch.orderAheadDays = raw.order_ahead_days as number;\n }\n if (raw.promos !== undefined) {\n branch.promos = array(raw.promos, invalid).map((value) => {\n const promo = object(value, invalid);\n return {\n id: string(promo.id, invalid),\n name: string(promo.name, invalid),\n description: string(promo.description, invalid),\n };\n });\n }\n });\n return store;\n}\n\nexport function presentMenu(menu: Menu, value: unknown, invalid: Invalid) {\n ((value as Wire).menus as Wire[]).forEach((raw, index) => {\n const section = menu.menus![index]!;\n if (raw.layout !== undefined) {\n if (![\"LIST\", \"GRID\", \"CARD\"].includes(raw.layout as string)) {\n throw invalid();\n }\n section.layout = raw.layout as NonNullable<typeof section.layout>;\n }\n if (raw.is_active !== undefined) {\n if (typeof raw.is_active !== \"boolean\") {\n throw invalid();\n }\n section.isActive = raw.is_active;\n }\n if (raw.info_banner_html !== undefined) {\n section.infoBannerHtml = string(raw.info_banner_html, invalid);\n }\n });\n return menu;\n}\n"],
5
+ "mappings": ";AAKA,IAAM,SAAS,CAAC,OAAgB,YAAqB;AACnD,MAAI,CAAC,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK,GAAG;AAC/D,UAAM,QAAQ;AAAA,EAChB;AACA,SAAO;AACT;AACA,IAAM,QAAQ,CAAC,OAAgB,YAAqB;AAClD,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,UAAM,QAAQ;AAAA,EAChB;AACA,SAAO;AACT;AACA,IAAM,SAAS,CAAC,OAAgB,YAAqB;AACnD,MAAI,OAAO,UAAU,UAAU;AAC7B,UAAM,QAAQ;AAAA,EAChB;AACA,SAAO;AACT;AAEO,SAAS,aAAa,OAAc,OAAgB,SAAkB;AAC3E,QAAM,SAAS;AACf,EAAC,OAAO,SAAoB,QAAQ,CAAC,KAAK,UAAU;AAClD,UAAM,SAAS,MAAM,SAAS,KAAK;AACnC,QAAI,IAAI,qBAAqB,QAAW;AACtC,UACE,CAAC,OAAO,cAAc,IAAI,gBAAgB,KAC1C,OAAO,IAAI,gBAAgB,IAAI,GAC/B;AACA,cAAM,QAAQ;AAAA,MAChB;AACA,aAAO,iBAAiB,IAAI;AAAA,IAC9B;AACA,QAAI,IAAI,WAAW,QAAW;AAC5B,aAAO,SAAS,MAAM,IAAI,QAAQ,OAAO,EAAE,IAAI,CAACA,WAAU;AACxD,cAAM,QAAQ,OAAOA,QAAO,OAAO;AACnC,eAAO;AAAA,UACL,IAAI,OAAO,MAAM,IAAI,OAAO;AAAA,UAC5B,MAAM,OAAO,MAAM,MAAM,OAAO;AAAA,UAChC,aAAa,OAAO,MAAM,aAAa,OAAO;AAAA,QAChD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACD,SAAO;AACT;AAEO,SAAS,YAAY,MAAY,OAAgB,SAAkB;AACxE,EAAE,MAAe,MAAiB,QAAQ,CAAC,KAAK,UAAU;AACxD,UAAM,UAAU,KAAK,MAAO,KAAK;AACjC,QAAI,IAAI,WAAW,QAAW;AAC5B,UAAI,CAAC,CAAC,QAAQ,QAAQ,MAAM,EAAE,SAAS,IAAI,MAAgB,GAAG;AAC5D,cAAM,QAAQ;AAAA,MAChB;AACA,cAAQ,SAAS,IAAI;AAAA,IACvB;AACA,QAAI,IAAI,cAAc,QAAW;AAC/B,UAAI,OAAO,IAAI,cAAc,WAAW;AACtC,cAAM,QAAQ;AAAA,MAChB;AACA,cAAQ,WAAW,IAAI;AAAA,IACzB;AACA,QAAI,IAAI,qBAAqB,QAAW;AACtC,cAAQ,iBAAiB,OAAO,IAAI,kBAAkB,OAAO;AAAA,IAC/D;AAAA,EACF,CAAC;AACD,SAAO;AACT;",
6
+ "names": ["value"]
7
+ }
@@ -18,6 +18,7 @@ import {
18
18
  } from "./mappers.js";
19
19
  var MAX_RETRY_WAIT_MS = 5e3;
20
20
  var wireFulfilment = (value) => value === "dineIn" ? "dine_in" : value;
21
+ var presentationError = () => new ZappieError("server_error", "Invalid storefront presentation");
21
22
  var RestTransport = class {
22
23
  constructor(opts = {}) {
23
24
  const url = new URL(opts.baseUrl ?? "https://api.zappie.ai/storefront/v1");
@@ -124,11 +125,16 @@ var RestTransport = class {
124
125
  { cause }
125
126
  );
126
127
  });
128
+ const presentation = import("./presentation.js");
127
129
  const response = this.request("/store", ctx).then(
128
130
  (value) => this.json(value)
129
131
  );
130
- const [{ mapStore }, raw] = await Promise.all([mapper, response]);
131
- return mapStore(
132
+ const [{ mapStore }, { presentStore }, raw] = await Promise.all([
133
+ mapper,
134
+ presentation,
135
+ response
136
+ ]);
137
+ const store = mapStore(
132
138
  raw,
133
139
  record,
134
140
  text,
@@ -141,8 +147,10 @@ var RestTransport = class {
141
147
  isBoolean,
142
148
  mapSeo
143
149
  );
150
+ return presentStore(store, raw, presentationError);
144
151
  }
145
152
  async getMenu(ctx) {
153
+ const presentation = import("./presentation.js");
146
154
  let cursor;
147
155
  const seen = /* @__PURE__ */ new Set();
148
156
  let output;
@@ -172,7 +180,11 @@ var RestTransport = class {
172
180
  };
173
181
  }
174
182
  const raw = record(await this.json(response));
175
- const page = mapMenu(raw, ctx.branchId);
183
+ const page = (await presentation).presentMenu(
184
+ mapMenu(raw, ctx.branchId),
185
+ raw,
186
+ presentationError
187
+ );
176
188
  if (ctx.metadataOnly) {
177
189
  if (Object.keys(page.items).length || raw.next_cursor) {
178
190
  throw new ZappieError("server_error", "Invalid metadata response");