@doswiftly/storefront-operations 12.0.0 → 13.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 +2 -2
- package/CHANGELOG.md +239 -0
- package/README.md +4 -3
- package/fragments.graphql +5 -2
- package/llms-full.txt +42 -6
- package/operations.json +31 -7
- package/package.json +1 -1
- package/queries.graphql +21 -2
- package/schema.graphql +150 -155
package/AGENTS.md
CHANGED
|
@@ -27,8 +27,8 @@ 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**: 13.1.0
|
|
31
|
+
- **Queries**: 50
|
|
32
32
|
- **Mutations**: 40
|
|
33
33
|
- **Fragments**: 100
|
|
34
34
|
<!-- AUTOGEN:STATS:END -->
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,244 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 13.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2e137ad: Added cart-aware shipping methods discovery: `Cart.requiresShipping`, `CartLine.requiresShipping`, and `cart.availableShippingMethods(address)`.
|
|
8
|
+
|
|
9
|
+
**What's new for the storefront**
|
|
10
|
+
- **`cart.requiresShipping: Boolean!`** — single signal whether the cart needs physical shipping at all. `false` for carts that contain only digital goods (downloads, gift cards, services, subscriptions). Use it to skip the shipping picker step in checkout entirely.
|
|
11
|
+
- **`cart.lines.nodes[].requiresShipping: Boolean!`** — per-line classification. Useful in cart drawer UI to flag physical vs digital lines independently.
|
|
12
|
+
- **`cart.availableShippingMethods(address: ShippingAddressInput!): AvailableShippingMethodsPayload!`** — field on `Cart` that returns shipping methods for the cart's contents at the given destination. The backend pulls subtotal and weight from the cart aggregate — you no longer need to compute them on the client. For digital-only carts the response is `{ methods: [], userErrors: [{ code: 'DIGITAL_ONLY_NO_SHIPPING' }] }`.
|
|
13
|
+
- **`CartAvailableShippingMethods` query** — new named operation ready to drop into your storefront codegen pipeline.
|
|
14
|
+
|
|
15
|
+
**Recommended checkout flow**
|
|
16
|
+
|
|
17
|
+
```graphql
|
|
18
|
+
query CartAvailableShippingMethods(
|
|
19
|
+
$cartId: ID!
|
|
20
|
+
$address: ShippingAddressInput!
|
|
21
|
+
) {
|
|
22
|
+
cart(id: $cartId) {
|
|
23
|
+
id
|
|
24
|
+
requiresShipping
|
|
25
|
+
availableShippingMethods(address: $address) {
|
|
26
|
+
methods {
|
|
27
|
+
id
|
|
28
|
+
name
|
|
29
|
+
price {
|
|
30
|
+
amount
|
|
31
|
+
currencyCode
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
userErrors {
|
|
35
|
+
code
|
|
36
|
+
message
|
|
37
|
+
field
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If `cart.requiresShipping === false`, skip rendering the shipping picker and proceed straight to payment. Calling `cartSelectShippingMethod` on a digital-only cart returns `userErrors[{ code: 'FORBIDDEN_SHIPPING_METHOD' }]`, matching the readiness check that `cartComplete` already enforces.
|
|
45
|
+
|
|
46
|
+
**Backward compatibility**
|
|
47
|
+
- The existing standalone `availableShippingMethods(address, cart: CartShippingInput)` query is **unchanged**. Keep using it for pre-cart shipping calculators on product detail pages (when the customer has not created a cart yet).
|
|
48
|
+
- The new field and new query are purely additive. No existing operations change shape.
|
|
49
|
+
|
|
50
|
+
**Free-shipping progress + delivery estimates are now localized**
|
|
51
|
+
|
|
52
|
+
The `freeShippingProgress.message` field and `estimatedDelivery.description` field used to be hardcoded in English. They now respect the `Accept-Language` header (defaults to Polish — `pl`). If your storefront previously parsed these strings, switch to the structured fields (`qualifies`, `progressPercent`, `remaining.amount`, `minDays`, `maxDays`) instead.
|
|
53
|
+
|
|
54
|
+
**Reason codes on shipping `userErrors`**
|
|
55
|
+
|
|
56
|
+
| Code | When | UI reaction |
|
|
57
|
+
| -------------------------- | -------------------------------- | -------------------------------------------- |
|
|
58
|
+
| `DIGITAL_ONLY_NO_SHIPPING` | Cart contains only digital items | Skip shipping picker, go straight to payment |
|
|
59
|
+
| `NO_SHIPPING_METHODS` | Address has no matching zone | Show "no delivery to your country" |
|
|
60
|
+
| `SHIPPING_ERROR` | Internal service error (rare) | Retry or fall back to pre-cart preview query |
|
|
61
|
+
|
|
62
|
+
**Why both packages bump together**
|
|
63
|
+
|
|
64
|
+
`@doswiftly/storefront-sdk` ships local copies of the cart fragments that this update extends with `requiresShipping`, so the SDK release pairs with the schema/operations release. `@doswiftly/commerce-policy` exports `NON_PHYSICAL_TYPES` for downstream tooling that wants to mirror the same classification on the client side.
|
|
65
|
+
|
|
66
|
+
## 13.0.0
|
|
67
|
+
|
|
68
|
+
### Major Changes
|
|
69
|
+
|
|
70
|
+
- 783ce01: Faceted aggregation accuracy + DX naming consolidation w `productFilters` API. Plus boolean facet count dla "availableForSale" + payload cleanup w Cart query.
|
|
71
|
+
|
|
72
|
+
## BREAKING — `AvailableFilters.matchCount` removed → `totalCount`
|
|
73
|
+
|
|
74
|
+
Pole `matchCount` zostało usunięte. Użyj `totalCount` — semantyka identyczna (produkty w current context PRZED zaaplikowaniem faceted filters), nazwa zaligned z `ProductConnection.totalCount` (Relay Connection spec).
|
|
75
|
+
|
|
76
|
+
**Migration** (1:1 rename):
|
|
77
|
+
|
|
78
|
+
```graphql
|
|
79
|
+
# PRZED
|
|
80
|
+
query Listing {
|
|
81
|
+
productFilters {
|
|
82
|
+
matchCount # ❌ removed
|
|
83
|
+
activeCount
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
# PO
|
|
88
|
+
query Listing {
|
|
89
|
+
productFilters {
|
|
90
|
+
totalCount # ✅ same semantics, Relay-aligned
|
|
91
|
+
activeCount
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## BREAKING — `ProductFilter.available: true` semantics
|
|
97
|
+
|
|
98
|
+
Filter teraz includuje untracked produkty (gift cards, digital, made-to-order — wszystko z `trackQuantity = false`). Semantyka zgodna z `Product.isAvailable` resolver — single source of truth dla "co jest sellable".
|
|
99
|
+
|
|
100
|
+
**Pre-fix**: `available: true` zwracał tylko produkty z fizycznym stockiem (`available > 0`), wykluczał untracked. Storefront widział "Dostępne (3)" w sidebar (computed via `isAvailable`), po kliku dostawał 2 produkty (SQL filter różny).
|
|
101
|
+
|
|
102
|
+
**Post-fix**: oba źródła zwracają to samo.
|
|
103
|
+
|
|
104
|
+
Jeśli polegałeś na exclusion untracked przez `available: true` (np. żeby ukryć gift cards) — użyj `ProductFilter.type` lub osobnej logiki klienckiej.
|
|
105
|
+
|
|
106
|
+
## NEW — `AvailableFilters.availableCount: Int!`
|
|
107
|
+
|
|
108
|
+
Nowy boolean facet count — liczba produktów spełniających `Product.isAvailable` w current context. Storefront UI renderuje "Dostępne (N)" w sidebar facet panel.
|
|
109
|
+
|
|
110
|
+
Exclude-self: gdy `AvailableFiltersInput.available` jest provided w `currentFilters`, `availableCount` IGNORUJE ten filter (pokazuje "ile produktów ten facet odsłoni gdy włączysz") — inne facets aplikują go normalnie.
|
|
111
|
+
|
|
112
|
+
## NEW — `AvailableFiltersInput.available: Boolean`
|
|
113
|
+
|
|
114
|
+
Storefront może passować obecny `available` filter context do `productFilters` query — wymagane dla exclude-self semantyki. Pomiń (null/undefined) gdy filtr "Dostępne" nie jest aktywny w UI.
|
|
115
|
+
|
|
116
|
+
## IMPROVEMENT — Faceted count accuracy (`productCount` mismatch eliminated)
|
|
117
|
+
|
|
118
|
+
Per-attribute / per-brand / per-category `productCount` teraz używa exclude-self aggregation pattern. Facet count MATCHUJE `products(filters).totalCount` gdy storefront aplikuje facet jako filter.
|
|
119
|
+
|
|
120
|
+
**Pre-fix**: `productFilters({categoryId, available: true}).attributes[producent].filterValues[Funko].productCount = 4` (ignoring `available`), `products({categoryId, available: true, attributes: [Funko]}).totalCount = 3` (3 sellable + 1 OOS Funko). Storefront pokazywał "Funko (4)", user dostawał 3 — utracone zaufanie do countów.
|
|
121
|
+
|
|
122
|
+
**Post-fix**: oba zwracają 3. Facet ignoruje WYŁĄCZNIE swój wymiar w `currentFilters`, aplikuje pozostałe + `available` + context (categoryId/collectionId/searchQuery).
|
|
123
|
+
|
|
124
|
+
## IMPROVEMENT — Cart query payload size
|
|
125
|
+
|
|
126
|
+
`Cart.lines` query w SDK używał obu form (`edges + nodes`) z których konsumowane były wyłącznie `nodes`. Backend serializował `CartLine` fragment 2× per request. Usunięcie duplikatu — redukcja payload typowo 5-20KB per cart request (proportional do liczby pozycji).
|
|
127
|
+
|
|
128
|
+
Brak API change widzialnej dla consumer — SDK używa `cart.lines.nodes` jak wcześniej. Internal SDK improvement.
|
|
129
|
+
|
|
130
|
+
## Example: end-to-end facet sidebar query (post-rename)
|
|
131
|
+
|
|
132
|
+
```graphql
|
|
133
|
+
query ProductListing($input: AvailableFiltersInput) {
|
|
134
|
+
productFilters(input: $input) {
|
|
135
|
+
totalCount # ile produktów w current context (przed faceted filters)
|
|
136
|
+
availableCount # ile sellable (boolean facet, exclude-self)
|
|
137
|
+
activeCount # input.currentFilters.length
|
|
138
|
+
attributes {
|
|
139
|
+
handle
|
|
140
|
+
filterValues {
|
|
141
|
+
value
|
|
142
|
+
productCount
|
|
143
|
+
} # per-value facet count (exclude-self per attrDef)
|
|
144
|
+
}
|
|
145
|
+
brands {
|
|
146
|
+
slug
|
|
147
|
+
name
|
|
148
|
+
productCount
|
|
149
|
+
} # per-brand facet count
|
|
150
|
+
categories {
|
|
151
|
+
slug
|
|
152
|
+
name
|
|
153
|
+
productCount
|
|
154
|
+
} # per-category facet count
|
|
155
|
+
priceRange {
|
|
156
|
+
min {
|
|
157
|
+
amount
|
|
158
|
+
currencyCode
|
|
159
|
+
}
|
|
160
|
+
max {
|
|
161
|
+
amount
|
|
162
|
+
currencyCode
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
products(
|
|
168
|
+
filters: [
|
|
169
|
+
{ available: true }
|
|
170
|
+
{ attributes: [{ attributeId: "producent", values: ["Funko"] }] }
|
|
171
|
+
]
|
|
172
|
+
) {
|
|
173
|
+
totalCount
|
|
174
|
+
nodes {
|
|
175
|
+
id
|
|
176
|
+
title
|
|
177
|
+
isAvailable
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Gdy storefront aplikuje "Dostępne + Producent=Funko" jako filter w UI:
|
|
184
|
+
|
|
185
|
+
```graphql
|
|
186
|
+
variables: {
|
|
187
|
+
input: {
|
|
188
|
+
available: true,
|
|
189
|
+
currentFilters: [{ attributeId: "producent", values: ["Funko"] }]
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- `availableCount` = liczba sellable Funko w context (ignoring `input.available`, applying `producent: Funko`)
|
|
195
|
+
- `attributes[producent].filterValues[Funko].productCount` = liczba sellable produktów (ignoring `producent`, applying `available: true`) = `products(filters).totalCount`
|
|
196
|
+
|
|
197
|
+
### Minor Changes
|
|
198
|
+
|
|
199
|
+
- e64cfc5: Brand entity available in Storefront API: filter products by canonical brand + read brand details per product.
|
|
200
|
+
|
|
201
|
+
**What's new in Storefront API**
|
|
202
|
+
- **`Product.brand: BrandSummary`** — every product now exposes its canonical brand (when assigned). Returns `{ id, name, slug, logo }` or `null` if the product has no brand. Resolved via DataLoader so listing 50 products incurs at most 1 brand query (no N+1).
|
|
203
|
+
- **`ProductFilter.brand: BrandFilter`** — new filter input accepting either `{ id: "uuid" }` or `{ slug: "funko" }`. Multiple `brand` entries in `filters[]` use OR semantics. Combine with other filters (price, tags, attributes) using AND across different field names.
|
|
204
|
+
- **`AvailableFilters.brands: [BrandFilterValue!]!`** — `productFilters` query now returns aggregated brands present in the current product set, each with `productCount`. Build a brand facet (checkbox list or dropdown with logo) directly from this response. Only active brands with at least one product in context are included.
|
|
205
|
+
|
|
206
|
+
**Example: brand facet + filter on a listing page**
|
|
207
|
+
|
|
208
|
+
```graphql
|
|
209
|
+
query Listing {
|
|
210
|
+
productFilters {
|
|
211
|
+
brands {
|
|
212
|
+
id
|
|
213
|
+
name
|
|
214
|
+
slug
|
|
215
|
+
logo
|
|
216
|
+
productCount
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
products(filters: [{ brand: { slug: "funko" } }]) {
|
|
220
|
+
totalCount
|
|
221
|
+
nodes {
|
|
222
|
+
id
|
|
223
|
+
title
|
|
224
|
+
brand {
|
|
225
|
+
name
|
|
226
|
+
slug
|
|
227
|
+
logo
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**Migration**
|
|
235
|
+
- Existing `Product.vendor: String` (legacy free-text) remains supported. New brand-based filtering uses `ProductFilter.brand` instead of `productVendor`.
|
|
236
|
+
- No breaking changes. All new fields are nullable / optional — queries written before this release continue to work.
|
|
237
|
+
|
|
238
|
+
**SDK**
|
|
239
|
+
|
|
240
|
+
Regenerated types include `BrandSummary`, `BrandFilter`, `BrandFilterValue`, and `Product.brand`. Import them from `@doswiftly/storefront-operations` schema types.
|
|
241
|
+
|
|
3
242
|
## 12.0.0
|
|
4
243
|
|
|
5
244
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -241,13 +241,14 @@ full executable body of each operation.
|
|
|
241
241
|
|
|
242
242
|
| Operation | Description |
|
|
243
243
|
| --- | --- |
|
|
244
|
-
| `AvailableShippingMethods` | Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. |
|
|
244
|
+
| `AvailableShippingMethods` | Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead. |
|
|
245
|
+
| `CartAvailableShippingMethods` | Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool. |
|
|
245
246
|
|
|
246
247
|
#### Attribute Filters
|
|
247
248
|
|
|
248
249
|
| Operation | Description |
|
|
249
250
|
| --- | --- |
|
|
250
|
-
| `ProductFilters` | Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`,
|
|
251
|
+
| `ProductFilters` | Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`, `searchQuery`, optional `available` (boolean for the availability facet), and optional `currentFilters` (array of attribute filters currently applied by the UI). For each visible & filterable attribute, returns either discrete value counts (for `SELECT` / `CHECKBOX` types) or numeric range bounds (for `SLIDER` types). Plus `priceRange`, `brands`, per-category counts, `activeCount` (length of `currentFilters`), `totalCount` (products in context — Relay-aligned), and `availableCount` (boolean facet count for `availableForSale`). All per-facet counts use exclude-self aggregation: a facet's count IGNORES its own currently-applied filter and APPLIES other filters — so the count reflects "products if this facet were toggled" rather than "products currently visible". Untracked inventory (gift cards, digital, made-to-order) is always counted as available. Use to render filter sidebars on listing/search pages. |
|
|
251
252
|
|
|
252
253
|
#### Loyalty Program
|
|
253
254
|
|
|
@@ -554,7 +555,7 @@ full executable body of each operation.
|
|
|
554
555
|
| `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[]`. |
|
|
555
556
|
| `PriceRangeFilter` | `PriceRange` | Min / max price range across products in the current listing context. Use to bound the price slider. |
|
|
556
557
|
| `CategoryFilterOption` | `CategoryFilterOption` | One category option in the categories filter — id, name, slug, count of products in this category within the current listing context, level + parentId for tree rendering. |
|
|
557
|
-
| `AvailableFilters` | `AvailableFilters` | Full result of the `productFilters($input)` query — attribute filters, price range, category filters, count of currently active filters, total products
|
|
558
|
+
| `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. |
|
|
558
559
|
|
|
559
560
|
#### Loyalty Program
|
|
560
561
|
|
package/fragments.graphql
CHANGED
|
@@ -407,6 +407,7 @@ fragment CartLine on CartLine {
|
|
|
407
407
|
productTitle
|
|
408
408
|
productHandle
|
|
409
409
|
productType
|
|
410
|
+
requiresShipping
|
|
410
411
|
}
|
|
411
412
|
|
|
412
413
|
# Buyer identity associated with the cart. Note: only `customerId` is currently persisted server-side; `email`, `phone`, `countryCode` are accepted in the input but ignored.
|
|
@@ -488,6 +489,7 @@ fragment Cart on Cart {
|
|
|
488
489
|
appliedGiftCards {
|
|
489
490
|
...CartAppliedGiftCard
|
|
490
491
|
}
|
|
492
|
+
requiresShipping
|
|
491
493
|
createdAt
|
|
492
494
|
updatedAt
|
|
493
495
|
}
|
|
@@ -1012,7 +1014,7 @@ fragment CategoryFilterOption on CategoryFilterOption {
|
|
|
1012
1014
|
parentId
|
|
1013
1015
|
}
|
|
1014
1016
|
|
|
1015
|
-
# Full result of the `productFilters($input)` query — attribute filters, price range, category filters, count of currently active filters, total products
|
|
1017
|
+
# 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.
|
|
1016
1018
|
fragment AvailableFilters on AvailableFilters {
|
|
1017
1019
|
attributes {
|
|
1018
1020
|
...AttributeDefinition
|
|
@@ -1024,7 +1026,8 @@ fragment AvailableFilters on AvailableFilters {
|
|
|
1024
1026
|
...CategoryFilterOption
|
|
1025
1027
|
}
|
|
1026
1028
|
activeCount
|
|
1027
|
-
|
|
1029
|
+
totalCount
|
|
1030
|
+
availableCount
|
|
1028
1031
|
}
|
|
1029
1032
|
|
|
1030
1033
|
# ============================================
|
package/llms-full.txt
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# DoSwiftly Storefront Operations — Full Reference
|
|
2
2
|
|
|
3
|
-
> Schema version: **
|
|
4
|
-
>
|
|
3
|
+
> Schema version: **13.1.0**
|
|
4
|
+
> 50 queries · 40 mutations · 100 fragments
|
|
5
5
|
|
|
6
6
|
Auto-generated from `.graphql` source files. Do not edit by hand — this file is
|
|
7
7
|
regenerated on every release to match the published schema.
|
|
@@ -676,7 +676,7 @@ query GiftCardValidate($code: String!, $amount: Float) {
|
|
|
676
676
|
|
|
677
677
|
**Section**: Shipping Methods
|
|
678
678
|
|
|
679
|
-
**Description**: Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price.
|
|
679
|
+
**Description**: Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead.
|
|
680
680
|
|
|
681
681
|
**Variables**:
|
|
682
682
|
- `$address`: `ShippingAddressInput!`
|
|
@@ -701,11 +701,44 @@ query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShipp
|
|
|
701
701
|
}
|
|
702
702
|
```
|
|
703
703
|
|
|
704
|
+
### Query: `CartAvailableShippingMethods`
|
|
705
|
+
|
|
706
|
+
**Section**: Shipping Methods
|
|
707
|
+
|
|
708
|
+
**Description**: Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool.
|
|
709
|
+
|
|
710
|
+
**Variables**:
|
|
711
|
+
- `$cartId`: `ID!`
|
|
712
|
+
- `$address`: `ShippingAddressInput!`
|
|
713
|
+
|
|
714
|
+
**Fragments used**: `AvailableShippingMethod`, `FreeShippingProgress`, `UserError`
|
|
715
|
+
|
|
716
|
+
**GraphQL**:
|
|
717
|
+
```graphql
|
|
718
|
+
query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {
|
|
719
|
+
cart(id: $cartId) {
|
|
720
|
+
id
|
|
721
|
+
requiresShipping
|
|
722
|
+
availableShippingMethods(address: $address) {
|
|
723
|
+
methods {
|
|
724
|
+
...AvailableShippingMethod
|
|
725
|
+
}
|
|
726
|
+
freeShippingProgress {
|
|
727
|
+
...FreeShippingProgress
|
|
728
|
+
}
|
|
729
|
+
userErrors {
|
|
730
|
+
...UserError
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
```
|
|
736
|
+
|
|
704
737
|
### Query: `ProductFilters`
|
|
705
738
|
|
|
706
739
|
**Section**: Attribute Filters
|
|
707
740
|
|
|
708
|
-
**Description**: Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`,
|
|
741
|
+
**Description**: Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`, `searchQuery`, optional `available` (boolean for the availability facet), and optional `currentFilters` (array of attribute filters currently applied by the UI). For each visible & filterable attribute, returns either discrete value counts (for `SELECT` / `CHECKBOX` types) or numeric range bounds (for `SLIDER` types). Plus `priceRange`, `brands`, per-category counts, `activeCount` (length of `currentFilters`), `totalCount` (products in context — Relay-aligned), and `availableCount` (boolean facet count for `availableForSale`). All per-facet counts use exclude-self aggregation: a facet's count IGNORES its own currently-applied filter and APPLIES other filters — so the count reflects "products if this facet were toggled" rather than "products currently visible". Untracked inventory (gift cards, digital, made-to-order) is always counted as available. Use to render filter sidebars on listing/search pages.
|
|
709
742
|
|
|
710
743
|
**Variables**:
|
|
711
744
|
- `$input`: `AvailableFiltersInput`
|
|
@@ -2943,6 +2976,7 @@ fragment CartLine on CartLine {
|
|
|
2943
2976
|
productTitle
|
|
2944
2977
|
productHandle
|
|
2945
2978
|
productType
|
|
2979
|
+
requiresShipping
|
|
2946
2980
|
}
|
|
2947
2981
|
```
|
|
2948
2982
|
|
|
@@ -3060,6 +3094,7 @@ fragment Cart on Cart {
|
|
|
3060
3094
|
appliedGiftCards {
|
|
3061
3095
|
...CartAppliedGiftCard
|
|
3062
3096
|
}
|
|
3097
|
+
requiresShipping
|
|
3063
3098
|
createdAt
|
|
3064
3099
|
updatedAt
|
|
3065
3100
|
}
|
|
@@ -3887,7 +3922,7 @@ fragment CategoryFilterOption on CategoryFilterOption {
|
|
|
3887
3922
|
|
|
3888
3923
|
**Section**: Attribute Filters
|
|
3889
3924
|
|
|
3890
|
-
**Description**: Full result of the `productFilters($input)` query — attribute filters, price range, category filters, count of currently active filters, total products
|
|
3925
|
+
**Description**: 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.
|
|
3891
3926
|
|
|
3892
3927
|
**Uses fragments**: `AttributeDefinition`, `CategoryFilterOption`, `PriceRangeFilter`
|
|
3893
3928
|
|
|
@@ -3904,7 +3939,8 @@ fragment AvailableFilters on AvailableFilters {
|
|
|
3904
3939
|
...CategoryFilterOption
|
|
3905
3940
|
}
|
|
3906
3941
|
activeCount
|
|
3907
|
-
|
|
3942
|
+
totalCount
|
|
3943
|
+
availableCount
|
|
3908
3944
|
}
|
|
3909
3945
|
```
|
|
3910
3946
|
|
package/operations.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"schemaVersion": "
|
|
2
|
+
"schemaVersion": "13.1.0",
|
|
3
3
|
"queries": [
|
|
4
4
|
{
|
|
5
5
|
"name": "Shop",
|
|
@@ -504,7 +504,7 @@
|
|
|
504
504
|
"name": "AvailableShippingMethods",
|
|
505
505
|
"kind": "query",
|
|
506
506
|
"section": "Shipping Methods",
|
|
507
|
-
"description": "Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for \"shipping cost preview\" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price.",
|
|
507
|
+
"description": "Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for \"shipping cost preview\" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead.",
|
|
508
508
|
"variables": [
|
|
509
509
|
{
|
|
510
510
|
"name": "address",
|
|
@@ -524,11 +524,35 @@
|
|
|
524
524
|
],
|
|
525
525
|
"body": "query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShippingInput) {\n availableShippingMethods(address: $address, cart: $cart) {\n methods {\n ...AvailableShippingMethod\n }\n freeShippingProgress {\n ...FreeShippingProgress\n }\n userErrors {\n ...UserError\n }\n }\n}"
|
|
526
526
|
},
|
|
527
|
+
{
|
|
528
|
+
"name": "CartAvailableShippingMethods",
|
|
529
|
+
"kind": "query",
|
|
530
|
+
"section": "Shipping Methods",
|
|
531
|
+
"description": "Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart \"shipping cost preview\" UIs on product detail pages, the standalone query remains the right tool.",
|
|
532
|
+
"variables": [
|
|
533
|
+
{
|
|
534
|
+
"name": "cartId",
|
|
535
|
+
"type": "ID!",
|
|
536
|
+
"defaultValue": null
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
"name": "address",
|
|
540
|
+
"type": "ShippingAddressInput!",
|
|
541
|
+
"defaultValue": null
|
|
542
|
+
}
|
|
543
|
+
],
|
|
544
|
+
"fragmentRefs": [
|
|
545
|
+
"AvailableShippingMethod",
|
|
546
|
+
"FreeShippingProgress",
|
|
547
|
+
"UserError"
|
|
548
|
+
],
|
|
549
|
+
"body": "query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {\n cart(id: $cartId) {\n id\n requiresShipping\n availableShippingMethods(address: $address) {\n methods {\n ...AvailableShippingMethod\n }\n freeShippingProgress {\n ...FreeShippingProgress\n }\n userErrors {\n ...UserError\n }\n }\n }\n}"
|
|
550
|
+
},
|
|
527
551
|
{
|
|
528
552
|
"name": "ProductFilters",
|
|
529
553
|
"kind": "query",
|
|
530
554
|
"section": "Attribute Filters",
|
|
531
|
-
"description": "Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`,
|
|
555
|
+
"description": "Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`, `searchQuery`, optional `available` (boolean for the availability facet), and optional `currentFilters` (array of attribute filters currently applied by the UI). For each visible & filterable attribute, returns either discrete value counts (for `SELECT` / `CHECKBOX` types) or numeric range bounds (for `SLIDER` types). Plus `priceRange`, `brands`, per-category counts, `activeCount` (length of `currentFilters`), `totalCount` (products in context — Relay-aligned), and `availableCount` (boolean facet count for `availableForSale`). All per-facet counts use exclude-self aggregation: a facet's count IGNORES its own currently-applied filter and APPLIES other filters — so the count reflects \"products if this facet were toggled\" rather than \"products currently visible\". Untracked inventory (gift cards, digital, made-to-order) is always counted as available. Use to render filter sidebars on listing/search pages.",
|
|
532
556
|
"variables": [
|
|
533
557
|
{
|
|
534
558
|
"name": "input",
|
|
@@ -2065,7 +2089,7 @@
|
|
|
2065
2089
|
"CartLineCost",
|
|
2066
2090
|
"ProductVariant"
|
|
2067
2091
|
],
|
|
2068
|
-
"body": "fragment CartLine on CartLine {\n id\n quantity\n variant {\n ...ProductVariant\n }\n cost {\n ...CartLineCost\n }\n attributes {\n key\n value\n }\n attributeSelections {\n ...AttributeSelection\n }\n productId\n productTitle\n productHandle\n productType\n}",
|
|
2092
|
+
"body": "fragment CartLine on CartLine {\n id\n quantity\n variant {\n ...ProductVariant\n }\n cost {\n ...CartLineCost\n }\n attributes {\n key\n value\n }\n attributeSelections {\n ...AttributeSelection\n }\n productId\n productTitle\n productHandle\n productType\n requiresShipping\n}",
|
|
2069
2093
|
"onType": "CartLine"
|
|
2070
2094
|
},
|
|
2071
2095
|
{
|
|
@@ -2118,7 +2142,7 @@
|
|
|
2118
2142
|
"MailingAddress",
|
|
2119
2143
|
"PageInfo"
|
|
2120
2144
|
],
|
|
2121
|
-
"body": "fragment Cart on Cart {\n id\n checkoutUrl\n totalQuantity\n cost {\n ...CartCost\n }\n lines(first: 100) {\n edges {\n cursor\n node {\n ... on CartLine {\n ...CartLine\n }\n }\n }\n nodes {\n ... on CartLine {\n ...CartLine\n }\n }\n pageInfo {\n ...PageInfo\n }\n totalCount\n }\n buyerIdentity {\n ...CartBuyerIdentity\n }\n discountCodes {\n ...CartDiscountCode\n }\n discountAllocations {\n ...CartDiscountAllocation\n }\n note\n attributes {\n key\n value\n }\n email\n phone\n shippingAddress {\n ...MailingAddress\n }\n billingAddress {\n ...MailingAddress\n }\n selectedShippingMethod {\n ...CartShippingMethod\n }\n selectedPaymentMethod {\n ...CartSelectedPaymentMethod\n }\n appliedGiftCards {\n ...CartAppliedGiftCard\n }\n createdAt\n updatedAt\n}",
|
|
2145
|
+
"body": "fragment Cart on Cart {\n id\n checkoutUrl\n totalQuantity\n cost {\n ...CartCost\n }\n lines(first: 100) {\n edges {\n cursor\n node {\n ... on CartLine {\n ...CartLine\n }\n }\n }\n nodes {\n ... on CartLine {\n ...CartLine\n }\n }\n pageInfo {\n ...PageInfo\n }\n totalCount\n }\n buyerIdentity {\n ...CartBuyerIdentity\n }\n discountCodes {\n ...CartDiscountCode\n }\n discountAllocations {\n ...CartDiscountAllocation\n }\n note\n attributes {\n key\n value\n }\n email\n phone\n shippingAddress {\n ...MailingAddress\n }\n billingAddress {\n ...MailingAddress\n }\n selectedShippingMethod {\n ...CartShippingMethod\n }\n selectedPaymentMethod {\n ...CartSelectedPaymentMethod\n }\n appliedGiftCards {\n ...CartAppliedGiftCard\n }\n requiresShipping\n createdAt\n updatedAt\n}",
|
|
2122
2146
|
"onType": "Cart"
|
|
2123
2147
|
},
|
|
2124
2148
|
{
|
|
@@ -2557,14 +2581,14 @@
|
|
|
2557
2581
|
"name": "AvailableFilters",
|
|
2558
2582
|
"kind": "fragment",
|
|
2559
2583
|
"section": "Attribute Filters",
|
|
2560
|
-
"description": "Full result of the `productFilters($input)` query — attribute filters, price range, category filters, count of currently active filters, total products
|
|
2584
|
+
"description": "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.",
|
|
2561
2585
|
"variables": [],
|
|
2562
2586
|
"fragmentRefs": [
|
|
2563
2587
|
"AttributeDefinition",
|
|
2564
2588
|
"CategoryFilterOption",
|
|
2565
2589
|
"PriceRangeFilter"
|
|
2566
2590
|
],
|
|
2567
|
-
"body": "fragment AvailableFilters on AvailableFilters {\n attributes {\n ...AttributeDefinition\n }\n priceRange {\n ...PriceRangeFilter\n }\n categories {\n ...CategoryFilterOption\n }\n activeCount\n
|
|
2591
|
+
"body": "fragment AvailableFilters on AvailableFilters {\n attributes {\n ...AttributeDefinition\n }\n priceRange {\n ...PriceRangeFilter\n }\n categories {\n ...CategoryFilterOption\n }\n activeCount\n totalCount\n availableCount\n}",
|
|
2568
2592
|
"onType": "AvailableFilters"
|
|
2569
2593
|
},
|
|
2570
2594
|
{
|
package/package.json
CHANGED
package/queries.graphql
CHANGED
|
@@ -368,7 +368,7 @@ query GiftCardValidate($code: String!, $amount: Float) {
|
|
|
368
368
|
# Shipping Methods
|
|
369
369
|
# ============================================
|
|
370
370
|
|
|
371
|
-
# Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price.
|
|
371
|
+
# Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead.
|
|
372
372
|
query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShippingInput) {
|
|
373
373
|
availableShippingMethods(address: $address, cart: $cart) {
|
|
374
374
|
methods {
|
|
@@ -383,11 +383,30 @@ query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShipp
|
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
+
# Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool.
|
|
387
|
+
query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {
|
|
388
|
+
cart(id: $cartId) {
|
|
389
|
+
id
|
|
390
|
+
requiresShipping
|
|
391
|
+
availableShippingMethods(address: $address) {
|
|
392
|
+
methods {
|
|
393
|
+
...AvailableShippingMethod
|
|
394
|
+
}
|
|
395
|
+
freeShippingProgress {
|
|
396
|
+
...FreeShippingProgress
|
|
397
|
+
}
|
|
398
|
+
userErrors {
|
|
399
|
+
...UserError
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
386
405
|
# ============================================
|
|
387
406
|
# Attribute Filters
|
|
388
407
|
# ============================================
|
|
389
408
|
|
|
390
|
-
# Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`,
|
|
409
|
+
# Returns the dynamic facet filters available for a listing context — pass `collectionId`, `categoryId`, `searchQuery`, optional `available` (boolean for the availability facet), and optional `currentFilters` (array of attribute filters currently applied by the UI). For each visible & filterable attribute, returns either discrete value counts (for `SELECT` / `CHECKBOX` types) or numeric range bounds (for `SLIDER` types). Plus `priceRange`, `brands`, per-category counts, `activeCount` (length of `currentFilters`), `totalCount` (products in context — Relay-aligned), and `availableCount` (boolean facet count for `availableForSale`). All per-facet counts use exclude-self aggregation: a facet's count IGNORES its own currently-applied filter and APPLIES other filters — so the count reflects "products if this facet were toggled" rather than "products currently visible". Untracked inventory (gift cards, digital, made-to-order) is always counted as available. Use to render filter sidebars on listing/search pages.
|
|
391
410
|
query ProductFilters($input: AvailableFiltersInput) {
|
|
392
411
|
productFilters(input: $input) {
|
|
393
412
|
...AvailableFilters
|
package/schema.graphql
CHANGED
|
@@ -72,6 +72,11 @@ type AttributeDefinition {
|
|
|
72
72
|
"""Attribute name (e.g., "Color", "Size")"""
|
|
73
73
|
name: String!
|
|
74
74
|
|
|
75
|
+
"""
|
|
76
|
+
Faza 3 Opcja A (2026-05-17): opcjonalne grupowanie atrybutu (np. "inventory", "marketing", "content"). NULL = atrybut bez grupy. Pomaga storefront-devom rozpoznać "te atrybuty są custom dane ERP" vs "te są filtrowalne cechy katalogu".
|
|
77
|
+
"""
|
|
78
|
+
namespace: String
|
|
79
|
+
|
|
75
80
|
"""Range bounds (for NUMBER, CURRENCY)"""
|
|
76
81
|
rangeBounds: AttributeRangeBounds
|
|
77
82
|
|
|
@@ -99,6 +104,11 @@ input AttributeFilterInput {
|
|
|
99
104
|
"""Minimum value (for NUMBER, CURRENCY)"""
|
|
100
105
|
minValue: Float
|
|
101
106
|
|
|
107
|
+
"""
|
|
108
|
+
Opcjonalny dyskryminator grupy atrybutów (np. "inventory", "marketing"). Faza 3 Opcja A (2026-05-17): konsolidacja MetaProperty-style custom fields pod AttributeDefinition — namespace pozwala filtrować "Marka z grupy XYZ" gdy multiple shopów ma `Marka` w różnych grupach.
|
|
109
|
+
"""
|
|
110
|
+
namespace: String
|
|
111
|
+
|
|
102
112
|
"""Text search (for TEXT, TEXTAREA)"""
|
|
103
113
|
textSearch: String
|
|
104
114
|
|
|
@@ -248,34 +258,51 @@ enum AttributeType {
|
|
|
248
258
|
"""Available filters for product listing"""
|
|
249
259
|
type AvailableFilters {
|
|
250
260
|
"""
|
|
251
|
-
|
|
261
|
+
Liczba currently applied filters w `currentFilters` (Storefront UI renderuje "Filtry (3)" w sidebar header).
|
|
252
262
|
"""
|
|
253
263
|
activeCount: Int!
|
|
254
264
|
|
|
255
265
|
"""Filterable attributes with values"""
|
|
256
266
|
attributes: [AttributeDefinition!]!
|
|
257
267
|
|
|
258
|
-
"""
|
|
259
|
-
|
|
268
|
+
"""
|
|
269
|
+
Boolean facet count: liczba produktów w current context spełniających `Product.isAvailable` (przynajmniej jeden aktywny wariant z untracked inventory LUB available>0). Exclude-self: jeśli `available: true` jest w `currentFilters`, count IGNORUJE ten filtr i pokazuje "ile produktów dostępnych w bazowym kontekście" (nie "ile teraz widać"). Spójne semantically z `attributes[].filterValues[].productCount` (per-enum-value facet) i `brands[].productCount` (per-brand facet) — wszystkie używają exclude-self per dimension. Untracked inventory (gift cards, digital, na-zamówienie) zawsze liczy się jako available.
|
|
270
|
+
"""
|
|
271
|
+
availableCount: Int!
|
|
260
272
|
|
|
261
273
|
"""
|
|
262
|
-
|
|
274
|
+
Faza 3 (2026-05-17): canonical Brand entities z product counts w current context. Storefront UI renderuje checkbox list / dropdown z logo per brand. Klik → `filters: [{brand: {slug}}]`. Tylko brandy z >0 produktów w current product set + isActive=true.
|
|
263
275
|
"""
|
|
264
|
-
|
|
276
|
+
brands: [BrandFilterValue!]
|
|
277
|
+
|
|
278
|
+
"""Categories available for filtering"""
|
|
279
|
+
categories: [CategoryFilterOption!]
|
|
265
280
|
|
|
266
281
|
"""Price range for filtering"""
|
|
267
282
|
priceRange: PriceRange
|
|
283
|
+
|
|
284
|
+
"""
|
|
285
|
+
Total products w current context (categoryId/collectionId/searchQuery) PRZED zaaplikowaniem faceted filters z `currentFilters`. Spójne z `ProductConnection.totalCount` Relay Connection spec — gdy `currentFilters` jest pusty, `AvailableFilters.totalCount` === `products(filters).totalCount`. Użyj do "Wszystkie produkty (N)" w sidebar header.
|
|
286
|
+
"""
|
|
287
|
+
totalCount: Int!
|
|
268
288
|
}
|
|
269
289
|
|
|
270
290
|
"""Context for available filters query"""
|
|
271
291
|
input AvailableFiltersInput {
|
|
292
|
+
"""
|
|
293
|
+
Czy filtr "dostępne" jest currently zaaplikowany. Mirror `ProductFilter.available`. Backend używa do exclude-self semantyki: gdy `available: true` (lub `false`) tutaj, `AvailableFilters.availableCount` IGNORUJE ten filtr (pokazuje "ile produktów ten facet odsłoni gdy włączysz"), pozostałe facet counts (`attributes[].filterValues[].productCount`, `brands[].productCount`, `categories[].productCount`) APLIKUJĄ go (pokazują "ile produktów w obecnym kontekście + tym facet"). Pomiń (null/undefined) gdy storefront nie aplikuje "dostępne" filter.
|
|
294
|
+
"""
|
|
295
|
+
available: Boolean
|
|
296
|
+
|
|
272
297
|
"""Category ID context (supports comma-separated UUIDs)"""
|
|
273
298
|
categoryId: ID
|
|
274
299
|
|
|
275
300
|
"""Collection ID context (supports comma-separated UUIDs)"""
|
|
276
301
|
collectionId: ID
|
|
277
302
|
|
|
278
|
-
"""
|
|
303
|
+
"""
|
|
304
|
+
Currently applied attribute filters (to update facet counts). Per facet `productCount` używa exclude-self pattern: gdy attrId X jest w currentFilters, facet values X.* są liczone IGNORUJĄC X (pokazują "ile produktów dla X=Y w pozostałym kontekście"). Pozostałe attrIds są aplikowane.
|
|
305
|
+
"""
|
|
279
306
|
currentFilters: [AttributeFilterInput!]
|
|
280
307
|
|
|
281
308
|
"""Search query context"""
|
|
@@ -598,6 +625,52 @@ type BrandColors {
|
|
|
598
625
|
secondary: BrandColorGroup
|
|
599
626
|
}
|
|
600
627
|
|
|
628
|
+
"""Filter products po brand (canonical entity)"""
|
|
629
|
+
input BrandFilter {
|
|
630
|
+
"""Brand ID — exact UUID match"""
|
|
631
|
+
id: ID
|
|
632
|
+
|
|
633
|
+
"""
|
|
634
|
+
Brand slug — URL-friendly identifier. Preferowany over `id` dla storefront refetch (stable po URL share).
|
|
635
|
+
"""
|
|
636
|
+
slug: String
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
"""Brand filter value z product count"""
|
|
640
|
+
type BrandFilterValue {
|
|
641
|
+
"""Brand ID"""
|
|
642
|
+
id: ID!
|
|
643
|
+
|
|
644
|
+
"""Brand logo URL (UI rendering)"""
|
|
645
|
+
logo: String
|
|
646
|
+
|
|
647
|
+
"""Brand name"""
|
|
648
|
+
name: String!
|
|
649
|
+
|
|
650
|
+
"""Liczba produktów z tym brandem w current product set"""
|
|
651
|
+
productCount: Int!
|
|
652
|
+
|
|
653
|
+
"""Brand slug (refetch payload — paste w ProductFilter.brand.slug)"""
|
|
654
|
+
slug: String!
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
"""
|
|
658
|
+
Brand summary — minimalna projekcja dla storefront catalog + filter rendering
|
|
659
|
+
"""
|
|
660
|
+
type BrandSummary {
|
|
661
|
+
"""Brand unique identifier"""
|
|
662
|
+
id: ID!
|
|
663
|
+
|
|
664
|
+
"""Brand logo URL (CDN-served, storefront-rendered)"""
|
|
665
|
+
logo: String
|
|
666
|
+
|
|
667
|
+
"""Brand display name (e.g. "Funko", "Nintendo")"""
|
|
668
|
+
name: String!
|
|
669
|
+
|
|
670
|
+
"""URL-friendly slug — używane dla future /marka/[slug] landing pages"""
|
|
671
|
+
slug: String!
|
|
672
|
+
}
|
|
673
|
+
|
|
601
674
|
"""Business hours for a day"""
|
|
602
675
|
type BusinessHour {
|
|
603
676
|
"""Closing time (HH:MM format)"""
|
|
@@ -703,6 +776,11 @@ type Cart implements Node {
|
|
|
703
776
|
"""Available payment methods dla tego cart (shop-configured providers)"""
|
|
704
777
|
availablePaymentMethods: [PaymentMethod!]!
|
|
705
778
|
|
|
779
|
+
"""
|
|
780
|
+
Cart-aware shipping methods discovery. Returns empty methods + DIGITAL_ONLY_NO_SHIPPING user error when the cart contains only non-physical items (digital, gift card, service, subscription). Physical / mixed carts get the same method computation as the standalone `availableShippingMethods` query but with subtotal and weight pulled from the cart aggregate.
|
|
781
|
+
"""
|
|
782
|
+
availableShippingMethods(address: ShippingAddressInput!): AvailableShippingMethodsPayload!
|
|
783
|
+
|
|
706
784
|
"""
|
|
707
785
|
Billing address ustawiony przez cartSetBillingAddress (jeśli różny od shipping)
|
|
708
786
|
"""
|
|
@@ -748,6 +826,11 @@ type Cart implements Node {
|
|
|
748
826
|
"""Product recommendations based on cart contents"""
|
|
749
827
|
recommendations(first: Int = 4): CartRecommendations
|
|
750
828
|
|
|
829
|
+
"""
|
|
830
|
+
True if any line in the cart requires physical shipping. False when every line is non-physical (digital, gift card, service, subscription). Use as the single signal to render or skip the shipping picker in checkout.
|
|
831
|
+
"""
|
|
832
|
+
requiresShipping: Boolean!
|
|
833
|
+
|
|
751
834
|
"""Selected payment method (PayU/Stripe/COD/etc.)"""
|
|
752
835
|
selectedPaymentMethod: PaymentMethod
|
|
753
836
|
|
|
@@ -1066,6 +1149,11 @@ type CartLine {
|
|
|
1066
1149
|
"""Quantity of this item"""
|
|
1067
1150
|
quantity: Int!
|
|
1068
1151
|
|
|
1152
|
+
"""
|
|
1153
|
+
True if this line item requires physical shipping (productType=PHYSICAL). False for digital, gift card, service, and subscription items. Use to skip the shipping step in checkout when every line is non-physical.
|
|
1154
|
+
"""
|
|
1155
|
+
requiresShipping: Boolean!
|
|
1156
|
+
|
|
1069
1157
|
"""Product variant being purchased"""
|
|
1070
1158
|
variant: ProductVariant!
|
|
1071
1159
|
}
|
|
@@ -1904,6 +1992,9 @@ type Customer implements Node {
|
|
|
1904
1992
|
"""Saved addresses (Relay Connection)"""
|
|
1905
1993
|
addresses(after: String, before: String, first: Int, last: Int): MailingAddressConnection!
|
|
1906
1994
|
|
|
1995
|
+
"""Customer custom field values (post-Opcja A unified custom fields)."""
|
|
1996
|
+
attributes(namespace: String): [EntityAttributeField!]!
|
|
1997
|
+
|
|
1907
1998
|
"""Company name (populated for COMPANY type)"""
|
|
1908
1999
|
companyName: String
|
|
1909
2000
|
|
|
@@ -1937,16 +2028,6 @@ type Customer implements Node {
|
|
|
1937
2028
|
"""Last name"""
|
|
1938
2029
|
lastName: String
|
|
1939
2030
|
|
|
1940
|
-
"""
|
|
1941
|
-
Lista meta properties (Relay Connection) — opcjonalnie scoped do namespace
|
|
1942
|
-
"""
|
|
1943
|
-
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
1944
|
-
|
|
1945
|
-
"""
|
|
1946
|
-
Pojedyncze meta property po (namespace, key) — dla zalogowanego klienta zwraca także private
|
|
1947
|
-
"""
|
|
1948
|
-
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
1949
|
-
|
|
1950
2031
|
"""Total orders count (UnsignedInt64 — BigInt-safe)"""
|
|
1951
2032
|
orderCount: UnsignedInt64!
|
|
1952
2033
|
|
|
@@ -2347,6 +2428,39 @@ enum EmailMarketingState {
|
|
|
2347
2428
|
UNSUBSCRIBED
|
|
2348
2429
|
}
|
|
2349
2430
|
|
|
2431
|
+
"""Attribute assignment per entity (polymorphic owner)"""
|
|
2432
|
+
type EntityAttributeField {
|
|
2433
|
+
"""AttributeDefinition ID"""
|
|
2434
|
+
definitionId: ID!
|
|
2435
|
+
|
|
2436
|
+
"""Attribute handle (URL-friendly identifier)"""
|
|
2437
|
+
handle: String!
|
|
2438
|
+
|
|
2439
|
+
"""EntityAttribute row ID"""
|
|
2440
|
+
id: ID!
|
|
2441
|
+
|
|
2442
|
+
"""
|
|
2443
|
+
Per-entity visibility override (NULL = use AttributeDefinition.isVisible).
|
|
2444
|
+
"""
|
|
2445
|
+
isVisibleOverride: Boolean
|
|
2446
|
+
|
|
2447
|
+
"""Attribute display name"""
|
|
2448
|
+
name: String!
|
|
2449
|
+
|
|
2450
|
+
"""
|
|
2451
|
+
Optional grouping namespace (e.g. "inventory", "marketing"). NULL = ungrouped.
|
|
2452
|
+
"""
|
|
2453
|
+
namespace: String
|
|
2454
|
+
|
|
2455
|
+
"""Attribute data type"""
|
|
2456
|
+
type: AttributeType!
|
|
2457
|
+
|
|
2458
|
+
"""
|
|
2459
|
+
Value serialized as JSON string. Storefront-dev parses according to `type` (TEXT/NUMBER/BOOLEAN/JSON/etc.). Null values serialize as JSON `null`.
|
|
2460
|
+
"""
|
|
2461
|
+
value: String!
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2350
2464
|
"""Exchange rate between currencies"""
|
|
2351
2465
|
type ExchangeRate {
|
|
2352
2466
|
"""Source currency code"""
|
|
@@ -3269,119 +3383,6 @@ enum MenuItemType {
|
|
|
3269
3383
|
SEARCH
|
|
3270
3384
|
}
|
|
3271
3385
|
|
|
3272
|
-
"""
|
|
3273
|
-
Payload `customerMetaPropertiesSet` — bulk upsert własnych meta properties klienta
|
|
3274
|
-
"""
|
|
3275
|
-
type MetaPropertiesSetPayload {
|
|
3276
|
-
"""Upserted meta properties (puste gdy userErrors)"""
|
|
3277
|
-
metaProperties: [MetaProperty!]!
|
|
3278
|
-
|
|
3279
|
-
"""User errors (code z `MetaPropertyErrorCode` enum)"""
|
|
3280
|
-
userErrors: [UserError!]!
|
|
3281
|
-
}
|
|
3282
|
-
|
|
3283
|
-
"""Pojedyncze pole dodatkowe (meta property) na encji commerce"""
|
|
3284
|
-
type MetaProperty implements Node {
|
|
3285
|
-
"""Created at"""
|
|
3286
|
-
createdAt: DateTime!
|
|
3287
|
-
|
|
3288
|
-
"""Unique identifier"""
|
|
3289
|
-
id: ID!
|
|
3290
|
-
|
|
3291
|
-
"""
|
|
3292
|
-
true = readable tylko przez authenticated context (admin/customer self). false (default) = readable również przez anonymous Storefront API.
|
|
3293
|
-
"""
|
|
3294
|
-
isPrivate: Boolean!
|
|
3295
|
-
|
|
3296
|
-
"""
|
|
3297
|
-
Key w obrębie namespace (3-64 chars). Unique per (ownerType, ownerId, namespace).
|
|
3298
|
-
"""
|
|
3299
|
-
key: String!
|
|
3300
|
-
|
|
3301
|
-
"""
|
|
3302
|
-
Namespace (3-64 chars). Zapobiega kolizji kluczy między aplikacjami. Reserved prefix: `doswiftly:` (platform).
|
|
3303
|
-
"""
|
|
3304
|
-
namespace: String!
|
|
3305
|
-
|
|
3306
|
-
"""Typ wartości — informuje klienta jak parse value."""
|
|
3307
|
-
type: MetaPropertyValueType!
|
|
3308
|
-
|
|
3309
|
-
"""Last updated at"""
|
|
3310
|
-
updatedAt: DateTime!
|
|
3311
|
-
|
|
3312
|
-
"""
|
|
3313
|
-
Wartość — zawsze String. Klient parsuje zgodnie z polem `type` (np. INTEGER → parseInt, JSON → JSON.parse).
|
|
3314
|
-
"""
|
|
3315
|
-
value: String!
|
|
3316
|
-
}
|
|
3317
|
-
|
|
3318
|
-
"""Paginated meta property list (Relay Connection)"""
|
|
3319
|
-
type MetaPropertyConnection {
|
|
3320
|
-
"""Edges (cursor + node pairs)"""
|
|
3321
|
-
edges: [MetaPropertyEdge!]!
|
|
3322
|
-
|
|
3323
|
-
"""Nodes (shortcut bez cursor)"""
|
|
3324
|
-
nodes: [MetaProperty!]!
|
|
3325
|
-
|
|
3326
|
-
"""Page info"""
|
|
3327
|
-
pageInfo: PageInfo!
|
|
3328
|
-
|
|
3329
|
-
"""Total count (max po wszystkich pages)"""
|
|
3330
|
-
totalCount: Int!
|
|
3331
|
-
}
|
|
3332
|
-
|
|
3333
|
-
"""Payload `customerMetaPropertyDelete`"""
|
|
3334
|
-
type MetaPropertyDeletePayload {
|
|
3335
|
-
"""ID usuniętego meta property (null gdy userErrors)"""
|
|
3336
|
-
deletedId: ID
|
|
3337
|
-
|
|
3338
|
-
"""User errors (code z `MetaPropertyErrorCode` enum)"""
|
|
3339
|
-
userErrors: [UserError!]!
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
"""Meta property edge (Relay pagination)"""
|
|
3343
|
-
type MetaPropertyEdge {
|
|
3344
|
-
"""Cursor for pagination"""
|
|
3345
|
-
cursor: String!
|
|
3346
|
-
|
|
3347
|
-
"""Meta property node"""
|
|
3348
|
-
node: MetaProperty!
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
|
-
"""Input dla pojedynczego meta property w bulk set"""
|
|
3352
|
-
input MetaPropertyInput {
|
|
3353
|
-
"""Visibility — true = admin/customer-only, false = storefront-readable"""
|
|
3354
|
-
isPrivate: Boolean = false
|
|
3355
|
-
|
|
3356
|
-
"""Key (3-64 chars)"""
|
|
3357
|
-
key: String!
|
|
3358
|
-
|
|
3359
|
-
"""Namespace (3-64 chars, NOT starting with `doswiftly:`)"""
|
|
3360
|
-
namespace: String!
|
|
3361
|
-
|
|
3362
|
-
"""Typ wartości (driver walidacji backend-side)"""
|
|
3363
|
-
type: MetaPropertyValueType!
|
|
3364
|
-
|
|
3365
|
-
"""Wartość jako String (parse zgodnie z type)"""
|
|
3366
|
-
value: String!
|
|
3367
|
-
}
|
|
3368
|
-
|
|
3369
|
-
"""
|
|
3370
|
-
Typy wartości — STRING (max 255 chars), TEXT (no cap), INTEGER, DECIMAL, BOOLEAN ("true"/"false"), JSON (stringified), DATE (YYYY-MM-DD), DATE_TIME (ISO 8601), URL (http(s)://...), COLOR (#RRGGBB / #RRGGBBAA hex).
|
|
3371
|
-
"""
|
|
3372
|
-
enum MetaPropertyValueType {
|
|
3373
|
-
BOOLEAN
|
|
3374
|
-
COLOR
|
|
3375
|
-
DATE
|
|
3376
|
-
DATE_TIME
|
|
3377
|
-
DECIMAL
|
|
3378
|
-
INTEGER
|
|
3379
|
-
JSON
|
|
3380
|
-
STRING
|
|
3381
|
-
TEXT
|
|
3382
|
-
URL
|
|
3383
|
-
}
|
|
3384
|
-
|
|
3385
3386
|
"""Monetary value with currency"""
|
|
3386
3387
|
type Money {
|
|
3387
3388
|
"""Decimal money amount"""
|
|
@@ -3463,12 +3464,6 @@ type Mutation {
|
|
|
3463
3464
|
"""Logout customer (clears auth cookie)"""
|
|
3464
3465
|
customerLogout: CustomerLogoutPayload!
|
|
3465
3466
|
|
|
3466
|
-
"""Bulk upsert własnych meta properties klienta (Bearer token wymagany)"""
|
|
3467
|
-
customerMetaPropertiesSet(properties: [MetaPropertyInput!]!): MetaPropertiesSetPayload!
|
|
3468
|
-
|
|
3469
|
-
"""Usuwa pojedyncze meta property klienta po (namespace, key)"""
|
|
3470
|
-
customerMetaPropertyDelete(key: String!, namespace: String!): MetaPropertyDeletePayload!
|
|
3471
|
-
|
|
3472
3467
|
"""Refresh access token"""
|
|
3473
3468
|
customerRefreshToken: CustomerRefreshTokenPayload!
|
|
3474
3469
|
|
|
@@ -3576,6 +3571,9 @@ type Order implements Node {
|
|
|
3576
3571
|
"""
|
|
3577
3572
|
accessToken: String!
|
|
3578
3573
|
|
|
3574
|
+
"""Order custom field values (post-Opcja A unified custom fields)."""
|
|
3575
|
+
attributes(namespace: String): [EntityAttributeField!]!
|
|
3576
|
+
|
|
3579
3577
|
"""Czy storefront może zainicjować płatność dla tego order"""
|
|
3580
3578
|
canCreatePayment: Boolean!
|
|
3581
3579
|
|
|
@@ -3602,12 +3600,6 @@ type Order implements Node {
|
|
|
3602
3600
|
"""
|
|
3603
3601
|
lineItems(after: String, first: Int = 10): OrderLineItemConnection!
|
|
3604
3602
|
|
|
3605
|
-
"""Lista meta properties (Relay Connection)"""
|
|
3606
|
-
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
3607
|
-
|
|
3608
|
-
"""Pojedyncze meta property po (namespace, key)"""
|
|
3609
|
-
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
3610
|
-
|
|
3611
3603
|
"""Order number (human-readable)"""
|
|
3612
3604
|
orderNumber: String!
|
|
3613
3605
|
|
|
@@ -4014,6 +4006,11 @@ type Product implements Node {
|
|
|
4014
4006
|
"""Average rating (1-5)"""
|
|
4015
4007
|
averageRating: Float
|
|
4016
4008
|
|
|
4009
|
+
"""
|
|
4010
|
+
Canonical brand entity (Faza 3, 2026-05-17). Resolved via DataLoader (N+1 safe dla list queries). Null gdy product nie ma przypisanego brand_id.
|
|
4011
|
+
"""
|
|
4012
|
+
brand: BrandSummary
|
|
4013
|
+
|
|
4017
4014
|
"""
|
|
4018
4015
|
Wszystkie kategorie do których produkt należy (M2M przez ProductCategory junction). Posortowane po junction.sortOrder ASC. Empty list gdy produkt nie jest w żadnej kategorii.
|
|
4019
4016
|
"""
|
|
@@ -4056,12 +4053,6 @@ type Product implements Node {
|
|
|
4056
4053
|
"""
|
|
4057
4054
|
isPurchasable: Boolean!
|
|
4058
4055
|
|
|
4059
|
-
"""Lista meta properties — Storefront API filtruje isPrivate=false"""
|
|
4060
|
-
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
4061
|
-
|
|
4062
|
-
"""Pojedyncze meta property — Storefront API filtruje isPrivate=false"""
|
|
4063
|
-
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
4064
|
-
|
|
4065
4056
|
"""
|
|
4066
4057
|
Per-product option definitions (Color, Size, …) with their available values. Use these to build a variant picker without aggregating `selectedOptions` manually.
|
|
4067
4058
|
"""
|
|
@@ -4109,7 +4100,9 @@ type Product implements Node {
|
|
|
4109
4100
|
"""Product variants (Relay Connection)"""
|
|
4110
4101
|
variants(after: String, before: String, first: Float, last: Float): ProductVariantConnection!
|
|
4111
4102
|
|
|
4112
|
-
"""
|
|
4103
|
+
"""
|
|
4104
|
+
Legacy vendor/brand name (free-text). Preferred: `brand` field (canonical Brand entity, Faza 3).
|
|
4105
|
+
"""
|
|
4113
4106
|
vendor: String
|
|
4114
4107
|
|
|
4115
4108
|
"""Catalog visibility (Faza 1.5) — PUBLIC | HIDDEN | BUNDLE_ONLY"""
|
|
@@ -4245,13 +4238,18 @@ type ProductEdge {
|
|
|
4245
4238
|
"""Single product filter"""
|
|
4246
4239
|
input ProductFilter {
|
|
4247
4240
|
"""
|
|
4248
|
-
DoSwiftly: dynamic attribute filters (configurator system).
|
|
4241
|
+
DoSwiftly: dynamic attribute filters (configurator + custom fields system). Optional `namespace` w AttributeFilterInput dyskryminuje per grupa atrybutów.
|
|
4249
4242
|
"""
|
|
4250
4243
|
attributes: [AttributeFilterInput!]
|
|
4251
4244
|
|
|
4252
4245
|
"""Filter by availability for sale"""
|
|
4253
4246
|
available: Boolean
|
|
4254
4247
|
|
|
4248
|
+
"""
|
|
4249
|
+
Faza 3 (2026-05-17): filter by canonical Brand entity. Wybierz po `slug` (URL-stable) lub `id` (raw UUID). Multiple `brand` entries w `filters[]` array = OR semantics. Vendor (legacy free-text) wciąż wspierany via `productVendor`.
|
|
4250
|
+
"""
|
|
4251
|
+
brand: BrandFilter
|
|
4252
|
+
|
|
4255
4253
|
"""Filter by category"""
|
|
4256
4254
|
category: CategoryFilter
|
|
4257
4255
|
|
|
@@ -4437,6 +4435,9 @@ enum ProductTypeEnum {
|
|
|
4437
4435
|
|
|
4438
4436
|
"""Product variant - purchasable unit"""
|
|
4439
4437
|
type ProductVariant {
|
|
4438
|
+
"""Variant custom field values (post-Opcja A unified custom fields)."""
|
|
4439
|
+
attributes(namespace: String): [EntityAttributeField!]!
|
|
4440
|
+
|
|
4440
4441
|
"""
|
|
4441
4442
|
Available stock (computed: stock - reserved). Null when track is disabled.
|
|
4442
4443
|
"""
|
|
@@ -4460,12 +4461,6 @@ type ProductVariant {
|
|
|
4460
4461
|
"""Whether variant is available for purchase"""
|
|
4461
4462
|
isAvailable: Boolean!
|
|
4462
4463
|
|
|
4463
|
-
"""Lista meta properties — Storefront API filtruje isPrivate=false"""
|
|
4464
|
-
metaProperties(first: Int = 10, namespace: String): MetaPropertyConnection!
|
|
4465
|
-
|
|
4466
|
-
"""Pojedyncze meta property — Storefront API filtruje isPrivate=false"""
|
|
4467
|
-
metaProperty(key: String!, namespace: String!): MetaProperty
|
|
4468
|
-
|
|
4469
4464
|
"""Variant price (Money). Default field — industry-standard schema."""
|
|
4470
4465
|
price: Money!
|
|
4471
4466
|
|