@doswiftly/storefront-operations 13.1.0 → 15.1.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 +3 -3
- package/CHANGELOG.md +236 -0
- package/README.md +17 -15
- package/fragments.graphql +57 -18
- package/llms-full.txt +175 -36
- package/operations.json +60 -36
- package/package.json +1 -1
- package/queries.graphql +26 -11
- package/schema.graphql +1213 -559
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**: 15.1.0
|
|
31
|
+
- **Queries**: 51
|
|
32
32
|
- **Mutations**: 40
|
|
33
|
-
- **Fragments**:
|
|
33
|
+
- **Fragments**: 101
|
|
34
34
|
<!-- AUTOGEN:STATS:END -->
|
|
35
35
|
|
|
36
36
|
## Loading order
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,241 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 15.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 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.
|
|
8
|
+
|
|
9
|
+
**New `CartClient` methods**
|
|
10
|
+
- `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`.
|
|
11
|
+
- `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.
|
|
12
|
+
- `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).
|
|
13
|
+
- `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.
|
|
14
|
+
|
|
15
|
+
**New `AuthClient` method**
|
|
16
|
+
- `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.
|
|
17
|
+
|
|
18
|
+
**Schema additions (operations package)**
|
|
19
|
+
- `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.
|
|
20
|
+
- `AvailableShippingMethod` gains the declarative `deliveryType` enum.
|
|
21
|
+
- `CartAppliedGiftCard` now exposes `id` — the stable handle to pass to `cartRemoveGiftCard` (no need to keep the raw gift card code on the client).
|
|
22
|
+
- New `CustomerAddresses` query — `customer.addresses(first: 50)` connection returning the address book, normalized to a node list by the SDK wrapper.
|
|
23
|
+
|
|
24
|
+
**SDK fragment changes**
|
|
25
|
+
|
|
26
|
+
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`.
|
|
27
|
+
|
|
28
|
+
**New React hook — server-driven checkout**
|
|
29
|
+
- `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.
|
|
30
|
+
|
|
31
|
+
**Field-level descriptions visible in your IDE**
|
|
32
|
+
|
|
33
|
+
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.
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
|
|
37
|
+
## 15.0.0
|
|
38
|
+
|
|
39
|
+
### Major Changes
|
|
40
|
+
|
|
41
|
+
- 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.
|
|
42
|
+
|
|
43
|
+
**Breaking** — the previous hand-written types were inaccurate, and correcting them changes some shapes:
|
|
44
|
+
- `Customer.orderCount` is now `string` (was `number`).
|
|
45
|
+
- `CartLineEdge` and `CartLineConnection.edges` were removed — the cart query never returned edges.
|
|
46
|
+
- 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.
|
|
47
|
+
|
|
48
|
+
**Fixed** — types that were missing fields or had the wrong nullability:
|
|
49
|
+
- `Order` now includes `accessToken`.
|
|
50
|
+
- `Cart` and `CartLine` now include `requiresShipping`.
|
|
51
|
+
- `Customer.displayName`, `Customer.emailMarketing`, and `Customer.totalSpent` are now correctly non-nullable.
|
|
52
|
+
- The duplicated `MailingAddress` definition is collapsed into one.
|
|
53
|
+
|
|
54
|
+
**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.
|
|
55
|
+
|
|
56
|
+
These are type-only changes — no runtime behaviour changed.
|
|
57
|
+
|
|
58
|
+
## 14.0.0
|
|
59
|
+
|
|
60
|
+
### Major Changes
|
|
61
|
+
|
|
62
|
+
- 1fda2ab: **BREAKING**: URL identifier unification (`slug` → `handle`) + type renames for naming consistency.
|
|
63
|
+
|
|
64
|
+
### What changed
|
|
65
|
+
|
|
66
|
+
URL-friendly identifiers across the storefront GraphQL API now consistently use `handle` (previously a mix of `slug` and `handle`). Canonical product brand entity renamed from `BrandSummary` to `Brand`. Shop branding type `Brand` renamed to `ShopBrand` to free the `Brand` name for the product entity.
|
|
67
|
+
|
|
68
|
+
### Field renames (GraphQL response)
|
|
69
|
+
|
|
70
|
+
| Type | Old | New |
|
|
71
|
+
| ---------------------------- | ------ | -------- |
|
|
72
|
+
| `Category` | `slug` | `handle` |
|
|
73
|
+
| `BlogPost` | `slug` | `handle` |
|
|
74
|
+
| `BlogCategory` | `slug` | `handle` |
|
|
75
|
+
| `BlogTag` | `slug` | `handle` |
|
|
76
|
+
| `LoyaltyReward` | `slug` | `handle` |
|
|
77
|
+
| `BrandFilter` (input) | `slug` | `handle` |
|
|
78
|
+
| `BrandFilterValue` | `slug` | `handle` |
|
|
79
|
+
| `ProductAttributeDefinition` | `slug` | `handle` |
|
|
80
|
+
| `CategoryFilterOption` | `slug` | `handle` |
|
|
81
|
+
|
|
82
|
+
### Type renames
|
|
83
|
+
- `BrandSummary` → `Brand` (canonical product brand entity, e.g. Funko, Nike). Update `__typename` checks and fragment spreads.
|
|
84
|
+
- `Brand` (shop branding) → `ShopBrand` (tenant-scoped shop metadata: logo, slogan, colors).
|
|
85
|
+
|
|
86
|
+
### Query args renamed
|
|
87
|
+
|
|
88
|
+
```graphql
|
|
89
|
+
# Old
|
|
90
|
+
query { category(slug: "figurki") { id name } }
|
|
91
|
+
query { blogPost(slug: "post-handle") { title } }
|
|
92
|
+
query BlogPosts($categorySlug: String, $tagSlug: String) {
|
|
93
|
+
blogPosts(categorySlug: $categorySlug, tagSlug: $tagSlug) { ... }
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
# New
|
|
97
|
+
query { category(handle: "figurki") { id name } }
|
|
98
|
+
query { blogPost(handle: "post-handle") { title } }
|
|
99
|
+
query BlogPosts($categoryHandle: String, $tagHandle: String) {
|
|
100
|
+
blogPosts(categoryHandle: $categoryHandle, tagHandle: $tagHandle) { ... }
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Brand filter input
|
|
105
|
+
|
|
106
|
+
```graphql
|
|
107
|
+
# Old
|
|
108
|
+
products(filters: [{ brand: { slug: "funko" } }]) { ... }
|
|
109
|
+
|
|
110
|
+
# New
|
|
111
|
+
products(filters: [{ brand: { handle: "funko" } }]) { ... }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### MenuItem resource union
|
|
115
|
+
|
|
116
|
+
```graphql
|
|
117
|
+
# Old
|
|
118
|
+
fragment MenuItem on MenuItem {
|
|
119
|
+
resource {
|
|
120
|
+
__typename
|
|
121
|
+
... on Category {
|
|
122
|
+
id
|
|
123
|
+
slug
|
|
124
|
+
name
|
|
125
|
+
}
|
|
126
|
+
... on BrandSummary {
|
|
127
|
+
id
|
|
128
|
+
slug
|
|
129
|
+
name
|
|
130
|
+
logo
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# New
|
|
136
|
+
fragment MenuItem on MenuItem {
|
|
137
|
+
resource {
|
|
138
|
+
__typename
|
|
139
|
+
... on Category {
|
|
140
|
+
id
|
|
141
|
+
handle
|
|
142
|
+
name
|
|
143
|
+
}
|
|
144
|
+
... on Brand {
|
|
145
|
+
id
|
|
146
|
+
handle
|
|
147
|
+
name
|
|
148
|
+
logo
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Migration steps
|
|
155
|
+
1. Update to `@doswiftly/storefront-operations@^14.0.0` + `@doswiftly/storefront-sdk@^14.0.0`.
|
|
156
|
+
2. Run your codegen — TypeScript will surface every call site that needs update.
|
|
157
|
+
3. Find/replace in storefront code:
|
|
158
|
+
- `.slug` → `.handle` on response fields listed above.
|
|
159
|
+
- `__typename === 'BrandSummary'` → `__typename === 'Brand'`.
|
|
160
|
+
- `{ brand: { slug } }` filter input → `{ brand: { handle } }`.
|
|
161
|
+
- `categorySlug`/`tagSlug` query args → `categoryHandle`/`tagHandle`.
|
|
162
|
+
4. Update GraphQL fragments selecting renamed fields.
|
|
163
|
+
5. Re-run codegen, fix any remaining TypeScript errors.
|
|
164
|
+
|
|
165
|
+
### Why
|
|
166
|
+
|
|
167
|
+
A single naming convention (`handle`) across all URL identifiers removes ambiguity and matches common e-commerce GraphQL convention. The `BrandSummary` suffix was misleading — no full `Brand` ObjectType existed, so the suffix suggested a missing parent type. Shop branding `Brand` blocked the cleaner name for the product entity.
|
|
168
|
+
|
|
169
|
+
Full migration guide with code examples: <https://docs.doswiftly.pl/storefront-developer/migration-14.0>
|
|
170
|
+
|
|
171
|
+
Naming conventions reference: <https://docs.doswiftly.pl/storefront-developer/api/naming-conventions>
|
|
172
|
+
|
|
173
|
+
### Minor Changes
|
|
174
|
+
|
|
175
|
+
- 507d487: Storefront GraphQL: nowy `attributeOptionsSearch(input)` query — paginated + searchable lista opcji atrybutu (Relay Connection).
|
|
176
|
+
|
|
177
|
+
Use case: storefront UI z dużymi filtrami atrybutów (np. 800+ licencji, materiałów, rozmiarów, kolorów) — dropdown z autocomplete i paginacją bez zassania całej listy. `productFilters.attributes[].filterValues[]` zostaje jako quick browse facets (top N), `attributeOptionsSearch` służy do drill-into specific filter card z searchem.
|
|
178
|
+
|
|
179
|
+
Dla **marek/producentów** preferuj dedykowane API `productFilters.brands[]` (Brand jest pełnoprawną encją z logo i productCount) — `attributeOptionsSearch(handle: "marka")` jest legacy fallback dla katalogów bez kanonicznej Brand entity.
|
|
180
|
+
|
|
181
|
+
**Input** (`AttributeOptionsSearchInput`):
|
|
182
|
+
- `handle: String!` — slug atrybutu (np. `"marka"`)
|
|
183
|
+
- `contextInput: AvailableFiltersInput` — produktowy kontekst (kategoria/kolekcja/search/currentFilters) z exclude-self semantyką
|
|
184
|
+
- `first: Int` (1–100, default 50)
|
|
185
|
+
- `after: String` — opaque cursor
|
|
186
|
+
- `search: String` — case-insensitive ILIKE (max 100 znaków)
|
|
187
|
+
- `sort: AttributeOptionsSearchSort` — `COUNT_DESC` (default, most popular first) lub `LABEL_ASC` (alfabetycznie z polskim collation)
|
|
188
|
+
|
|
189
|
+
**Output** (`AttributeFilterValueConnection`): `edges { cursor node }`, `nodes` (shortcut), `pageInfo`, `totalCount`. Wspiera atrybuty `SELECT`/`CHECKBOX`/`RADIO`/`COLOR` (z bazy `attribute_options`) oraz `TEXT`/`TEXTAREA` (z aggregated unique values).
|
|
190
|
+
|
|
191
|
+
**Migration**: brak breaking changes — `productFilters` zachowuje obecny shape. Jeśli budujesz "Pokaż wszystkie marki" UI, przenoś logikę na nowy query żeby uniknąć over-fetching.
|
|
192
|
+
|
|
193
|
+
- 6f8d873: `CartWarningCode` enum gains a fourth value, `SHIPPING_METHOD_AUTO_CLEARED`.
|
|
194
|
+
|
|
195
|
+
Cart line mutations (`cartAddLines`, `cartUpdateLines`, `cartRemoveLines`) now reconcile the selected shipping method against cart contents on every call. When a mutation leaves the cart with no items that require physical delivery (digital-only or empty) but a shipping method is still set, the method is cleared automatically and the mutation response surfaces a `CartWarning { code: "SHIPPING_METHOD_AUTO_CLEARED", target: "selectedShippingMethod", message: "…" }`.
|
|
196
|
+
|
|
197
|
+
This closes the gap where a mixed cart that lost its last physical line via the storefront UI would persist a stale shipping method and fail later at checkout submit with `FORBIDDEN_SHIPPING_METHOD`. There is no escape hatch needed on the storefront — the mutation accepts non-null `shippingMethodId` only, so the backend takes responsibility for the transition.
|
|
198
|
+
|
|
199
|
+
**Storefront impact**: none required. Reads of `cart.selectedShippingMethod` already returned `null` once cleared. Apps that already consume the `warnings[]` array (Apps that follow the standard `userErrors[]` + `warnings[]` payload shape) will surface the auto-clear toast for free. Apps that ignore `warnings[]` continue to work — the mutation still succeeds and the cart is left in a consistent state.
|
|
200
|
+
|
|
201
|
+
**Locale**: `message` is translated server-side (`Accept-Language`) — for example, `pl` returns "Wybrana metoda wysyłki została usunięta, ponieważ koszyk nie wymaga już dostawy.", `en` returns "The selected shipping method was removed because the cart no longer requires delivery."
|
|
202
|
+
|
|
203
|
+
The warning fires only on the line mutation that triggers the transition, never on subsequent reads or unrelated mutations. Decrementing a physical line quantity from `3` to `1`, removing one physical line while another remains, or adding digital lines to a mixed cart all leave the shipping method untouched.
|
|
204
|
+
|
|
205
|
+
- 1fda2ab: Storefront navigation menus: typed `resource` field eksponowany w `MenuItem` fragment + nowy typ linku `BRAND`.
|
|
206
|
+
|
|
207
|
+
**Co nowego**:
|
|
208
|
+
- `MenuItem.resource: MenuItemResource` — typed union (`Category` / `Collection` / `ShopPage` / `Product` / `BrandSummary`) wystawiony w `MenuItem` fragment na wszystkich 3 poziomach zagnieżdżenia. Każdy member zwraca `slug` (Category/Product/BrandSummary) lub `handle` (Collection/ShopPage) gotowy do budowy własnego URL.
|
|
209
|
+
- `MenuItemType.BRAND` — nowy typ linku menu dla marek producentów. `BrandSummary` w `resource` union (`id`, `name`, `slug`, `logo`).
|
|
210
|
+
- `MenuItem.url` dla `BRAND` resolve'uje się serwerowo jako `/brands/<slug>` — spójnie z istniejącą konwencją `/categories|/collections|/pages|/products/<slug>`.
|
|
211
|
+
|
|
212
|
+
**Dwa sposoby renderingu link target**:
|
|
213
|
+
1. **Standard route convention** — użyj `item.url` jak dotąd. Działa jeśli storefront ma routy `/categories/[slug]`, `/collections/[handle]`, `/pages/[handle]`, `/products/[handle]`, `/brands/[slug]`.
|
|
214
|
+
2. **Custom routing** (Next.js single-segment, Remix, itd.) — odczytaj `item.resource.__typename` + per-type `slug`/`handle`:
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
function buildHref(item: MenuItem): string {
|
|
218
|
+
if (!item.resource) return item.url ?? "#";
|
|
219
|
+
switch (item.resource.__typename) {
|
|
220
|
+
case "Category":
|
|
221
|
+
return `/${item.resource.slug}`;
|
|
222
|
+
case "Collection":
|
|
223
|
+
return `/${item.resource.handle}`;
|
|
224
|
+
case "ShopPage":
|
|
225
|
+
return `/${item.resource.handle}`;
|
|
226
|
+
case "Product":
|
|
227
|
+
return `/p/${item.resource.handle}`;
|
|
228
|
+
case "BrandSummary":
|
|
229
|
+
return `/marka/${item.resource.slug}`;
|
|
230
|
+
}
|
|
231
|
+
return item.url ?? "#";
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
**Performance**: wszystkie resource lookupy są batchowane per request — zero N+1 nawet w głębokich menu.
|
|
236
|
+
|
|
237
|
+
**Migration**: brak breaking changes. Kod używający tylko `item.url` działa bez zmian. `resource` jest opt-in dopóki nie zaczniesz go selekcjonować w custom fragmencie.
|
|
238
|
+
|
|
3
239
|
## 13.1.0
|
|
4
240
|
|
|
5
241
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ full executable body of each operation.
|
|
|
168
168
|
|
|
169
169
|
| Operation | Description |
|
|
170
170
|
| --- | --- |
|
|
171
|
-
| `Product` | Fetches a single product by `id` or `handle` (URL
|
|
171
|
+
| `Product` | Fetches a single product by `id` or `handle` (URL-friendly identifier). Pass either — whichever is provided wins; if both are missing, returns null. Returns null if the product is not storefront-accessible (must be `ACTIVE` status with `PUBLIC` or `BUNDLE_ONLY` visibility). |
|
|
172
172
|
| `ProductConfigurator` | Fetches a product together with its filtered attribute definitions, optimized for the configurator UI (e.g. customer-facing text fields, finishing options, scoped variants). `fillingMode: "CUSTOMER"` returns only customer-facing attributes; pass `"BOTH"` to also include attributes shared with the merchant admin. Single round-trip — saves a separate `attributes` query. |
|
|
173
173
|
| `Products` | Paginated product list (Relay Connection, default page size 20, max 100). The `query` argument supports a structured search syntax — `tag:summer`, `vendor:foo`, `product_type:shirts`, `variants.price:>10`, plus `AND`/`OR`/`NOT` — falling back to free-text title/content search. The `filters[]` array uses multi-filter logic: same field name appears multiple times → OR; different fields → AND. Sort: `RELEVANCE`, `TITLE`, `PRICE`, `NEWEST`, `OLDEST`, `BEST_SELLING`. The response includes a `filters` block for faceted navigation (counts per filterable attribute value). |
|
|
174
174
|
| `ProductSearch` | Full-text product search — `$query` is required. Functionally equivalent to `Products` with `$query` set, minus the `sortKey` argument (search defaults to relevance ranking). Use for the search results page; combine with `filters[]` for guided refinement. |
|
|
@@ -185,7 +185,7 @@ full executable body of each operation.
|
|
|
185
185
|
|
|
186
186
|
| Operation | Description |
|
|
187
187
|
| --- | --- |
|
|
188
|
-
| `Category` | Fetches a single category by `id` or `
|
|
188
|
+
| `Category` | Fetches a single category by `id` or `handle` with its parent and immediate children. Use for breadcrumbs and sub-navigation. Nested queries on `parent` / `children` are batched server-side — safe to use in lists without N+1 concerns. |
|
|
189
189
|
| `Categories` | Returns active root categories for the shop. Each root exposes its `children` — build the tree client-side by walking those fields (server batches the lookups, no N+1). The hierarchy is not depth-capped server-side. Use for nav mega-menus and category pages. |
|
|
190
190
|
|
|
191
191
|
#### Cart
|
|
@@ -200,6 +200,7 @@ full executable body of each operation.
|
|
|
200
200
|
| --- | --- |
|
|
201
201
|
| `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
202
|
| `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. |
|
|
203
|
+
| `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
204
|
| `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
205
|
| `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
206
|
|
|
@@ -280,8 +281,8 @@ full executable body of each operation.
|
|
|
280
281
|
|
|
281
282
|
| Operation | Description |
|
|
282
283
|
| --- | --- |
|
|
283
|
-
| `BlogPosts` | Paginated list of published blog posts. Filter by `
|
|
284
|
-
| `BlogPost` | Fetches a single blog post by `id` or `
|
|
284
|
+
| `BlogPosts` | Paginated list of published blog posts. Filter by `categoryHandle`, `tagHandle`, or `featured` (boolean flag, not enum). Sort: `PUBLISHED_AT` (default), `TITLE`, `VIEW_COUNT`, or `CREATED_AT`. Public; no auth required. |
|
|
285
|
+
| `BlogPost` | Fetches a single blog post by `id` or `handle`. Visibility-gated: returns null if the post is not yet `PUBLISHED` or if its publish date is in the future (scheduled posts stay hidden until their publish time). Side effect: fetching a post increments its `view_count` asynchronously (does not block the response). |
|
|
285
286
|
| `BlogCategories` | Lists all blog categories with per-category `postCount` and SEO metadata. Use to render category navigation on blog pages. Public; no auth required. |
|
|
286
287
|
| `BlogTags` | Lists blog tags with usage counts (`postCount` per tag). Use to render a tag cloud. Public; no auth required. |
|
|
287
288
|
|
|
@@ -302,7 +303,7 @@ full executable body of each operation.
|
|
|
302
303
|
|
|
303
304
|
| Operation | Description |
|
|
304
305
|
| --- | --- |
|
|
305
|
-
| `Menu` | Fetches a navigation menu by `handle` (e.g. `"main-menu"`, `"footer"`, `"mobile"`). Returns the nested item tree. Each item is typed as one of: `HTTP`, `FRONTPAGE`, `SEARCH`, `CATALOG`, `BLOG`, `PRODUCT`, `COLLECTION`, `CATEGORY`, or `
|
|
306
|
+
| `Menu` | Fetches a navigation menu by `handle` (e.g. `"main-menu"`, `"footer"`, `"mobile"`). Returns the nested item tree (up to 3 levels). Each item is typed as one of: `HTTP`, `FRONTPAGE`, `SEARCH`, `CATALOG`, `BLOG`, `PRODUCT`, `COLLECTION`, `CATEGORY`, `PAGE`, or `BRAND` — switch on the type to render the right link target. Each resource-linked item exposes both a pre-resolved `url` (standard `/categories\|/collections\|/pages\|/products\|/brands/<handle>` convention) and a typed `resource` union with the raw handle so storefronts with custom routing can construct their own paths instead. All resource lookups are batched per request — no N+1 even for deep menus. |
|
|
306
307
|
|
|
307
308
|
#### Content: URL Redirects
|
|
308
309
|
|
|
@@ -459,13 +460,14 @@ full executable body of each operation.
|
|
|
459
460
|
|
|
460
461
|
| Fragment | On Type | Description |
|
|
461
462
|
| --- | --- | --- |
|
|
462
|
-
| `Category` | `Category` | Category node in the shop's taxonomy — name,
|
|
463
|
+
| `Category` | `Category` | Category node in the shop's taxonomy — name, handle, hero image, depth `level`, materialized `path`, product count, sort order. Spread on category cards and breadcrumb segments. Does NOT include parent / children — query those fields separately and let the server batch them. |
|
|
463
464
|
|
|
464
465
|
#### Customer
|
|
465
466
|
|
|
466
467
|
| Fragment | On Type | Description |
|
|
467
468
|
| --- | --- | --- |
|
|
468
|
-
| `
|
|
469
|
+
| `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. |
|
|
470
|
+
| `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
471
|
| `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
472
|
| `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
473
|
| `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. |
|
|
@@ -483,7 +485,7 @@ full executable body of each operation.
|
|
|
483
485
|
| `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
486
|
| `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
487
|
| `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`. |
|
|
488
|
+
| `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
489
|
| `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
490
|
|
|
489
491
|
#### Shop
|
|
@@ -543,7 +545,7 @@ full executable body of each operation.
|
|
|
543
545
|
| `ShippingCarrier` | `ShippingCarrier` | Carrier offering the shipping method — id, display name, logo URL, internal service code. |
|
|
544
546
|
| `DeliveryEstimate` | `DeliveryEstimate` | Estimated delivery window — `minDays` to `maxDays` plus a human-readable description (e.g. "2-4 business days"). |
|
|
545
547
|
| `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. |
|
|
548
|
+
| `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
549
|
|
|
548
550
|
#### Attribute Filters
|
|
549
551
|
|
|
@@ -554,7 +556,7 @@ full executable body of each operation.
|
|
|
554
556
|
| `AttributeFilterValue` | `AttributeFilterValue` | One discrete value in a filterable attribute (e.g. "Red" for the Color filter). Includes `productCount` in the current listing context (for "(12)" badges next to filter values), optional swatch and price modifier. |
|
|
555
557
|
| `AttributeDefinition` | `AttributeDefinition` | Filterable attribute exposed on the storefront — name, type (SELECT / CHECKBOX / SLIDER / etc.), filterability flags, display order, and either discrete `filterValues[]` or numeric `rangeBounds`. Spread inside `availableFilters.attributes[]`. |
|
|
556
558
|
| `PriceRangeFilter` | `PriceRange` | Min / max price range across products in the current listing context. Use to bound the price slider. |
|
|
557
|
-
| `CategoryFilterOption` | `CategoryFilterOption` | One category option in the categories filter — id, name,
|
|
559
|
+
| `CategoryFilterOption` | `CategoryFilterOption` | One category option in the categories filter — id, name, handle, count of products in this category within the current listing context, level + parentId for tree rendering. |
|
|
558
560
|
| `AvailableFilters` | `AvailableFilters` | Full result of the `productFilters($input)` query — attribute filters, price range, category filters, brands, count of currently active filters, total products in context (Relay-aligned `totalCount`), and boolean facet count for `availableForSale` (`availableCount`). Spread on listing pages to render filter sidebars. Per-facet counts (`attributes[].filterValues[].productCount`, `brands[].productCount`, `categories[].productCount`, `availableCount`) use exclude-self aggregation: when a dimension is in `input.currentFilters` / `input.available`, its own facet count IGNORES that filter (shows "how many products would appear if this facet were toggled"), while OTHER dimensions APPLY their filters. This matches industry convention for accurate facet UX. |
|
|
559
561
|
|
|
560
562
|
#### Loyalty Program
|
|
@@ -593,9 +595,9 @@ full executable body of each operation.
|
|
|
593
595
|
|
|
594
596
|
| Fragment | On Type | Description |
|
|
595
597
|
| --- | --- | --- |
|
|
596
|
-
| `BlogCategory` | `BlogCategory` | Blog category — name,
|
|
597
|
-
| `BlogTag` | `BlogTag` | Blog tag — name,
|
|
598
|
-
| `BlogPost` | `BlogPost` | Full blog post — title,
|
|
598
|
+
| `BlogCategory` | `BlogCategory` | Blog category — name, handle, description, post count. Spread on category navigation and post category labels. |
|
|
599
|
+
| `BlogTag` | `BlogTag` | Blog tag — name, handle, post count. Spread on tag clouds and post tag labels. |
|
|
600
|
+
| `BlogPost` | `BlogPost` | Full blog post — title, handle, excerpt, content (with `contentFormat` indicating HTML/Markdown), featured image, author, category, tags, publish date, reading time, view + comment counts, SEO metadata. Spread on the post detail page. |
|
|
599
601
|
|
|
600
602
|
#### Store Availability (BOPIS / multi-location)
|
|
601
603
|
|
|
@@ -613,13 +615,13 @@ full executable body of each operation.
|
|
|
613
615
|
|
|
614
616
|
| Fragment | On Type | Description |
|
|
615
617
|
| --- | --- | --- |
|
|
616
|
-
| `ShopPage` | `ShopPage` | CMS page — handle (URL
|
|
618
|
+
| `ShopPage` | `ShopPage` | CMS page — handle (URL-friendly identifier), title, body (HTML), excerpt, SEO metadata, publish date. Spread on About / Privacy / Terms / Returns Policy pages. |
|
|
617
619
|
|
|
618
620
|
#### Navigation Menus
|
|
619
621
|
|
|
620
622
|
| Fragment | On Type | Description |
|
|
621
623
|
| --- | --- | --- |
|
|
622
|
-
| `MenuItem` | `MenuItem` | One menu item with up to 3 levels of nested children — title, URL, type (`HTTP` / `FRONTPAGE` / `SEARCH` / `CATALOG` / `BLOG` / `PRODUCT` / `COLLECTION` / `CATEGORY` / `PAGE`), `resourceId` for typed items, optional image. Switch on `type` to
|
|
624
|
+
| `MenuItem` | `MenuItem` | One menu item with up to 3 levels of nested children — title, URL, type (`HTTP` / `FRONTPAGE` / `SEARCH` / `CATALOG` / `BLOG` / `PRODUCT` / `COLLECTION` / `CATEGORY` / `PAGE` / `BRAND`), `resourceId` for typed items, optional image, and a typed `resource` union (Category / Collection / ShopPage / Product / Brand) carrying the linked resource's handle. Two ways to render the link target: (1) use the pre-resolved `url` if your storefront follows the standard `/categories\|/collections\|/pages\|/products\|/brands/<handle>` route convention; (2) read `resource.__typename` + the per-type `handle` and build your own paths if your storefront uses different routing. Switch on `type` to decide which static (FRONTPAGE/SEARCH/CATALOG/BLOG) or dynamic target to render. |
|
|
623
625
|
| `Menu` | `Menu` | Navigation menu — handle (e.g. "main-menu", "footer", "mobile"), title, nested item tree. Returned by `menu($handle)` query. |
|
|
624
626
|
|
|
625
627
|
#### URL Redirects
|
package/fragments.graphql
CHANGED
|
@@ -119,7 +119,7 @@ fragment ProductCard on Product {
|
|
|
119
119
|
vendor
|
|
120
120
|
categories {
|
|
121
121
|
id
|
|
122
|
-
|
|
122
|
+
handle
|
|
123
123
|
name
|
|
124
124
|
}
|
|
125
125
|
isAvailable
|
|
@@ -225,11 +225,11 @@ fragment Collection on Collection {
|
|
|
225
225
|
# Categories
|
|
226
226
|
# ============================================
|
|
227
227
|
|
|
228
|
-
# Category node in the shop's taxonomy — name,
|
|
228
|
+
# Category node in the shop's taxonomy — name, handle, hero image, depth `level`, materialized `path`, product count, sort order. Spread on category cards and breadcrumb segments. Does NOT include parent / children — query those fields separately and let the server batch them.
|
|
229
229
|
fragment Category on Category {
|
|
230
230
|
id
|
|
231
231
|
name
|
|
232
|
-
|
|
232
|
+
handle
|
|
233
233
|
description
|
|
234
234
|
image {
|
|
235
235
|
...ImageCard
|
|
@@ -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.
|
|
@@ -503,8 +516,9 @@ fragment CartShippingMethod on CartShippingMethod {
|
|
|
503
516
|
}
|
|
504
517
|
}
|
|
505
518
|
|
|
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`.
|
|
519
|
+
# 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
520
|
fragment CartAppliedGiftCard on CartAppliedGiftCard {
|
|
521
|
+
id
|
|
508
522
|
maskedCode
|
|
509
523
|
lastCharacters
|
|
510
524
|
appliedAmount {
|
|
@@ -924,11 +938,12 @@ fragment FreeShippingProgress on FreeShippingProgress {
|
|
|
924
938
|
message
|
|
925
939
|
}
|
|
926
940
|
|
|
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.
|
|
941
|
+
# 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
942
|
fragment AvailableShippingMethod on AvailableShippingMethod {
|
|
929
943
|
id
|
|
930
944
|
name
|
|
931
945
|
description
|
|
946
|
+
deliveryType
|
|
932
947
|
carrier {
|
|
933
948
|
...ShippingCarrier
|
|
934
949
|
}
|
|
@@ -1004,11 +1019,11 @@ fragment PriceRangeFilter on PriceRange {
|
|
|
1004
1019
|
}
|
|
1005
1020
|
}
|
|
1006
1021
|
|
|
1007
|
-
# One category option in the categories filter — id, name,
|
|
1022
|
+
# One category option in the categories filter — id, name, handle, count of products in this category within the current listing context, level + parentId for tree rendering.
|
|
1008
1023
|
fragment CategoryFilterOption on CategoryFilterOption {
|
|
1009
1024
|
id
|
|
1010
1025
|
name
|
|
1011
|
-
|
|
1026
|
+
handle
|
|
1012
1027
|
productCount
|
|
1013
1028
|
level
|
|
1014
1029
|
parentId
|
|
@@ -1121,7 +1136,7 @@ fragment LoyaltyTransaction on LoyaltyTransaction {
|
|
|
1121
1136
|
fragment LoyaltyReward on LoyaltyReward {
|
|
1122
1137
|
id
|
|
1123
1138
|
name
|
|
1124
|
-
|
|
1139
|
+
handle
|
|
1125
1140
|
type
|
|
1126
1141
|
pointsCost
|
|
1127
1142
|
discountPercent
|
|
@@ -1271,28 +1286,28 @@ fragment Wishlist on Wishlist {
|
|
|
1271
1286
|
# Blog
|
|
1272
1287
|
# ============================================
|
|
1273
1288
|
|
|
1274
|
-
# Blog category — name,
|
|
1289
|
+
# Blog category — name, handle, description, post count. Spread on category navigation and post category labels.
|
|
1275
1290
|
fragment BlogCategory on BlogCategory {
|
|
1276
1291
|
id
|
|
1277
1292
|
name
|
|
1278
|
-
|
|
1293
|
+
handle
|
|
1279
1294
|
description
|
|
1280
1295
|
postCount
|
|
1281
1296
|
}
|
|
1282
1297
|
|
|
1283
|
-
# Blog tag — name,
|
|
1298
|
+
# Blog tag — name, handle, post count. Spread on tag clouds and post tag labels.
|
|
1284
1299
|
fragment BlogTag on BlogTag {
|
|
1285
1300
|
id
|
|
1286
1301
|
name
|
|
1287
|
-
|
|
1302
|
+
handle
|
|
1288
1303
|
postCount
|
|
1289
1304
|
}
|
|
1290
1305
|
|
|
1291
|
-
# Full blog post — title,
|
|
1306
|
+
# Full blog post — title, handle, excerpt, content (with `contentFormat` indicating HTML/Markdown), featured image, author, category, tags, publish date, reading time, view + comment counts, SEO metadata. Spread on the post detail page.
|
|
1292
1307
|
fragment BlogPost on BlogPost {
|
|
1293
1308
|
id
|
|
1294
1309
|
title
|
|
1295
|
-
|
|
1310
|
+
handle
|
|
1296
1311
|
excerpt
|
|
1297
1312
|
content
|
|
1298
1313
|
contentFormat
|
|
@@ -1427,7 +1442,7 @@ fragment VariantStoreAvailability on ProductVariant {
|
|
|
1427
1442
|
# Pages
|
|
1428
1443
|
# ============================================
|
|
1429
1444
|
|
|
1430
|
-
# CMS page — handle (URL
|
|
1445
|
+
# CMS page — handle (URL-friendly identifier), title, body (HTML), excerpt, SEO metadata, publish date. Spread on About / Privacy / Terms / Returns Policy pages.
|
|
1431
1446
|
fragment ShopPage on ShopPage {
|
|
1432
1447
|
id
|
|
1433
1448
|
handle
|
|
@@ -1447,13 +1462,21 @@ fragment ShopPage on ShopPage {
|
|
|
1447
1462
|
# Navigation Menus
|
|
1448
1463
|
# ============================================
|
|
1449
1464
|
|
|
1450
|
-
# One menu item with up to 3 levels of nested children — title, URL, type (`HTTP` / `FRONTPAGE` / `SEARCH` / `CATALOG` / `BLOG` / `PRODUCT` / `COLLECTION` / `CATEGORY` / `PAGE`), `resourceId` for typed items, optional image. Switch on `type` to
|
|
1465
|
+
# One menu item with up to 3 levels of nested children — title, URL, type (`HTTP` / `FRONTPAGE` / `SEARCH` / `CATALOG` / `BLOG` / `PRODUCT` / `COLLECTION` / `CATEGORY` / `PAGE` / `BRAND`), `resourceId` for typed items, optional image, and a typed `resource` union (Category / Collection / ShopPage / Product / Brand) carrying the linked resource's handle. Two ways to render the link target: (1) use the pre-resolved `url` if your storefront follows the standard `/categories|/collections|/pages|/products|/brands/<handle>` route convention; (2) read `resource.__typename` + the per-type `handle` and build your own paths if your storefront uses different routing. Switch on `type` to decide which static (FRONTPAGE/SEARCH/CATALOG/BLOG) or dynamic target to render.
|
|
1451
1466
|
fragment MenuItem on MenuItem {
|
|
1452
1467
|
id
|
|
1453
1468
|
title
|
|
1454
1469
|
url
|
|
1455
1470
|
type
|
|
1456
1471
|
resourceId
|
|
1472
|
+
resource {
|
|
1473
|
+
__typename
|
|
1474
|
+
... on Category { id handle name }
|
|
1475
|
+
... on Collection { id handle title }
|
|
1476
|
+
... on ShopPage { id handle title }
|
|
1477
|
+
... on Product { id handle title }
|
|
1478
|
+
... on Brand { id handle name logo }
|
|
1479
|
+
}
|
|
1457
1480
|
image {
|
|
1458
1481
|
...Image
|
|
1459
1482
|
}
|
|
@@ -1463,12 +1486,28 @@ fragment MenuItem on MenuItem {
|
|
|
1463
1486
|
url
|
|
1464
1487
|
type
|
|
1465
1488
|
resourceId
|
|
1489
|
+
resource {
|
|
1490
|
+
__typename
|
|
1491
|
+
... on Category { id handle name }
|
|
1492
|
+
... on Collection { id handle title }
|
|
1493
|
+
... on ShopPage { id handle title }
|
|
1494
|
+
... on Product { id handle title }
|
|
1495
|
+
... on Brand { id handle name logo }
|
|
1496
|
+
}
|
|
1466
1497
|
items {
|
|
1467
1498
|
id
|
|
1468
1499
|
title
|
|
1469
1500
|
url
|
|
1470
1501
|
type
|
|
1471
1502
|
resourceId
|
|
1503
|
+
resource {
|
|
1504
|
+
__typename
|
|
1505
|
+
... on Category { id handle name }
|
|
1506
|
+
... on Collection { id handle title }
|
|
1507
|
+
... on ShopPage { id handle title }
|
|
1508
|
+
... on Product { id handle title }
|
|
1509
|
+
... on Brand { id handle name logo }
|
|
1510
|
+
}
|
|
1472
1511
|
}
|
|
1473
1512
|
}
|
|
1474
1513
|
}
|
|
@@ -1528,7 +1567,7 @@ fragment ProductAttributeOption on ProductAttributeOption {
|
|
|
1528
1567
|
fragment ProductAttributeDefinition on ProductAttributeDefinition {
|
|
1529
1568
|
id
|
|
1530
1569
|
name
|
|
1531
|
-
|
|
1570
|
+
handle
|
|
1532
1571
|
description
|
|
1533
1572
|
type
|
|
1534
1573
|
fillingMode
|