@zappie/sdk 0.1.7 → 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @zappie/sdk
2
2
 
3
+ ## 0.1.8
4
+
5
+ ### Patch Changes
6
+
7
+ - 4f3cdc3: Add `quote.feesTotal`, and `startingPrice(item)` and `showsFromPrice(item)` in the new `@zappie/sdk/pricing` entry point. Type the place route's `distance_meters` and new `duration_seconds` as optional, and pass both through.
8
+
3
9
  ## 0.1.7
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -58,6 +58,7 @@ Prices always come from Zappie. The cart shows a local estimate while it waits,
58
58
  | `@zappie/sdk/customer` and `/customer/react` | Signed-in customers, their orders and saved addresses |
59
59
  | `@zappie/sdk/integrations` | Public integration IDs and browser keys |
60
60
  | `@zappie/sdk/places` | Address search and delivery-zone checks |
61
+ | `@zappie/sdk/pricing` | An item's starting price and whether to show it as "From" |
61
62
  | `@zappie/sdk/server` | Server-side reads, storefront URLs and `verifyWebhook()` |
62
63
  | `@zappie/sdk/template` and `/template/react` | Template settings and responsive images for template authors |
63
64
  | `@zappie/sdk/testing` | A mock client for your tests |
@@ -80,6 +81,31 @@ The SDK is at `0.x`. Its API can change in a minor release while it settles. Eac
80
81
 
81
82
  MIT
82
83
 
84
+ ### Quote fees
85
+
86
+ `quote.feesTotal` is the sum of `quote.fees`, so a template can print one
87
+ "Fees" line without adding money itself. It leaves delivery out (that is
88
+ `quote.delivery`) and is zero when there are no fees. Each quote line has
89
+ `optionsTotal` when the API prices its options.
90
+
91
+ ### Starting price
92
+
93
+ `startingPrice(item)` from `@zappie/sdk/pricing` returns the price an item starts from: the cheapest
94
+ available variant plus the cheapest choice of each required add-on group. It is
95
+ the rule Zappie uses for a product's starting price. When no variant is
96
+ available, it uses the cheapest variant. `showsFromPrice(item)` says whether to
97
+ show that price as "From": available variants differ in price, or a required
98
+ add-on raises it. `item.price` is the branch price, not the starting price.
99
+
100
+ The API sends one `available` flag per item and variant, and a free-text
101
+ `stockLabel`. It doesn't say whether an item is sold out or unavailable for
102
+ another reason, so `isAvailable` covers both.
103
+
104
+ ### Place routes
105
+
106
+ `places.place(placeId, { branchId })` returns `route` with optional
107
+ `distance_meters` and `duration_seconds`, as the API sends them.
108
+
83
109
  ### Maps browser key
84
110
 
85
111
  `createIntegrations().get()` from `@zappie/sdk/integrations` returns optional
@@ -1,5 +1,5 @@
1
1
  import { M as Money, I as Item, a as Menu } from '../models-D__QbqEd.js';
2
- import { C as CartLine } from '../types-B-iTMrVV.js';
2
+ import { C as CartLine } from '../types-gQXU2gEA.js';
3
3
 
4
4
  /** A line that can move to the destination branch, with its old and new estimates. */
5
5
  type BranchSwitchKeptLine = {
@@ -1,6 +1,6 @@
1
- import { Z as Zappie } from '../client-iVMHfAFn.js';
1
+ import { Z as Zappie } from '../client-DzUauU-b.js';
2
2
  import { I as Item } from '../models-D__QbqEd.js';
3
- import '../types-B-iTMrVV.js';
3
+ import '../types-gQXU2gEA.js';
4
4
 
5
5
  /** Register an item already read by a partial/selected catalogue, without fetching it again. */
6
6
  declare function prepareItem(client: Zappie, item: Item, options: {
@@ -103,6 +103,7 @@ function mapQuote(value, branchId) {
103
103
  return fail();
104
104
  }
105
105
  const deliveryCharges = charges.filter((c) => c.code === "delivery");
106
+ const fees = charges.filter((c) => c.code !== "delivery");
106
107
  const integer = (value2) => {
107
108
  if (!Number.isSafeInteger(value2) || Number(value2) < 0) {
108
109
  return fail();
@@ -151,7 +152,11 @@ function mapQuote(value, branchId) {
151
152
  ...v.lines_unpriced === true ? { linesUnpriced: true } : {},
152
153
  subtotal,
153
154
  total,
154
- fees: charges.filter((c) => c.code !== "delivery"),
155
+ fees,
156
+ feesTotal: sumMoney(
157
+ fees.map((c) => c.amount),
158
+ money(0, subtotal.currency)
159
+ ),
155
160
  discounts,
156
161
  ...deliveryCharges.length ? {
157
162
  delivery: sumMoney(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/cart/quote-response.ts"],
4
- "sourcesContent": ["// Quote responses load lazily with the quote client, outside the synchronous core.\nimport { ZappieError } from \"../errors\";\nimport { sumMoney } from \"../internal/money-math\";\nimport { money } from \"../money\";\nimport { mapMoney, record } from \"../transport/mappers\";\nimport type { CartFix, CartFulfilment, CartIssue, Quote } from \"./types\";\nfunction fail(): never {\n throw new ZappieError(\"server_error\", \"Invalid quote response\");\n}\nfunction text(value: unknown): string {\n if (typeof value !== \"string\") {\n return fail();\n }\n return value;\n}\nfunction list(value: unknown): unknown[] {\n if (!Array.isArray(value)) {\n return fail();\n }\n return value;\n}\n/** A fix newer than this SDK is dropped (undefined); a malformed known fix still fails. */\nfunction fix(value: unknown): CartFix | undefined {\n const v = record(value);\n switch (v.action) {\n case \"remove_line\":\n case \"accept_price\":\n case \"remove_promo\":\n case \"change_address\":\n case \"choose_region\":\n return { action: v.action };\n case \"choose_option\":\n case \"choose_other_option\":\n return { action: v.action, groupId: text(v.group_id) };\n case \"set_quantity\": {\n if (\n !Number.isInteger(v.quantity) ||\n Number(v.quantity) < 1 ||\n Number(v.quantity) > 99\n ) {\n return fail();\n }\n return { action: v.action, quantity: v.quantity as number };\n }\n case \"schedule\":\n return {\n action: v.action,\n ...(v.next_open_at ? { nextOpenAt: text(v.next_open_at) } : {}),\n ...(v.slots ? { slots: list(v.slots).map(text) } : {}),\n };\n case \"switch_fulfilment\": {\n const types = list(v.types)\n .map(text)\n .filter((t) => [\"pickup\", \"delivery\", \"dine_in\"].includes(t));\n return types.length\n ? { action: v.action, types: types as CartFulfilment[\"type\"][] }\n : undefined;\n }\n default:\n return undefined;\n }\n}\nconst codes = [\n \"item_unavailable\",\n \"item_not_found\",\n \"option_unavailable\",\n \"option_required\",\n \"option_limit\",\n \"quantity_limit\",\n \"price_changed\",\n \"branch_closed\",\n \"fulfilment_unavailable\",\n \"outside_delivery_zone\",\n \"delivery_region_required\",\n \"schedule_invalid\",\n \"promo_invalid\",\n \"promo_min_spend\",\n \"min_order\",\n \"menu_version_stale\",\n];\nexport function mapQuote(value: unknown, branchId: string): Quote {\n const v = record(value);\n if (\n v.branch_id !== branchId ||\n !Number.isFinite(Date.parse(text(v.expires_at)))\n ) {\n return fail();\n }\n const subtotal = mapMoney(v.subtotal);\n const checkout = record(v.checkout);\n if (\n typeof v.can_checkout !== \"boolean\" ||\n typeof checkout.ready !== \"boolean\" ||\n (checkout.reason !== undefined &&\n checkout.reason !== \"issues\" &&\n checkout.reason !== \"customer_required\")\n ) {\n return fail();\n }\n const amount = (raw: unknown) => {\n const result = mapMoney(raw);\n if (result.currency !== subtotal.currency) {\n return fail();\n }\n return result;\n };\n const charge = (raw: unknown) => {\n const c = record(raw);\n return {\n code: text(c.code),\n label: text(c.label),\n amount: amount(c.amount),\n };\n };\n const charges = list(v.charges).map(charge);\n const discounts = list(v.discounts).map(charge);\n const total = amount(v.total);\n if (\n sumMoney(\n [...charges, ...discounts].map((c) => c.amount),\n subtotal,\n ).amountMinor !== total.amountMinor\n ) {\n return fail();\n }\n const deliveryCharges = charges.filter((c) => c.code === \"delivery\");\n const integer = (value: unknown) => {\n if (!Number.isSafeInteger(value) || Number(value) < 0) {\n return fail();\n }\n return value as number;\n };\n const fulfilment =\n v.fulfilment === undefined ? undefined : record(v.fulfilment);\n if (\n v.menu_version_stale !== undefined &&\n typeof v.menu_version_stale !== \"boolean\"\n ) {\n return fail();\n }\n const lines = list(v.lines).map((raw) => {\n const l = record(raw);\n if (\n (l.unit_price == null || l.line_total == null) &&\n v.lines_unpriced !== true\n ) {\n return fail();\n }\n return {\n ...(l.item_id !== undefined ? { itemId: text(l.item_id) } : {}),\n ...(l.name !== undefined ? { name: text(l.name) } : {}),\n ...(l.quantity !== undefined ? { quantity: integer(l.quantity) } : {}),\n ...(l.status !== undefined ? { status: text(l.status) } : {}),\n ...(l.options_total !== undefined\n ? {\n optionsTotal:\n l.options_total === null ? null : amount(l.options_total),\n }\n : {}),\n lineId: text(l.line_id),\n unitPrice: l.unit_price == null ? null : amount(l.unit_price),\n total: l.line_total == null ? null : amount(l.line_total),\n };\n });\n if (\n v.lines_unpriced !== true &&\n sumMoney(\n lines.map((line) => line.total!),\n money(0, subtotal.currency),\n ).amountMinor !== subtotal.amountMinor\n ) {\n return fail();\n }\n return {\n ...(v.menu_version_stale !== undefined\n ? { menuVersionStale: v.menu_version_stale as boolean }\n : {}),\n ...(fulfilment\n ? {\n fulfilment: {\n type: text(fulfilment.type),\n scheduledFor:\n fulfilment.scheduled_for === null\n ? null\n : text(fulfilment.scheduled_for),\n ...(fulfilment.eta_minutes !== undefined\n ? { etaMinutes: list(fulfilment.eta_minutes).map(integer) }\n : {}),\n ...(fulfilment.delivery_region_id !== undefined\n ? { deliveryRegionId: text(fulfilment.delivery_region_id) }\n : {}),\n },\n }\n : {}),\n lines,\n ...(v.lines_unpriced === true ? { linesUnpriced: true } : {}),\n subtotal,\n total,\n fees: charges.filter((c) => c.code !== \"delivery\"),\n discounts,\n ...(deliveryCharges.length\n ? {\n delivery: sumMoney(\n deliveryCharges.map((c) => c.amount),\n money(0, subtotal.currency),\n ),\n }\n : {}),\n menuVersion: text(v.menu_version),\n expiresAt: text(v.expires_at),\n issues: list(v.issues).flatMap((raw): CartIssue[] => {\n const i = record(raw);\n const code = text(i.code);\n const severity = text(i.severity);\n const known =\n codes.includes(code) &&\n [\"blocking\", \"warning\", \"info\"].includes(severity);\n const fixes = list(i.fixes)\n .map(fix)\n .filter((f): f is CartFix => f !== undefined);\n const lineId = i.line_id ? text(i.line_id) : undefined;\n if (!known && (severity === \"warning\" || severity === \"info\")) {\n console.warn(\n `Zappie quote issue \"${code}\" is newer than this SDK and was skipped`,\n );\n return [];\n }\n // A blocking (or unknown-severity) issue newer than the SDK keeps checkout blocked.\n return [\n {\n code: known ? (code as CartIssue[\"code\"]) : \"item_unavailable\",\n severity: known ? (severity as CartIssue[\"severity\"]) : \"blocking\",\n message: text(i.message),\n source: \"quote\",\n ...(lineId ? { lineId } : {}),\n ...(i.group_id ? { groupId: text(i.group_id) } : {}),\n ...(i.data || !known\n ? {\n data: {\n ...(i.data ? record(i.data) : {}),\n ...(known ? {} : { code }),\n },\n }\n : {}),\n fixes:\n !known && !fixes.length && lineId\n ? [{ action: \"remove_line\" }]\n : fixes,\n },\n ];\n }),\n canCheckout: v.can_checkout,\n checkout: {\n ready: checkout.ready,\n ...(checkout.reason !== undefined ? { reason: checkout.reason } : {}),\n },\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// Quote responses load lazily with the quote client, outside the synchronous core.\nimport { ZappieError } from \"../errors\";\nimport { sumMoney } from \"../internal/money-math\";\nimport { money } from \"../money\";\nimport { mapMoney, record } from \"../transport/mappers\";\nimport type { CartFix, CartFulfilment, CartIssue, Quote } from \"./types\";\nfunction fail(): never {\n throw new ZappieError(\"server_error\", \"Invalid quote response\");\n}\nfunction text(value: unknown): string {\n if (typeof value !== \"string\") {\n return fail();\n }\n return value;\n}\nfunction list(value: unknown): unknown[] {\n if (!Array.isArray(value)) {\n return fail();\n }\n return value;\n}\n/** A fix newer than this SDK is dropped (undefined); a malformed known fix still fails. */\nfunction fix(value: unknown): CartFix | undefined {\n const v = record(value);\n switch (v.action) {\n case \"remove_line\":\n case \"accept_price\":\n case \"remove_promo\":\n case \"change_address\":\n case \"choose_region\":\n return { action: v.action };\n case \"choose_option\":\n case \"choose_other_option\":\n return { action: v.action, groupId: text(v.group_id) };\n case \"set_quantity\": {\n if (\n !Number.isInteger(v.quantity) ||\n Number(v.quantity) < 1 ||\n Number(v.quantity) > 99\n ) {\n return fail();\n }\n return { action: v.action, quantity: v.quantity as number };\n }\n case \"schedule\":\n return {\n action: v.action,\n ...(v.next_open_at ? { nextOpenAt: text(v.next_open_at) } : {}),\n ...(v.slots ? { slots: list(v.slots).map(text) } : {}),\n };\n case \"switch_fulfilment\": {\n const types = list(v.types)\n .map(text)\n .filter((t) => [\"pickup\", \"delivery\", \"dine_in\"].includes(t));\n return types.length\n ? { action: v.action, types: types as CartFulfilment[\"type\"][] }\n : undefined;\n }\n default:\n return undefined;\n }\n}\nconst codes = [\n \"item_unavailable\",\n \"item_not_found\",\n \"option_unavailable\",\n \"option_required\",\n \"option_limit\",\n \"quantity_limit\",\n \"price_changed\",\n \"branch_closed\",\n \"fulfilment_unavailable\",\n \"outside_delivery_zone\",\n \"delivery_region_required\",\n \"schedule_invalid\",\n \"promo_invalid\",\n \"promo_min_spend\",\n \"min_order\",\n \"menu_version_stale\",\n];\nexport function mapQuote(value: unknown, branchId: string): Quote {\n const v = record(value);\n if (\n v.branch_id !== branchId ||\n !Number.isFinite(Date.parse(text(v.expires_at)))\n ) {\n return fail();\n }\n const subtotal = mapMoney(v.subtotal);\n const checkout = record(v.checkout);\n if (\n typeof v.can_checkout !== \"boolean\" ||\n typeof checkout.ready !== \"boolean\" ||\n (checkout.reason !== undefined &&\n checkout.reason !== \"issues\" &&\n checkout.reason !== \"customer_required\")\n ) {\n return fail();\n }\n const amount = (raw: unknown) => {\n const result = mapMoney(raw);\n if (result.currency !== subtotal.currency) {\n return fail();\n }\n return result;\n };\n const charge = (raw: unknown) => {\n const c = record(raw);\n return {\n code: text(c.code),\n label: text(c.label),\n amount: amount(c.amount),\n };\n };\n const charges = list(v.charges).map(charge);\n const discounts = list(v.discounts).map(charge);\n const total = amount(v.total);\n if (\n sumMoney(\n [...charges, ...discounts].map((c) => c.amount),\n subtotal,\n ).amountMinor !== total.amountMinor\n ) {\n return fail();\n }\n const deliveryCharges = charges.filter((c) => c.code === \"delivery\");\n const fees = charges.filter((c) => c.code !== \"delivery\");\n const integer = (value: unknown) => {\n if (!Number.isSafeInteger(value) || Number(value) < 0) {\n return fail();\n }\n return value as number;\n };\n const fulfilment =\n v.fulfilment === undefined ? undefined : record(v.fulfilment);\n if (\n v.menu_version_stale !== undefined &&\n typeof v.menu_version_stale !== \"boolean\"\n ) {\n return fail();\n }\n const lines = list(v.lines).map((raw) => {\n const l = record(raw);\n if (\n (l.unit_price == null || l.line_total == null) &&\n v.lines_unpriced !== true\n ) {\n return fail();\n }\n return {\n ...(l.item_id !== undefined ? { itemId: text(l.item_id) } : {}),\n ...(l.name !== undefined ? { name: text(l.name) } : {}),\n ...(l.quantity !== undefined ? { quantity: integer(l.quantity) } : {}),\n ...(l.status !== undefined ? { status: text(l.status) } : {}),\n ...(l.options_total !== undefined\n ? {\n optionsTotal:\n l.options_total === null ? null : amount(l.options_total),\n }\n : {}),\n lineId: text(l.line_id),\n unitPrice: l.unit_price == null ? null : amount(l.unit_price),\n total: l.line_total == null ? null : amount(l.line_total),\n };\n });\n if (\n v.lines_unpriced !== true &&\n sumMoney(\n lines.map((line) => line.total!),\n money(0, subtotal.currency),\n ).amountMinor !== subtotal.amountMinor\n ) {\n return fail();\n }\n return {\n ...(v.menu_version_stale !== undefined\n ? { menuVersionStale: v.menu_version_stale as boolean }\n : {}),\n ...(fulfilment\n ? {\n fulfilment: {\n type: text(fulfilment.type),\n scheduledFor:\n fulfilment.scheduled_for === null\n ? null\n : text(fulfilment.scheduled_for),\n ...(fulfilment.eta_minutes !== undefined\n ? { etaMinutes: list(fulfilment.eta_minutes).map(integer) }\n : {}),\n ...(fulfilment.delivery_region_id !== undefined\n ? { deliveryRegionId: text(fulfilment.delivery_region_id) }\n : {}),\n },\n }\n : {}),\n lines,\n ...(v.lines_unpriced === true ? { linesUnpriced: true } : {}),\n subtotal,\n total,\n fees,\n feesTotal: sumMoney(\n fees.map((c) => c.amount),\n money(0, subtotal.currency),\n ),\n discounts,\n ...(deliveryCharges.length\n ? {\n delivery: sumMoney(\n deliveryCharges.map((c) => c.amount),\n money(0, subtotal.currency),\n ),\n }\n : {}),\n menuVersion: text(v.menu_version),\n expiresAt: text(v.expires_at),\n issues: list(v.issues).flatMap((raw): CartIssue[] => {\n const i = record(raw);\n const code = text(i.code);\n const severity = text(i.severity);\n const known =\n codes.includes(code) &&\n [\"blocking\", \"warning\", \"info\"].includes(severity);\n const fixes = list(i.fixes)\n .map(fix)\n .filter((f): f is CartFix => f !== undefined);\n const lineId = i.line_id ? text(i.line_id) : undefined;\n if (!known && (severity === \"warning\" || severity === \"info\")) {\n console.warn(\n `Zappie quote issue \"${code}\" is newer than this SDK and was skipped`,\n );\n return [];\n }\n // A blocking (or unknown-severity) issue newer than the SDK keeps checkout blocked.\n return [\n {\n code: known ? (code as CartIssue[\"code\"]) : \"item_unavailable\",\n severity: known ? (severity as CartIssue[\"severity\"]) : \"blocking\",\n message: text(i.message),\n source: \"quote\",\n ...(lineId ? { lineId } : {}),\n ...(i.group_id ? { groupId: text(i.group_id) } : {}),\n ...(i.data || !known\n ? {\n data: {\n ...(i.data ? record(i.data) : {}),\n ...(known ? {} : { code }),\n },\n }\n : {}),\n fixes:\n !known && !fixes.length && lineId\n ? [{ action: \"remove_line\" }]\n : fixes,\n },\n ];\n }),\n canCheckout: v.can_checkout,\n checkout: {\n ready: checkout.ready,\n ...(checkout.reason !== undefined ? { reason: checkout.reason } : {}),\n },\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["value"]
7
7
  }
@@ -1,5 +1,5 @@
1
- import { T as Transport, R as RequestCtx } from '../client-iVMHfAFn.js';
2
- import { Q as QuoteRequest, a as Quote } from '../types-B-iTMrVV.js';
1
+ import { T as Transport, R as RequestCtx } from '../client-DzUauU-b.js';
2
+ import { Q as QuoteRequest, a as Quote } from '../types-gQXU2gEA.js';
3
3
  import '../models-D__QbqEd.js';
4
4
 
5
5
  type QuoteClientOptions = {
@@ -1,5 +1,5 @@
1
1
  import { a as Menu, C as CategoryPage, S as Store, I as Item, M as Money, F as Formatters } from './models-D__QbqEd.js';
2
- import { Q as QuoteRequest, g as WireQuote, i as CartIssueCode, d as CartModule, b as CartLineInput } from './types-B-iTMrVV.js';
2
+ import { Q as QuoteRequest, g as WireQuote, i as CartIssueCode, d as CartModule, b as CartLineInput } from './types-gQXU2gEA.js';
3
3
 
4
4
  /**
5
5
  * The store, branch, order context and source menu that identify a saved catalogue.
package/dist/debug.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { b as CartLineInput, a as Quote } from './types-B-iTMrVV.js';
1
+ import { b as CartLineInput, a as Quote } from './types-gQXU2gEA.js';
2
2
  import { M as Money, a as Menu, B as Branch } from './models-D__QbqEd.js';
3
- import { Z as Zappie } from './client-iVMHfAFn.js';
3
+ import { Z as Zappie } from './client-DzUauU-b.js';
4
4
 
5
5
  type StoredLine = CartLineInput & {
6
6
  id: string;
package/dist/debug.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/debug.ts
2
- var VERSION = true ? "0.1.7" : "0.0.0-development";
2
+ var VERSION = true ? "0.1.8" : "0.0.0-development";
3
3
  function installDebug(client) {
4
4
  if (typeof window === "undefined" || client.mode !== "test") {
5
5
  return;
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { T as Transport, R as RequestCtx, f as MenuFulfilment, g as MenuResponse, h as SelectedMenuRequest, i as SelectedMenuResponse, j as SearchRequest, k as SearchPage, l as MenuVersion, a as ZappieError } from './client-iVMHfAFn.js';
2
- export { A as API_VERSION, E as ErrorDetails, m as MenuReadOptions, M as MenuResource, n as ReadOptions, S as StorageLike, c as StoreResource, Z as Zappie, b as ZappieClientOptions, b as ZappieConfig, e as ZappieErrorCode, d as ZappieEvents, o as createZappie, p as isZappieError } from './client-iVMHfAFn.js';
1
+ import { T as Transport, R as RequestCtx, f as MenuFulfilment, g as MenuResponse, h as SelectedMenuRequest, i as SelectedMenuResponse, j as SearchRequest, k as SearchPage, l as MenuVersion, a as ZappieError } from './client-DzUauU-b.js';
2
+ export { A as API_VERSION, E as ErrorDetails, m as MenuReadOptions, M as MenuResource, n as ReadOptions, S as StorageLike, c as StoreResource, Z as Zappie, b as ZappieClientOptions, b as ZappieConfig, e as ZappieErrorCode, d as ZappieEvents, o as createZappie, p as isZappieError } from './client-DzUauU-b.js';
3
3
  import { S as Store, C as CategoryPage, I as Item, O as OptionGroup, M as Money, a as Menu, B as Branch } from './models-D__QbqEd.js';
4
4
  export { b as Category, c as Choice, d as Currency, F as Formatters, e as Fulfilment, f as MenuSchedule, g as MenuSection, h as MoneyFormatOptions, i as OpeningTime, j as OptionCondition, P as PaymentMethodKind, k as SpecialHours, V as Variant, l as format, m as money } from './models-D__QbqEd.js';
5
- import { Q as QuoteRequest, g as WireQuote, f as CartIssue, b as CartLineInput, C as CartLine } from './types-B-iTMrVV.js';
6
- export { h as CartFix, c as CartFulfilment, i as CartIssueCode, d as CartModule, j as CartSnapshot, e as CartState, k as CartSyncAdapter, l as CartSyncDocument, D as DeliveryAddress, a as Quote, m as WireFix, W as WireFulfilment, n as WireIssue, o as WireLine, p as WireMoney } from './types-B-iTMrVV.js';
5
+ import { Q as QuoteRequest, g as WireQuote, f as CartIssue, b as CartLineInput, C as CartLine } from './types-gQXU2gEA.js';
6
+ export { h as CartFix, c as CartFulfilment, i as CartIssueCode, d as CartModule, j as CartSnapshot, e as CartState, k as CartSyncAdapter, l as CartSyncDocument, D as DeliveryAddress, a as Quote, m as WireFix, W as WireFulfilment, n as WireIssue, o as WireLine, p as WireMoney } from './types-gQXU2gEA.js';
7
7
 
8
8
  /** REST connection options. A custom fetch can provide tracing or mock responses. */
9
9
  type RestTransportOptions = {
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ import { previewItem } from "./cart/preview.js";
10
10
  import { estimateUnitPrice } from "./cart/price.js";
11
11
  import { resolveSourceMenu } from "./cart/source-menu.js";
12
12
  import { validateLine } from "./cart/validate.js";
13
- var VERSION = true ? "0.1.7" : "0.0.0-development";
13
+ var VERSION = true ? "0.1.8" : "0.0.0-development";
14
14
  export {
15
15
  API_VERSION,
16
16
  RestTransport,
@@ -1,6 +1,6 @@
1
- import { R as RequestCtx } from '../client-iVMHfAFn.js';
1
+ import { R as RequestCtx } from '../client-DzUauU-b.js';
2
2
  import '../models-D__QbqEd.js';
3
- import '../types-B-iTMrVV.js';
3
+ import '../types-gQXU2gEA.js';
4
4
 
5
5
  /** Public integration IDs and browser keys. Your app decides when to load scripts. */
6
6
  type Integrations = {
@@ -1,5 +1,5 @@
1
- import { c as CartFulfilment, W as WireFulfilment } from './types-B-iTMrVV.js';
2
- export { v as validateConfig } from './client-iVMHfAFn.js';
1
+ import { c as CartFulfilment, W as WireFulfilment } from './types-gQXU2gEA.js';
2
+ export { v as validateConfig } from './client-DzUauU-b.js';
3
3
  import './models-D__QbqEd.js';
4
4
 
5
5
  /**
@@ -1,3 +1,3 @@
1
- export { q as CATALOGUE_MAX_AGE_MS, r as CATALOGUE_MAX_BYTES, s as CatalogueScope, t as SavedCatalogue, u as catalogueDigest, w as catalogueKey, x as createCatalogueStorage, y as createSavedCatalogue } from '../client-iVMHfAFn.js';
1
+ export { q as CATALOGUE_MAX_AGE_MS, r as CATALOGUE_MAX_BYTES, s as CatalogueScope, t as SavedCatalogue, u as catalogueDigest, w as catalogueKey, x as createCatalogueStorage, y as createSavedCatalogue } from '../client-DzUauU-b.js';
2
2
  import '../models-D__QbqEd.js';
3
- import '../types-B-iTMrVV.js';
3
+ import '../types-gQXU2gEA.js';
@@ -9,7 +9,7 @@ type PlaceSuggestion = {
9
9
  secondary?: string;
10
10
  };
11
11
  /**
12
- * A resolved place with address text, coordinates and an optional route distance in metres.
12
+ * A resolved place with address text, coordinates and, when you pass a branch, the route from that branch.
13
13
  */
14
14
  type Place = {
15
15
  place_id: string;
@@ -18,8 +18,12 @@ type Place = {
18
18
  country?: string;
19
19
  lat: number;
20
20
  lng: number;
21
+ /** The route from the branch. Each field is absent when the API doesn't send it. */
21
22
  route?: {
22
- distance_meters: number;
23
+ /** Road distance in metres. */
24
+ distance_meters?: number;
25
+ /** Travel time in seconds. */
26
+ duration_seconds?: number;
23
27
  };
24
28
  };
25
29
  /**
@@ -0,0 +1,116 @@
1
+ declare const MoneyBrand: unique symbol;
2
+ /** A normalized ISO 4217 currency code. */
3
+ type Currency = string;
4
+ /** A frozen amount in safe integer minor units. Create it with money(). */
5
+ type Money = {
6
+ readonly amountMinor: number;
7
+ readonly currency: Currency;
8
+ readonly [MoneyBrand]: true;
9
+ };
10
+
11
+ /** An exact variant with its selected attributes, price and availability. */
12
+ type Variant = {
13
+ /** A low-stock hint for shoppers. This does not give a stock balance. */
14
+ lowStock?: boolean;
15
+ id: string;
16
+ attributeChoices: Record<string, string>;
17
+ price: Money;
18
+ isAvailable: boolean;
19
+ };
20
+ /** Item details and choices, with at least one exact variant. */
21
+ type Item = {
22
+ /** Fulfilment modes returned for this item. */
23
+ fulfilment?: string[];
24
+ /** Exact source placements returned by targeted item reads. */
25
+ placements?: {
26
+ menuId: string;
27
+ categoryId: string;
28
+ }[];
29
+ /** Stock text for display. Use isAvailable and the server quote to check eligibility. */
30
+ stockLabel?: string;
31
+ promo?: {
32
+ name: string;
33
+ };
34
+ id: string;
35
+ name: string;
36
+ description?: string;
37
+ images: string[];
38
+ price: Money;
39
+ compareAtPrice?: Money;
40
+ isAvailable: boolean;
41
+ quantity: {
42
+ min: number;
43
+ max?: number;
44
+ step: number;
45
+ };
46
+ variants: [Variant, ...Variant[]];
47
+ optionGroups: OptionGroup[];
48
+ tags: string[];
49
+ };
50
+ /** Conditions that change option rules when selected choices match. */
51
+ type OptionCondition = {
52
+ groupId: string;
53
+ choiceIds: string[];
54
+ match: "any" | "all";
55
+ count?: {
56
+ op: "eq" | "gte" | "lte";
57
+ value: number;
58
+ };
59
+ setRequired?: boolean | null;
60
+ setMin?: number | null;
61
+ setMax?: number | null;
62
+ };
63
+ /** Addon or exact-variant attribute selection rules. */
64
+ type OptionGroup = {
65
+ id: string;
66
+ name: string;
67
+ helpText?: string;
68
+ kind: "select" | "text" | "number" | "date" | "time" | "toggle";
69
+ required: boolean;
70
+ isAvailable?: boolean;
71
+ isInformational?: boolean;
72
+ selection: {
73
+ min: number;
74
+ max?: number;
75
+ };
76
+ isAttribute: boolean;
77
+ choices: Choice[];
78
+ input?: {
79
+ maxLength?: number;
80
+ placeholder?: string;
81
+ };
82
+ conditions?: OptionCondition[];
83
+ };
84
+ /** A selectable option. Attribute prices are zero to avoid charging twice. */
85
+ type Choice = {
86
+ /** An attribute price adjustment for display. Cart calculations exclude it. */
87
+ priceHint?: Money;
88
+ id: string;
89
+ name: string;
90
+ price: Money;
91
+ isAvailable: boolean;
92
+ quantity?: {
93
+ min: number;
94
+ max?: number;
95
+ };
96
+ };
97
+
98
+ /**
99
+ * The lowest price a shopper can pay for one of this item, by the same rule
100
+ * Zappie uses for a product's starting price: the cheapest available variant
101
+ * plus the cheapest choice of each required add-on group. When no variant is
102
+ * available, the cheapest variant is used.
103
+ *
104
+ * @param item - The catalogue item.
105
+ * @returns A display price. Use a server quote for checkout totals.
106
+ */
107
+ declare function startingPrice(item: Item): Money;
108
+ /**
109
+ * Whether to show the starting price as "From": true when available variants
110
+ * have different prices, or a required add-on raises the starting price.
111
+ *
112
+ * @param item - The catalogue item.
113
+ */
114
+ declare function showsFromPrice(item: Item): boolean;
115
+
116
+ export { showsFromPrice, startingPrice };
@@ -0,0 +1,30 @@
1
+ // src/pricing/index.ts
2
+ import { sumMoney } from "../internal/money-math.js";
3
+ var cheapest = (prices) => prices.filter((price) => price.amountMinor >= 0).sort((a, b) => a.amountMinor - b.amountMinor)[0];
4
+ function startingPrice(item) {
5
+ const available = item.variants.filter((variant) => variant.isAvailable);
6
+ const base = cheapest(
7
+ (available.length ? available : item.variants).map(
8
+ (variant) => variant.price
9
+ )
10
+ ) ?? item.price;
11
+ return sumMoney(
12
+ item.optionGroups.flatMap(
13
+ (group) => !group.isAttribute && group.required && group.kind === "select" ? cheapest(group.choices.map((choice) => choice.price)) ?? [] : []
14
+ ),
15
+ base
16
+ );
17
+ }
18
+ function showsFromPrice(item) {
19
+ const prices = [
20
+ ...new Set(
21
+ item.variants.filter((variant) => variant.isAvailable).map((variant) => variant.price.amountMinor)
22
+ )
23
+ ];
24
+ return prices.length > 1 || prices.length === 1 && startingPrice(item).amountMinor > prices[0];
25
+ }
26
+ export {
27
+ showsFromPrice,
28
+ startingPrice
29
+ };
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/pricing/index.ts"],
4
+ "sourcesContent": ["import { sumMoney } from \"../internal/money-math\";\nimport type { Item } from \"../models\";\nimport type { Money } from \"../money\";\n\nconst cheapest = (prices: Money[]): Money | undefined =>\n prices\n .filter((price) => price.amountMinor >= 0)\n .sort((a, b) => a.amountMinor - b.amountMinor)[0];\n\n/**\n * The lowest price a shopper can pay for one of this item, by the same rule\n * Zappie uses for a product's starting price: the cheapest available variant\n * plus the cheapest choice of each required add-on group. When no variant is\n * available, the cheapest variant is used.\n *\n * @param item - The catalogue item.\n * @returns A display price. Use a server quote for checkout totals.\n */\nexport function startingPrice(item: Item): Money {\n const available = item.variants.filter((variant) => variant.isAvailable);\n const base =\n cheapest(\n (available.length ? available : item.variants).map(\n (variant) => variant.price,\n ),\n ) ?? item.price;\n return sumMoney(\n item.optionGroups.flatMap((group) =>\n !group.isAttribute && group.required && group.kind === \"select\"\n ? (cheapest(group.choices.map((choice) => choice.price)) ?? [])\n : [],\n ),\n base,\n );\n}\n\n/**\n * Whether to show the starting price as \"From\": true when available variants\n * have different prices, or a required add-on raises the starting price.\n *\n * @param item - The catalogue item.\n */\nexport function showsFromPrice(item: Item): boolean {\n const prices = [\n ...new Set(\n item.variants\n .filter((variant) => variant.isAvailable)\n .map((variant) => variant.price.amountMinor),\n ),\n ];\n return (\n prices.length > 1 ||\n (prices.length === 1 && startingPrice(item).amountMinor > prices[0]!)\n );\n}\n"],
5
+ "mappings": ";AAAA,SAAS,gBAAgB;AAIzB,IAAM,WAAW,CAAC,WAChB,OACG,OAAO,CAAC,UAAU,MAAM,eAAe,CAAC,EACxC,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC;AAW7C,SAAS,cAAc,MAAmB;AAC/C,QAAM,YAAY,KAAK,SAAS,OAAO,CAAC,YAAY,QAAQ,WAAW;AACvE,QAAM,OACJ;AAAA,KACG,UAAU,SAAS,YAAY,KAAK,UAAU;AAAA,MAC7C,CAAC,YAAY,QAAQ;AAAA,IACvB;AAAA,EACF,KAAK,KAAK;AACZ,SAAO;AAAA,IACL,KAAK,aAAa;AAAA,MAAQ,CAAC,UACzB,CAAC,MAAM,eAAe,MAAM,YAAY,MAAM,SAAS,WAClD,SAAS,MAAM,QAAQ,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC,KAAK,CAAC,IAC3D,CAAC;AAAA,IACP;AAAA,IACA;AAAA,EACF;AACF;AAQO,SAAS,eAAe,MAAqB;AAClD,QAAM,SAAS;AAAA,IACb,GAAG,IAAI;AAAA,MACL,KAAK,SACF,OAAO,CAAC,YAAY,QAAQ,WAAW,EACvC,IAAI,CAAC,YAAY,QAAQ,MAAM,WAAW;AAAA,IAC/C;AAAA,EACF;AACA,SACE,OAAO,SAAS,KACf,OAAO,WAAW,KAAK,cAAc,IAAI,EAAE,cAAc,OAAO,CAAC;AAEtE;",
6
+ "names": []
7
+ }
@@ -1,9 +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-D__QbqEd.js';
3
- import { a as ZappieError, Z as Zappie } from '../client-iVMHfAFn.js';
3
+ import { a as ZappieError, Z as Zappie } from '../client-DzUauU-b.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-B-iTMrVV.js';
6
+ import { d as CartModule, C as CartLine, c as CartFulfilment, e as CartState, f as CartIssue } from '../types-gQXU2gEA.js';
7
7
 
8
8
  /** Subscribe to storefront identity and branch-directory data. */
9
9
  declare function useStore(): {
@@ -1,7 +1,7 @@
1
1
  import { a as Menu, F as Formatters, S as Store } from '../models-D__QbqEd.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-iVMHfAFn.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-DzUauU-b.js';
3
3
  export { VERSION } from '../index.js';
4
- import { d as CartModule, b as CartLineInput } from '../types-B-iTMrVV.js';
4
+ import { d as CartModule, b as CartLineInput } from '../types-gQXU2gEA.js';
5
5
 
6
6
  /**
7
7
  * A URL string, URL object or Request you can parse as a storefront route.
@@ -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-iVMHfAFn.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
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-B-iTMrVV.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,6 +1,6 @@
1
1
  import { a as Menu } from '../models-D__QbqEd.js';
2
- import { j as SearchRequest, k as SearchPage } from '../client-iVMHfAFn.js';
3
- import '../types-B-iTMrVV.js';
2
+ import { j as SearchRequest, k as SearchPage } from '../client-DzUauU-b.js';
3
+ import '../types-gQXU2gEA.js';
4
4
 
5
5
  /** Loaded only when search is used; request authentication/retries stay in REST. */
6
6
  declare function searchPage(ctx: SearchRequest, read: (path: string) => Promise<unknown>, mapMenu: (value: unknown, branchId: string) => Menu, invalid: () => Error): Promise<SearchPage>;
@@ -1,3 +1,3 @@
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-iVMHfAFn.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-DzUauU-b.js';
2
2
  import '../models-D__QbqEd.js';
3
- import '../types-B-iTMrVV.js';
3
+ import '../types-gQXU2gEA.js';
@@ -142,6 +142,8 @@ type Quote = {
142
142
  label: string;
143
143
  amount: Money;
144
144
  }[];
145
+ /** The sum of `fees`, so you can show one fees line. Zero when there are none. Delivery is not included. */
146
+ feesTotal: Money;
145
147
  delivery?: Money;
146
148
  discounts: {
147
149
  code: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zappie/sdk",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Typed, framework-agnostic client for Zappie menus, carts, checkout, auth and events.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -61,6 +61,10 @@
61
61
  "types": "./dist/visits/index.d.ts",
62
62
  "import": "./dist/visits/index.js"
63
63
  },
64
+ "./pricing": {
65
+ "types": "./dist/pricing/index.d.ts",
66
+ "import": "./dist/pricing/index.js"
67
+ },
64
68
  "./template": {
65
69
  "types": "./dist/template/index.d.ts",
66
70
  "import": "./dist/template/index.js"
@@ -109,7 +113,7 @@
109
113
  "url": "https://github.com/zappie/storefront/issues"
110
114
  },
111
115
  "scripts": {
112
- "build": "tsup && tsup --config tsup.checkout.config.ts && tsup --config tsup.customer.config.ts && tsup --config tsup.places.config.ts && tsup --config tsup.url.config.ts && tsup --config tsup.visits.config.ts && tsup --config tsup.site-config.config.ts && tsup --config tsup.template.config.ts && node ../../scripts/release-packages/build-modules.mjs",
116
+ "build": "tsup && tsup --config tsup.checkout.config.ts && tsup --config tsup.customer.config.ts && tsup --config tsup.places.config.ts && tsup --config tsup.url.config.ts && tsup --config tsup.visits.config.ts && tsup --config tsup.pricing.config.ts && tsup --config tsup.site-config.config.ts && tsup --config tsup.template.config.ts && node ../../scripts/release-packages/build-modules.mjs",
113
117
  "test": "vitest run --root ../.. packages/sdk/test",
114
118
  "size": "node ../../scripts/release-packages/size-sdk.mjs && size-limit"
115
119
  }