@viu/emporix-sdk-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,519 @@
1
+ # @viu/emporix-sdk-react
2
+
3
+ ## 1.0.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#15](https://github.com/viuteam/emporix-sdk/pull/15) [`5c51a58`](https://github.com/viuteam/emporix-sdk/commit/5c51a58313c63cb7a9e34a4c5e6dc1da2017a827) Thanks [@amnael1](https://github.com/amnael1)! - `credentials.storefront.context` (`{ currency, siteCode, targetLocation }`)
8
+ is now sent at anonymous-login so `prices.matchByContext` resolves prices
9
+ from the session. Adds the `useMatchPrices` React hook. The next-app-router
10
+ and vite-spa examples now include an anonymous guest-checkout flow.
11
+
12
+ BREAKING: `CartService.create` now returns the generated `CartCreated`
13
+ (`{ cartId, yrn }`) — the actual create-endpoint response — instead of the
14
+ `Cart` GET model. Read `cart.cartId` (not `cart.id`) from the result.
15
+
16
+ - [#41](https://github.com/viuteam/emporix-sdk/pull/41) [`c10fc2d`](https://github.com/viuteam/emporix-sdk/commit/c10fc2d362c12cc881caddd301b7f987ba989d47) Thanks [@amnael1](https://github.com/amnael1)! - API-quota reduction: sane QueryClient defaults + bootstrap deduplication.
17
+
18
+ **QueryClient defaults** (only applied when no `queryClient` prop is passed):
19
+ - `staleTime: 30s` — fresh-within-30s policy reduces refetch-on-mount churn.
20
+ - `refetchOnWindowFocus: false` — tabbing back no longer refetches all queries.
21
+ - `retry: 1` — single retry on failure instead of three (caps failed-request
22
+ cost at 2× per query).
23
+
24
+ **Per-hook staleTime overrides:**
25
+ - `useSites`, `useDefaultSite`, `usePaymentModes` — 10 min.
26
+ - `useCategory(ies)`, `useCategoryTree`, `useProductsInCategory(Infinite)`,
27
+ `useMySegment*` — 5 min.
28
+ - `useProducts(Infinite)`, `useProduct`, `useProductByCode`, `useProductSearch`,
29
+ `useMatchPrices` — 60 s.
30
+ - `useCustomerSession.customer` (meQuery) — 30 s.
31
+ - Cart, Addresses keep the 30s default (or 0 where freshness matters).
32
+
33
+ **Bootstrap dedup:**
34
+ - `useActiveCart({ create: true })` and `useCustomerSession.login` cart
35
+ onboarding share a single `bootstrapCart` cache entry — parallel mounts
36
+ trigger one server call instead of N.
37
+ - `useCustomerSession.login` honours `customer.preferredSite` via the same
38
+ `meQuery` cache key — login fires 1 `GET /customer/me` when the cache hits,
39
+ 2 in the worst-case timing race (vs always 2 before).
40
+
41
+ No breaking changes. Consumers passing their own `queryClient` to
42
+ `EmporixProvider` keep their existing defaults.
43
+
44
+ - [#47](https://github.com/viuteam/emporix-sdk/pull/47) [`765c54e`](https://github.com/viuteam/emporix-sdk/commit/765c54e8fd61e33cb0d4cc241415e9c56f45c729) Thanks [@amnael1](https://github.com/amnael1)! - B2B foundation:
45
+ - New `CompanyContextProvider` (auto-mounted inside `EmporixProvider`) and `useActiveCompany()` hook.
46
+ - New B2B read hooks: `useMyCompanies`, `useCompany`, `useCompanyContacts`, `useCompanyLocations`, `useCompanyGroups`.
47
+ - New admin mutation hooks: `useCreateCompany`/`useUpdateCompany`/`useDeleteCompany`, `useAssignContact`/`useUpdateContactAssignment`/`useUnassignContact`, `useCreateLocation`/`useUpdateLocation`/`useDeleteLocation`.
48
+ - Convenience hook `useCompanySwitcher()`.
49
+ - New storage keys `"activeLegalEntityId"` and `"refreshToken"` with `get`/`set` helpers on every backend (`useCustomerSession` writes the refresh token through them on login/refresh, clears on logout).
50
+ - New SSR prop `EmporixProvider.initialActiveLegalEntityId` for hydration.
51
+ - New telemetry event `{ type: "company:switched", from, to, durationMs }`.
52
+ - `useCart`, `useCheckout`, `useCustomerAddresses`, `useActiveCart`, `usePaymentModes` now include the active `legalEntityId` in their query keys (and `useCheckout` merges it into the order payload) so cart/orders are scoped per company.
53
+
54
+ Switching company calls `customer.refresh({ legalEntityId })` (eager token rescope), drops the stored cart id, and invalidates company-scoped queries. Without a persisted refresh token in storage, switch falls back to a local-state-only update.
55
+
56
+ - [#34](https://github.com/viuteam/emporix-sdk/pull/34) [`c77ca8c`](https://github.com/viuteam/emporix-sdk/commit/c77ca8caccf522c7cead8dba84042e92c428d893) Thanks [@amnael1](https://github.com/amnael1)! - Add four catalog-UX hooks to `@viu/emporix-sdk-react`:
57
+ - `useProductByCode(code)` — single-product lookup via the `code` field. For slug-based routes (`/products/[slug]`).
58
+ - `useProductSearch(query, params?)` — full-text product search. Disabled on empty query; pair with consumer-side debouncing.
59
+ - `useProductsInCategory(categoryId, params?)` — paginated products for a category landing page.
60
+ - `useProductsInCategoryInfinite(categoryId, params?)` — infinite-scroll variant of the same.
61
+
62
+ All four follow the established `useReadAuth` + `enabled`-gate patterns. No SDK change.
63
+
64
+ - [#33](https://github.com/viuteam/emporix-sdk/pull/33) [`a61917e`](https://github.com/viuteam/emporix-sdk/commit/a61917ed59dde4a01ca9b09b7dd86adc7538ba40) Thanks [@amnael1](https://github.com/amnael1)! - Add customer-account hooks to `@viu/emporix-sdk-react`:
65
+ - `useUpdateCustomer()` — mutation for profile updates, invalidates `useCustomerSession.customer`.
66
+ - `useChangePassword()` — mutation for password change. Customer-only.
67
+ - `useCustomerAddresses()` — query for the customer's address list.
68
+ - `useAddressMutations()` — `{ add, update, remove }` mutations following the `useCartMutations` shape.
69
+ - `usePasswordReset()` — 2-step anonymous flow: `{ request, confirm }`.
70
+
71
+ Internal: a shared `useCustomerOnlyCtx` helper now lives in `hooks/internal/use-read-auth.ts` for hooks that intentionally throw on missing customer token. The previously-local `customerOnlyCtx` in `useCheckout` stays (with different semantics — gates a query via `enabled`).
72
+
73
+ No SDK change.
74
+
75
+ - [#26](https://github.com/viuteam/emporix-sdk/pull/26) [`18e34a0`](https://github.com/viuteam/emporix-sdk/commit/18e34a03cbf4fbfe15a7e4995228bb5268b0e2ee) Thanks [@amnael1](https://github.com/amnael1)! - Customer-cart onboarding on login. After `useCustomerSession.login()` (or the SSO flows `socialLogin` / `exchangeToken`) succeeds, the SDK now automatically loads (or creates) the customer's open Emporix cart for the configured `siteCode` and merges any guest cart into it. The resulting `cartId` is written into `EmporixStorage`, so the UI sees the cart immediately.
76
+
77
+ **SDK (`@viu/emporix-sdk`)**
78
+ - `EmporixClient.config` is now a public read-only field, so hosts can read static settings such as `storefront.context.siteCode` without re-plumbing.
79
+ - **BREAKING:** `CartService.getCurrent(auth)` is now `getCurrent(auth, { siteCode, type?, legalEntityId?, create? })`. `siteCode` is required per the Emporix spec. Returns `null` on 404; with `create: true`, Emporix creates a new cart if none matches.
80
+ - **BREAKING / fix:** `CartService.merge(anonymousCartId, auth)` is now `merge(customerCartId, anonymousCartIds: string[], auth)`. The old signature put the wrong cart-id in the path and sent an empty body — it never actually worked against Emporix. The new signature matches the documented contract (`POST /cart/{tenant}/carts/{customerCartId}/merge` with body `{ carts: […] }`).
81
+
82
+ **React (`@viu/emporix-sdk-react`)**
83
+ - `useCustomerSession.login()`, `socialLogin()`, and `exchangeToken()` now run a best-effort cart-onboarding step: `client.carts.getCurrent({ siteCode, create: true })` to load (or create) the customer cart, then `client.carts.merge(customerCartId, [anonCartId])` if a guest `cartId` was in storage, and finally `storage.setCartId(customerCartId)`. Failures are swallowed so login never blocks on cart trouble. Skipped silently if no `storefront.context.siteCode` is configured.
84
+
85
+ **Migration**
86
+
87
+ ```ts
88
+ // SDK getCurrent:
89
+ - const cart = await client.carts.getCurrent(auth.customer(token));
90
+ + const cart = await client.carts.getCurrent(auth.customer(token), { siteCode: "main" });
91
+
92
+ // SDK merge:
93
+ - await client.carts.merge(anonCartId, auth.customer(token));
94
+ + await client.carts.merge(customerCartId, [anonCartId], auth.customer(token));
95
+ ```
96
+
97
+ React consumers do not need to change anything — the new behavior kicks in automatically as long as the client's `storefront.context.siteCode` is set (the vite-spa Example already does).
98
+
99
+ - [#19](https://github.com/viuteam/emporix-sdk/pull/19) [`2f823b8`](https://github.com/viuteam/emporix-sdk/commit/2f823b8eb72eca17863757c3f6ccbf3e76442ee3) Thanks [@amnael1](https://github.com/amnael1)! - Add real customer logout. `customers.logout(auth)` calls
100
+ `GET /customer/{tenant}/logout?accessToken=…` authorized with the customer
101
+ token, invalidating it server-side (204). `useCustomerSession().logout()` is
102
+ now async: it performs the server logout best-effort (ignoring failures, e.g.
103
+ an already-expired token) and then clears the local session. The token is
104
+ sent only as a query param the SDK never logs (the logger uses the path, not
105
+ the full URL).
106
+
107
+ - [#22](https://github.com/viuteam/emporix-sdk/pull/22) [`5770532`](https://github.com/viuteam/emporix-sdk/commit/57705327b4d58b1ac410ee958f85ae858a6c862d) Thanks [@amnael1](https://github.com/amnael1)! - Add `SegmentService` (storefront reads only): `list`, `get`, `listItems`,
108
+ `listSegmentItems`, `getCategoryTree`, plus the hydrate helpers
109
+ `listMyProductIds` / `listMyCategoryIds` / `listMyProducts` /
110
+ `listMyCategories` that map segment-item ids to real `Product` /
111
+ `Category` objects via parallel `products.get` / `categories.get` calls.
112
+ All methods require a customer/raw `AuthContext` and use the shared
113
+ `requireCustomer` guard (also adopted by `customer.ts` and `payment.ts`).
114
+
115
+ React adds three lightweight hooks: `useMySegments`, `useMySegmentItems`,
116
+ `useMySegmentCategoryTree`. Each reads the customer token from the
117
+ storage and is `enabled: false` when there is no token (no network call
118
+ for guests). Exposed on the `@viu/emporix-sdk/segment` subpath.
119
+
120
+ - [#18](https://github.com/viuteam/emporix-sdk/pull/18) [`7da7b21`](https://github.com/viuteam/emporix-sdk/commit/7da7b217912782ba5d9b3f1e959d78d70c32c4ba) Thanks [@amnael1](https://github.com/amnael1)! - Add customer token refresh. `customers.refresh({ refreshToken, saasToken?,
121
+ legalEntityId? }, auth?)` calls `GET /customer/{tenant}/refreshauthtoken`
122
+ (authorized with an anonymous token, default), returning a new
123
+ `CustomerSession` with the **same `sessionId`**. The refresh endpoint does
124
+ not return a `saas_token`, so the original is carried forward via the
125
+ `saasToken` input. `useCustomerSession` now captures the refresh/saas tokens
126
+ at `login`, exposes `refreshToken`, and adds a `refreshSession()` action
127
+ that exchanges the refresh token and updates the stored customer token.
128
+
129
+ - [#14](https://github.com/viuteam/emporix-sdk/pull/14) [`4d87f11`](https://github.com/viuteam/emporix-sdk/commit/4d87f11a022996a49dad04af1404394cdd60804f) Thanks [@amnael1](https://github.com/amnael1)! - BREAKING: every service request body now uses the generated OpenAPI request
130
+ type. `carts.create` takes `CreateCart`, `carts.addItem` takes
131
+ `CartItemRequest` (now requires `product`/`quantity`/`price`),
132
+ `carts.updateItem` takes `UpdateCartItem`, `checkout.placeOrder` takes
133
+ `RequestCheckout`, `checkout.placeOrderFromQuote` takes
134
+ `RequestFromQuoteCheckout`, `payments.authorize` takes
135
+ `AuthorizePaymentRequest` (`{ order: { id }, … }`),
136
+ `customers.changePassword` takes `{ currentPassword, newPassword }`,
137
+ `customers.confirmPasswordReset` takes `{ token, password }`,
138
+ `customers.signup`/`update`/`addresses.*` take the generated DTOs. All
139
+ ergonomic input wrappers and input transformations are removed — callers
140
+ send the exact wire shape. `useCartMutations.addItem`/`updateItem` mutation
141
+ variables change accordingly. `CustomerService.login` keeps its literal
142
+ `{ email, password }` input and snake_case `CustomerSession` response (no
143
+ generated request type exists for it).
144
+
145
+ - [#21](https://github.com/viuteam/emporix-sdk/pull/21) [`877c2ab`](https://github.com/viuteam/emporix-sdk/commit/877c2abf791a6d67d438849cd800d5704ec486cb) Thanks [@amnael1](https://github.com/amnael1)! - Add `MediaService`. `client.media.create({ kind: "blob" | "link", ... })`
146
+ posts to `POST /media/{tenant}/assets` (multipart for BLOB, JSON for LINK);
147
+ convenience helpers `uploadFile`, `link`, `attachToProduct`,
148
+ `detachFromProduct`, `listForProduct` wrap the common product-attachment
149
+ flows. `HttpClient` now passes `FormData` bodies through `fetch` verbatim
150
+ (no Content-Type/JSON-stringify). React adds a thin `useProductMedia(id)`
151
+ hook that reads `productMedia` from the existing product query (no
152
+ service-token call in the browser).
153
+
154
+ BREAKING: `ProductService.media` is removed — it called a path
155
+ (`/product/{tenant}/products/{id}/media`) that does not exist in the
156
+ Emporix Product API. Migrate to `client.media.listForProduct(productId)`
157
+ (admin/server) or read `product.productMedia` from `client.products.get`
158
+ (storefront).
159
+
160
+ - [#37](https://github.com/viuteam/emporix-sdk/pull/37) [`380796a`](https://github.com/viuteam/emporix-sdk/commit/380796a53d9543b379b21eb414e3ebc5586e55f8) Thanks [@amnael1](https://github.com/amnael1)! - Add Site Settings Service binding — first stage of multi-site foundation.
161
+
162
+ **SDK**
163
+ - `client.sites.list()` — list active sites for the tenant.
164
+ - `client.sites.get(code)` — retrieve one site by code.
165
+ - `client.sites.current()` — convenience for the `default: true` site.
166
+ - New `Site` type mirroring the `SiteDto` schema (code, name, active,
167
+ default, currency, languages, homeBase, shipToCountries, …).
168
+
169
+ **React**
170
+ - `useSites()` — list active sites.
171
+ - `useDefaultSite()` — the default site.
172
+
173
+ No breaking changes. The active-site runtime context (provider state,
174
+ `setSite`, cache-key migration) follows in MS-2.
175
+
176
+ - [#38](https://github.com/viuteam/emporix-sdk/pull/38) [`cf2af9d`](https://github.com/viuteam/emporix-sdk/commit/cf2af9dbc3b025d3ef8d6cb2657f0c339cce2b7e) Thanks [@amnael1](https://github.com/amnael1)! - Multi-site MS-2: observable site context + cache-key migration.
177
+
178
+ **Provider**
179
+ - `<EmporixProvider initialSiteCode>` prop — resolution order: prop →
180
+ `storage.getSiteCode()` → static `client.config.…context.siteCode` →
181
+ `null`.
182
+
183
+ **Hooks**
184
+ - `useSiteContext()` — returns `{ siteCode, currency, targetLocation,
185
+ setSite }` for the active site. In MS-2 `currency` and `targetLocation`
186
+ are `null` (populated in MS-4). `setSite(code)` writes storage, clears
187
+ `storage.cartId` (carts are site-aware), and invalidates all
188
+ `["emporix"]` queries.
189
+
190
+ **Storage**
191
+ - `EmporixStorage.{get,set}SiteCode` across all three backends (memory,
192
+ localStorage, cookie). localStorage key: `emporix.siteCode`.
193
+
194
+ **Cache keys**
195
+ - All site-aware query keys (`useProducts`, `useCategories`, `useCart`,
196
+ `useActiveCart`, `useCartMutations`, `useMatchPrices`, `useMySegment*`,
197
+ `usePaymentModes`, etc.) now include `siteCode`. Different sites =
198
+ separate cache entries. Internal change — no consumer subscribed
199
+ directly to query keys.
200
+
201
+ No breaking changes. Existing single-site apps work unchanged — they
202
+ implicitly run with the static config's `siteCode` (or `null`).
203
+
204
+ - [#39](https://github.com/viuteam/emporix-sdk/pull/39) [`141521c`](https://github.com/viuteam/emporix-sdk/commit/141521c91f88171006067255294a45b9fdc01a43) Thanks [@amnael1](https://github.com/amnael1)! - Multi-site MS-3: server-side session-context sync.
205
+
206
+ **SDK**
207
+ - `client.sessionContext.get()` — `GET /session-context/{tenant}/me/context`.
208
+ Returns `null` (not throws) when the server returns 404 — i.e. when the
209
+ user has not created a cart yet and no session-context exists.
210
+ - `client.sessionContext.patch(input)` — `PATCH /session-context/{tenant}/me/context`
211
+ with optimistic-locking. Looks up `metadata.version` via GET first
212
+ unless caller provides one. Returns `true` when applied, `false` when
213
+ there is no session context yet (404 on the GET → patch skipped).
214
+ - New `SessionContext` and `SessionContextPatch` types.
215
+
216
+ **React**
217
+ - `setSite()` is now async. It flips local state + storage + cart-id
218
+ - cache-invalidation synchronously (optimistic UI), then PATCHes the
219
+ server. Skips the PATCH when no session exists yet (404 on GET).
220
+ - `useSiteContext()` gains `isSwitching: boolean` and
221
+ `switchError: Error | null`. The optimistic state is NOT rolled back
222
+ on PATCH failure — surface the error in UI; the next user interaction
223
+ retries.
224
+
225
+ No breaking changes. Existing call sites continue to work — `setSite("X")`
226
+ without `await` still flips the UI; awaiting it blocks until the
227
+ server-side sync completes.
228
+
229
+ - [#40](https://github.com/viuteam/emporix-sdk/pull/40) [`b23d3eb`](https://github.com/viuteam/emporix-sdk/commit/b23d3eb51ebe98a0d1f90499409b1b509810722c) Thanks [@amnael1](https://github.com/amnael1)! - Multi-site MS-4: currency + targetLocation auto-derive, preferredSite honour.
230
+
231
+ **Provider**
232
+ - `useSiteContext().currency` and `useSiteContext().targetLocation` are no
233
+ longer always `null`. They derive from the active site's DTO
234
+ (`site.currency` and `site.homeBase.address.country`), cached for 5
235
+ minutes via React-Query.
236
+ - `setSite(code)` fetches the site DTO, populates `currency` /
237
+ `targetLocation`, and includes all three fields in the
238
+ `sessionContext.patch` body so the server is fully in sync.
239
+ - On provider mount with a pre-resolved `siteCode` (from `initialSiteCode`
240
+ prop, storage, or static config), the site DTO is fetched once so
241
+ `currency` and `targetLocation` populate without a user-driven switch.
242
+
243
+ **Login**
244
+ - `useCustomerSession.login` (and `socialLogin` / `exchangeToken`) now read
245
+ `customer.preferredSite`. If it's set and differs from the active site,
246
+ the SDK calls `setSite(preferredSite)` — same flow as a user-driven
247
+ switch. Best-effort: a failure here never blocks login.
248
+
249
+ No breaking changes. Storefronts without `preferredSite` set on their
250
+ customers see no behavior change.
251
+
252
+ - [#42](https://github.com/viuteam/emporix-sdk/pull/42) [`8d22fb8`](https://github.com/viuteam/emporix-sdk/commit/8d22fb8d4cdf5e2ddeba7273ffe4b41a1630d463) Thanks [@amnael1](https://github.com/amnael1)! - Add opt-in telemetry channel for observability + ops-tuning.
253
+
254
+ **SDK (additive)**
255
+ - `TokenProvider.onRefresh(listener)` — optional subscription to
256
+ token-refresh events. `DefaultTokenProvider` implements it (anonymous
257
+ refresh path).
258
+
259
+ **React (additive)**
260
+ - `<EmporixProvider onTelemetry={fn}>` — receives a typed event stream
261
+ covering cache hit/miss, refetches, errors, mutations, auth refreshes,
262
+ and storage writes.
263
+ - `useEmporixTelemetry()` — returns `{ emit }` for consumer-side custom
264
+ events on the same channel.
265
+ - `EmporixStorage.subscribeAll(listener)` — optional subscription to all
266
+ storage write events. Implemented in all three built-in adapters
267
+ (memory, localStorage, cookie).
268
+
269
+ **Event types:**
270
+ - `cache.hit`, `cache.miss`, `query.refetch`, `query.error`
271
+ - `mutation.success`, `mutation.error`
272
+ - `auth.refresh`
273
+ - `storage.write`
274
+ - `custom`
275
+
276
+ No breaking changes. The entire telemetry layer is no-op when
277
+ `onTelemetry` is not passed. Existing `TokenProvider` / `EmporixStorage`
278
+ implementations continue to work without implementing the new optional
279
+ methods.
280
+
281
+ - [#50](https://github.com/viuteam/emporix-sdk/pull/50) [`4157818`](https://github.com/viuteam/emporix-sdk/commit/4157818c27b32ff32a1a41235bc7920137402f88) Thanks [@amnael1](https://github.com/amnael1)! - Order service hooks:
282
+ - Customer-facing: `useMyOrders`, `useMyOrdersInfinite`, `useOrder`, `useCancelOrder`, `useOrderTransition`, `useReorder`.
283
+ - Service-account (backoffice): `useSalesOrder`, `useUpdateSalesOrder` — inert when `auth` is undefined so storefront apps can import them for types without unexpected backend traffic.
284
+ - New `prefetchOrder` SSR helper alongside `prefetchProduct` / `prefetchCart`.
285
+ - `useMyOrders` / `useMyOrdersInfinite` default `legalEntityId` from `useActiveCompany`; explicit `null` disables. Switching the active company auto-invalidates order queries because `legalEntityId` is part of the cache key.
286
+ - `useReorder` uses a single `cart.addItemsBatch` call instead of N sequential `addItem` requests. Per-entry HTTP status feeds the unchanged `{ added, errors }` mutation result; partial failures still don't throw. Caps at 200 line-items (Emporix server-side limit).
287
+
288
+ - [#24](https://github.com/viuteam/emporix-sdk/pull/24) [`2014f71`](https://github.com/viuteam/emporix-sdk/commit/2014f710ee363f35aea1d8af0e85bce69a5bc40a) Thanks [@amnael1](https://github.com/amnael1)! - Harmonize all paginated SDK surfaces on `PaginatedItems<T>`. Removes the
289
+ legacy `Page<T>` shape (whose `total` was always `NaN`, since the HTTP
290
+ client never exposed `X-Total-Count`) and the `paginate()` async
291
+ iterator.
292
+
293
+ **BREAKING:**
294
+ - `ProductService.list` / `ProductService.search` now return
295
+ `PaginatedItems<Product>` (`{ items, pageNumber, pageSize, hasNextPage }`)
296
+ instead of `Page<Product>` (`{ items, total, offset, limit }`).
297
+ - `CategoryService.list` returns `PaginatedItems<Category>`;
298
+ `CategoryService.productsIn` returns `PaginatedItems<Product>`.
299
+ - `useProducts` / `useCategories` now resolve to `PaginatedItems<T>`.
300
+ - `Page<T>` and `paginate()` are no longer exported from `@viu/emporix-sdk`.
301
+
302
+ **Fixed:**
303
+ - `useProductsInfinite` previously over-fetched a trailing empty page
304
+ before terminating, and its `getNextPageParam` was tied to the
305
+ fetched-page count rather than the cursor. It now derives the next
306
+ page from `last.hasNextPage` / `last.pageNumber + 1` — same pattern as
307
+ the segment-hydrate infinite hooks.
308
+
309
+ **Added:**
310
+ - `useCategoriesInfinite` — mirror of `useProductsInfinite`.
311
+ - `iterateAll<T>(fetchPage, start?)` async iterator over
312
+ `PaginatedItems<T>`. Replaces `paginate()` for "iterate every item
313
+ across pages" use cases.
314
+
315
+ **Migration:**
316
+
317
+ ```ts
318
+ // Before
319
+ const { items, total } = await client.products.list({
320
+ pageNumber: 1,
321
+ pageSize: 50,
322
+ });
323
+ // total was always NaN.
324
+
325
+ // After
326
+ const { items, hasNextPage } = await client.products.list({
327
+ pageNumber: 1,
328
+ pageSize: 50,
329
+ });
330
+ ```
331
+
332
+ ```ts
333
+ // Before
334
+ for await (const p of paginate((offset, limit) => svc.list(...), 50)) { ... }
335
+
336
+ // After
337
+ for await (const p of svc.listAll({ pageSize: 50 })) { ... }
338
+ // or, for custom sources:
339
+ for await (const x of iterateAll<X>((pageNumber) => fetchPage(pageNumber))) { ... }
340
+ ```
341
+
342
+ - [#31](https://github.com/viuteam/emporix-sdk/pull/31) [`13f23bd`](https://github.com/viuteam/emporix-sdk/commit/13f23bd9016903c59ca1bfa0b340ff096587131e) Thanks [@amnael1](https://github.com/amnael1)! - Add npm publish readiness metadata: `license` (MIT), `repository`, `bugs`, `homepage`, `author`, `keywords` in `package.json`. Adds the `LICENSE` file at the repo root (npm includes it in each package tarball automatically). No code changes; the next release will be the first one with full npm-side metadata for discoverability + provenance attestation.
343
+
344
+ - [#48](https://github.com/viuteam/emporix-sdk/pull/48) [`5f330d5`](https://github.com/viuteam/emporix-sdk/commit/5f330d521119e36ca95b8cfc3bed049572fd1c03) Thanks [@amnael1](https://github.com/amnael1)! - Raise Node.js engines floor from `>=18` to `>=20.19.0`. Node 18 reached end-of-life on 30 April 2025; Node 20 LTS (≥ 20.19.0, which ships flag-free `require(esm)`) is the new minimum. Development happens on Node 24 LTS (`.nvmrc` updated); CI exercises Node 20, 22, and 24.
345
+
346
+ No code changes — no SDK feature uses a Node API beyond what Node 20 provides. Browser consumers are unaffected.
347
+
348
+ - [#3](https://github.com/viuteam/emporix-sdk/pull/3) [`e2f74db`](https://github.com/viuteam/emporix-sdk/commit/e2f74db04edb1d4250add83a4b8208bc33e326c7) Thanks [@amnael1](https://github.com/amnael1)! - Add @viu/emporix-sdk-react: provider, pluggable token storage, customer
349
+ session, query hooks, cart mutations with optimistic updates, error helpers and
350
+ SSR prefetch helpers. Core: expose EmporixClient.tenant for query-key namespacing.
351
+
352
+ - [#23](https://github.com/viuteam/emporix-sdk/pull/23) [`027b816`](https://github.com/viuteam/emporix-sdk/commit/027b816c171e81263b99b791916e33816f148839) Thanks [@amnael1](https://github.com/amnael1)! - Segment hydrate now uses a single Emporix `POST /search` per page instead
353
+ of N+1 `GET /products/{id}` calls. New
354
+ `ProductService.searchByIds(ids, { chunkSize? }, auth?)` and
355
+ `CategoryService.searchByIds(...)` POST `/search` with
356
+ `q="id:(id1,id2,…)"`, chunking at 100 IDs by default. Adds the generic
357
+ `PaginatedItems<T>` (`{ items, pageNumber, pageSize, hasNextPage }`) in
358
+ `core/context.ts`.
359
+
360
+ **BREAKING:** `SegmentService.listMyProducts` and
361
+ `SegmentService.listMyCategories` now return `PaginatedItems<Product>` /
362
+ `PaginatedItems<Category>` instead of a flat `Product[]` / `Category[]`.
363
+ `SegmentService.listItems` gains optional `pageNumber` / `pageSize`
364
+ params (additive). `listMyProductIds` / `listMyCategoryIds` are
365
+ unchanged.
366
+
367
+ React adds four new hooks: `useMySegmentProducts` /
368
+ `useMySegmentProductsInfinite` and `useMySegmentCategories` /
369
+ `useMySegmentCategoriesInfinite`. The infinite variants use
370
+ `useInfiniteQuery` with a `pageNumber` cursor and `hasNextPage`-driven
371
+ `getNextPageParam`. All four are disabled when no customer token is in
372
+ storage.
373
+
374
+ - [#20](https://github.com/viuteam/emporix-sdk/pull/20) [`4cda829`](https://github.com/viuteam/emporix-sdk/commit/4cda82963d307fa12b1e1e628be31879f464ed9d) Thanks [@amnael1](https://github.com/amnael1)! - Add Emporix customer SSO support. `customers.socialLogin({ code, redirectUri,
375
+ codeVerifier?, sessionId? })` performs the Authorization-Code code exchange
376
+ (`POST /customer/{tenant}/socialLogin`); `customers.exchangeToken({
377
+ subjectToken, config? })` performs the RFC 8693 token exchange
378
+ (`POST /customer/{tenant}/exchangeauthtoken`). Both default to anonymous auth
379
+ and return a `CustomerSession` (now with optional `socialAccessToken` /
380
+ `socialIdToken` from socialLogin); `expires_in` is normalized to a number
381
+ across both flows. `useCustomerSession` gains `socialLogin` and
382
+ `exchangeToken` actions that store the session like `login`.
383
+
384
+ - [#35](https://github.com/viuteam/emporix-sdk/pull/35) [`9a260c8`](https://github.com/viuteam/emporix-sdk/commit/9a260c8963a3c44f489d3433e3db624447a5bd4e) Thanks [@amnael1](https://github.com/amnael1)! - `useCart` and `useCartMutations` now read the active cartId from `storage`
385
+ when their `cartId` argument is omitted. Pair with `useActiveCart` to drop
386
+ the `useCartMutations(cartId ?? "")` boilerplate:
387
+ - `useCart()` — disabled until storage has a cartId, then auto-resolves.
388
+ - `useCartMutations()` — resolves cartId at mutate-time; throws
389
+ `EmporixError("no cartId available…")` if storage is empty when a
390
+ mutation runs.
391
+
392
+ `useActiveCart` is now a thin wrapper around `useCart` and shares the same
393
+ React-Query cache key. Optimistic updates from `useCartMutations` now
394
+ propagate to every cart-aware view in one place.
395
+
396
+ `useCreateCart` additionally invalidates `["emporix","cart"]` on success so
397
+ `useActiveCart` picks up the new storage cartId on the next render.
398
+
399
+ `useActiveCart`'s `data` now correctly returns `null` (not `undefined`)
400
+ when storage has no cartId and `create` was not requested — matches the
401
+ documented empty-state signal.
402
+
403
+ No breaking changes — every old call signature still works.
404
+
405
+ - [#32](https://github.com/viuteam/emporix-sdk/pull/32) [`7c90d08`](https://github.com/viuteam/emporix-sdk/commit/7c90d0835f881b4b9528d30d5cda6e823e742b4e) Thanks [@amnael1](https://github.com/amnael1)! - Add `useActiveCart(opts?)` hook to `@viu/emporix-sdk-react`. Resolves to the cart matching `storage.cartId`; with `opts.create = true`, bootstraps a new cart via `client.carts.getCurrent({siteCode, create: true})` when storage is empty.
406
+
407
+ Returns `UseQueryResult<Cart | null>`. Coexists with `useCart(cartId)` (different query-key); use `useActiveCart` for "the storefront's current cart" and `useCart(cartId)` for known ids.
408
+
409
+ Useful for:
410
+ - Cart-page mounts: `useActiveCart({ create: true })`.
411
+ - Header mini-cart: `useActiveCart()` (read-only, no auto-create).
412
+ - B2B quote carts in parallel to shopping carts: `useActiveCart({ create: true, type: "quote" })`.
413
+
414
+ No SDK change; uses the existing `client.carts.getCurrent` and `client.carts.get` APIs. Auto-detects customer vs anonymous auth like the other read hooks.
415
+
416
+ - [#25](https://github.com/viuteam/emporix-sdk/pull/25) [`277ae71`](https://github.com/viuteam/emporix-sdk/commit/277ae7195ab9eecb87677fff4e8fcd16ea3b920b) Thanks [@amnael1](https://github.com/amnael1)! - Hook-only guest checkout + persistent anonymous cart.
417
+
418
+ **SDK (`@viu/emporix-sdk`)**
419
+ - New `AnonymousSessionStore` interface and optional `TokenProvider.attachAnonymousStore` method. When a host (e.g. `EmporixProvider`) supplies a store, `DefaultTokenProvider` bootstraps `anon` from the store on first use (taking the refresh-token path, so `sessionId` is preserved) and writes the rotated `refreshToken` + `sessionId` back after every login / refresh. With no store attached, behavior is identical to before.
420
+ - `invalidateAnonymous()` now also clears the attached store (`write(null)`).
421
+ - `EmporixClient.tokenProvider` is now a public, read-only field — so hosts can call `attachAnonymousStore` after construction.
422
+
423
+ **React (`@viu/emporix-sdk-react`)**
424
+ - `TokenStorage` renamed to `EmporixStorage` (alias `TokenStorage` is kept). New methods: `getCartId / setCartId`, `getAnonymousSession / setAnonymousSession`. All three storage backends — memory, `localStorage`, cookie — implement them.
425
+ - `EmporixProvider` wires the storage's anonymous-session accessors to the SDK's `attachAnonymousStore` so the anonymous cart can survive a browser reload.
426
+ - New `useCreateCart` mutation hook: auto-detects customer vs anonymous auth and persists `cartId` via `storage.setCartId`.
427
+ - `useCheckout` no longer throws on missing customer token — it auto-detects (customer if a token is stored, else anonymous). `usePaymentModes` keeps its customer-only behavior. Backward-compatible for existing logged-in flows.
428
+
429
+ **Migration**
430
+
431
+ No code change needed for existing consumers — both packages' changes are additive or strict supersets. New persistence kicks in automatically when consumers use one of the persistent storage backends (`createLocalStorageStorage()` or `createCookieStorage()`).
432
+
433
+ ### Patch Changes
434
+
435
+ - [#45](https://github.com/viuteam/emporix-sdk/pull/45) [`3f700d8`](https://github.com/viuteam/emporix-sdk/commit/3f700d8fbd4796429f998dd441c64816b3c5bfdb) Thanks [@amnael1](https://github.com/amnael1)! - Internal cleanup: drop the redundant `authKind` field from `useReadAuth`'s
436
+ return type and from `bootstrapCart`'s parameter list. Both duplicated
437
+ `ctx.kind` (the discriminator of `AuthContext`) — callers now compose
438
+ `ctx.kind` directly into query keys.
439
+
440
+ No public API changes. No cache-key shape changes (`authKind` values stay
441
+ identical: `"customer"`, `"anonymous"`, etc.). All 151 React tests stay
442
+ green.
443
+
444
+ - [#28](https://github.com/viuteam/emporix-sdk/pull/28) [`4fc01ef`](https://github.com/viuteam/emporix-sdk/commit/4fc01ef737c9397407937ee9ca8098a781ac075e) Thanks [@amnael1](https://github.com/amnael1)! - Add live end-to-end test suite (`@viu/emporix-e2e`, private) running through the `examples/vite-spa` Example against the `viu` tenant. Six specs cover the four critical user flows:
445
+ - **`catalog.spec.ts`** — anonymous catalog renders 12 products; only `GET /anonymous/login` + `GET /product/viu/products` hit Emporix on `/`.
446
+ - **`customer-session.spec.ts`** — login resolves the customer profile + stores the token; logout clears the token.
447
+ - **`guest-checkout.spec.ts`** — `useCreateCart` → `useCartMutations.addItem` → `useCheckout.placeOrder` (anonymous) → real order `EONxxxx` placed on `viu`.
448
+ - **`customer-cart-onboarding.spec.ts`** — guest cart created → login → `GET /cart/viu/carts?siteCode=main&create=true` + `POST /merge` fire → `storage.cartId` switched to the customer cart.
449
+
450
+ This is the first **live** verification of the PR #26 customer-cart-onboarding flow, previously covered only by MSW mocks. No SDK/React code changes — the suite is purely additive test infrastructure (separate `e2e/` workspace package, `@playwright/test` v1.49, `workflow_dispatch` CI workflow). Credentials are env-driven (`EMPORIX_TEST_CUSTOMER_EMAIL` / `_PASSWORD`); login-bound specs skip cleanly without them. Passwords are filled via a custom `fillSecret` helper that bypasses `page.fill()` so values never appear in the HTML report or action log.
451
+
452
+ Local runs: `pnpm e2e`. CI runs: trigger `e2e.yml` from the Actions tab. See [`docs/e2e.md`](../docs/e2e.md) for authoring workflow + Playwright Agent CLI usage.
453
+
454
+ - [#11](https://github.com/viuteam/emporix-sdk/pull/11) [`40f8e65`](https://github.com/viuteam/emporix-sdk/commit/40f8e65177699685c1114714f5b3f080cfab89f2) Thanks [@amnael1](https://github.com/amnael1)! - Order `exports` conditions so `types` resolves first. Node and the
455
+ TypeScript resolver evaluate `exports` conditions in declaration order;
456
+ with `import`/`require` listed before `types`, the `types` condition was
457
+ never reached, emitting build warnings and preventing consumers from
458
+ picking up the generated `.d.ts` entry points. Every subpath in both
459
+ packages now uses `{ types, import, require }` order.
460
+
461
+ - [#44](https://github.com/viuteam/emporix-sdk/pull/44) [`d0cc756`](https://github.com/viuteam/emporix-sdk/commit/d0cc75603db779447c4ffe84aa349c8e59db13df) Thanks [@amnael1](https://github.com/amnael1)! - Include LICENSE in the published npm tarballs. The `files` array already
462
+ declared `LICENSE` but the file was only present at the repo root; npm
463
+ publishes per-package, so a copy now lives inside each package directory.
464
+ Fixes "License: not specified" on npmjs.com and unblocks corporate
465
+ license-compliance scanners (Snyk, Black Duck).
466
+
467
+ - [#46](https://github.com/viuteam/emporix-sdk/pull/46) [`11ca224`](https://github.com/viuteam/emporix-sdk/commit/11ca22430e376814819faec0f9946a234ef0e9bd) Thanks [@ndyn](https://github.com/ndyn)! - Pre-1.0 publish metadata polish:
468
+ - **`@viu/emporix-sdk-react`**: tighten the `@tanstack/react-query` peer
469
+ range from `^5.0.0` to `^5.51.0`. This matches the version the package
470
+ is developed and tested against. The previous range claimed support
471
+ for v5.0–v5.50 that was never exercised in CI; tightening avoids a
472
+ silent runtime mismatch for consumers who happen to be on those older
473
+ patch versions.
474
+ - **Both packages**: replace the bare-string `author: "viuteam"` with an
475
+ `author` object — `{ "name": "viu", "url": "https://github.com/viuteam" }`
476
+ — so the npm package page shows "viu" (our display name) and links
477
+ back to the GitHub org page (`viuteam`, the actual org slug).
478
+ - **`LICENSE` (root and per-package)**: the MIT copyright holder is now
479
+ `VIU AG` (the legal entity) instead of the GitHub org slug `viuteam`,
480
+ so license-compliance scanners attribute the package correctly.
481
+
482
+ - [#45](https://github.com/viuteam/emporix-sdk/pull/45) [`1bf87ce`](https://github.com/viuteam/emporix-sdk/commit/1bf87cec82a04f816200351881a6c77eabc4ed5f) Thanks [@amnael1](https://github.com/amnael1)! - Internal redundancy cleanup. All changes are non-breaking — public API
483
+ unchanged, all 151 React tests stay green.
484
+
485
+ **Storage**
486
+ - Extract `createListenerSet<T>()` helper used by all three backends'
487
+ `subscribeAll` — single try/catch wrapper instead of three copies.
488
+ - Extract `parseAnonymousSession()` helper for the JSON-parse-with-fallback
489
+ shared by localStorage and cookie backends.
490
+
491
+ **Hooks**
492
+ - `emporixKey(resource, args, ctx)` helper centralizes the
493
+ `["emporix", resource, …args, { tenant, authKind, siteCode? }]` cache
494
+ key shape used by 15+ Read hooks.
495
+ - `useEmporixInfinite()` helper centralizes the `initialPageParam: 1` +
496
+ `getNextPageParam` cursor logic shared by 6 infinite-scroll hooks
497
+ (products, categories, segments).
498
+
499
+ **Auth**
500
+ - `useCheckout` now uses the central `useReadAuth` hook instead of a
501
+ local `checkoutCtx` helper.
502
+ - `usePaymentModes` cache key gains a stable `authKind: "customer"`
503
+ component for consistency with other hooks.
504
+
505
+ **Customer session**
506
+ - `useCustomerSession` bundles the three separate `useState` calls
507
+ (token / refreshToken / saasToken) into a single `SessionState` object.
508
+ Login / logout / refresh / SSO flows now flip the session atomically
509
+ via one `setSession(...)` call instead of three. Same public API,
510
+ same behaviour — only an internal state-shape consolidation.
511
+
512
+ - [#27](https://github.com/viuteam/emporix-sdk/pull/27) [`ffb4b07`](https://github.com/viuteam/emporix-sdk/commit/ffb4b07db5186c70783fc6cbf60c6d586ed36eab) Thanks [@amnael1](https://github.com/amnael1)! - Refactor `hooks/queries.ts` into domain-aligned files (`use-products.ts`, `use-categories.ts`, `use-cart.ts`) matching the rest of the package. The shared `useReadAuth` helper now lives in `hooks/internal/use-read-auth.ts`. `use-cart-mutations.ts` is consolidated into `use-cart.ts`, which now holds every cart hook (read + mutations + create).
513
+
514
+ **Fix:** `useCategoriesInfinite` is now re-exported from the package root. It was defined but not exported in the prior release.
515
+
516
+ No public hook name, behavior, or query-key changed. Consumer imports from `@viu/emporix-sdk-react` continue to work.
517
+
518
+ - Updated dependencies [[`5c51a58`](https://github.com/viuteam/emporix-sdk/commit/5c51a58313c63cb7a9e34a4c5e6dc1da2017a827), [`bda4bd8`](https://github.com/viuteam/emporix-sdk/commit/bda4bd8b5b02e2b397f3a0751a45ac204b8572a0), [`765c54e`](https://github.com/viuteam/emporix-sdk/commit/765c54e8fd61e33cb0d4cc241415e9c56f45c729), [`f18e55c`](https://github.com/viuteam/emporix-sdk/commit/f18e55ceec9784e5aad6e95604e016c5858f9bdc), [`f312f22`](https://github.com/viuteam/emporix-sdk/commit/f312f228f17686476ce3458436758bd05af63fce), [`e10854f`](https://github.com/viuteam/emporix-sdk/commit/e10854fc9ef11fec74f24e65dedbe11c3ca09d22), [`959c6cc`](https://github.com/viuteam/emporix-sdk/commit/959c6cc3d0a4a37870cb72d5573b6fde9b0faa65), [`d52bcdc`](https://github.com/viuteam/emporix-sdk/commit/d52bcdc79433daaf143586264a409cad57e404a1), [`18e34a0`](https://github.com/viuteam/emporix-sdk/commit/18e34a03cbf4fbfe15a7e4995228bb5268b0e2ee), [`2f823b8`](https://github.com/viuteam/emporix-sdk/commit/2f823b8eb72eca17863757c3f6ccbf3e76442ee3), [`5770532`](https://github.com/viuteam/emporix-sdk/commit/57705327b4d58b1ac410ee958f85ae858a6c862d), [`7da7b21`](https://github.com/viuteam/emporix-sdk/commit/7da7b217912782ba5d9b3f1e959d78d70c32c4ba), [`4fc01ef`](https://github.com/viuteam/emporix-sdk/commit/4fc01ef737c9397407937ee9ca8098a781ac075e), [`5f6cb4a`](https://github.com/viuteam/emporix-sdk/commit/5f6cb4ad207f4a1c8562d1da1713255762b9c436), [`40f8e65`](https://github.com/viuteam/emporix-sdk/commit/40f8e65177699685c1114714f5b3f080cfab89f2), [`4cdfa41`](https://github.com/viuteam/emporix-sdk/commit/4cdfa411ffb48b79510b0e98faa9ddf6f8c0600c), [`4d87f11`](https://github.com/viuteam/emporix-sdk/commit/4d87f11a022996a49dad04af1404394cdd60804f), [`693c58c`](https://github.com/viuteam/emporix-sdk/commit/693c58c5d148eeef746aef18a8f5dada766d7041), [`59b78a8`](https://github.com/viuteam/emporix-sdk/commit/59b78a87d1dd56568e068c0a7738223714cb086b), [`877c2ab`](https://github.com/viuteam/emporix-sdk/commit/877c2abf791a6d67d438849cd800d5704ec486cb), [`380796a`](https://github.com/viuteam/emporix-sdk/commit/380796a53d9543b379b21eb414e3ebc5586e55f8), [`141521c`](https://github.com/viuteam/emporix-sdk/commit/141521c91f88171006067255294a45b9fdc01a43), [`8d22fb8`](https://github.com/viuteam/emporix-sdk/commit/8d22fb8d4cdf5e2ddeba7273ffe4b41a1630d463), [`4157818`](https://github.com/viuteam/emporix-sdk/commit/4157818c27b32ff32a1a41235bc7920137402f88), [`2014f71`](https://github.com/viuteam/emporix-sdk/commit/2014f710ee363f35aea1d8af0e85bce69a5bc40a), [`d0cc756`](https://github.com/viuteam/emporix-sdk/commit/d0cc75603db779447c4ffe84aa349c8e59db13df), [`dfabb02`](https://github.com/viuteam/emporix-sdk/commit/dfabb02882ca65e2a32e4a52082c0b14dc71faa8), [`11ca224`](https://github.com/viuteam/emporix-sdk/commit/11ca22430e376814819faec0f9946a234ef0e9bd), [`13f23bd`](https://github.com/viuteam/emporix-sdk/commit/13f23bd9016903c59ca1bfa0b340ff096587131e), [`5f330d5`](https://github.com/viuteam/emporix-sdk/commit/5f330d521119e36ca95b8cfc3bed049572fd1c03), [`e2f74db`](https://github.com/viuteam/emporix-sdk/commit/e2f74db04edb1d4250add83a4b8208bc33e326c7), [`027b816`](https://github.com/viuteam/emporix-sdk/commit/027b816c171e81263b99b791916e33816f148839), [`4cda829`](https://github.com/viuteam/emporix-sdk/commit/4cda82963d307fa12b1e1e628be31879f464ed9d), [`277ae71`](https://github.com/viuteam/emporix-sdk/commit/277ae7195ab9eecb87677fff4e8fcd16ea3b920b)]:
519
+ - @viu/emporix-sdk@1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 VIU AG
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # @viu/emporix-sdk-react
2
+
3
+ React bindings for `@viu/emporix-sdk`, built on
4
+ [`@tanstack/react-query`](https://tanstack.com/query) v5. Supports React 18 & 19.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ pnpm add @viu/emporix-sdk-react @viu/emporix-sdk @tanstack/react-query react
10
+ ```
11
+
12
+ `@viu/emporix-sdk`, `@tanstack/react-query` and `react` are peer dependencies.
13
+
14
+ ## Provider
15
+
16
+ ```tsx
17
+ import { EmporixClient } from "@viu/emporix-sdk";
18
+ import { EmporixProvider, createLocalStorageStorage } from "@viu/emporix-sdk-react";
19
+
20
+ const client = new EmporixClient({
21
+ tenant: "mytenant",
22
+ credentials: { backend: { clientId: "", secret: "" }, storefront: { clientId: "x" } },
23
+ });
24
+
25
+ <EmporixProvider client={client} storage={createLocalStorageStorage()}>
26
+ <App />
27
+ </EmporixProvider>;
28
+ ```
29
+
30
+ Create `EmporixClient` **once** (per app, or once per server for SSR) — never
31
+ per request/render.
32
+
33
+ ## Hooks
34
+
35
+ | Hook | Purpose |
36
+ | --- | --- |
37
+ | `useCustomerSession()` | `customerToken`, `customer`, `isAuthenticated`, `login`, `signup`, `logout`, `refresh` |
38
+ | `useProduct` / `useProducts` / `useProductsInfinite` / `useProductByCode` / `useProductSearch` | product reads |
39
+ | `useCategory` / `useCategories` / `useCategoryTree` / `useProductsInCategory(Infinite)` | category reads |
40
+ | `useCart(cartId?)` / `useActiveCart(opts?)` / `useCreateCart()` | cart read + bootstrap |
41
+ | `useCartMutations(cartId?)` | add/update/remove/clear/coupons/addresses — optimistic + rollback |
42
+ | `useCheckout()` / `usePaymentModes()` | checkout flow + payment-mode list |
43
+ | `useMatchPrices()` / `useProductMedia()` | price + media reads |
44
+ | `useMySegments` / `useMySegmentItems` / `useMySegment{Products,Categories}(Infinite)` / `useMySegmentCategoryTree` | customer-segment reads |
45
+ | `useUpdateCustomer` / `useChangePassword` / `usePasswordReset` | account management |
46
+ | `useCustomerAddresses` / `useAddressMutations` | address CRUD |
47
+ | `useSites` / `useDefaultSite` / `useSiteContext` | multi-site context |
48
+ | `useActiveCompany` / `useCompanySwitcher` | active legal entity (B2B) |
49
+ | `useMyCompanies` / `useCompany` / `useCompanyContacts` / `useCompanyLocations` / `useCompanyGroups` | B2B reads |
50
+ | `useCreateCompany` / `useUpdateCompany` / `useDeleteCompany` | B2B admin mutations |
51
+ | `useAssignContact` / `useUpdateContactAssignment` / `useUnassignContact` | B2B contact-assignment mutations |
52
+ | `useCreateLocation` / `useUpdateLocation` / `useDeleteLocation` | B2B location mutations |
53
+
54
+ Query keys are namespaced `["emporix", resource, ...args, meta]` where `meta`
55
+ holds the cache discriminators — at minimum `{ tenant, authKind }`, plus
56
+ `siteCode` for site-aware hooks and `legalEntityId` for B2B-aware hooks (cart,
57
+ checkout, addresses, etc. invalidate automatically on company switch).
58
+ Every query hook accepts `{ auth }` to override the token kind for that
59
+ call (default: `customer` if a token is stored, else `anonymous`).
60
+
61
+ ## Storage adapters
62
+
63
+ `createMemoryStorage` (default, SSR-safe), `createLocalStorageStorage`,
64
+ `createCookieStorage`. Trade-offs and CSRF notes in
65
+ [`../../docs/react.md`](../../docs/react.md).
66
+
67
+ ## Errors & SSR
68
+
69
+ `<EmporixErrorBoundary>` and `useEmporixErrorHandler` for error coordination;
70
+ `prefetchProduct` / `prefetchCart` for server-side hydration. See
71
+ [`../../docs/react.md`](../../docs/react.md).
72
+
73
+ ## Subpath exports
74
+
75
+ `.`, `./provider`, `./hooks`, `./storage`, `./ssr`.
76
+
77
+ ## Authors
78
+
79
+ - **Dominic Fritschi** — _Maintainer_ — [VIU](https://www.viu.ch)
80
+ - **Andreas Nebiker** — _Contributor_ — [VIU](https://www.viu.ch)
81
+ - The **Team at VIU** — _Contributors_ — [VIU](https://www.viu.ch)
82
+
83
+ ## License
84
+
85
+ This project is licensed under the MIT License — see the [LICENSE](./LICENSE) file for details.