@zappie/sdk 0.1.4 → 0.1.6

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.
@@ -1,8 +1,9 @@
1
1
  export { VERSION } from '../index.js';
2
2
  import { I as Item, a as Menu, S as Store, M as Money } from '../models-Rg5kQUkv.js';
3
- import { c as ZappieError, Z as Zappie, d as CartModule, e as CartLine, b as CartFulfilment, f as CartState, g as CartIssue } from '../client-RFcLoZ04.js';
3
+ import { a as ZappieError, Z as Zappie } from '../client-Bm2cCYea.js';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
  import { ReactNode } from 'react';
6
+ import { d as CartModule, C as CartLine, c as CartFulfilment, e as CartState, f as CartIssue } from '../types-CuDvfW9g.js';
6
7
 
7
8
  /** Subscribe to storefront identity and branch-directory data. */
8
9
  declare function useStore(): {
@@ -1,6 +1,7 @@
1
1
  import { a as Menu, F as Formatters, S as Store } from '../models-Rg5kQUkv.js';
2
- import { c as ZappieError, h as ZappieConfig, d as CartModule, T as Transport, S as StorageLike, i as StoreResource, M as MenuResource, j as ZappieEvents, C as CartLineInput, k as Cache } from '../client-RFcLoZ04.js';
2
+ import { a as ZappieError, b as ZappieConfig, T as Transport, S as StorageLike, c as StoreResource, M as MenuResource, d as ZappieEvents, C as Cache } from '../client-Bm2cCYea.js';
3
3
  export { VERSION } from '../index.js';
4
+ import { d as CartModule, b as CartLineInput } from '../types-CuDvfW9g.js';
4
5
 
5
6
  /**
6
7
  * A URL string, URL object or Request you can parse as a storefront route.
@@ -210,6 +211,7 @@ declare function createZappieServer({ secretKey, ...config }: ZappieServerConfig
210
211
  transport?: Transport;
211
212
  baseUrl?: string;
212
213
  fetch?: typeof fetch;
214
+ customerBase?: string;
213
215
  storage?: StorageLike;
214
216
  cart?: {
215
217
  ttl?: number;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/site-config/types.ts"],
4
- "sourcesContent": ["/**\n * Default site budgets: 200 KB gzipped JavaScript, 50 KB gzipped CSS and 2500 ms LCP.\n */\nexport const DEFAULT_BUDGETS = {\n jsKbGz: 200,\n cssKbGz: 50,\n lcpMs: 2500,\n} as const;\n\n/**\n * Package names and patterns allowed globally without a site-specific allowlist entry.\n */\nexport const GLOBAL_PACKAGE_ALLOWLIST = [\n \"react\",\n \"react-dom\",\n \"react-router\",\n \"@react-router/*\",\n \"@zappie/*\",\n \"tailwindcss\",\n \"@tailwindcss/*\",\n \"clsx\",\n \"tailwind-merge\",\n \"class-variance-authority\",\n \"lucide-react\",\n \"date-fns\",\n \"embla-carousel-react\",\n \"framer-motion/motion\",\n \"zod\",\n \"@radix-ui/*\",\n \"sonner\",\n \"vaul\",\n] as const;\n\n/**\n * The store, theme and optional build policies you export from zappie.config.ts.\n */\nexport type ZappieSiteConfig = {\n store: string;\n branches?: string[];\n theme: string;\n prerender?: string[];\n menu?: { hiddenCategories?: string[] };\n packages?: { allow?: string[] };\n outbound?: { allow?: string[] };\n /** Each budget left out uses DEFAULT_BUDGETS. */\n budgets?: {\n jsKbGz?: number;\n cssKbGz?: number;\n lcpMs?: number;\n override?: { reason: string };\n };\n analytics?: { forward?: { ga4?: string; metaPixel?: string } };\n};\n\n/**\n * A configuration diagnostic with its field path, stable code, message and severity.\n */\nexport type ConfigIssue = {\n path: string;\n code:\n | \"invalid_type\"\n | \"missing_required\"\n | \"unknown_key\"\n | \"invalid_format\"\n | \"duplicate\"\n | \"redundant_allowlist\"\n | \"budget_override\";\n message: string;\n severity: \"error\" | \"warning\";\n};\n\n/**\n * Configuration validation results. Warnings alone do not make valid false.\n */\nexport type ConfigValidation = { valid: boolean; issues: ConfigIssue[] };\n"],
4
+ "sourcesContent": ["/**\n * Default site budgets: 200 KB gzipped JavaScript, 50 KB gzipped CSS and 2500 ms LCP.\n */\nexport const DEFAULT_BUDGETS = {\n jsKbGz: 200,\n cssKbGz: 50,\n lcpMs: 2500,\n} as const;\n\n/**\n * Package names and patterns allowed globally without a site-specific allowlist entry.\n */\nexport const GLOBAL_PACKAGE_ALLOWLIST = [\n \"react\",\n \"react-dom\",\n \"react-router\",\n \"@react-router/*\",\n \"@zappie/*\",\n \"tailwindcss\",\n \"@tailwindcss/*\",\n \"clsx\",\n \"tailwind-merge\",\n \"class-variance-authority\",\n \"lucide-react\",\n \"date-fns\",\n \"embla-carousel-react\",\n \"framer-motion/motion\",\n \"zod\",\n \"@radix-ui/*\",\n \"sonner\",\n \"vaul\",\n] as const;\n\n/**\n * The store, theme and optional build policies you export from zappie.config.ts.\n */\nexport type ZappieSiteConfig = {\n store: string;\n branches?: string[];\n theme: string;\n prerender?: string[];\n menu?: { hiddenCategories?: string[] };\n packages?: { allow?: string[] };\n outbound?: { allow?: string[] };\n /** Each budget left out uses DEFAULT_BUDGETS. */\n budgets?: {\n jsKbGz?: number;\n cssKbGz?: number;\n lcpMs?: number;\n override?: { reason: string };\n };\n /** Records explained exceptions to storefront readiness checks. */\n readiness?: {\n /** Lets this site skip the initial route discovery requirement. */\n routeDiscovery?: {\n /** Explains why this site does not use initial route discovery. */\n optOut: {\n /** States the constraint that prevents initial route discovery. */\n reason: string;\n };\n };\n };\n analytics?: { forward?: { ga4?: string; metaPixel?: string } };\n};\n\n/**\n * A configuration diagnostic with its field path, stable code, message and severity.\n */\nexport type ConfigIssue = {\n path: string;\n code:\n | \"invalid_type\"\n | \"missing_required\"\n | \"unknown_key\"\n | \"invalid_format\"\n | \"duplicate\"\n | \"redundant_allowlist\"\n | \"budget_override\";\n message: string;\n severity: \"error\" | \"warning\";\n};\n\n/**\n * Configuration validation results. Warnings alone do not make valid false.\n */\nexport type ConfigValidation = { valid: boolean; issues: ConfigIssue[] };\n"],
5
5
  "mappings": ";AAGO,IAAM,kBAAkB;AAAA,EAC7B,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,OAAO;AACT;AAKO,IAAM,2BAA2B;AAAA,EACtC;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;",
6
6
  "names": []
7
7
  }
@@ -17,6 +17,7 @@ var rootKeys = [
17
17
  "packages",
18
18
  "outbound",
19
19
  "budgets",
20
+ "readiness",
20
21
  "analytics"
21
22
  ];
22
23
  function validateConfig(input) {
@@ -80,6 +81,37 @@ function validateConfig(input) {
80
81
  });
81
82
  });
82
83
  budgets(input.budgets, issue, error);
84
+ nested(
85
+ input.readiness,
86
+ "readiness",
87
+ ["routeDiscovery"],
88
+ issue,
89
+ (readiness) => {
90
+ nested(
91
+ readiness.routeDiscovery,
92
+ "readiness.routeDiscovery",
93
+ ["optOut"],
94
+ issue,
95
+ (routeDiscovery) => {
96
+ nested(
97
+ routeDiscovery.optOut,
98
+ "readiness.routeDiscovery.optOut",
99
+ ["reason"],
100
+ issue,
101
+ (optOut) => {
102
+ if (typeof optOut.reason !== "string" || !optOut.reason.trim()) {
103
+ error(
104
+ "readiness.routeDiscovery.optOut.reason",
105
+ "invalid_format",
106
+ "Expected a non-blank reason. Explain why the site disables initial route discovery."
107
+ );
108
+ }
109
+ }
110
+ );
111
+ }
112
+ );
113
+ }
114
+ );
83
115
  analytics(input.analytics, issue, error);
84
116
  return {
85
117
  valid: !issues.some((entry) => entry.severity === "error"),
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/site-config/validate.ts"],
4
- "sourcesContent": ["import {\n globallyAllowed,\n hostname,\n nonblank,\n packageName,\n record,\n unknownKeys,\n} from \"./helpers\";\nimport { analytics, budgets, nested } from \"./sections\";\nimport type { ConfigIssue, ConfigValidation } from \"./types\";\n\nconst rootKeys = [\n \"store\",\n \"branches\",\n \"theme\",\n \"prerender\",\n \"menu\",\n \"packages\",\n \"outbound\",\n \"budgets\",\n \"analytics\",\n] as const;\n\n/**\n * Validate a site configuration and collect errors and warnings without changing it.\n * Checks required fields, unknown keys, allowlists, budgets and analytics settings.\n *\n * @param input - The configuration value to inspect.\n * @returns Diagnostics and a valid flag that is true when there are no errors.\n */\nexport function validateConfig(input: unknown): ConfigValidation {\n const issues: ConfigIssue[] = [];\n const issue = (value: ConfigIssue) => issues.push(value);\n const error = (path: string, code: ConfigIssue[\"code\"], message: string) =>\n issue({ path, code, message, severity: \"error\" });\n if (!record(input)) {\n return {\n valid: false,\n issues: [\n {\n path: \"$\",\n code: \"invalid_type\",\n message:\n \"Expected a configuration object. Export an object from zappie.config.ts.\",\n severity: \"error\",\n },\n ],\n };\n }\n unknownKeys(input, rootKeys, \"\", issue);\n requiredSlug(input.store, \"store\", error);\n theme(input.theme, error);\n stringList(input.branches, \"branches\", error, {\n nonempty: true,\n unique: true,\n slug: true,\n });\n paths(input.prerender, error);\n nested(input.menu, \"menu\", [\"hiddenCategories\"], issue, (value) => {\n stringList(value.hiddenCategories, \"menu.hiddenCategories\", error);\n });\n nested(input.packages, \"packages\", [\"allow\"], issue, (value) => {\n const allow = stringList(value.allow, \"packages.allow\", error);\n allow?.forEach((name, index) => {\n if (globallyAllowed(name)) {\n issue({\n path: `packages.allow[${index}]`,\n code: \"redundant_allowlist\",\n message: `${name} is already globally allowed. Remove it from packages.allow.`,\n severity: \"warning\",\n });\n } else if (!packageName(name)) {\n error(\n `packages.allow[${index}]`,\n \"invalid_format\",\n \"Expected a lowercase npm package name. Use a package name such as lucide-react or @scope/name.\",\n );\n }\n });\n });\n nested(input.outbound, \"outbound\", [\"allow\"], issue, (value) => {\n const allow = stringList(value.allow, \"outbound.allow\", error);\n allow?.forEach((host, index) => {\n if (!hostname(host)) {\n error(\n `outbound.allow[${index}]`,\n \"invalid_format\",\n \"Expected a hostname or leading *. subdomain pattern without a scheme, path, port or IP. Use a value such as api.example.com.\",\n );\n }\n });\n });\n budgets(input.budgets, issue, error);\n analytics(input.analytics, issue, error);\n return {\n valid: !issues.some((entry) => entry.severity === \"error\"),\n issues,\n };\n}\n\ntype AddError = (\n path: string,\n code: ConfigIssue[\"code\"],\n message: string,\n) => void;\n\nfunction requiredSlug(value: unknown, path: string, error: AddError) {\n if (!nonblank(value)) {\n error(\n path,\n value === undefined ? \"missing_required\" : \"invalid_type\",\n `Expected a non-empty storefront slug. Set ${path} to the slug used by createZappie().`,\n );\n }\n}\n\nfunction theme(value: unknown, error: AddError) {\n if (typeof value !== \"string\") {\n error(\n \"theme\",\n value === undefined ? \"missing_required\" : \"invalid_type\",\n \"Expected a relative .json path. Set theme to a value such as ./theme.json.\",\n );\n } else if (\n !/\\.json$/.test(value) ||\n /^(?:[a-z][a-z\\d+.-]*:|\\/|\\\\)/i.test(value) ||\n value.includes(\"\\\\\")\n ) {\n error(\n \"theme\",\n \"invalid_format\",\n \"Expected a relative .json path. Use a value such as ./theme.json.\",\n );\n }\n}\n\nfunction stringList(\n value: unknown,\n path: string,\n error: AddError,\n options: { nonempty?: boolean; unique?: boolean; slug?: boolean } = {},\n) {\n if (value === undefined) {\n return undefined;\n }\n if (\n !Array.isArray(value) ||\n value.some((entry) => typeof entry !== \"string\")\n ) {\n error(\n path,\n \"invalid_type\",\n `Expected an array of strings. Set ${path} to string values.`,\n );\n return undefined;\n }\n if (options.nonempty && value.length === 0) {\n error(\n path,\n \"invalid_format\",\n `Expected at least one value. Add a value to ${path} or remove the field.`,\n );\n }\n value.forEach((entry, index) => {\n if (options.slug && !nonblank(entry)) {\n error(\n `${path}[${index}]`,\n \"invalid_format\",\n \"Expected a non-empty branch slug. Remove the empty value or use a branch slug.\",\n );\n }\n });\n if (options.unique && new Set(value).size !== value.length) {\n error(\n path,\n \"duplicate\",\n `Expected unique values. Remove duplicates from ${path}.`,\n );\n }\n return value;\n}\n\nfunction paths(value: unknown, error: AddError) {\n const values = stringList(value, \"prerender\", error, { unique: true });\n values?.forEach((path, index) => {\n if (!path.startsWith(\"/\")) {\n error(\n `prerender[${index}]`,\n \"invalid_format\",\n \"Expected a route starting with /. Prefix the route with /.\",\n );\n }\n });\n}\n"],
5
- "mappings": ";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,WAAW,SAAS,cAAc;AAG3C,IAAM,WAAW;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,eAAe,OAAkC;AAC/D,QAAM,SAAwB,CAAC;AAC/B,QAAM,QAAQ,CAAC,UAAuB,OAAO,KAAK,KAAK;AACvD,QAAM,QAAQ,CAAC,MAAc,MAA2B,YACtD,MAAM,EAAE,MAAM,MAAM,SAAS,UAAU,QAAQ,CAAC;AAClD,MAAI,CAAC,OAAO,KAAK,GAAG;AAClB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,QAAQ;AAAA,QACN;AAAA,UACE,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SACE;AAAA,UACF,UAAU;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,cAAY,OAAO,UAAU,IAAI,KAAK;AACtC,eAAa,MAAM,OAAO,SAAS,KAAK;AACxC,QAAM,MAAM,OAAO,KAAK;AACxB,aAAW,MAAM,UAAU,YAAY,OAAO;AAAA,IAC5C,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,EACR,CAAC;AACD,QAAM,MAAM,WAAW,KAAK;AAC5B,SAAO,MAAM,MAAM,QAAQ,CAAC,kBAAkB,GAAG,OAAO,CAAC,UAAU;AACjE,eAAW,MAAM,kBAAkB,yBAAyB,KAAK;AAAA,EACnE,CAAC;AACD,SAAO,MAAM,UAAU,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC,UAAU;AAC9D,UAAM,QAAQ,WAAW,MAAM,OAAO,kBAAkB,KAAK;AAC7D,WAAO,QAAQ,CAAC,MAAM,UAAU;AAC9B,UAAI,gBAAgB,IAAI,GAAG;AACzB,cAAM;AAAA,UACJ,MAAM,kBAAkB,KAAK;AAAA,UAC7B,MAAM;AAAA,UACN,SAAS,GAAG,IAAI;AAAA,UAChB,UAAU;AAAA,QACZ,CAAC;AAAA,MACH,WAAW,CAAC,YAAY,IAAI,GAAG;AAC7B;AAAA,UACE,kBAAkB,KAAK;AAAA,UACvB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACD,SAAO,MAAM,UAAU,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC,UAAU;AAC9D,UAAM,QAAQ,WAAW,MAAM,OAAO,kBAAkB,KAAK;AAC7D,WAAO,QAAQ,CAAC,MAAM,UAAU;AAC9B,UAAI,CAAC,SAAS,IAAI,GAAG;AACnB;AAAA,UACE,kBAAkB,KAAK;AAAA,UACvB;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACD,UAAQ,MAAM,SAAS,OAAO,KAAK;AACnC,YAAU,MAAM,WAAW,OAAO,KAAK;AACvC,SAAO;AAAA,IACL,OAAO,CAAC,OAAO,KAAK,CAAC,UAAU,MAAM,aAAa,OAAO;AAAA,IACzD;AAAA,EACF;AACF;AAQA,SAAS,aAAa,OAAgB,MAAc,OAAiB;AACnE,MAAI,CAAC,SAAS,KAAK,GAAG;AACpB;AAAA,MACE;AAAA,MACA,UAAU,SAAY,qBAAqB;AAAA,MAC3C,6CAA6C,IAAI;AAAA,IACnD;AAAA,EACF;AACF;AAEA,SAAS,MAAM,OAAgB,OAAiB;AAC9C,MAAI,OAAO,UAAU,UAAU;AAC7B;AAAA,MACE;AAAA,MACA,UAAU,SAAY,qBAAqB;AAAA,MAC3C;AAAA,IACF;AAAA,EACF,WACE,CAAC,UAAU,KAAK,KAAK,KACrB,gCAAgC,KAAK,KAAK,KAC1C,MAAM,SAAS,IAAI,GACnB;AACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,WACP,OACA,MACA,OACA,UAAoE,CAAC,GACrE;AACA,MAAI,UAAU,QAAW;AACvB,WAAO;AAAA,EACT;AACA,MACE,CAAC,MAAM,QAAQ,KAAK,KACpB,MAAM,KAAK,CAAC,UAAU,OAAO,UAAU,QAAQ,GAC/C;AACA;AAAA,MACE;AAAA,MACA;AAAA,MACA,qCAAqC,IAAI;AAAA,IAC3C;AACA,WAAO;AAAA,EACT;AACA,MAAI,QAAQ,YAAY,MAAM,WAAW,GAAG;AAC1C;AAAA,MACE;AAAA,MACA;AAAA,MACA,+CAA+C,IAAI;AAAA,IACrD;AAAA,EACF;AACA,QAAM,QAAQ,CAAC,OAAO,UAAU;AAC9B,QAAI,QAAQ,QAAQ,CAAC,SAAS,KAAK,GAAG;AACpC;AAAA,QACE,GAAG,IAAI,IAAI,KAAK;AAAA,QAChB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACD,MAAI,QAAQ,UAAU,IAAI,IAAI,KAAK,EAAE,SAAS,MAAM,QAAQ;AAC1D;AAAA,MACE;AAAA,MACA;AAAA,MACA,kDAAkD,IAAI;AAAA,IACxD;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,MAAM,OAAgB,OAAiB;AAC9C,QAAM,SAAS,WAAW,OAAO,aAAa,OAAO,EAAE,QAAQ,KAAK,CAAC;AACrE,UAAQ,QAAQ,CAAC,MAAM,UAAU;AAC/B,QAAI,CAAC,KAAK,WAAW,GAAG,GAAG;AACzB;AAAA,QACE,aAAa,KAAK;AAAA,QAClB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;",
4
+ "sourcesContent": ["import {\n globallyAllowed,\n hostname,\n nonblank,\n packageName,\n record,\n unknownKeys,\n} from \"./helpers\";\nimport { analytics, budgets, nested } from \"./sections\";\nimport type { ConfigIssue, ConfigValidation } from \"./types\";\n\nconst rootKeys = [\n \"store\",\n \"branches\",\n \"theme\",\n \"prerender\",\n \"menu\",\n \"packages\",\n \"outbound\",\n \"budgets\",\n \"readiness\",\n \"analytics\",\n] as const;\n\n/**\n * Validate a site configuration and collect errors and warnings without changing it.\n * Checks required fields, unknown keys, allowlists, budgets and analytics settings.\n *\n * @param input - The configuration value to inspect.\n * @returns Diagnostics and a valid flag that is true when there are no errors.\n */\nexport function validateConfig(input: unknown): ConfigValidation {\n const issues: ConfigIssue[] = [];\n const issue = (value: ConfigIssue) => issues.push(value);\n const error = (path: string, code: ConfigIssue[\"code\"], message: string) =>\n issue({ path, code, message, severity: \"error\" });\n if (!record(input)) {\n return {\n valid: false,\n issues: [\n {\n path: \"$\",\n code: \"invalid_type\",\n message:\n \"Expected a configuration object. Export an object from zappie.config.ts.\",\n severity: \"error\",\n },\n ],\n };\n }\n unknownKeys(input, rootKeys, \"\", issue);\n requiredSlug(input.store, \"store\", error);\n theme(input.theme, error);\n stringList(input.branches, \"branches\", error, {\n nonempty: true,\n unique: true,\n slug: true,\n });\n paths(input.prerender, error);\n nested(input.menu, \"menu\", [\"hiddenCategories\"], issue, (value) => {\n stringList(value.hiddenCategories, \"menu.hiddenCategories\", error);\n });\n nested(input.packages, \"packages\", [\"allow\"], issue, (value) => {\n const allow = stringList(value.allow, \"packages.allow\", error);\n allow?.forEach((name, index) => {\n if (globallyAllowed(name)) {\n issue({\n path: `packages.allow[${index}]`,\n code: \"redundant_allowlist\",\n message: `${name} is already globally allowed. Remove it from packages.allow.`,\n severity: \"warning\",\n });\n } else if (!packageName(name)) {\n error(\n `packages.allow[${index}]`,\n \"invalid_format\",\n \"Expected a lowercase npm package name. Use a package name such as lucide-react or @scope/name.\",\n );\n }\n });\n });\n nested(input.outbound, \"outbound\", [\"allow\"], issue, (value) => {\n const allow = stringList(value.allow, \"outbound.allow\", error);\n allow?.forEach((host, index) => {\n if (!hostname(host)) {\n error(\n `outbound.allow[${index}]`,\n \"invalid_format\",\n \"Expected a hostname or leading *. subdomain pattern without a scheme, path, port or IP. Use a value such as api.example.com.\",\n );\n }\n });\n });\n budgets(input.budgets, issue, error);\n nested(\n input.readiness,\n \"readiness\",\n [\"routeDiscovery\"],\n issue,\n (readiness) => {\n nested(\n readiness.routeDiscovery,\n \"readiness.routeDiscovery\",\n [\"optOut\"],\n issue,\n (routeDiscovery) => {\n nested(\n routeDiscovery.optOut,\n \"readiness.routeDiscovery.optOut\",\n [\"reason\"],\n issue,\n (optOut) => {\n if (typeof optOut.reason !== \"string\" || !optOut.reason.trim()) {\n error(\n \"readiness.routeDiscovery.optOut.reason\",\n \"invalid_format\",\n \"Expected a non-blank reason. Explain why the site disables initial route discovery.\",\n );\n }\n },\n );\n },\n );\n },\n );\n analytics(input.analytics, issue, error);\n return {\n valid: !issues.some((entry) => entry.severity === \"error\"),\n issues,\n };\n}\n\ntype AddError = (\n path: string,\n code: ConfigIssue[\"code\"],\n message: string,\n) => void;\n\nfunction requiredSlug(value: unknown, path: string, error: AddError) {\n if (!nonblank(value)) {\n error(\n path,\n value === undefined ? \"missing_required\" : \"invalid_type\",\n `Expected a non-empty storefront slug. Set ${path} to the slug used by createZappie().`,\n );\n }\n}\n\nfunction theme(value: unknown, error: AddError) {\n if (typeof value !== \"string\") {\n error(\n \"theme\",\n value === undefined ? \"missing_required\" : \"invalid_type\",\n \"Expected a relative .json path. Set theme to a value such as ./theme.json.\",\n );\n } else if (\n !/\\.json$/.test(value) ||\n /^(?:[a-z][a-z\\d+.-]*:|\\/|\\\\)/i.test(value) ||\n value.includes(\"\\\\\")\n ) {\n error(\n \"theme\",\n \"invalid_format\",\n \"Expected a relative .json path. Use a value such as ./theme.json.\",\n );\n }\n}\n\nfunction stringList(\n value: unknown,\n path: string,\n error: AddError,\n options: { nonempty?: boolean; unique?: boolean; slug?: boolean } = {},\n) {\n if (value === undefined) {\n return undefined;\n }\n if (\n !Array.isArray(value) ||\n value.some((entry) => typeof entry !== \"string\")\n ) {\n error(\n path,\n \"invalid_type\",\n `Expected an array of strings. Set ${path} to string values.`,\n );\n return undefined;\n }\n if (options.nonempty && value.length === 0) {\n error(\n path,\n \"invalid_format\",\n `Expected at least one value. Add a value to ${path} or remove the field.`,\n );\n }\n value.forEach((entry, index) => {\n if (options.slug && !nonblank(entry)) {\n error(\n `${path}[${index}]`,\n \"invalid_format\",\n \"Expected a non-empty branch slug. Remove the empty value or use a branch slug.\",\n );\n }\n });\n if (options.unique && new Set(value).size !== value.length) {\n error(\n path,\n \"duplicate\",\n `Expected unique values. Remove duplicates from ${path}.`,\n );\n }\n return value;\n}\n\nfunction paths(value: unknown, error: AddError) {\n const values = stringList(value, \"prerender\", error, { unique: true });\n values?.forEach((path, index) => {\n if (!path.startsWith(\"/\")) {\n error(\n `prerender[${index}]`,\n \"invalid_format\",\n \"Expected a route starting with /. Prefix the route with /.\",\n );\n }\n });\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,7 @@
1
- import { T as Transport, l as ZappieErrorCode, d as CartModule, S as StorageLike, i as StoreResource, M as MenuResource, j as ZappieEvents, C as CartLineInput, k as Cache } from '../client-RFcLoZ04.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-Bm2cCYea.js';
2
2
  import { S as Store, a as Menu, F as Formatters } from '../models-Rg5kQUkv.js';
3
3
  export { VERSION } from '../index.js';
4
+ import { d as CartModule, b as CartLineInput } from '../types-CuDvfW9g.js';
4
5
 
5
6
  /** Store and branch menus supplied to the deterministic mock transport. */
6
7
  type MockFixtures = {
@@ -24,6 +25,7 @@ declare function createMockZappie(input: MockFixtures, opts?: MockOptions): Read
24
25
  transport?: Transport;
25
26
  baseUrl?: string;
26
27
  fetch?: typeof fetch;
28
+ customerBase?: string;
27
29
  storage?: StorageLike;
28
30
  cart?: {
29
31
  ttl?: number;
@@ -17,6 +17,7 @@ import {
17
17
  text
18
18
  } from "./mappers.js";
19
19
  var MAX_RETRY_WAIT_MS = 5e3;
20
+ var wireFulfilment = (value) => value === "dineIn" ? "dine_in" : value;
20
21
  var RestTransport = class {
21
22
  constructor(opts = {}) {
22
23
  const url = new URL(opts.baseUrl ?? "https://api.zappie.ai/storefront/v1");
@@ -149,6 +150,7 @@ var RestTransport = class {
149
150
  do {
150
151
  const query = new URLSearchParams({
151
152
  branch: ctx.branchId,
153
+ ...ctx.fulfilment ? { fulfilment: wireFulfilment(ctx.fulfilment) } : {},
152
154
  ...ctx.metadataOnly ? { metadata_only: "true" } : {},
153
155
  ...cursor ? { cursor } : {}
154
156
  });
@@ -279,7 +281,10 @@ var RestTransport = class {
279
281
  const value = record(
280
282
  await this.json(
281
283
  await this.request(
282
- `/menus/version?${new URLSearchParams({ branch: ctx.branchId })}`,
284
+ `/menus/version?${new URLSearchParams({
285
+ branch: ctx.branchId,
286
+ ...ctx.fulfilment ? { fulfilment: wireFulfilment(ctx.fulfilment) } : {}
287
+ })}`,
283
288
  ctx
284
289
  )
285
290
  )
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/transport/rest.ts"],
4
- "sourcesContent": ["import type { QuoteRequest, WireQuote } from \"../cart/types\";\nimport { ZappieError, wireError } from \"../errors\";\nimport { delay, throwIfAborted } from \"../internal/abort\";\nimport { payloadKey } from \"../internal/digest\";\nimport { money } from \"../money\";\nimport {\n hours,\n isBoolean,\n isString,\n list,\n mapItem,\n mapMenu,\n mapMoney,\n mapSeo,\n opt,\n record,\n text,\n} from \"./mappers\";\nimport type { SelectedMenuRequest } from \"./selected-menu\";\nimport type {\n MenuResponse,\n MenuVersion,\n RequestCtx,\n SearchRequest,\n Transport,\n} from \"./types\";\n/** The longest Retry-After the SDK waits out before surfacing the error instead. */\nconst MAX_RETRY_WAIT_MS = 5_000;\n/** REST connection options. A custom fetch can provide tracing or mock responses. */\nexport type RestTransportOptions = { baseUrl?: string; fetch?: typeof fetch };\n/** REST read adapter for the versioned storefront API. */\nexport class RestTransport implements Transport {\n readonly baseUrl: string;\n private fetcher: typeof fetch;\n constructor(opts: RestTransportOptions = {}) {\n const url = new URL(opts.baseUrl ?? \"https://api.zappie.ai/storefront/v1\");\n if (\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n (url.protocol !== \"https:\" &&\n !(\n url.protocol === \"http:\" &&\n [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)\n ))\n ) {\n throw new ZappieError(\n \"invalid_request\",\n \"REST baseUrl requires HTTPS, or HTTP on localhost\",\n );\n }\n this.baseUrl = url.href.replace(/\\/$/, \"\");\n // Called as this.fetcher(), a stored global fetch would run with the\n // transport as `this`, which Workers refuses (\"Illegal invocation\").\n this.fetcher =\n opts.fetch ?? ((input, init) => globalThis.fetch(input, init));\n }\n private async request(\n path: string,\n ctx: RequestCtx,\n etag?: string,\n body?: QuoteRequest,\n ): Promise<Response> {\n if (typeof window !== \"undefined\" && ctx.key.startsWith(\"sk_\")) {\n throw new ZappieError(\n \"invalid_request\",\n \"Secret keys cannot be sent from a browser\",\n );\n }\n for (let attempt = 0; ; attempt++) {\n throwIfAborted(ctx.signal);\n let response: Response;\n try {\n response = await this.fetcher(this.baseUrl + path, {\n method: body ? \"POST\" : \"GET\",\n ...(body ? { body: JSON.stringify(body) } : {}),\n // Workers reject redirect: \"error\"; a redirect is still refused below\n // as a non-OK response (a 3xx, or opaqueredirect in browsers).\n redirect: \"manual\",\n headers: {\n ...(body ? { \"Content-Type\": \"application/json\" } : {}),\n ...(ctx.idempotencyKey\n ? { \"Idempotency-Key\": ctx.idempotencyKey }\n : {}),\n Authorization: `Bearer ${ctx.key}`,\n \"Zappie-Version\": ctx.version,\n ...(etag ? { \"If-None-Match\": etag } : {}),\n },\n ...(ctx.signal ? { signal: ctx.signal } : {}),\n });\n } catch (error) {\n throwIfAborted(ctx.signal);\n if (attempt >= 2) {\n throw new ZappieError(\n \"network_error\",\n \"Could not reach the storefront service\",\n { cause: error },\n );\n }\n await delay(100 * 2 ** attempt + Math.random() * 100, ctx.signal);\n continue;\n }\n if (response.ok || response.status === 304) {\n return response;\n }\n const retry = response.headers.get(\"Retry-After\");\n const seconds = retry !== null ? Number(retry) : NaN;\n const wait =\n retry !== null\n ? Number.isFinite(seconds)\n ? Math.max(0, seconds * 1000)\n : Math.max(0, Date.parse(retry) - Date.now())\n : NaN;\n // A longer wait than this is the caller's decision, not a silently stalled read.\n const tooLong = Number.isFinite(wait) && wait > MAX_RETRY_WAIT_MS;\n if (\n attempt < 2 &&\n !tooLong &&\n (response.status === 429 || response.status >= 500)\n ) {\n await response.body?.cancel();\n await delay(\n Number.isFinite(wait)\n ? wait\n : 100 * 2 ** attempt + Math.random() * 100,\n ctx.signal,\n );\n continue;\n }\n const errorBody = await response.json().catch(() => ({}));\n throw wireError(\n errorBody,\n response.status,\n response.headers.get(\"Zappie-Request-Id\") ?? undefined,\n );\n }\n }\n private async json(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch (cause) {\n if (cause instanceof Error && cause.name === \"AbortError\") {\n throw cause;\n }\n throw new ZappieError(\n \"server_error\",\n \"The storefront service returned invalid JSON\",\n { status: response.status, cause },\n );\n }\n }\n async quote(ctx: RequestCtx, body: QuoteRequest): Promise<WireQuote> {\n body = (await import(\"./quote-body\")).quoteBody(body);\n const request = {\n ...ctx,\n idempotencyKey:\n ctx.idempotencyKey ?? (await payloadKey(JSON.stringify(body))),\n };\n const raw = await this.json(\n await this.request(\"/quote\", request, undefined, body),\n );\n // The cart's quote client validates and maps the response.\n return raw as WireQuote;\n }\n async getStore(ctx: RequestCtx) {\n const mapper = import(\"./store\").catch((cause) => {\n throw new ZappieError(\n \"server_error\",\n \"Could not load the storefront store mapper\",\n { cause },\n );\n });\n const response = this.request(\"/store\", ctx).then((value) =>\n this.json(value),\n );\n const [{ mapStore }, raw] = await Promise.all([mapper, response]);\n return mapStore(\n raw,\n record,\n text,\n list,\n hours,\n mapMoney,\n money,\n opt,\n isString,\n isBoolean,\n mapSeo,\n );\n }\n async getMenu(\n ctx: RequestCtx & {\n branchId: string;\n ifNoneMatch?: string;\n metadataOnly?: boolean;\n },\n ): Promise<MenuResponse> {\n let cursor: string | undefined;\n const seen = new Set<string>();\n let output: ReturnType<typeof mapMenu> | undefined;\n let etag: string | undefined;\n do {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.metadataOnly ? { metadata_only: \"true\" } : {}),\n ...(cursor ? { cursor } : {}),\n });\n const response = await this.request(\n `/menus?${query}`,\n ctx,\n cursor ? undefined : ctx.ifNoneMatch,\n );\n if (response.status === 304) {\n if (cursor) {\n throw new ZappieError(\n \"server_error\",\n \"Unexpected 304 on a continuation page\",\n );\n }\n return {\n notModified: true,\n ...(response.headers.get(\"ETag\")\n ? { etag: response.headers.get(\"ETag\")! }\n : {}),\n };\n }\n const raw = record(await this.json(response));\n const page = mapMenu(raw, ctx.branchId);\n if (ctx.metadataOnly) {\n if (Object.keys(page.items).length || raw.next_cursor) {\n throw new ZappieError(\"server_error\", \"Invalid metadata response\");\n }\n return { menu: { ...page, completeness: \"partial\" } };\n }\n if (!output) {\n output = page;\n etag = response.headers.get(\"ETag\") ?? undefined;\n } else {\n if (page.version !== output.version) {\n throw new ZappieError(\n \"server_error\",\n \"Menu changed while fetching pages; retry the read\",\n );\n }\n Object.assign(output.items, page.items);\n for (const category of page.categories) {\n const existing = output.categories.find((c) => c.id === category.id);\n if (existing) {\n existing.itemIds = [\n ...new Set([...existing.itemIds, ...category.itemIds]),\n ];\n } else {\n output.categories.push(category);\n }\n }\n for (const section of page.menus ?? []) {\n const existing = output.menus?.find((m) => m.id === section.id);\n if (existing) {\n existing.categoryIds = [\n ...new Set([...existing.categoryIds, ...section.categoryIds]),\n ];\n } else {\n output.menus?.push(section);\n }\n }\n }\n cursor =\n typeof raw.next_cursor === \"string\" && raw.next_cursor\n ? raw.next_cursor\n : undefined;\n if (cursor) {\n if (seen.has(cursor)) {\n throw new ZappieError(\n \"server_error\",\n \"Repeated menu pagination cursor\",\n );\n }\n seen.add(cursor);\n }\n } while (cursor);\n if (output.menus && output.menus.length > 1) {\n output.id = `branch:${ctx.branchId}`;\n output.name = \"Catalogue\";\n delete output.schedule;\n }\n for (const category of output.categories) {\n for (const id of category.itemIds) {\n if (!Object.prototype.hasOwnProperty.call(output.items, id)) {\n throw new ZappieError(\n \"server_error\",\n \"Category refers to a missing item\",\n );\n }\n }\n }\n if (Object.keys(output.items).length > 2000) {\n console.warn(\"Zappie menu contains more than 2,000 items\");\n }\n return { menu: output, ...(etag ? { etag } : {}) };\n }\n async getSelectedMenu(ctx: SelectedMenuRequest) {\n return (await import(\"./selected-menu\")).selectedMenu(\n ctx,\n (path) => this.request(path, ctx, ctx.ifNoneMatch),\n (response) => this.json(response),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid selected menu\"),\n );\n }\n async search(ctx: SearchRequest) {\n return (await import(\"./search\")).searchPage(\n ctx,\n async (path) => this.json(await this.request(path, ctx)),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid search page\"),\n );\n }\n async getCategoryPage(\n ctx: RequestCtx & {\n branchId: string;\n menuId: string;\n categoryId: string;\n cursor?: string;\n },\n ) {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.cursor ? { cursor: ctx.cursor } : {}),\n });\n const raw = await this.json(\n await this.request(\n `/menus/${encodeURIComponent(ctx.menuId)}/categories/${encodeURIComponent(ctx.categoryId)}/items?${query}`,\n ctx,\n ),\n );\n // Loaded on first use: only progressive catalogues read pages.\n return (await import(\"./category-page\")).categoryPage(\n record(raw),\n ctx,\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid category page\"),\n );\n }\n async getMenuVersion(\n ctx: RequestCtx & { branchId: string },\n ): Promise<MenuVersion> {\n const value = record(\n await this.json(\n await this.request(\n `/menus/version?${new URLSearchParams({ branch: ctx.branchId })}`,\n ctx,\n ),\n ),\n );\n if (\n typeof value.menu_version !== \"string\" ||\n typeof value.availability_hash !== \"string\"\n ) {\n throw new ZappieError(\"server_error\", \"Invalid menu version response\");\n }\n return {\n version: value.menu_version,\n availabilityHash: value.availability_hash,\n };\n }\n async getItem(ctx: RequestCtx & { branchId: string; itemId: string }) {\n const item = mapItem(\n await this.json(\n await this.request(\n `/items/${encodeURIComponent(ctx.itemId)}?${new URLSearchParams({ branch: ctx.branchId })}`,\n ctx,\n ),\n ),\n );\n if (item.id !== ctx.itemId) {\n throw new ZappieError(\n \"server_error\",\n \"Item response identity does not match the request\",\n );\n }\n return item;\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { QuoteRequest, WireQuote } from \"../cart/types\";\nimport type { MenuFulfilment } from \"../client\";\nimport { ZappieError, wireError } from \"../errors\";\nimport { delay, throwIfAborted } from \"../internal/abort\";\nimport { payloadKey } from \"../internal/digest\";\nimport { money } from \"../money\";\nimport {\n hours,\n isBoolean,\n isString,\n list,\n mapItem,\n mapMenu,\n mapMoney,\n mapSeo,\n opt,\n record,\n text,\n} from \"./mappers\";\nimport type { SelectedMenuRequest } from \"./selected-menu\";\nimport type {\n MenuResponse,\n MenuVersion,\n RequestCtx,\n SearchRequest,\n Transport,\n} from \"./types\";\n/** The longest Retry-After the SDK waits out before surfacing the error instead. */\nconst MAX_RETRY_WAIT_MS = 5_000;\nconst wireFulfilment = (value: MenuFulfilment) =>\n value === \"dineIn\" ? \"dine_in\" : value;\n/** REST connection options. A custom fetch can provide tracing or mock responses. */\nexport type RestTransportOptions = { baseUrl?: string; fetch?: typeof fetch };\n/** REST read adapter for the versioned storefront API. */\nexport class RestTransport implements Transport {\n readonly baseUrl: string;\n private fetcher: typeof fetch;\n constructor(opts: RestTransportOptions = {}) {\n const url = new URL(opts.baseUrl ?? \"https://api.zappie.ai/storefront/v1\");\n if (\n url.username ||\n url.password ||\n url.search ||\n url.hash ||\n (url.protocol !== \"https:\" &&\n !(\n url.protocol === \"http:\" &&\n [\"localhost\", \"127.0.0.1\", \"[::1]\"].includes(url.hostname)\n ))\n ) {\n throw new ZappieError(\n \"invalid_request\",\n \"REST baseUrl requires HTTPS, or HTTP on localhost\",\n );\n }\n this.baseUrl = url.href.replace(/\\/$/, \"\");\n // Called as this.fetcher(), a stored global fetch would run with the\n // transport as `this`, which Workers refuses (\"Illegal invocation\").\n this.fetcher =\n opts.fetch ?? ((input, init) => globalThis.fetch(input, init));\n }\n private async request(\n path: string,\n ctx: RequestCtx,\n etag?: string,\n body?: QuoteRequest,\n ): Promise<Response> {\n if (typeof window !== \"undefined\" && ctx.key.startsWith(\"sk_\")) {\n throw new ZappieError(\n \"invalid_request\",\n \"Secret keys cannot be sent from a browser\",\n );\n }\n for (let attempt = 0; ; attempt++) {\n throwIfAborted(ctx.signal);\n let response: Response;\n try {\n response = await this.fetcher(this.baseUrl + path, {\n method: body ? \"POST\" : \"GET\",\n ...(body ? { body: JSON.stringify(body) } : {}),\n // Workers reject redirect: \"error\"; a redirect is still refused below\n // as a non-OK response (a 3xx, or opaqueredirect in browsers).\n redirect: \"manual\",\n headers: {\n ...(body ? { \"Content-Type\": \"application/json\" } : {}),\n ...(ctx.idempotencyKey\n ? { \"Idempotency-Key\": ctx.idempotencyKey }\n : {}),\n Authorization: `Bearer ${ctx.key}`,\n \"Zappie-Version\": ctx.version,\n ...(etag ? { \"If-None-Match\": etag } : {}),\n },\n ...(ctx.signal ? { signal: ctx.signal } : {}),\n });\n } catch (error) {\n throwIfAborted(ctx.signal);\n if (attempt >= 2) {\n throw new ZappieError(\n \"network_error\",\n \"Could not reach the storefront service\",\n { cause: error },\n );\n }\n await delay(100 * 2 ** attempt + Math.random() * 100, ctx.signal);\n continue;\n }\n if (response.ok || response.status === 304) {\n return response;\n }\n const retry = response.headers.get(\"Retry-After\");\n const seconds = retry !== null ? Number(retry) : NaN;\n const wait =\n retry !== null\n ? Number.isFinite(seconds)\n ? Math.max(0, seconds * 1000)\n : Math.max(0, Date.parse(retry) - Date.now())\n : NaN;\n // A longer wait than this is the caller's decision, not a silently stalled read.\n const tooLong = Number.isFinite(wait) && wait > MAX_RETRY_WAIT_MS;\n if (\n attempt < 2 &&\n !tooLong &&\n (response.status === 429 || response.status >= 500)\n ) {\n await response.body?.cancel();\n await delay(\n Number.isFinite(wait)\n ? wait\n : 100 * 2 ** attempt + Math.random() * 100,\n ctx.signal,\n );\n continue;\n }\n const errorBody = await response.json().catch(() => ({}));\n throw wireError(\n errorBody,\n response.status,\n response.headers.get(\"Zappie-Request-Id\") ?? undefined,\n );\n }\n }\n private async json(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch (cause) {\n if (cause instanceof Error && cause.name === \"AbortError\") {\n throw cause;\n }\n throw new ZappieError(\n \"server_error\",\n \"The storefront service returned invalid JSON\",\n { status: response.status, cause },\n );\n }\n }\n async quote(ctx: RequestCtx, body: QuoteRequest): Promise<WireQuote> {\n body = (await import(\"./quote-body\")).quoteBody(body);\n const request = {\n ...ctx,\n idempotencyKey:\n ctx.idempotencyKey ?? (await payloadKey(JSON.stringify(body))),\n };\n const raw = await this.json(\n await this.request(\"/quote\", request, undefined, body),\n );\n // The cart's quote client validates and maps the response.\n return raw as WireQuote;\n }\n async getStore(ctx: RequestCtx) {\n const mapper = import(\"./store\").catch((cause) => {\n throw new ZappieError(\n \"server_error\",\n \"Could not load the storefront store mapper\",\n { cause },\n );\n });\n const response = this.request(\"/store\", ctx).then((value) =>\n this.json(value),\n );\n const [{ mapStore }, raw] = await Promise.all([mapper, response]);\n return mapStore(\n raw,\n record,\n text,\n list,\n hours,\n mapMoney,\n money,\n opt,\n isString,\n isBoolean,\n mapSeo,\n );\n }\n async getMenu(\n ctx: RequestCtx & {\n branchId: string;\n ifNoneMatch?: string;\n metadataOnly?: boolean;\n fulfilment?: MenuFulfilment;\n },\n ): Promise<MenuResponse> {\n let cursor: string | undefined;\n const seen = new Set<string>();\n let output: ReturnType<typeof mapMenu> | undefined;\n let etag: string | undefined;\n do {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.fulfilment\n ? { fulfilment: wireFulfilment(ctx.fulfilment) }\n : {}),\n ...(ctx.metadataOnly ? { metadata_only: \"true\" } : {}),\n ...(cursor ? { cursor } : {}),\n });\n const response = await this.request(\n `/menus?${query}`,\n ctx,\n cursor ? undefined : ctx.ifNoneMatch,\n );\n if (response.status === 304) {\n if (cursor) {\n throw new ZappieError(\n \"server_error\",\n \"Unexpected 304 on a continuation page\",\n );\n }\n return {\n notModified: true,\n ...(response.headers.get(\"ETag\")\n ? { etag: response.headers.get(\"ETag\")! }\n : {}),\n };\n }\n const raw = record(await this.json(response));\n const page = mapMenu(raw, ctx.branchId);\n if (ctx.metadataOnly) {\n if (Object.keys(page.items).length || raw.next_cursor) {\n throw new ZappieError(\"server_error\", \"Invalid metadata response\");\n }\n return { menu: { ...page, completeness: \"partial\" } };\n }\n if (!output) {\n output = page;\n etag = response.headers.get(\"ETag\") ?? undefined;\n } else {\n if (page.version !== output.version) {\n throw new ZappieError(\n \"server_error\",\n \"Menu changed while fetching pages; retry the read\",\n );\n }\n Object.assign(output.items, page.items);\n for (const category of page.categories) {\n const existing = output.categories.find((c) => c.id === category.id);\n if (existing) {\n existing.itemIds = [\n ...new Set([...existing.itemIds, ...category.itemIds]),\n ];\n } else {\n output.categories.push(category);\n }\n }\n for (const section of page.menus ?? []) {\n const existing = output.menus?.find((m) => m.id === section.id);\n if (existing) {\n existing.categoryIds = [\n ...new Set([...existing.categoryIds, ...section.categoryIds]),\n ];\n } else {\n output.menus?.push(section);\n }\n }\n }\n cursor =\n typeof raw.next_cursor === \"string\" && raw.next_cursor\n ? raw.next_cursor\n : undefined;\n if (cursor) {\n if (seen.has(cursor)) {\n throw new ZappieError(\n \"server_error\",\n \"Repeated menu pagination cursor\",\n );\n }\n seen.add(cursor);\n }\n } while (cursor);\n if (output.menus && output.menus.length > 1) {\n output.id = `branch:${ctx.branchId}`;\n output.name = \"Catalogue\";\n delete output.schedule;\n }\n for (const category of output.categories) {\n for (const id of category.itemIds) {\n if (!Object.prototype.hasOwnProperty.call(output.items, id)) {\n throw new ZappieError(\n \"server_error\",\n \"Category refers to a missing item\",\n );\n }\n }\n }\n if (Object.keys(output.items).length > 2000) {\n console.warn(\"Zappie menu contains more than 2,000 items\");\n }\n return { menu: output, ...(etag ? { etag } : {}) };\n }\n async getSelectedMenu(ctx: SelectedMenuRequest) {\n return (await import(\"./selected-menu\")).selectedMenu(\n ctx,\n (path) => this.request(path, ctx, ctx.ifNoneMatch),\n (response) => this.json(response),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid selected menu\"),\n );\n }\n async search(ctx: SearchRequest) {\n return (await import(\"./search\")).searchPage(\n ctx,\n async (path) => this.json(await this.request(path, ctx)),\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid search page\"),\n );\n }\n async getCategoryPage(\n ctx: RequestCtx & {\n branchId: string;\n menuId: string;\n categoryId: string;\n cursor?: string;\n },\n ) {\n const query = new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.cursor ? { cursor: ctx.cursor } : {}),\n });\n const raw = await this.json(\n await this.request(\n `/menus/${encodeURIComponent(ctx.menuId)}/categories/${encodeURIComponent(ctx.categoryId)}/items?${query}`,\n ctx,\n ),\n );\n // Loaded on first use: only progressive catalogues read pages.\n return (await import(\"./category-page\")).categoryPage(\n record(raw),\n ctx,\n mapMenu,\n () => new ZappieError(\"server_error\", \"Invalid category page\"),\n );\n }\n async getMenuVersion(\n ctx: RequestCtx & { branchId: string; fulfilment?: MenuFulfilment },\n ): Promise<MenuVersion> {\n const value = record(\n await this.json(\n await this.request(\n `/menus/version?${new URLSearchParams({\n branch: ctx.branchId,\n ...(ctx.fulfilment\n ? { fulfilment: wireFulfilment(ctx.fulfilment) }\n : {}),\n })}`,\n ctx,\n ),\n ),\n );\n if (\n typeof value.menu_version !== \"string\" ||\n typeof value.availability_hash !== \"string\"\n ) {\n throw new ZappieError(\"server_error\", \"Invalid menu version response\");\n }\n return {\n version: value.menu_version,\n availabilityHash: value.availability_hash,\n };\n }\n async getItem(ctx: RequestCtx & { branchId: string; itemId: string }) {\n const item = mapItem(\n await this.json(\n await this.request(\n `/items/${encodeURIComponent(ctx.itemId)}?${new URLSearchParams({ branch: ctx.branchId })}`,\n ctx,\n ),\n ),\n );\n if (item.id !== ctx.itemId) {\n throw new ZappieError(\n \"server_error\",\n \"Item response identity does not match the request\",\n );\n }\n return item;\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,5 +1,6 @@
1
1
  import { a as Menu } from '../models-Rg5kQUkv.js';
2
- import { q as SearchRequest, r as SearchPage } from '../client-RFcLoZ04.js';
2
+ import { j as SearchRequest, k as SearchPage } from '../client-Bm2cCYea.js';
3
+ import '../types-CuDvfW9g.js';
3
4
 
4
5
  /** Loaded only when search is used; request authentication/retries stay in REST. */
5
6
  declare function searchPage(ctx: SearchRequest, read: (path: string) => Promise<unknown>, mapMenu: (value: unknown, branchId: string) => Menu, invalid: () => Error): Promise<SearchPage>;
@@ -1,2 +1,3 @@
1
- export { Y as SelectedMenu, o as SelectedMenuRequest, p as SelectedMenuResponse, _ as SelectedMenuTransport, $ as createSelectedMenuReader, a0 as selectedMenu, a1 as selectedMenuKey } from '../client-RFcLoZ04.js';
1
+ export { z as SelectedMenu, h as SelectedMenuRequest, i as SelectedMenuResponse, B as SelectedMenuTransport, D as createSelectedMenuReader, F as selectedMenu, G as selectedMenuKey } from '../client-Bm2cCYea.js';
2
2
  import '../models-Rg5kQUkv.js';
3
+ import '../types-CuDvfW9g.js';