@doswiftly/storefront-operations 14.0.0 → 16.0.0

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/AGENTS.md CHANGED
@@ -27,10 +27,10 @@ consumer's `codegen.ts` references this package's `.graphql` files as
27
27
  live in the consumer's repo.
28
28
 
29
29
  <!-- AUTOGEN:STATS:BEGIN — auto-regenerated, do not edit by hand -->
30
- - **Schema version**: 14.0.0
31
- - **Queries**: 50
30
+ - **Schema version**: 16.0.0
31
+ - **Queries**: 52
32
32
  - **Mutations**: 40
33
- - **Fragments**: 100
33
+ - **Fragments**: 102
34
34
  <!-- AUTOGEN:STATS:END -->
35
35
 
36
36
  ## Loading order
@@ -174,6 +174,29 @@ semantics). Idempotency is the caller's responsibility. If you need retries
174
174
  on a mutation, wrap with your own backoff logic and check `userErrors` to
175
175
  decide whether to retry.
176
176
 
177
+ ### Configuration sources — `doswiftly.config.ts` first, env vars only as fallback
178
+
179
+ `@doswiftly/storefront-sdk` takes `apiUrl` and `shopSlug` as **explicit
180
+ `config`** on `createStorefrontClient` / `<StorefrontProvider>`. The SDK does
181
+ not read env vars, sniff hostnames, or inspect request headers — the
182
+ storefront supplies the values.
183
+
184
+ Scaffolded storefronts (`doswiftly init`) ship a config helper
185
+ (`lib/graphql/config.ts`) that walks three sources in order:
186
+
187
+ 1. **`doswiftly.config.ts`** — preferred. Committed config file generated at
188
+ scaffolding. No env wiring needed in normal use.
189
+ 2. **`NEXT_PUBLIC_API_URL` + `NEXT_PUBLIC_SHOP_SLUG`** — fallback for local
190
+ dev or storefronts scaffolded outside `doswiftly init`. `doswiftly dev`
191
+ rewrites `NEXT_PUBLIC_API_URL` at runtime to point at a local CORS proxy.
192
+ 3. **`http://localhost:8000` + `demo-shop`** — last-resort defaults.
193
+
194
+ If you generate code that falls back to env vars, use **exactly these names**.
195
+ **Do NOT invent alternatives** like `API_URL`, `STOREFRONT_URL`,
196
+ `TENANT_SLUG` — `doswiftly dev` keys off the canonical names; alternatives
197
+ mean the proxy starts but the storefront still calls the production API
198
+ directly, and CORS errors only surface on the first client-side mutation.
199
+
177
200
  ## When in doubt
178
201
 
179
202
  - **Schema question** ("what fields are on `X`?") → grep `schema.graphql`
package/CHANGELOG.md CHANGED
@@ -1,5 +1,299 @@
1
1
  # Changelog
2
2
 
3
+ ## 16.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - ca67b92: **BREAKING** — the formatting utilities switch from a hardcoded 13-currency map to runtime-driven `Intl.NumberFormat` resolution. Every ISO 4217 currency known to the runtime is now supported, with the locale-correct symbol and separators.
8
+
9
+ **API changes**
10
+ - `formatDate(date, locale)` — `locale` is now a required argument. The previous hardcoded `'en-US'` default is gone.
11
+ - `formatDateTime(date, locale)` — `locale` required.
12
+ - `formatNumber(num, locale)` — `locale` required.
13
+ - `formatPrice(price, locale?)` — `locale` is now optional. When omitted, the runtime default (`Intl.NumberFormat().resolvedOptions().locale`) is used. The previous `CURRENCY_LOCALES`-keyed auto-selection is gone.
14
+ - `formatAmount(amount, currencyCode, locale?)` — third argument added (optional).
15
+ - `formatPriceRange(min, max, locale?)` — third argument added (optional).
16
+ - `getCurrencySymbol(code, locale?)` — second argument added (optional). The symbol is derived from `Intl.NumberFormat.formatToParts()`; output depends on the locale (`getCurrencySymbol('PLN', 'pl-PL') === 'zł'`, `getCurrencySymbol('PLN', 'en-US') === 'PLN'`).
17
+
18
+ **Removed exports**
19
+ - `CURRENCY_SYMBOLS` — the 13-entry map is gone. Use `getCurrencySymbol(code, locale)` instead, or read the symbol from `Intl.NumberFormat.formatToParts()` directly.
20
+ - `CURRENCY_LOCALES` — the locale-to-currency map is gone. Pick the locale from the storefront's i18n context (or read `shop.localeToCurrencyMap` from the API for per-shop overrides) and pass it to `formatPrice` / `formatAmount`.
21
+
22
+ **Why**
23
+ - The hardcoded 13-currency map covered ~7% of the ISO 4217 currencies the API now ships in `CurrencyCode` (~180 entries). Every currency outside that list silently fell back to `en-US` formatting — a Brazilian (BRL) or Indian (INR) storefront had to either write its own formatter or accept US-style output.
24
+ - Locale↔currency is a **per-shop** decision (`shop.localeToCurrencyMap` in the schema is explicitly marked SSOT). A globally-hardcoded map in the SDK is the wrong place for that knowledge — it makes Polish customers of a multi-locale shop unable to see PLN in `'en-PL'` even when the merchant configured it.
25
+ - Money precision: `formatPrice` and `formatAmount` no longer apply `parseFloat` to `Money.amount`. Decimal strings are forwarded to `Intl.NumberFormat.format()` verbatim, preserving precision across locales and unusual subunit currencies (JPY/KRW zero-decimal, BHD/JOD/KWD three-decimal, ISK rounding).
26
+
27
+ **Migration**
28
+
29
+ ```ts
30
+ // Before
31
+ import {
32
+ formatDate,
33
+ formatPrice,
34
+ CURRENCY_LOCALES,
35
+ CURRENCY_SYMBOLS,
36
+ } from "@doswiftly/storefront-sdk";
37
+
38
+ formatDate(order.createdAt); // hardcoded "Dec 9, 2025"
39
+ formatPrice({ amount: "12.50", currencyCode: "PLN" }); // auto-picked pl-PL → "12,50 zł"
40
+ CURRENCY_LOCALES.PLN; // "pl-PL"
41
+ CURRENCY_SYMBOLS.PLN; // "zł"
42
+
43
+ // After (Client Components)
44
+ import { useLocale } from "next-intl";
45
+ import {
46
+ formatDate,
47
+ formatPrice,
48
+ getCurrencySymbol,
49
+ } from "@doswiftly/storefront-sdk";
50
+
51
+ const locale = useLocale(); // e.g. "pl-PL"
52
+ formatDate(order.createdAt, locale);
53
+ formatPrice({ amount: "12.50", currencyCode: "PLN" }, locale);
54
+ getCurrencySymbol("PLN", locale); // "zł"
55
+
56
+ // After (Server Components / Route Handlers)
57
+ import { getLocale } from "next-intl/server";
58
+ const locale = await getLocale();
59
+ formatDate(order.createdAt, locale);
60
+ ```
61
+
62
+ For per-shop locale↔currency overrides, read `shop.localeToCurrencyMap` from the Storefront API and resolve the locale yourself before calling `formatPrice` / `formatAmount`.
63
+
64
+ **New: Context-driven format hooks**
65
+
66
+ `@doswiftly/storefront-sdk/react` now ships convenience hooks that pull the active language from `useLanguageStore` (inside `<StorefrontProvider>`) and return a memoised, locale-bound formatter. Use them when you don't want to pass `locale` to every call:
67
+
68
+ ```ts
69
+ import {
70
+ useFormatPrice,
71
+ useFormatAmount,
72
+ useFormatPriceRange,
73
+ useFormatDate,
74
+ useFormatDateTime,
75
+ useFormatNumber,
76
+ useGetCurrencySymbol,
77
+ } from '@doswiftly/storefront-sdk/react';
78
+
79
+ function Cart() {
80
+ const formatPrice = useFormatPrice();
81
+ return <span>{formatPrice(item.price)}</span>;
82
+ }
83
+
84
+ function OrderRow() {
85
+ const formatDate = useFormatDate();
86
+ return <span>{formatDate(order.processedAt)}</span>;
87
+ }
88
+ ```
89
+
90
+ Each hook accepts an optional last `localeOverride` argument that wins over the store value — useful for a "show in US format" toggle on a single element:
91
+
92
+ ```ts
93
+ const formatPrice = useFormatPrice();
94
+ return <span>{formatPrice(item.price, 'en-US')}</span>;
95
+ ```
96
+
97
+ Resolution per call: `localeOverride` → `useLanguageStore().language` → runtime default.
98
+
99
+ Use the vanilla `formatPrice` / `formatDate` / etc. from `@doswiftly/storefront-sdk` (no hook) with an explicit `locale` for server components, e-mail templates, or any code path outside a provider.
100
+
101
+ `formatPercentage` is unchanged.
102
+
103
+ `@doswiftly/storefront-operations` bumped to keep linked parity — no operations changes.
104
+
105
+ ### Minor Changes
106
+
107
+ - 885c011: Re-export checkout types from the public API. `CartClient` methods added in 15.1.0 (`getAvailablePaymentMethods`, `getAvailableShippingMethods`) returned typed payloads, but the named types were not part of the public surface — component props and function signatures touching these payloads required `Awaited<ReturnType<...>>` workarounds. This change closes that gap.
108
+
109
+ **Newly exported types:**
110
+ - `PaymentMethod`, `AvailablePaymentMethods` — shape returned by `CartClient.getAvailablePaymentMethods()`
111
+ - `AvailableShippingMethod`, `AvailableShippingMethodsPayload`, `FreeShippingProgress`, `DeliveryEstimate`, `ShippingCarrier`, `DeliveryType` — shape returned by `CartClient.getAvailableShippingMethods()`
112
+ - `PickupPoint`, `PickupPointInput` — locker / pickup-point delivery (surfaced via `MailingAddress.pickupPoint` and `CartAddressInput.pickupPoint`)
113
+ - `CartAttributeInput` — input shape for `CartClient.updateAttributes()`
114
+ - `ShippingAddressInput` — input shape for the `availableShippingMethods` standalone query
115
+
116
+ **Example:**
117
+
118
+ ```ts
119
+ import type {
120
+ AvailableShippingMethodsPayload,
121
+ AvailableShippingMethod,
122
+ DeliveryType,
123
+ } from "@doswiftly/storefront-sdk";
124
+
125
+ function isPickup(method: AvailableShippingMethod): boolean {
126
+ return method.deliveryType === "PICKUP_POINT";
127
+ }
128
+ ```
129
+
130
+ No runtime changes — types already existed internally; this change only adds them to the published surface.
131
+
132
+ `@doswiftly/storefront-operations` bumped to keep linked parity — no operations changes.
133
+
134
+ - fd3d199: Expose `cart.cost.totalDiscount` and `cart.cost.totalShipping` through the `CartCost` fragment.
135
+
136
+ The schema already exposed `CartCost.totalDiscount: Money!` (aggregate of every entry in `cart.discountAllocations`) and `CartCost.totalShipping: Money` (cost of the currently selected shipping method, `null` until one is selected), but the shared `CartCost` fragment did not select them — so they were not part of the typed `Cart.cost` returned by `CartClient`. Storefronts wanting to render a single "Discounts: -X" row or a shipping summary had to either sum `discountAllocations` client-side (precision-sensitive across currencies) or query the schema directly and bypass the typed surface.
137
+
138
+ After this release, both fields are part of `Cart.cost` returned by every `CartClient` operation — `get`, `create`, `addItems`, `updateItems`, `removeItems`, `setShippingAddress`, `setBillingAddress`, `selectShippingMethod`, `selectPaymentMethod`, `updateBuyerIdentity`, `updateDiscountCodes`, `updateNote`, `updateAttributes`, `applyGiftCard`, `removeGiftCard`, `updateGiftCardRecipient`.
139
+
140
+ **Example:**
141
+
142
+ ```ts
143
+ const cart = await cartClient.get(cartId);
144
+
145
+ const summary = {
146
+ subtotal: cart.cost.subtotal,
147
+ discount: cart.cost.totalDiscount, // Money — defaults to amount 0
148
+ shipping: cart.cost.totalShipping, // Money | null — null until selectShippingMethod()
149
+ tax: cart.cost.totalTax,
150
+ total: cart.cost.total, // grand total — use directly on checkout summary
151
+ };
152
+ ```
153
+
154
+ `totalDiscount` is required (defaults to amount 0 when no discount applies). `totalShipping` is nullable — `null` means no shipping method has been selected yet, an amount of 0 means a free-shipping method was selected.
155
+
156
+ Additive — existing consumers keep every field they had before, no breaking change.
157
+
158
+ `@doswiftly/storefront-operations` bumped to keep linked parity (fragment update).
159
+
160
+ - 51091df: Expose `Cart.status` and `Cart.completedOrder` so storefronts can detect a completed (or expired / abandoned) cart on read, without having to attempt a mutation first and react to its `userErrors[].code`.
161
+
162
+ **New fields**
163
+ - `Cart.status: CartStatus!` — lifecycle status (`ACTIVE`, `ABANDONED`, `CONVERTED`, `RECOVERED`, `EXPIRED`). Only `ACTIVE` carts accept mutations; any other status rejects subsequent mutations with `CartErrorCode.ALREADY_COMPLETED`.
164
+ - `Cart.completedOrder: Order` — the order this cart converted into. Populated only when `status === CONVERTED`; null on every other status.
165
+
166
+ **Why**
167
+
168
+ When a buyer returned to the checkout page after completing their order (SSR re-render, deep link, "back" button after redirect), the SDK could only ask `cart(id)` for the cart and got the full pre-completion state back — every form value still there, the "Pay" button still active. The first mutation then failed with `ALREADY_COMPLETED`, after the buyer had already filled out fields. With `status` exposed, the storefront detects the terminal state on initial render and redirects to the order confirmation directly — using the `accessToken` on `completedOrder` for guest tracking, no extra `orderByToken` round-trip.
169
+
170
+ **Example**
171
+
172
+ ```ts
173
+ const cart = await cartClient.get(cartId);
174
+ if (!cart) {
175
+ // Cart not found — guide the buyer to create a new one
176
+ return redirect("/cart");
177
+ }
178
+ if (cart.status !== "ACTIVE") {
179
+ if (cart.completedOrder) {
180
+ // Render the order confirmation page off the data you already have
181
+ return redirect(`/order/${cart.completedOrder.accessToken}`);
182
+ }
183
+ // Abandoned / expired — start a fresh cart
184
+ return redirect("/cart/new");
185
+ }
186
+ // Render the checkout form normally
187
+ ```
188
+
189
+ Additive — every existing query that selects the shared `Cart` fragment now sees the two new fields automatically. No breaking change.
190
+
191
+ `@doswiftly/storefront-operations` bumped to keep linked parity — schema sync delivers the new fields and the `CartStatus` enum.
192
+
193
+ - f4efab9: Add `ShopConfigFields` fragment + `query ShopConfig` for `<StorefrontProvider>` setup.
194
+
195
+ `<StorefrontProvider shopData={...}>` from `@doswiftly/storefront-sdk/react` expects a `ShopConfig` payload with a specific shape: currency setup (including `localeToCurrencyMap`), language setup, and bot protection. Until now the storefront had to hand-write the field selection, and it was easy to miss `localeToCurrencyMap` (used internally by the SDK for browser-locale-based currency detection) or to add an extra field that didn't match the `ShopConfig` interface.
196
+
197
+ **New**
198
+ - `fragment ShopConfigFields on Shop` — minimal selection that matches the `ShopConfig` interface 1:1.
199
+ - `query ShopConfig { shop { ...ShopConfigFields } }` — ready-to-use query for the provider.
200
+
201
+ **Example**
202
+
203
+ ```ts
204
+ const SHOP_CONFIG_QUERY = /* GraphQL */ `
205
+ query ShopConfig {
206
+ shop {
207
+ ...ShopConfigFields
208
+ }
209
+ }
210
+ `;
211
+
212
+ // In a Server Component:
213
+ const { data } = await execute(SHOP_CONFIG_QUERY);
214
+ return <StorefrontProvider shopData={data.shop}>{children}</StorefrontProvider>;
215
+ ```
216
+
217
+ Use the larger `Shop` fragment + `query Shop` when you also need branding / contact / business hours for your UI; use `ShopConfig` when you only need to bootstrap the provider.
218
+
219
+ Additive — no breaking change.
220
+
221
+ `@doswiftly/storefront-sdk` bumped to keep linked parity — no code change.
222
+
223
+ ### Patch Changes
224
+
225
+ - 7ce8ac4: Clarify how `apiUrl` and `shopSlug` are configured.
226
+
227
+ `createStorefrontClient` and `<StorefrontProvider>` take `apiUrl` and `shopSlug` as **explicit `config`** — the SDK does not read environment variables, sniff hostnames, or inspect request headers. The storefront supplies the values; the SDK uses them verbatim.
228
+
229
+ Scaffolded storefronts ship a config helper (`lib/graphql/config.ts`) that resolves the two values from these sources, in order:
230
+ 1. `doswiftly.config.ts` (preferred — committed file generated at `doswiftly init`)
231
+ 2. `NEXT_PUBLIC_API_URL` + `NEXT_PUBLIC_SHOP_SLUG` (fallback, used for local development)
232
+ 3. `http://localhost:8000` + `demo-shop` (defaults — smoke test only)
233
+
234
+ Scratch-built storefronts can skip the helper and pass values into `config={}` directly.
235
+
236
+ **What's new**
237
+ - `@doswiftly/storefront-sdk` README: new `## Configuration` section between `## Installation` and `## Quick Start`. Documents the explicit-config requirement, the three-source resolver shipped with scaffolded storefronts, and when env-var wiring actually matters.
238
+ - `@doswiftly/storefront-operations` `AGENTS.md`: new `### Configuration sources` convention inside `## Critical conventions — DO NOT hallucinate`. AI assistants now prefer `doswiftly.config.ts` and only fall back to the canonical env-var names.
239
+
240
+ Documentation only — no code change.
241
+
242
+ ## 15.1.0
243
+
244
+ ### Minor Changes
245
+
246
+ - 94c2603: Storefront SDK now covers the full checkout flow end-to-end through typed methods — no more dropping to raw GraphQL for shipping discovery, payment listing, guest order lookup, cart attributes, or saved-address pickers.
247
+
248
+ **New `CartClient` methods**
249
+ - `getAvailableShippingMethods(cartId, address)` — cart-aware shipping discovery. Resolves with `null` when the cart does not exist or has expired (symmetric with `get(cartId)`), otherwise with `AvailableShippingMethodsPayload`: `methods[]` (each carrying `deliveryType` — `HOME`, `PICKUP_POINT`, `LOCKER` — so the storefront picks the pickup / locker UI without inferring from the method name), `freeShippingProgress` (best progress across methods, for the picker banner), and `userErrors[]` populated for business conditions like `DIGITAL_ONLY_NO_SHIPPING` (cart with no shippable lines) or `NO_SHIPPING_METHODS` (unsupported address). Branch on `result.userErrors[0].code`; the `message` is localized per the request's `Accept-Language`.
250
+ - `getAvailablePaymentMethods()` — shop-level list of active payment methods. Returns `AvailablePaymentMethods`: `methods[]` (sorted by merchant position) and `defaultMethod` (merchant-flagged pre-selection). Prefer `result.defaultMethod ?? result.methods.find(m => m.isDefault)` — the merchant may override the default independently of per-method flags.
251
+ - `getOrderByToken(token, email?)` — guest order lookup by opaque access token (returned in `complete().order.accessToken`). Optional `email` adds defense in depth; mismatch returns the same `null` shape as an invalid token. Rate-limited server-side (5 req / min per IP + shop).
252
+ - `updateAttributes(cartId, attributes)` — replace-all of the cart's custom `{ key, value }` pairs (delivery instructions, gift-wrap flags, B2B PO numbers). Pass an empty array to clear.
253
+
254
+ **New `AuthClient` method**
255
+ - `getAddresses()` — saved address book of the authenticated customer (shipping + billing). Resolves with the address list when authenticated, with `null` when no auth context reached the server (symmetric with `getCustomer()`). Each entry carries B2B fields (`taxId`, `vatNumber`) and the `isDefault` flag, so the same list serves both shipping and billing pickers.
256
+
257
+ **Schema additions (operations package)**
258
+ - `MailingAddress` now exposes `taxId`, `vatNumber`, and `pickupPoint`. The new `PickupPoint` type (`provider`, `pointId`, `name?`, `address?`) is returned on shipping addresses delivered to a parcel locker / collection point.
259
+ - `AvailableShippingMethod` gains the declarative `deliveryType` enum.
260
+ - `CartAppliedGiftCard` now exposes `id` — the stable handle to pass to `cartRemoveGiftCard` (no need to keep the raw gift card code on the client).
261
+ - New `CustomerAddresses` query — `customer.addresses(first: 50)` connection returning the address book, normalized to a node list by the SDK wrapper.
262
+
263
+ **SDK fragment changes**
264
+
265
+ The SDK's `MailingAddress` fragment now selects `taxId`, `vatNumber`, and `pickupPoint`. Existing types that include a mailing address (`Cart.shippingAddress`, `Cart.billingAddress`, `Order.shippingAddress`, `Customer.defaultAddress`) get these fields automatically. The `AvailableShippingMethod` fragment selects `deliveryType`. The `CartAppliedGiftCard` fragment selects `id`.
266
+
267
+ **New React hook — server-driven checkout**
268
+ - `useCart(cartId, options?)` — sister of `useCartManager`, bound to an explicit `cartId` instead of the `cart-id` cookie. Use it for SSR-rendered checkout, deep-link order recovery, and admin "view this cart" UIs where the cart id is known up front and the cookie is irrelevant. Returns the loaded cart plus the same mutation surface as the cookie-based hook (`addItems`, `updateItems`, `removeItems`, `updateBuyerIdentity`, `setShippingAddress`, `updateDiscountCodes`, `updateNote`, `updateAttributes` — each resolves with `{ cart, warnings }` so low-stock and partial-availability hints flow through), reactive `isLoading` / `error` / `operation` state, and a `refetch()` callback. `autoFetch: false` + `initialCart` lets you skip the hydration round-trip when the server already resolved the cart.
269
+
270
+ **Field-level descriptions visible in your IDE**
271
+
272
+ Hovering on any cart / order / customer / payment / shipping field in your editor now shows a precise English description of what the field carries — when to use it, what its values mean, what is null vs zero, which enum values are relevant. The descriptions were rewritten across every type selected by the SDK so the storefront-developer audience does not need to look anywhere else for the contract. Codegen is configured (`preResolveTypes: false`) to propagate those descriptions through every generated fragment / query type, so the same hover works on the typed shape your codegen produces.
273
+
274
+ These additions are backward-compatible — existing calls keep their current signatures and return the same fields (plus the new ones). The mailing-address fragment expansion is purely additive.
275
+
276
+ ## 15.0.0
277
+
278
+ ### Major Changes
279
+
280
+ - a11f9e4: GraphQL types are now generated from the storefront schema instead of being hand-written. Every exported cart, order, customer, and payment type is derived directly from the schema, so they can no longer drift out of sync with the API.
281
+
282
+ **Breaking** — the previous hand-written types were inaccurate, and correcting them changes some shapes:
283
+ - `Customer.orderCount` is now `string` (was `number`).
284
+ - `CartLineEdge` and `CartLineConnection.edges` were removed — the cart query never returned edges.
285
+ - Fields previously typed as `string` are now precise string-literal union types: `Order.status`, `Order.paymentStatus`, `Order.fulfillmentStatus`, `PaymentSession.status`, `CartLine.productType`, `Money.currencyCode`, `CartWarning.code`, and the `AttributeSelection` mode fields. Reading these values still works; assigning arbitrary strings into them no longer compiles.
286
+
287
+ **Fixed** — types that were missing fields or had the wrong nullability:
288
+ - `Order` now includes `accessToken`.
289
+ - `Cart` and `CartLine` now include `requiresShipping`.
290
+ - `Customer.displayName`, `Customer.emailMarketing`, and `Customer.totalSpent` are now correctly non-nullable.
291
+ - The duplicated `MailingAddress` definition is collapsed into one.
292
+
293
+ **New** — the schema enum types used by the public types are now exported: `CurrencyCode`, `CountryCode`, `LanguageCode`, `ProductTypeEnum`, `WeightUnit`, `CartWarningCode`, `StorefrontOrderStatus`, `OrderPaymentStatus`, `OrderFulfillmentStatus`, `EmailMarketingState`, `MarketingOptInLevel`, and the attribute enums.
294
+
295
+ These are type-only changes — no runtime behaviour changed.
296
+
3
297
  ## 14.0.0
4
298
 
5
299
  ### Major Changes
package/README.md CHANGED
@@ -163,6 +163,7 @@ full executable body of each operation.
163
163
  | Operation | Description |
164
164
  | --- | --- |
165
165
  | `Shop` | Returns shop configuration: name, base + supported currencies, supported locales, branding (logo, colors, fonts, social links), contact info, active payment methods, brand metadata, money format template, and the list of countries the shop ships to. Public; no auth required. Call once per session and cache — almost everything else is contextualized by the shop returned here. |
166
+ | `ShopConfig` | Minimal Shop payload for `<StorefrontProvider shopData={...}>` from `@doswiftly/storefront-sdk/react`. Returns exactly the fields the SDK's `ShopConfig` interface declares — currency setup (with `localeToCurrencyMap` for browser-locale-based currency detection), language setup, and bot protection. Cache for the session; refetch when the merchant updates currency / language settings or you want to pick up new bot-protection rules. |
166
167
 
167
168
  #### Products
168
169
 
@@ -200,6 +201,7 @@ full executable body of each operation.
200
201
  | --- | --- |
201
202
  | `Customer` | Full customer profile — basic info plus the first 10 addresses and first 10 orders. Heaviest customer query; for narrow use cases prefer `CustomerProfile` (no orders / addresses) or `CustomerOrder` (single order). Returns null if unauthenticated. |
202
203
  | `CustomerProfile` | Lightweight customer profile (no orders, no addresses list). Use for settings / profile pages that only need basic customer info — much cheaper than `Customer`. Returns null if unauthenticated. |
204
+ | `CustomerAddresses` | Authenticated customer's saved address book — used on checkout to let the buyer pick a previously-used shipping / billing address instead of typing it. Each entry carries B2B invoicing fields (`taxId`, `vatNumber`) and the `isDefault` flag so the same list serves both as the shipping picker and as the billing/invoice picker. Returns up to 50 addresses (Relay Connection — buyers rarely keep more); for the unauthenticated case the connection is empty (no error). The default address is also surfaced as `Customer.defaultAddress`. |
203
205
  | `CustomerOrder` | Single order by `orderId`. Returns only orders that belong to the authenticated customer (cross-customer access returns null, not an error). Much cheaper than fetching the full `Customer` payload to access one order. Use on the order detail page. |
204
206
  | `OrderByToken` | Fetch a single order using its opaque access token (`Order.accessToken`) — designed for guest order summary pages where the buyer has not signed in. The token is returned in `cartComplete.order.accessToken` immediately after checkout completes; persist it in an HTTP-only cookie (preferred) or `sessionStorage` for the post-checkout page (NEVER `localStorage`). Optional `email` parameter adds defense-in-depth: when provided, it is matched case-insensitively against the order's buyer email; on mismatch the query returns `null` exactly like an invalid token (the response shape is identical, so an attacker cannot distinguish "token valid, wrong email" from "token invalid"). Rate-limited to 5 requests per minute per IP+shop combination to deter token enumeration; clients exceeding the limit receive a GraphQL error with `extensions.code: THROTTLED`. The response is marked `Cache-Control: no-store` so per-customer order data is never served from CDN or browser cache between users. Safe to retry; the token is permanent until the order is deleted. |
205
207
 
@@ -465,7 +467,8 @@ full executable body of each operation.
465
467
 
466
468
  | Fragment | On Type | Description |
467
469
  | --- | --- | --- |
468
- | `MailingAddress` | `MailingAddress` | Customer mailing address full street/city/state/country/postal code with names and phone. `isDefault` flips when this address is the default for shipping. Spread on the address book UI and order summaries. |
470
+ | `PickupPoint` | `PickupPoint` | Selected pickup point (parcel locker or collection point) attached to a delivery address. Returned on `MailingAddress.pickupPoint` when the buyer chose a non-home delivery method (`deliveryType` other than `HOME`). Null for home delivery. |
471
+ | `MailingAddress` | `MailingAddress` | Customer mailing address — full street/city/state/country/postal code with names and phone. `isDefault` flips when this address is the default for shipping. Carries B2B invoicing fields (`taxId`, `vatNumber`) and the selected `pickupPoint` for parcel locker / collection point deliveries. Spread on the address book UI, checkout shipping/billing forms, and order summaries. |
469
472
  | `CustomerAccessToken` | `CustomerAccessToken` | Customer access token returned by `customerLogin` / `customerSignup` / `customerActivate` / `customerResetPassword` / `customerRefreshToken`. The token's JWT TTL is 24h; cookie max-age is 30d. |
470
473
  | `Customer` | `Customer` | Customer profile — basic info plus B2B fields (`taxId`, `vatNumber`, `regon`, `companyName`), default address, lifetime stats (`orderCount`, `totalSpent`), marketing preferences. Use on profile / settings pages. |
471
474
  | `Order` | `Order` | Order summary — number, totals (cost / tax / shipping), payment + fulfillment status, shipping address, item count, lifecycle timestamps, plus payment capability signal (`canCreatePayment` + `paymentMethodType`) the storefront uses to decide post-completion payment flow without hard-coded provider checks. Spread on the order list, order detail, and the `cartComplete` mutation result. Line items are not included here. |
@@ -474,7 +477,7 @@ full executable body of each operation.
474
477
 
475
478
  | Fragment | On Type | Description |
476
479
  | --- | --- | --- |
477
- | `CartCost` | `CartCost` | Cart-level totals — subtotal, total, tax, duty, checkout charge. Spread inside the `Cart` fragment. |
480
+ | `CartCost` | `CartCost` | Cart-level totals — subtotal, total, tax, duty, discount, shipping, checkout charge. Spread inside the `Cart` fragment. |
478
481
  | `CartLineCost` | `CartLineCost` | Per-line cost breakdown — unit price, line subtotal/total, compare-at unit price (for showing strikethroughs). Spread inside `CartLine`. |
479
482
  | `AttributeSelection` | `AttributeSelection` | Typed snapshot of a customer-filled product attribute (configurator selection) on a cart line. Includes the chosen option / text value, fillingMode, billingMode, surcharge, tax class, and any linked variant. Distinct from `CartLine.attributes` which holds raw key-value line item properties (free-form, untyped). |
480
483
  | `CartLine` | `CartLine` | A single line in the cart — variant + quantity + per-line cost, plus dual attribute storage: `attributes[]` for free-form key-value properties (gift wrap, engraving notes), `attributeSelections[]` for typed configurator answers. |
@@ -483,7 +486,7 @@ full executable body of each operation.
483
486
  | `CartDiscountAllocation` | `CartDiscountAllocation` | Allocation of a discount code to the cart total — pairs the code with the actual money discounted. Use to show "saved X with code Y" in cart UI. |
484
487
  | `Cart` | `Cart` | Full cart shape — totals, line items (paginated up to 100), buyer identity, applied discount codes + allocations, note, custom attributes, plus fulfillment fields (email/phone/addresses/shipping method/payment method/applied gift cards). Spread on the cart drawer / cart page; refetch after every cart mutation including completion lifecycle. |
485
488
  | `CartShippingMethod` | `CartShippingMethod` | Shipping method selected on a cart (D8 term unification — wcześniej ShippingRate). Returned przez `cart.selectedShippingMethod` po `cartSelectShippingMethod` mutation. |
486
- | `CartAppliedGiftCard` | `CartAppliedGiftCard` | Gift card applied to a cart — masked code for display, last 4 chars for matching, applied amount + remaining balance. Balance NIE debited yet — actual deduction atomically at `cartComplete`. |
489
+ | `CartAppliedGiftCard` | `CartAppliedGiftCard` | Gift card applied to a cart — `id` is the stable handle the storefront passes to `cartRemoveGiftCard` (no need to keep the raw code around). Plus masked code for display, last 4 chars for matching, applied amount + remaining balance. Balance NIE debited yet — actual deduction atomically at `cartComplete`. |
487
490
  | `CartSelectedPaymentMethod` | `PaymentMethod` | Payment method (integration provider) selected on a cart — id, name, provider code, type category (CARD/BLIK/BANK_TRANSFER/etc.), icon, description, isDefault, supportedCurrencies. Storefront UI używa do iconography + selection display. |
488
491
 
489
492
  #### Shop
@@ -499,6 +502,7 @@ full executable body of each operation.
499
502
  | `BotProtectionProvider` | `BotProtectionProviderInfo` | Bot-protection provider config (provider name, site key, script URL) for storefront-side challenge widgets. |
500
503
  | `BotProtection` | `BotProtectionInfo` | Bot-protection setup for the shop — primary + fallback provider, and the list of `protectedOperations` (mutation names that require a challenge token). Spread inside `Shop`. |
501
504
  | `Shop` | `Shop` | The shop itself — name, primary domain, currencies + locales, branding, contact info, business hours, bot-protection config. Spread on the root `shop` query; cache for the session. |
505
+ | `ShopConfigFields` | `Shop` | Minimal Shop fields consumed by `<StorefrontProvider shopData={...}>` from `@doswiftly/storefront-sdk/react`. Spread this in your `shop` query to get a response shape that matches the SDK `ShopConfig` interface 1:1 — no manual field selection, no drift when the SDK adds optional fields. Use the larger `Shop` fragment when you also need branding / contact / business hours for your UI. |
502
506
 
503
507
  #### Payment Methods
504
508
 
@@ -543,7 +547,7 @@ full executable body of each operation.
543
547
  | `ShippingCarrier` | `ShippingCarrier` | Carrier offering the shipping method — id, display name, logo URL, internal service code. |
544
548
  | `DeliveryEstimate` | `DeliveryEstimate` | Estimated delivery window — `minDays` to `maxDays` plus a human-readable description (e.g. "2-4 business days"). |
545
549
  | `FreeShippingProgress` | `FreeShippingProgress` | Free-shipping progress info — `qualifies` flag, current cart amount, threshold, remaining to qualify, percent progress, and a localized message ("Add 20 PLN more for free shipping"). Use to render a free-shipping progress bar in the cart drawer. |
546
- | `AvailableShippingMethod` | `AvailableShippingMethod` | One shipping method offered for the destination + cart — id, name, carrier, price, free-shipping progress, estimated delivery, sort order. Spread on the checkout shipping step. |
550
+ | `AvailableShippingMethod` | `AvailableShippingMethod` | One shipping method offered for the destination + cart — id, name, carrier, price, free-shipping progress, estimated delivery, sort order. `deliveryType` signals whether to render a pickup-point / locker picker (`HOME` vs `PICKUP_POINT` / `LOCKER`) so the storefront does not have to infer it from the method name. Spread on the checkout shipping step. |
547
551
 
548
552
  #### Attribute Filters
549
553
 
package/fragments.graphql CHANGED
@@ -244,7 +244,15 @@ fragment Category on Category {
244
244
  # Customer
245
245
  # ============================================
246
246
 
247
- # Customer mailing address full street/city/state/country/postal code with names and phone. `isDefault` flips when this address is the default for shipping. Spread on the address book UI and order summaries.
247
+ # Selected pickup point (parcel locker or collection point) attached to a delivery address. Returned on `MailingAddress.pickupPoint` when the buyer chose a non-home delivery method (`deliveryType` other than `HOME`). Null for home delivery.
248
+ fragment PickupPoint on PickupPoint {
249
+ provider
250
+ pointId
251
+ name
252
+ address
253
+ }
254
+
255
+ # Customer mailing address — full street/city/state/country/postal code with names and phone. `isDefault` flips when this address is the default for shipping. Carries B2B invoicing fields (`taxId`, `vatNumber`) and the selected `pickupPoint` for parcel locker / collection point deliveries. Spread on the address book UI, checkout shipping/billing forms, and order summaries.
248
256
  fragment MailingAddress on MailingAddress {
249
257
  id
250
258
  streetLine1
@@ -261,6 +269,11 @@ fragment MailingAddress on MailingAddress {
261
269
  stateCode
262
270
  postalCode
263
271
  isDefault
272
+ taxId
273
+ vatNumber
274
+ pickupPoint {
275
+ ...PickupPoint
276
+ }
264
277
  }
265
278
 
266
279
  # Customer access token returned by `customerLogin` / `customerSignup` / `customerActivate` / `customerResetPassword` / `customerRefreshToken`. The token's JWT TTL is 24h; cookie max-age is 30d.
@@ -334,7 +347,7 @@ fragment Order on Order {
334
347
  # Cart
335
348
  # ============================================
336
349
 
337
- # Cart-level totals — subtotal, total, tax, duty, checkout charge. Spread inside the `Cart` fragment.
350
+ # Cart-level totals — subtotal, total, tax, duty, discount, shipping, checkout charge. Spread inside the `Cart` fragment.
338
351
  fragment CartCost on CartCost {
339
352
  total {
340
353
  ...Money
@@ -351,6 +364,12 @@ fragment CartCost on CartCost {
351
364
  checkoutCharge {
352
365
  ...Money
353
366
  }
367
+ totalDiscount {
368
+ ...Money
369
+ }
370
+ totalShipping {
371
+ ...Money
372
+ }
354
373
  }
355
374
 
356
375
  # Per-line cost breakdown — unit price, line subtotal/total, compare-at unit price (for showing strikethroughs). Spread inside `CartLine`.
@@ -492,6 +511,15 @@ fragment Cart on Cart {
492
511
  requiresShipping
493
512
  createdAt
494
513
  updatedAt
514
+ status
515
+ completedOrder {
516
+ id
517
+ orderNumber
518
+ accessToken
519
+ status
520
+ paymentStatus
521
+ fulfillmentStatus
522
+ }
495
523
  }
496
524
 
497
525
  # Shipping method selected on a cart (D8 term unification — wcześniej ShippingRate). Returned przez `cart.selectedShippingMethod` po `cartSelectShippingMethod` mutation.
@@ -503,8 +531,9 @@ fragment CartShippingMethod on CartShippingMethod {
503
531
  }
504
532
  }
505
533
 
506
- # Gift card applied to a cart — masked code for display, last 4 chars for matching, applied amount + remaining balance. Balance NIE debited yet — actual deduction atomically at `cartComplete`.
534
+ # Gift card applied to a cart — `id` is the stable handle the storefront passes to `cartRemoveGiftCard` (no need to keep the raw code around). Plus masked code for display, last 4 chars for matching, applied amount + remaining balance. Balance NIE debited yet — actual deduction atomically at `cartComplete`.
507
535
  fragment CartAppliedGiftCard on CartAppliedGiftCard {
536
+ id
508
537
  maskedCode
509
538
  lastCharacters
510
539
  appliedAmount {
@@ -647,6 +676,21 @@ fragment Shop on Shop {
647
676
  }
648
677
  }
649
678
 
679
+ # Minimal Shop fields consumed by `<StorefrontProvider shopData={...}>` from `@doswiftly/storefront-sdk/react`. Spread this in your `shop` query to get a response shape that matches the SDK `ShopConfig` interface 1:1 — no manual field selection, no drift when the SDK adds optional fields. Use the larger `Shop` fragment when you also need branding / contact / business hours for your UI.
680
+ fragment ShopConfigFields on Shop {
681
+ currencyCode
682
+ supportedCurrencies
683
+ localeToCurrencyMap {
684
+ locale
685
+ currency
686
+ }
687
+ defaultLanguage
688
+ supportedLanguages
689
+ botProtection {
690
+ ...BotProtection
691
+ }
692
+ }
693
+
650
694
  # ============================================
651
695
  # Payment Methods
652
696
  # ============================================
@@ -924,11 +968,12 @@ fragment FreeShippingProgress on FreeShippingProgress {
924
968
  message
925
969
  }
926
970
 
927
- # One shipping method offered for the destination + cart — id, name, carrier, price, free-shipping progress, estimated delivery, sort order. Spread on the checkout shipping step.
971
+ # One shipping method offered for the destination + cart — id, name, carrier, price, free-shipping progress, estimated delivery, sort order. `deliveryType` signals whether to render a pickup-point / locker picker (`HOME` vs `PICKUP_POINT` / `LOCKER`) so the storefront does not have to infer it from the method name. Spread on the checkout shipping step.
928
972
  fragment AvailableShippingMethod on AvailableShippingMethod {
929
973
  id
930
974
  name
931
975
  description
976
+ deliveryType
932
977
  carrier {
933
978
  ...ShippingCarrier
934
979
  }