@doswiftly/storefront-sdk 13.1.0 → 14.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/CHANGELOG.md +181 -0
  2. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,186 @@
1
1
  # Changelog
2
2
 
3
+ ## 14.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 1fda2ab: **BREAKING**: URL identifier unification (`slug` → `handle`) + type renames for naming consistency.
8
+
9
+ ### What changed
10
+
11
+ 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.
12
+
13
+ ### Field renames (GraphQL response)
14
+
15
+ | Type | Old | New |
16
+ | ---------------------------- | ------ | -------- |
17
+ | `Category` | `slug` | `handle` |
18
+ | `BlogPost` | `slug` | `handle` |
19
+ | `BlogCategory` | `slug` | `handle` |
20
+ | `BlogTag` | `slug` | `handle` |
21
+ | `LoyaltyReward` | `slug` | `handle` |
22
+ | `BrandFilter` (input) | `slug` | `handle` |
23
+ | `BrandFilterValue` | `slug` | `handle` |
24
+ | `ProductAttributeDefinition` | `slug` | `handle` |
25
+ | `CategoryFilterOption` | `slug` | `handle` |
26
+
27
+ ### Type renames
28
+ - `BrandSummary` → `Brand` (canonical product brand entity, e.g. Funko, Nike). Update `__typename` checks and fragment spreads.
29
+ - `Brand` (shop branding) → `ShopBrand` (tenant-scoped shop metadata: logo, slogan, colors).
30
+
31
+ ### Query args renamed
32
+
33
+ ```graphql
34
+ # Old
35
+ query { category(slug: "figurki") { id name } }
36
+ query { blogPost(slug: "post-handle") { title } }
37
+ query BlogPosts($categorySlug: String, $tagSlug: String) {
38
+ blogPosts(categorySlug: $categorySlug, tagSlug: $tagSlug) { ... }
39
+ }
40
+
41
+ # New
42
+ query { category(handle: "figurki") { id name } }
43
+ query { blogPost(handle: "post-handle") { title } }
44
+ query BlogPosts($categoryHandle: String, $tagHandle: String) {
45
+ blogPosts(categoryHandle: $categoryHandle, tagHandle: $tagHandle) { ... }
46
+ }
47
+ ```
48
+
49
+ ### Brand filter input
50
+
51
+ ```graphql
52
+ # Old
53
+ products(filters: [{ brand: { slug: "funko" } }]) { ... }
54
+
55
+ # New
56
+ products(filters: [{ brand: { handle: "funko" } }]) { ... }
57
+ ```
58
+
59
+ ### MenuItem resource union
60
+
61
+ ```graphql
62
+ # Old
63
+ fragment MenuItem on MenuItem {
64
+ resource {
65
+ __typename
66
+ ... on Category {
67
+ id
68
+ slug
69
+ name
70
+ }
71
+ ... on BrandSummary {
72
+ id
73
+ slug
74
+ name
75
+ logo
76
+ }
77
+ }
78
+ }
79
+
80
+ # New
81
+ fragment MenuItem on MenuItem {
82
+ resource {
83
+ __typename
84
+ ... on Category {
85
+ id
86
+ handle
87
+ name
88
+ }
89
+ ... on Brand {
90
+ id
91
+ handle
92
+ name
93
+ logo
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ ### Migration steps
100
+ 1. Update to `@doswiftly/storefront-operations@^14.0.0` + `@doswiftly/storefront-sdk@^14.0.0`.
101
+ 2. Run your codegen — TypeScript will surface every call site that needs update.
102
+ 3. Find/replace in storefront code:
103
+ - `.slug` → `.handle` on response fields listed above.
104
+ - `__typename === 'BrandSummary'` → `__typename === 'Brand'`.
105
+ - `{ brand: { slug } }` filter input → `{ brand: { handle } }`.
106
+ - `categorySlug`/`tagSlug` query args → `categoryHandle`/`tagHandle`.
107
+ 4. Update GraphQL fragments selecting renamed fields.
108
+ 5. Re-run codegen, fix any remaining TypeScript errors.
109
+
110
+ ### Why
111
+
112
+ 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.
113
+
114
+ Full migration guide with code examples: <https://docs.doswiftly.pl/storefront-developer/migration-14.0>
115
+
116
+ Naming conventions reference: <https://docs.doswiftly.pl/storefront-developer/api/naming-conventions>
117
+
118
+ ### Minor Changes
119
+
120
+ - 507d487: Storefront GraphQL: nowy `attributeOptionsSearch(input)` query — paginated + searchable lista opcji atrybutu (Relay Connection).
121
+
122
+ 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.
123
+
124
+ 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.
125
+
126
+ **Input** (`AttributeOptionsSearchInput`):
127
+ - `handle: String!` — slug atrybutu (np. `"marka"`)
128
+ - `contextInput: AvailableFiltersInput` — produktowy kontekst (kategoria/kolekcja/search/currentFilters) z exclude-self semantyką
129
+ - `first: Int` (1–100, default 50)
130
+ - `after: String` — opaque cursor
131
+ - `search: String` — case-insensitive ILIKE (max 100 znaków)
132
+ - `sort: AttributeOptionsSearchSort` — `COUNT_DESC` (default, most popular first) lub `LABEL_ASC` (alfabetycznie z polskim collation)
133
+
134
+ **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).
135
+
136
+ **Migration**: brak breaking changes — `productFilters` zachowuje obecny shape. Jeśli budujesz "Pokaż wszystkie marki" UI, przenoś logikę na nowy query żeby uniknąć over-fetching.
137
+
138
+ - 6f8d873: `CartWarningCode` enum gains a fourth value, `SHIPPING_METHOD_AUTO_CLEARED`.
139
+
140
+ 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: "…" }`.
141
+
142
+ 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.
143
+
144
+ **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.
145
+
146
+ **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."
147
+
148
+ 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.
149
+
150
+ - 1fda2ab: Storefront navigation menus: typed `resource` field eksponowany w `MenuItem` fragment + nowy typ linku `BRAND`.
151
+
152
+ **Co nowego**:
153
+ - `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.
154
+ - `MenuItemType.BRAND` — nowy typ linku menu dla marek producentów. `BrandSummary` w `resource` union (`id`, `name`, `slug`, `logo`).
155
+ - `MenuItem.url` dla `BRAND` resolve'uje się serwerowo jako `/brands/<slug>` — spójnie z istniejącą konwencją `/categories|/collections|/pages|/products/<slug>`.
156
+
157
+ **Dwa sposoby renderingu link target**:
158
+ 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]`.
159
+ 2. **Custom routing** (Next.js single-segment, Remix, itd.) — odczytaj `item.resource.__typename` + per-type `slug`/`handle`:
160
+
161
+ ```ts
162
+ function buildHref(item: MenuItem): string {
163
+ if (!item.resource) return item.url ?? "#";
164
+ switch (item.resource.__typename) {
165
+ case "Category":
166
+ return `/${item.resource.slug}`;
167
+ case "Collection":
168
+ return `/${item.resource.handle}`;
169
+ case "ShopPage":
170
+ return `/${item.resource.handle}`;
171
+ case "Product":
172
+ return `/p/${item.resource.handle}`;
173
+ case "BrandSummary":
174
+ return `/marka/${item.resource.slug}`;
175
+ }
176
+ return item.url ?? "#";
177
+ }
178
+ ```
179
+
180
+ **Performance**: wszystkie resource lookupy są batchowane per request — zero N+1 nawet w głębokich menu.
181
+
182
+ **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.
183
+
3
184
  ## 13.1.0
4
185
 
5
186
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doswiftly/storefront-sdk",
3
- "version": "13.1.0",
3
+ "version": "14.0.0",
4
4
  "description": "Storefront runtime SDK for DoSwiftly Commerce — layered transport, middleware pipeline, React providers, Zustand stores, cache strategies. 0 runtime dependencies in core.",
5
5
  "type": "module",
6
6
  "sideEffects": false,