@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 +26 -3
- package/CHANGELOG.md +294 -0
- package/README.md +8 -4
- package/fragments.graphql +49 -4
- package/llms-full.txt +117 -6
- package/operations.json +58 -11
- package/package.json +1 -1
- package/queries.graphql +22 -0
- package/schema.graphql +1084 -481
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**:
|
|
31
|
-
- **Queries**:
|
|
30
|
+
- **Schema version**: 16.0.0
|
|
31
|
+
- **Queries**: 52
|
|
32
32
|
- **Mutations**: 40
|
|
33
|
-
- **Fragments**:
|
|
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
|
-
| `
|
|
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
|
-
#
|
|
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
|
}
|