@zappie/sdk 0.1.7 → 0.1.9

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,18 @@
1
1
  # @zappie/sdk
2
2
 
3
+ ## 0.1.9
4
+
5
+ ### Patch Changes
6
+
7
+ - e7cc5f0: New entry `@zappie/sdk/checkout-defaults`: the phone, delivery address, region and scheduled time a checkout starts from, by the default storefront's rule. It keeps what the shopper typed, remembers the last address on this device only, and re-runs on sign-in through `watchCheckoutDefaults()`.
8
+ - 1c3cbbf: `@zappie/sdk/pricing` now uses the `Item` and `Money` types from `@zappie/sdk` instead of its own copies. `startingPrice(item)` accepts an `Item` from `@zappie/sdk`, and `format.money(startingPrice(item))` type-checks.
9
+
10
+ ## 0.1.8
11
+
12
+ ### Patch Changes
13
+
14
+ - 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.
15
+
3
16
  ## 0.1.7
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -55,9 +55,11 @@ Prices always come from Zappie. The cart shows a local estimate while it waits,
55
55
  | `@zappie/sdk` | The client, the cart, money formatting and errors |
56
56
  | `@zappie/sdk/react` | `ZappieProvider` and hooks such as `useStore`, `useMenu` and `useCart` |
57
57
  | `@zappie/sdk/checkout` | Opening Zappie's hosted checkout and checking it completed |
58
+ | `@zappie/sdk/checkout-defaults` | The phone, address, region and time a signed-in checkout starts from |
58
59
  | `@zappie/sdk/customer` and `/customer/react` | Signed-in customers, their orders and saved addresses |
59
60
  | `@zappie/sdk/integrations` | Public integration IDs and browser keys |
60
61
  | `@zappie/sdk/places` | Address search and delivery-zone checks |
62
+ | `@zappie/sdk/pricing` | An item's starting price and whether to show it as "From" |
61
63
  | `@zappie/sdk/server` | Server-side reads, storefront URLs and `verifyWebhook()` |
62
64
  | `@zappie/sdk/template` and `/template/react` | Template settings and responsive images for template authors |
63
65
  | `@zappie/sdk/testing` | A mock client for your tests |
@@ -80,6 +82,31 @@ The SDK is at `0.x`. Its API can change in a minor release while it settles. Eac
80
82
 
81
83
  MIT
82
84
 
85
+ ### Quote fees
86
+
87
+ `quote.feesTotal` is the sum of `quote.fees`, so a template can print one
88
+ "Fees" line without adding money itself. It leaves delivery out (that is
89
+ `quote.delivery`) and is zero when there are no fees. Each quote line has
90
+ `optionsTotal` when the API prices its options.
91
+
92
+ ### Starting price
93
+
94
+ `startingPrice(item)` from `@zappie/sdk/pricing` returns the price an item starts from: the cheapest
95
+ available variant plus the cheapest choice of each required add-on group. It is
96
+ the rule Zappie uses for a product's starting price. When no variant is
97
+ available, it uses the cheapest variant. `showsFromPrice(item)` says whether to
98
+ show that price as "From": available variants differ in price, or a required
99
+ add-on raises it. `item.price` is the branch price, not the starting price.
100
+
101
+ The API sends one `available` flag per item and variant, and a free-text
102
+ `stockLabel`. It doesn't say whether an item is sold out or unavailable for
103
+ another reason, so `isAvailable` covers both.
104
+
105
+ ### Place routes
106
+
107
+ `places.place(placeId, { branchId })` returns `route` with optional
108
+ `distance_meters` and `duration_seconds`, as the API sends them.
109
+
83
110
  ### Maps browser key
84
111
 
85
112
  `createIntegrations().get()` from `@zappie/sdk/integrations` returns optional
@@ -99,3 +126,56 @@ anchors `#c-cat_<id>`. Already-prefixed IDs stay unchanged. Legacy cart,
99
126
  checkout and sign-in overlays become routes. Unsupported legacy UI parameters
100
127
  are dropped. Ad click IDs, `ref`, and every `utm_*` parameter retain their values.
101
128
  Payment-return and printed QR routes retain their existing parameters.
129
+
130
+ ### Checkout defaults
131
+
132
+ `@zappie/sdk/checkout-defaults` fills in checkout contact details the way the
133
+ default storefront does, so every template follows one rule.
134
+
135
+ - **Phone.** `defaultPhone(typed, customer)` keeps a number typed in this
136
+ session. Otherwise it shows the signed-in customer's masked number with
137
+ `fromAccount: true` and no `contactPhone`. Leave `contact_phone` out of the
138
+ checkout and Zappie uses the customer's verified number. Show the masked
139
+ number as a line or a placeholder, not as typed text.
140
+ - **Address.** `defaultAddress()` returns the last delivery address used on
141
+ this device, otherwise the customer's first saved address. Unless the fee
142
+ type is `"fixed"`, an address without a place ID counts as none. The cart's
143
+ own delivery address always wins in `checkoutDefaults()`.
144
+ - **Remembering.** `rememberAddress(address)` keeps the address line, place ID
145
+ and coordinates in `localStorage`, on this device only.
146
+ `rememberAddress(null)` forgets them. Both do nothing where storage is
147
+ blocked.
148
+ - **Region.** `defaultRegion(branch, chosen)` keeps a chosen region and
149
+ otherwise picks the branch's only one.
150
+ - **Time.** `restoreSchedule(fulfilment, branchId)` keeps the cart's scheduled
151
+ time when it belongs to this branch and parses. Drop it otherwise.
152
+ - **Applying.** `prefillFulfilment(fulfilment, branch)` fills the only region
153
+ and drops a stale time. It never changes the fulfilment type or address, and
154
+ returns the same object when nothing changes.
155
+ - **Sign-in.** `watchCheckoutDefaults(customer, read, listener)` publishes
156
+ `checkoutDefaults()` now and again each time the shopper signs in or out. On
157
+ sign-in it fetches `/me/addresses` first.
158
+
159
+ ```ts
160
+ import {
161
+ prefillFulfilment,
162
+ rememberAddress,
163
+ watchCheckoutDefaults,
164
+ } from "@zappie/sdk/checkout-defaults";
165
+
166
+ const stop = watchCheckoutDefaults(
167
+ customer,
168
+ () => ({ branch, fulfilment: cart.fulfilment, typedPhone: phone }),
169
+ (defaults) => {
170
+ setPhonePlaceholder(defaults.phone.display);
171
+ setAddressDraft(defaults.address);
172
+ const next = prefillFulfilment(cart.fulfilment, branch);
173
+ if (next && next !== cart.fulfilment) {
174
+ cart.setFulfilment(next);
175
+ }
176
+ },
177
+ );
178
+
179
+ // When the shopper picks a delivery address:
180
+ rememberAddress(address);
181
+ ```
@@ -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 = {
@@ -0,0 +1,131 @@
1
+ import { DeliveryAddress, Branch, CartFulfilment } from '@zappie/sdk';
2
+ import { SessionCustomer, SavedAddress, CustomerClient } from '@zappie/sdk/customer';
3
+
4
+ /**
5
+ * How the branch prices delivery. Only `"fixed"` lets an address without a place ID stand.
6
+ */
7
+ type DeliveryFeeType = "region" | "distance" | "fixed";
8
+ /**
9
+ * The browser storage the remembered address uses. Pass `null` to remember nothing.
10
+ */
11
+ type AddressStorage = Pick<Storage, "getItem" | "setItem" | "removeItem">;
12
+ /**
13
+ * The phone a checkout shows, and the `contact_phone` to send.
14
+ */
15
+ type PhoneDefault = {
16
+ /** The typed number, else the signed-in customer's masked number, else "". */
17
+ display: string;
18
+ /** The typed number. Absent when the account's number applies: checkout then uses the verified number. */
19
+ contactPhone?: string;
20
+ /** True when `display` is the signed-in customer's masked number. */
21
+ fromAccount: boolean;
22
+ };
23
+ /**
24
+ * What the defaults are worked out from.
25
+ */
26
+ type CheckoutDefaultsInput = {
27
+ branch: Branch;
28
+ /** The cart's fulfilment. Its type and its address are the shopper's choice and are kept. */
29
+ fulfilment: CartFulfilment | null;
30
+ customer?: SessionCustomer | null;
31
+ /** The signed-in customer's saved addresses, in the order `/me/addresses` returns them. */
32
+ savedAddresses?: readonly SavedAddress[];
33
+ /** The phone typed in this session. */
34
+ typedPhone?: string;
35
+ /** Defaults to `"distance"`, as the default storefront does. */
36
+ feeType?: DeliveryFeeType;
37
+ /** Defaults to `localStorage`. */
38
+ storage?: AddressStorage | null;
39
+ };
40
+ /**
41
+ * The contact details a checkout starts from.
42
+ */
43
+ type CheckoutDefaults = {
44
+ phone: PhoneDefault;
45
+ /** The cart's delivery address, else the last one used on this device, else the first saved one. */
46
+ address?: DeliveryAddress;
47
+ /** The cart's region, else the branch's only region. */
48
+ deliveryRegionId?: string;
49
+ /** The cart's scheduled time when it belongs to this branch and parses. */
50
+ scheduledFor?: string;
51
+ };
52
+ /**
53
+ * The phone to show: the one typed in this session, otherwise the signed-in customer's.
54
+ * The session holds only a masked number, so send no `contact_phone` for it.
55
+ *
56
+ * @param typed - The phone typed in this session.
57
+ * @param customer - The signed-in customer, or null.
58
+ */
59
+ declare function defaultPhone(typed: string | undefined, customer: SessionCustomer | null | undefined): PhoneDefault;
60
+ /**
61
+ * The last delivery address used on this device, or undefined. Under distance or region fees,
62
+ * an entry without a place ID counts as none and is removed.
63
+ *
64
+ * @param options - The fee type and storage.
65
+ */
66
+ declare function rememberedAddress(options?: {
67
+ feeType?: DeliveryFeeType;
68
+ storage?: AddressStorage | null;
69
+ }): DeliveryAddress | undefined;
70
+ /**
71
+ * Remember the delivery address the shopper used, on this device only. Stores the address
72
+ * line, place ID and coordinates, nothing else. Does nothing where storage is blocked.
73
+ *
74
+ * @param value - The address, or null to forget it.
75
+ * @param storage - Defaults to `localStorage`.
76
+ */
77
+ declare function rememberAddress(value: DeliveryAddress | null, storage?: AddressStorage | null): void;
78
+ /**
79
+ * The address to start from: the last one used on this device, otherwise the customer's
80
+ * first saved address. Under distance or region fees, an address without a place ID counts as none.
81
+ *
82
+ * @param options - Saved addresses, fee type and storage.
83
+ */
84
+ declare function defaultAddress(options?: {
85
+ savedAddresses?: readonly SavedAddress[];
86
+ feeType?: DeliveryFeeType;
87
+ storage?: AddressStorage | null;
88
+ }): DeliveryAddress | undefined;
89
+ /**
90
+ * The region to start from: the one already chosen, otherwise the branch's only region.
91
+ *
92
+ * @param branch - The cart's branch.
93
+ * @param chosen - The region already chosen.
94
+ */
95
+ declare function defaultRegion(branch: Branch, chosen?: string): string | undefined;
96
+ /**
97
+ * The cart's scheduled time if it belongs to this branch and parses, otherwise undefined.
98
+ * Drop a time this returns undefined for: it can never become valid here.
99
+ *
100
+ * @param fulfilment - The cart's fulfilment.
101
+ * @param branchId - The branch the shopper is ordering from.
102
+ */
103
+ declare function restoreSchedule(fulfilment: CartFulfilment | null, branchId: string): string | undefined;
104
+ /**
105
+ * The contact details a checkout starts from, by the default storefront's rule.
106
+ * It reads the cart and never changes it, so the fulfilment type stays the shopper's.
107
+ *
108
+ * @param input - The branch, cart fulfilment, customer and what was typed.
109
+ */
110
+ declare function checkoutDefaults(input: CheckoutDefaultsInput): CheckoutDefaults;
111
+ /**
112
+ * The cart fulfilment with the defaults applied: the only region filled in and a stale
113
+ * schedule dropped. Same type, same address. Returns the same object when nothing changes.
114
+ *
115
+ * @param fulfilment - The cart's fulfilment.
116
+ * @param branch - The cart's branch.
117
+ */
118
+ declare function prefillFulfilment<T extends CartFulfilment | null>(fulfilment: T, branch: Branch): T;
119
+ /**
120
+ * Work out the defaults now and again whenever the shopper signs in or out. On sign-in it
121
+ * fetches the saved addresses first. A failed fetch counts as none. A slower, older run never
122
+ * lands last.
123
+ *
124
+ * @param customer - The customer client.
125
+ * @param read - Returns the current inputs, read on each run.
126
+ * @param listener - Receives each result.
127
+ * @returns Stops listening.
128
+ */
129
+ declare function watchCheckoutDefaults(customer: Pick<CustomerClient, "state" | "customer" | "onChange" | "addresses">, read: () => Omit<CheckoutDefaultsInput, "customer" | "savedAddresses">, listener: (defaults: CheckoutDefaults) => void): () => void;
130
+
131
+ export { type AddressStorage, type CheckoutDefaults, type CheckoutDefaultsInput, type DeliveryFeeType, type PhoneDefault, checkoutDefaults, defaultAddress, defaultPhone, defaultRegion, prefillFulfilment, rememberAddress, rememberedAddress, restoreSchedule, watchCheckoutDefaults };
@@ -0,0 +1,192 @@
1
+ // src/checkout-defaults/index.ts
2
+ var KEY = "zappie_delivery_address";
3
+ function local(storage) {
4
+ if (storage !== void 0) {
5
+ return storage;
6
+ }
7
+ try {
8
+ return typeof window === "undefined" ? null : window.localStorage;
9
+ } catch {
10
+ return null;
11
+ }
12
+ }
13
+ function attempt(fn) {
14
+ try {
15
+ return fn();
16
+ } catch {
17
+ return void 0;
18
+ }
19
+ }
20
+ function text(value) {
21
+ return typeof value === "string" && value.trim() ? value : void 0;
22
+ }
23
+ function point(lat, lng) {
24
+ return typeof lat === "number" && typeof lng === "number" && Number.isFinite(lat) && Number.isFinite(lng) ? { coordinates: { lat, lng } } : {};
25
+ }
26
+ function address(line1, placeId, lat, lng) {
27
+ return line1 ? { line1, ...placeId ? { placeId } : {}, ...point(lat, lng) } : void 0;
28
+ }
29
+ function defaultPhone(typed, customer) {
30
+ const value = typed?.trim();
31
+ if (value) {
32
+ return { display: value, contactPhone: value, fromAccount: false };
33
+ }
34
+ const masked = customer?.phone_masked;
35
+ return masked ? { display: masked, fromAccount: true } : { display: "", fromAccount: false };
36
+ }
37
+ function rememberedAddress(options = {}) {
38
+ const storage = local(options.storage);
39
+ const raw = storage ? attempt(() => storage.getItem(KEY)) : void 0;
40
+ if (!raw) {
41
+ return void 0;
42
+ }
43
+ const stored = attempt(() => JSON.parse(raw));
44
+ const placeId = text(stored?.placeId);
45
+ if (!placeId && options.feeType !== "fixed") {
46
+ attempt(() => storage?.removeItem(KEY));
47
+ return void 0;
48
+ }
49
+ return address(
50
+ text(stored?.address),
51
+ placeId,
52
+ stored?.latitude,
53
+ stored?.longitude
54
+ );
55
+ }
56
+ function rememberAddress(value, storage) {
57
+ const target = local(storage);
58
+ if (!target) {
59
+ return;
60
+ }
61
+ if (!value?.line1.trim()) {
62
+ attempt(() => target.removeItem(KEY));
63
+ return;
64
+ }
65
+ attempt(
66
+ () => target.setItem(
67
+ KEY,
68
+ JSON.stringify({
69
+ address: value.line1,
70
+ placeId: value.placeId ?? "",
71
+ latitude: value.coordinates?.lat,
72
+ longitude: value.coordinates?.lng
73
+ })
74
+ )
75
+ );
76
+ }
77
+ function defaultAddress(options = {}) {
78
+ const remembered = rememberedAddress(options);
79
+ if (remembered) {
80
+ return remembered;
81
+ }
82
+ const first = options.savedAddresses?.[0];
83
+ const placeId = text(first?.place_id);
84
+ if (!first || !placeId && options.feeType !== "fixed") {
85
+ return void 0;
86
+ }
87
+ return address(text(first.line1), placeId, first.lat, first.lng);
88
+ }
89
+ function defaultRegion(branch, chosen) {
90
+ if (chosen) {
91
+ return chosen;
92
+ }
93
+ const regions = branch.deliveryRegions ?? [];
94
+ return regions.length === 1 ? regions[0]?.id : void 0;
95
+ }
96
+ function restoreSchedule(fulfilment, branchId) {
97
+ const at = fulfilment && fulfilment.type !== "dine_in" ? fulfilment.scheduledFor : void 0;
98
+ return at && fulfilment?.branchId === branchId && Number.isFinite(Date.parse(at)) ? at : void 0;
99
+ }
100
+ function checkoutDefaults(input) {
101
+ const delivery = input.fulfilment?.type === "delivery" ? input.fulfilment : void 0;
102
+ const address2 = delivery?.address ?? defaultAddress(input);
103
+ const deliveryRegionId = defaultRegion(
104
+ input.branch,
105
+ delivery?.deliveryRegionId
106
+ );
107
+ const scheduledFor = restoreSchedule(input.fulfilment, input.branch.id);
108
+ return {
109
+ phone: defaultPhone(input.typedPhone, input.customer),
110
+ ...address2 ? { address: address2 } : {},
111
+ ...deliveryRegionId ? { deliveryRegionId } : {},
112
+ ...scheduledFor ? { scheduledFor } : {}
113
+ };
114
+ }
115
+ function prefillFulfilment(fulfilment, branch) {
116
+ if (!fulfilment || fulfilment.type === "dine_in") {
117
+ return fulfilment;
118
+ }
119
+ const scheduledFor = restoreSchedule(fulfilment, branch.id);
120
+ const regionId = fulfilment.type === "delivery" ? defaultRegion(branch, fulfilment.deliveryRegionId) : void 0;
121
+ const dropSchedule = fulfilment.scheduledFor !== void 0 && !scheduledFor;
122
+ const addRegion = fulfilment.type === "delivery" && regionId !== fulfilment.deliveryRegionId;
123
+ if (!dropSchedule && !addRegion) {
124
+ return fulfilment;
125
+ }
126
+ const next = { ...fulfilment };
127
+ if (dropSchedule) {
128
+ delete next.scheduledFor;
129
+ }
130
+ if (addRegion && next.type === "delivery" && regionId) {
131
+ next.deliveryRegionId = regionId;
132
+ }
133
+ return next;
134
+ }
135
+ function watchCheckoutDefaults(customer, read, listener) {
136
+ let run = 0;
137
+ let stopped = false;
138
+ let signedIn;
139
+ const publish = (savedAddresses) => {
140
+ listener(
141
+ checkoutDefaults({
142
+ ...read(),
143
+ customer: customer.customer,
144
+ ...savedAddresses ? { savedAddresses } : {}
145
+ })
146
+ );
147
+ };
148
+ const refresh = () => {
149
+ const current = ++run;
150
+ const id = customer.state === "authenticated" ? customer.customer?.id : void 0;
151
+ signedIn = id;
152
+ if (!id) {
153
+ publish(void 0);
154
+ return;
155
+ }
156
+ void customer.addresses().then(
157
+ (saved) => {
158
+ if (!stopped && current === run) {
159
+ publish(saved);
160
+ }
161
+ },
162
+ () => {
163
+ if (!stopped && current === run) {
164
+ publish(void 0);
165
+ }
166
+ }
167
+ );
168
+ };
169
+ const off = customer.onChange((state, next) => {
170
+ const id = state === "authenticated" ? next?.id : void 0;
171
+ if (id !== signedIn) {
172
+ refresh();
173
+ }
174
+ });
175
+ refresh();
176
+ return () => {
177
+ stopped = true;
178
+ off();
179
+ };
180
+ }
181
+ export {
182
+ checkoutDefaults,
183
+ defaultAddress,
184
+ defaultPhone,
185
+ defaultRegion,
186
+ prefillFulfilment,
187
+ rememberAddress,
188
+ rememberedAddress,
189
+ restoreSchedule,
190
+ watchCheckoutDefaults
191
+ };
192
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/checkout-defaults/index.ts"],
4
+ "sourcesContent": ["// The declarations import the installed core so shared types keep one identity.\n// eslint-disable-next-line no-restricted-imports\nimport type { Branch, CartFulfilment, DeliveryAddress } from \"@zappie/sdk\";\n// eslint-disable-next-line no-restricted-imports\nimport type {\n CustomerClient,\n SavedAddress,\n SessionCustomer,\n} from \"@zappie/sdk/customer\";\n\n/**\n * How the branch prices delivery. Only `\"fixed\"` lets an address without a place ID stand.\n */\nexport type DeliveryFeeType = \"region\" | \"distance\" | \"fixed\";\n\n/**\n * The browser storage the remembered address uses. Pass `null` to remember nothing.\n */\nexport type AddressStorage = Pick<\n Storage,\n \"getItem\" | \"setItem\" | \"removeItem\"\n>;\n\n/**\n * The phone a checkout shows, and the `contact_phone` to send.\n */\nexport type PhoneDefault = {\n /** The typed number, else the signed-in customer's masked number, else \"\". */\n display: string;\n /** The typed number. Absent when the account's number applies: checkout then uses the verified number. */\n contactPhone?: string;\n /** True when `display` is the signed-in customer's masked number. */\n fromAccount: boolean;\n};\n\n/**\n * What the defaults are worked out from.\n */\nexport type CheckoutDefaultsInput = {\n branch: Branch;\n /** The cart's fulfilment. Its type and its address are the shopper's choice and are kept. */\n fulfilment: CartFulfilment | null;\n customer?: SessionCustomer | null;\n /** The signed-in customer's saved addresses, in the order `/me/addresses` returns them. */\n savedAddresses?: readonly SavedAddress[];\n /** The phone typed in this session. */\n typedPhone?: string;\n /** Defaults to `\"distance\"`, as the default storefront does. */\n feeType?: DeliveryFeeType;\n /** Defaults to `localStorage`. */\n storage?: AddressStorage | null;\n};\n\n/**\n * The contact details a checkout starts from.\n */\nexport type CheckoutDefaults = {\n phone: PhoneDefault;\n /** The cart's delivery address, else the last one used on this device, else the first saved one. */\n address?: DeliveryAddress;\n /** The cart's region, else the branch's only region. */\n deliveryRegionId?: string;\n /** The cart's scheduled time when it belongs to this branch and parses. */\n scheduledFor?: string;\n};\n\n// The default storefront's key and shape, so a host page and its templates share one entry.\nconst KEY = \"zappie_delivery_address\";\n\ntype Stored = {\n address?: unknown;\n placeId?: unknown;\n latitude?: unknown;\n longitude?: unknown;\n};\n\nfunction local(storage: AddressStorage | null | undefined) {\n if (storage !== undefined) {\n return storage;\n }\n try {\n return typeof window === \"undefined\" ? null : window.localStorage;\n } catch {\n return null;\n }\n}\n\nfunction attempt<T>(fn: () => T): T | undefined {\n try {\n return fn();\n } catch {\n return undefined;\n }\n}\n\nfunction text(value: unknown): string | undefined {\n return typeof value === \"string\" && value.trim() ? value : undefined;\n}\n\nfunction point(lat: unknown, lng: unknown) {\n return typeof lat === \"number\" &&\n typeof lng === \"number\" &&\n Number.isFinite(lat) &&\n Number.isFinite(lng)\n ? { coordinates: { lat, lng } }\n : {};\n}\n\nfunction address(\n line1: string | undefined,\n placeId: string | undefined,\n lat: unknown,\n lng: unknown,\n): DeliveryAddress | undefined {\n return line1\n ? { line1, ...(placeId ? { placeId } : {}), ...point(lat, lng) }\n : undefined;\n}\n\n/**\n * The phone to show: the one typed in this session, otherwise the signed-in customer's.\n * The session holds only a masked number, so send no `contact_phone` for it.\n *\n * @param typed - The phone typed in this session.\n * @param customer - The signed-in customer, or null.\n */\nexport function defaultPhone(\n typed: string | undefined,\n customer: SessionCustomer | null | undefined,\n): PhoneDefault {\n const value = typed?.trim();\n if (value) {\n return { display: value, contactPhone: value, fromAccount: false };\n }\n const masked = customer?.phone_masked;\n return masked\n ? { display: masked, fromAccount: true }\n : { display: \"\", fromAccount: false };\n}\n\n/**\n * The last delivery address used on this device, or undefined. Under distance or region fees,\n * an entry without a place ID counts as none and is removed.\n *\n * @param options - The fee type and storage.\n */\nexport function rememberedAddress(\n options: { feeType?: DeliveryFeeType; storage?: AddressStorage | null } = {},\n): DeliveryAddress | undefined {\n const storage = local(options.storage);\n const raw = storage ? attempt(() => storage.getItem(KEY)) : undefined;\n if (!raw) {\n return undefined;\n }\n const stored = attempt(() => JSON.parse(raw) as Stored);\n const placeId = text(stored?.placeId);\n if (!placeId && options.feeType !== \"fixed\") {\n attempt(() => storage?.removeItem(KEY));\n return undefined;\n }\n return address(\n text(stored?.address),\n placeId,\n stored?.latitude,\n stored?.longitude,\n );\n}\n\n/**\n * Remember the delivery address the shopper used, on this device only. Stores the address\n * line, place ID and coordinates, nothing else. Does nothing where storage is blocked.\n *\n * @param value - The address, or null to forget it.\n * @param storage - Defaults to `localStorage`.\n */\nexport function rememberAddress(\n value: DeliveryAddress | null,\n storage?: AddressStorage | null,\n): void {\n const target = local(storage);\n if (!target) {\n return;\n }\n if (!value?.line1.trim()) {\n attempt(() => target.removeItem(KEY));\n return;\n }\n attempt(() =>\n target.setItem(\n KEY,\n JSON.stringify({\n address: value.line1,\n placeId: value.placeId ?? \"\",\n latitude: value.coordinates?.lat,\n longitude: value.coordinates?.lng,\n }),\n ),\n );\n}\n\n/**\n * The address to start from: the last one used on this device, otherwise the customer's\n * first saved address. Under distance or region fees, an address without a place ID counts as none.\n *\n * @param options - Saved addresses, fee type and storage.\n */\nexport function defaultAddress(\n options: {\n savedAddresses?: readonly SavedAddress[];\n feeType?: DeliveryFeeType;\n storage?: AddressStorage | null;\n } = {},\n): DeliveryAddress | undefined {\n const remembered = rememberedAddress(options);\n if (remembered) {\n return remembered;\n }\n const first = options.savedAddresses?.[0];\n const placeId = text(first?.place_id);\n if (!first || (!placeId && options.feeType !== \"fixed\")) {\n return undefined;\n }\n return address(text(first.line1), placeId, first.lat, first.lng);\n}\n\n/**\n * The region to start from: the one already chosen, otherwise the branch's only region.\n *\n * @param branch - The cart's branch.\n * @param chosen - The region already chosen.\n */\nexport function defaultRegion(\n branch: Branch,\n chosen?: string,\n): string | undefined {\n if (chosen) {\n return chosen;\n }\n const regions = branch.deliveryRegions ?? [];\n return regions.length === 1 ? regions[0]?.id : undefined;\n}\n\n/**\n * The cart's scheduled time if it belongs to this branch and parses, otherwise undefined.\n * Drop a time this returns undefined for: it can never become valid here.\n *\n * @param fulfilment - The cart's fulfilment.\n * @param branchId - The branch the shopper is ordering from.\n */\nexport function restoreSchedule(\n fulfilment: CartFulfilment | null,\n branchId: string,\n): string | undefined {\n const at =\n fulfilment && fulfilment.type !== \"dine_in\"\n ? fulfilment.scheduledFor\n : undefined;\n return at &&\n fulfilment?.branchId === branchId &&\n Number.isFinite(Date.parse(at))\n ? at\n : undefined;\n}\n\n/**\n * The contact details a checkout starts from, by the default storefront's rule.\n * It reads the cart and never changes it, so the fulfilment type stays the shopper's.\n *\n * @param input - The branch, cart fulfilment, customer and what was typed.\n */\nexport function checkoutDefaults(\n input: CheckoutDefaultsInput,\n): CheckoutDefaults {\n const delivery =\n input.fulfilment?.type === \"delivery\" ? input.fulfilment : undefined;\n const address = delivery?.address ?? defaultAddress(input);\n const deliveryRegionId = defaultRegion(\n input.branch,\n delivery?.deliveryRegionId,\n );\n const scheduledFor = restoreSchedule(input.fulfilment, input.branch.id);\n return {\n phone: defaultPhone(input.typedPhone, input.customer),\n ...(address ? { address } : {}),\n ...(deliveryRegionId ? { deliveryRegionId } : {}),\n ...(scheduledFor ? { scheduledFor } : {}),\n };\n}\n\n/**\n * The cart fulfilment with the defaults applied: the only region filled in and a stale\n * schedule dropped. Same type, same address. Returns the same object when nothing changes.\n *\n * @param fulfilment - The cart's fulfilment.\n * @param branch - The cart's branch.\n */\nexport function prefillFulfilment<T extends CartFulfilment | null>(\n fulfilment: T,\n branch: Branch,\n): T {\n if (!fulfilment || fulfilment.type === \"dine_in\") {\n return fulfilment;\n }\n const scheduledFor = restoreSchedule(fulfilment, branch.id);\n const regionId =\n fulfilment.type === \"delivery\"\n ? defaultRegion(branch, fulfilment.deliveryRegionId)\n : undefined;\n const dropSchedule = fulfilment.scheduledFor !== undefined && !scheduledFor;\n const addRegion =\n fulfilment.type === \"delivery\" && regionId !== fulfilment.deliveryRegionId;\n if (!dropSchedule && !addRegion) {\n return fulfilment;\n }\n const next: Exclude<CartFulfilment, { type: \"dine_in\" }> = { ...fulfilment };\n if (dropSchedule) {\n delete next.scheduledFor;\n }\n if (addRegion && next.type === \"delivery\" && regionId) {\n next.deliveryRegionId = regionId;\n }\n return next as T;\n}\n\n/**\n * Work out the defaults now and again whenever the shopper signs in or out. On sign-in it\n * fetches the saved addresses first. A failed fetch counts as none. A slower, older run never\n * lands last.\n *\n * @param customer - The customer client.\n * @param read - Returns the current inputs, read on each run.\n * @param listener - Receives each result.\n * @returns Stops listening.\n */\nexport function watchCheckoutDefaults(\n customer: Pick<\n CustomerClient,\n \"state\" | \"customer\" | \"onChange\" | \"addresses\"\n >,\n read: () => Omit<CheckoutDefaultsInput, \"customer\" | \"savedAddresses\">,\n listener: (defaults: CheckoutDefaults) => void,\n): () => void {\n let run = 0;\n let stopped = false;\n let signedIn: string | undefined;\n const publish = (savedAddresses: readonly SavedAddress[] | undefined) => {\n listener(\n checkoutDefaults({\n ...read(),\n customer: customer.customer,\n ...(savedAddresses ? { savedAddresses } : {}),\n }),\n );\n };\n const refresh = () => {\n const current = ++run;\n const id =\n customer.state === \"authenticated\" ? customer.customer?.id : undefined;\n signedIn = id;\n if (!id) {\n publish(undefined);\n return;\n }\n void customer.addresses().then(\n (saved) => {\n if (!stopped && current === run) {\n publish(saved);\n }\n },\n () => {\n if (!stopped && current === run) {\n publish(undefined);\n }\n },\n );\n };\n const off = customer.onChange((state, next) => {\n const id = state === \"authenticated\" ? next?.id : undefined;\n if (id !== signedIn) {\n refresh();\n }\n });\n refresh();\n return () => {\n stopped = true;\n off();\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["address"]
7
+ }
@@ -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.9" : "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.9" : "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,21 @@
1
+ import { Item, Money } from '@zappie/sdk';
2
+
3
+ /**
4
+ * The lowest price a shopper can pay for one of this item, by the same rule
5
+ * Zappie uses for a product's starting price: the cheapest available variant
6
+ * plus the cheapest choice of each required add-on group. When no variant is
7
+ * available, the cheapest variant is used.
8
+ *
9
+ * @param item - The catalogue item.
10
+ * @returns A display price. Use a server quote for checkout totals.
11
+ */
12
+ declare function startingPrice(item: Item): Money;
13
+ /**
14
+ * Whether to show the starting price as "From": true when available variants
15
+ * have different prices, or a required add-on raises the starting price.
16
+ *
17
+ * @param item - The catalogue item.
18
+ */
19
+ declare function showsFromPrice(item: Item): boolean;
20
+
21
+ 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\";\n// The declarations import the installed core so Money keeps the core's brand.\n// eslint-disable-next-line no-restricted-imports\nimport type { Item, Money } from \"@zappie/sdk\";\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;AAKzB,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.9",
4
4
  "description": "Typed, framework-agnostic client for Zappie menus, carts, checkout, auth and events.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -45,6 +45,10 @@
45
45
  "types": "./dist/checkout/index.d.ts",
46
46
  "import": "./dist/checkout/index.js"
47
47
  },
48
+ "./checkout-defaults": {
49
+ "types": "./dist/checkout-defaults/index.d.ts",
50
+ "import": "./dist/checkout-defaults/index.js"
51
+ },
48
52
  "./customer": {
49
53
  "types": "./dist/customer/index.d.ts",
50
54
  "import": "./dist/customer/index.js"
@@ -61,6 +65,10 @@
61
65
  "types": "./dist/visits/index.d.ts",
62
66
  "import": "./dist/visits/index.js"
63
67
  },
68
+ "./pricing": {
69
+ "types": "./dist/pricing/index.d.ts",
70
+ "import": "./dist/pricing/index.js"
71
+ },
64
72
  "./template": {
65
73
  "types": "./dist/template/index.d.ts",
66
74
  "import": "./dist/template/index.js"
@@ -109,7 +117,7 @@
109
117
  "url": "https://github.com/zappie/storefront/issues"
110
118
  },
111
119
  "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",
120
+ "build": "tsup && tsup --config tsup.checkout.config.ts && tsup --config tsup.checkout-defaults.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
121
  "test": "vitest run --root ../.. packages/sdk/test",
114
122
  "size": "node ../../scripts/release-packages/size-sdk.mjs && size-limit"
115
123
  }