# @viu/emporix-sdk-react

## 2.33.1

### Patch Changes

- [#257](https://github.com/viuteam/emporix-sdk/pull/257) [`a333cb2`](https://github.com/viuteam/emporix-sdk/commit/a333cb2550d23a6431d12beb15caba3092158722) Thanks [@amnael1](https://github.com/amnael1)! - docs: link the changelog from every package README

  npmjs.com renders only a package's README — the registry has no changelog field
  at all, so there is nothing for the website to show. Each README now carries a
  Changelog section pointing at `CHANGELOG.md` on GitHub, at the copy inside the
  published tarball (served by unpkg), and at the per-version Releases.

  Published as a patch on purpose: npmjs.com shows the README of the _published_
  version, so a docs-only change that is never released never reaches the page.

  `@viu/emporix-mixins` also gains `README.md`, `CHANGELOG.md` and `LICENSE` in its
  `files` array. It listed only `dist`, and while npm ships a README and a LICENSE
  regardless, it does **not** ship a CHANGELOG — verified against the published
  tarball, which had no `CHANGELOG.md`. The link the new section adds would have
  been dead.

## 2.33.0

### Minor Changes

- [#255](https://github.com/viuteam/emporix-sdk/pull/255) [`0bc0307`](https://github.com/viuteam/emporix-sdk/commit/0bc0307c26766b1419e31c2cba832b00165d5f13) Thanks [@amnael1](https://github.com/amnael1)! - feat(react): accept a host-owned customer token

  `customerSession="external"` tells `EmporixProvider` that the customer token was
  handed in by a host application — an Emporix Managed Dashboard module, an
  embedded admin UI. The SDK then never bootstraps a company context from it,
  never attempts a refresh, reports a 401 through `onCustomerSessionExpired`, and
  treats a changed `initialCustomerToken` as authoritative.

  Also fixes a latent bug in the default `"owned"` mode: storage identity no
  longer depends on `initialCustomerToken`, so delivering a new token stops
  silently discarding `cartId`, `siteCode`, `language` and
  `activeLegalEntityId`.

  See the Managed Dashboard section in `packages/react/README.md` and the new
  `examples/md-module`.

## 2.32.0

### Minor Changes

- [#254](https://github.com/viuteam/emporix-sdk/pull/254) [`47570cd`](https://github.com/viuteam/emporix-sdk/commit/47570cdf2a5ab10bd17c385610367daef7aa73ee) Thanks [@amnael1](https://github.com/amnael1)! - feat(sdk): opt into absolute match counts on the list facades

  Pass `totalCount: true` to any list facade to get `X-Total-Count` back as
  `page.totalCount`, and an exact `hasNextPage` instead of the page-size guess.
  Off by default: Emporix computes the count with a second query, so turning it
  on for every list would be a silent cost on every storefront.

  From React the four single-page list hooks accept the same flag — `useProducts`,
  `useProductSearch`, `useCategories`, `useCategorySearch`. It is part of the query
  key, so a totals request is never served a cached page without them. The
  `*Infinite` hooks do not offer it: `hasNextPage` already terminates them.

  Three facades deliberately keep the guess — `categories.productsIn`,
  `segments.listMyProducts` and `segments.listMyCategories`. They page over an
  assignments list and hydrate the hits in a second call, so a total there would
  count assignments rather than the items returned.

## 2.31.0

### Patch Changes

- [#216](https://github.com/viuteam/emporix-sdk/pull/216) [`ca45e50`](https://github.com/viuteam/emporix-sdk/commit/ca45e50bf79ab7d72c51c0687f383bffeecfcf6d) Thanks [@amnael1](https://github.com/amnael1)! - `@viu/emporix-sdk-next` no longer depends on `@viu/emporix-sdk-react`. The peer
  dependency is gone, and the built package contains zero imports of it.

  A server-first Next app therefore installs **three** packages instead of four,
  and with them no React and no `@tanstack/react-query` — both are peers of the
  React bindings, and neither has anything to do with a mode where the browser makes
  no Emporix calls at all.

  What moved to `@viu/emporix-sdk` (`core/session-storage.ts`), completing the step
  that started with `STORAGE_KEYS`:

  | Export                                                        | Was                                                     |
  | ------------------------------------------------------------- | ------------------------------------------------------- |
  | `EmporixStorage`, `TokenStorage`, `PersistedAnonymousSession` | the session-persistence contract                        |
  | `parseAnonymousSession`                                       | parses a stored anonymous session                       |
  | `createCookieBackedStorage`, `CookieIo`                       | the whole key-to-accessor mapping                       |
  | `createServerStorage`, `ServerCookieJar`                      | an `EmporixStorage` over any cookie jar                 |
  | `serverAuth`                                                  | customer context when a token is stored, else anonymous |

  None of it imports React — `createServerStorage` fits Next, Remix, SvelteKit,
  Nitro or a plain Node handler, and it was only ever in the React package because
  that is where the browser backends live. Those stay: `createMemoryStorage`,
  `createLocalStorage`, `createSessionStorage`, `createCookieStorage` and the
  `subscribeAll` listener set are genuinely browser concerns.

  **Nothing to change in your code.** `@viu/emporix-sdk-react` re-exports every
  moved name from `./storage` and `/ssr`, with one definition only. One type is now
  derived rather than re-declared: `PersistedAnonymousSession` is
  `Pick<StoredAnonymousSession, "refreshToken" | "sessionId">`, which is what it
  always was in practice — the browser adapters deliberately persist only those two
  fields, while a server store may also keep the access token.

- [#215](https://github.com/viuteam/emporix-sdk/pull/215) [`e9d019d`](https://github.com/viuteam/emporix-sdk/commit/e9d019d4a5bf3238311dab11e5fb856ce5689004) Thanks [@amnael1](https://github.com/amnael1)! - Move the eight session keys into the core SDK: `STORAGE_KEYS` and
  `EmporixStorageKey` are now exported from `@viu/emporix-sdk`.

  They were never a React concern. The same eight strings are cookie names in a
  Next `proxy.ts`, Web Storage keys in a browser adapter, and record fields in a
  server-side session store — but they lived in `@viu/emporix-sdk-react`, which is
  why `@viu/emporix-sdk-next` depended on the React bindings to name a cookie. Six
  of the seven files in that package imported nothing else from it.

  Nothing to change in your code. `@viu/emporix-sdk-react` re-exports both from
  `./storage` and `/ssr`, and `@viu/emporix-sdk-next` still re-exports
  `STORAGE_KEYS` from `/session`. There is exactly one definition, and a test
  asserts object identity across all three paths — a copy would be the one drift
  that silently breaks a session by writing a cookie under one name and reading it
  under another.

  Measured on the built output: `@viu/emporix-sdk-next` reached for
  `@viu/emporix-sdk-react` in seven places before, and now does so in one —
  `server-session.ts`, for `createServerStorage`, `serverAuth` and the
  `EmporixStorage` type. Removing that last one (and the peer dependency with it) is
  a follow-up, because it touches a public signature.

## 2.30.1

### Patch Changes

- [#213](https://github.com/viuteam/emporix-sdk/pull/213) [`1ea2b81`](https://github.com/viuteam/emporix-sdk/commit/1ea2b81c00b6df2e4a659cf629ed5393b42c9890) Thanks [@amnael1](https://github.com/amnael1)! - Fix the multi-device cart: a cart closed by a checkout elsewhere no longer leaves
  the other devices broken.

  Emporix allows a customer one open cart per site and placing an order closes it.
  The cart id is cached per device (`emporix.cartId` in React storage, in the
  session cookie or store in Next), so every other device where that customer is
  signed in kept calling a closed cart and got `404`. It never recovered:
  `useActiveCart({ create: true })` only bootstraps when the id is `null`, and a
  stale id is not `null`. In Next it was worse — the read happens in a Server
  Component, so the `404` reached the error boundary, and `addToCart` failed
  forever because it found a non-null id and never created a new cart.

  **React.** `useCart` and `useCartMutations` treat a `404` on the **stored** id as
  «this cart is gone»: they clear `storage.cartId` and drop the `cart-bootstrap`
  cache, so the next render bootstraps a fresh cart. Silent by design — the cart no
  longer exists server-side, so the shopper sees an empty bag rather than an error.
  An explicitly passed id (`useCart("other-cart")`) never touches storage, and only
  a `404` counts: a `403` or `5xx` means «not now», not «gone».

  The emporix-scoped `retry` default no longer retries a `404` at all. It is an
  answer, not a failure, and Emporix bills the repeat — a stale cart id used to pay
  for the same answer twice on every mount.

  **Next.** `withEmporixSessionMutable` now flushes the session handle even when the
  callback throws. In store mode the handle buffers in memory and wrote once at the
  end, so a failed Server Action discarded whatever it had already set — including a
  rotated anonymous refresh token. Emporix rotates that token on every refresh, so
  the session was left pointing at one the tenant had already invalidated, the next
  request fell back to a fresh login with a new `sessionId`, and the guest lost
  their cart. Cookie mode always wrote through and was never affected. A store
  failure during the flush is swallowed rather than replacing the caller's error.

  The dead-cart-id recovery itself is documented rather than automated on the Next
  side, because a Server Component **cannot** heal it: a read-only handle does not
  write. The package README and `examples/next-server-first` show the rule — render
  the empty state on a read, clear and re-create inside the next write.

## 2.29.0

### Patch Changes

- [#208](https://github.com/viuteam/emporix-sdk/pull/208) [`b58ca35`](https://github.com/viuteam/emporix-sdk/commit/b58ca35230b08db1c42f1e58874f8ea2d82684da) Thanks [@amnael1](https://github.com/amnael1)! - Document the new import service in both package READMEs, which ship in the npm
  tarballs.

  `@viu/emporix-sdk-next` gains a Route Handler that re-emits
  `client.imports.streamRun(runId)` as Server-Sent Events to the browser, including
  the abort-on-disconnect line and why this is Node runtime only. No package code
  changed: `getEmporixServiceClient` needs no per-service registration, and cache
  tags have nothing to add for a service whose reads are not cacheable.

  `@viu/emporix-sdk-react` states why admin-only services have no hooks, with the
  import service as the clearest case — every operation needs client-credentials
  with the `importtool.import_trigger` scope, and the provider is configured with a
  public storefront client id.

## 2.28.2

### Patch Changes

- [#206](https://github.com/viuteam/emporix-sdk/pull/206) [`2ec8094`](https://github.com/viuteam/emporix-sdk/commit/2ec8094835c195b13f1139a37cd70cbddd6c9a85) Thanks [@amnael1](https://github.com/amnael1)! - `categories.tree()` and `useCategoryTree()` now return `CategoryNode[]` instead of
  `Category[]`.

  The declared type was factually wrong. `/category-trees` answers with the
  generated `CategoryTree` shape — measured against a live tenant on 2026-08-04,
  every node carried `subcategories` or nothing, and none carried `parentId`:

  |                                 | `parentId` | `subcategories` |
  | ------------------------------- | ---------- | --------------- |
  | `Category`                      | yes        | no              |
  | `CategoryTree` / `CategoryNode` | no         | yes             |

  So a tree node's children were invisible to consumers without a cast, while
  `node.parentId` compiled and was always `undefined`. Type-only change, no runtime
  difference — but code that read `parentId` off a tree node will now fail to
  compile, which is the point.

  `useCategoryTree` carried the same wrong type through to React consumers and is
  corrected with it. `packages/react` typechecking is what surfaced it.

  The doc comment on `tree()` also pointed readers to `subcategories()` for drilling
  down. Both methods read `/categories/{id}/assignments` and differ only in the
  `ref.type` they keep — `subcategories()` keeps `"CATEGORY"`, `productsIn()` keeps
  `"PRODUCT"`. On the tenant this was measured on, the `"CATEGORY"` filter answered
  empty for every category because the hierarchy lives in the trees instead, and
  `childCategories()` (which hits `/categories/{id}/subcategories`) answers **404**
  for a tree root. The children are inline in `subcategories`, and the comment now
  says so.

## 2.27.0

### Minor Changes

- [#193](https://github.com/viuteam/emporix-sdk/pull/193) [`0a053e9`](https://github.com/viuteam/emporix-sdk/commit/0a053e90a6588868ca0925abf8a1b8b110563a39) Thanks [@amnael1](https://github.com/amnael1)! - `STORAGE_KEYS` is now exported from `@viu/emporix-sdk-react/ssr`, and is the
  single source of the eight persisted session keys.

  `emporix.customerToken`, `emporix.cartId`, `emporix.anonymousSession`,
  `emporix.siteCode`, `emporix.language`, `emporix.activeLegalEntityId`,
  `emporix.refreshToken`, `emporix.saasToken` — cookie names in the cookie
  backends, Web Storage keys in the localStorage/sessionStorage ones. Server code
  that has to name a key without going through an `EmporixStorage` can read them
  instead of duplicating the literal.

  Internally the eight strings lived in three places: the `EmporixStorageKey`
  union, the cookie backends and the Web Storage backends. They now come from one
  object typed `satisfies Record<EmporixStorageKey, string>`, so a ninth session
  key cannot be half-added — a union member without an entry, or an entry without
  a union member, is a compile error.

  Exported from `./ssr` rather than `./storage` on purpose: `./storage` carries
  the `"use client"` banner and must not be imported from a Next `proxy.ts` or a
  Route Handler. No behaviour changed, and no key name changed.

## 2.26.0

### Minor Changes

- [#189](https://github.com/viuteam/emporix-sdk/pull/189) [`248350b`](https://github.com/viuteam/emporix-sdk/commit/248350b96dfa88b63c7f8122ce211cd34d471f19) Thanks [@amnael1](https://github.com/amnael1)! - Query-key normalization for the last two hand-keyed read hooks.

  **`useAvailability` / `useAvailabilities` — cache-invalidating.** Both now build
  their key through `emporixKey` like every other read hook:

  ```
  before: ["emporix", "availability", { tenant, productId, siteCode, anon, defaultAvailableOnNotFound }]
  after:  ["emporix", "availability", productId, siteCode, defaultAvailableOnNotFound, { tenant, authKind }]
  ```

  Existing cached availability entries are orphaned and refetch once. Auth
  behaviour is unchanged: the hooks still read anonymously unless you pass
  `customerToken`, and a token in storage does not change that. They are now
  prefetchable via `prefetchEmporix` — see the descriptor table in `docs/react.md`.

  **`prefetchEmporix` gains `mode` — not cache-invalidating.** `"customer"` keys
  `authKind: "customer"` regardless of the context kind, matching customer-gated
  hooks like `useOrder` and `useMyOrders`. `prefetchOrder` now sets it, which fixes
  prefetching with an `auth.raw(jwt)` context — that previously keyed `"raw"` and
  the hook never found the entry. With `auth.customer(token)` the key is unchanged,
  so nothing is orphaned by this half.

## 2.25.0

### Minor Changes

- [#182](https://github.com/viuteam/emporix-sdk/pull/182) [`534df9c`](https://github.com/viuteam/emporix-sdk/commit/534df9c13d2ee359b1b7aaceaeb93c70b6eb5dc1) Thanks [@amnael1](https://github.com/amnael1)! - Server-runtime support on the `./ssr` entry.
  - `createServerStorage(jar)` — an `EmporixStorage` over a caller-supplied cookie
    jar, for RSC / Server Actions / Route Handlers / loaders. Synchronous, so
    `await cookies()` stays with the caller. Read-only unless a `set` accessor is
    given (Next forbids cookie writes during a Server Component render); writes
    then no-op and warn once per key.
  - `serverAuth(storage)` — resolves the same `AuthContext` the client hooks
    resolve (customer if a token is stored, else anonymous). `authKind` is part of
    every query key, so this prevents silent cache misses.
  - `prefetchEmporix(qc, opts)` — server-side prefetch for any read hook whose key
    is built with `emporixKey`, replacing the need for a helper per resource.
    `prefetchProduct` / `prefetchCart` / `prefetchOrder` keep their signatures and
    are now wrappers.

  No new dependency and no `next` import — the jar shape works for any server
  framework. `useAvailability` / `useAvailabilities` are not prefetchable; their
  keys predate `emporixKey`. See `docs/react.md`.

## 2.24.0

### Minor Changes

- [#180](https://github.com/viuteam/emporix-sdk/pull/180) [`a9b0ca8`](https://github.com/viuteam/emporix-sdk/commit/a9b0ca88e8293300da769986bfbbdfd4141c12fd) Thanks [@amnael1](https://github.com/amnael1)! - fix(react): export the remaining hooks-barrel symbols from the package root

  Twelve symbols were reachable only through the `@viu/emporix-sdk-react/hooks`
  subpath because the package-root barrel omitted them, so the top-level import
  the README documents did not resolve:
  - Hooks: `useApprovals`, `useApproval`, `useCreateApproval`, `useUpdateApproval`,
    `useCategorySearch`
  - Types: `UseUpdateApprovalVars`, `UseOrderOptions`, `UseCancelOrderVars`,
    `UseOrderTransitionVars`, `UseReorderVars`, `UseReorderResult`,
    `UseUpdateSalesOrderVars`

  All twelve are now re-exported from the root like every other hook, so the root
  barrel and the `./hooks` subpath expose the same surface again.

## 2.22.0

### Minor Changes

- [#160](https://github.com/viuteam/emporix-sdk/pull/160) [`7a63559`](https://github.com/viuteam/emporix-sdk/commit/7a635592f7233dc35f35502538d6695d428897cf) Thanks [@amnael1](https://github.com/amnael1)! - Add storefront-facing facade methods and matching React hooks. Additive and
  backward-compatible.
  - **Cart** — `carts.validate`, `listItems`, `refresh`, `changeSite`,
    `changeCurrency`, `updateItemsBatch` (state-changing ops re-fetch and return
    the updated cart). Hooks: `useCartValidation`, `useCartItems`, and
    `refresh`/`changeSite`/`changeCurrency`/`updateItemsBatch` on the
    `useCartMutations` bundle.
  - **Customer** — double opt-in (`confirmSignup`/`resendActivation`),
    login-email change (`changeEmail`/`confirmEmailChange`), and address
    `get`/`addTags`/`removeTags`. Hooks: `useConfirmSignup`,
    `useResendActivation`, `useChangeEmail`, `useConfirmEmailChange`,
    `useCustomerAddress`, `useAddAddressTags`, `useRemoveAddressTags`.
  - **Category** — `categories.parents`, `childCategories` (dedicated
    `/subcategories`), `getTree` (single tree by id). Hooks:
    `useCategoryParents`, `useChildCategories`, `useCategoryTreeById`.
  - **Payment** — `payments.getMode`, `initialize` (frontend, no scope). Hooks:
    `usePaymentMode`, `useInitializePayment`.
  - **Session context** — `sessionContext.addAttribute`/`removeAttribute`. Hooks:
    `useAddSessionAttribute`, `useRemoveSessionAttribute`.

## 2.19.1

### Patch Changes

- [#141](https://github.com/viuteam/emporix-sdk/pull/141) [`8fb0f52`](https://github.com/viuteam/emporix-sdk/commit/8fb0f529b439db9fe4199b861952237b3e7ec72d) Thanks [@amnael1](https://github.com/amnael1)! - Internal refactor: split the oversized `EmporixProvider` (557 LOC) and
  `CompanyContextProvider` (248 LOC) into focused internal hooks and co-located
  type/site-context modules. `EmporixProvider` is now a composition facade
  (`useEmporixQueryDefaults`, `useProviderWiring`, `useTelemetrySource`,
  `useCustomerTokenRefresher`) and `SiteContextProvider` lives in its own module
  with a de-duplicated switch tail. No change to the public API, rendered output,
  effect timing, or types — all 298 unit tests pass unchanged.

## 2.18.0

### Minor Changes

- [#137](https://github.com/viuteam/emporix-sdk/pull/137) [`9ef7c51`](https://github.com/viuteam/emporix-sdk/commit/9ef7c51d933d9b78be1880ce19d6f7312ffcd20e) Thanks [@amnael1](https://github.com/amnael1)! - Add a type-safe mixin filter builder. `@viu/emporix-mixins` now exports
  `mixinQuery`/`and`/`or`/`raw` to build Emporix `q` filters from generated
  `MixinDescriptor`s, with attribute names and value types checked at compile
  time and the entity carried through `MixinDescriptor<T, E>` / `MixinFilter<E>`.
  Localized attributes are supported via a `{ lang, ... }` operator.
  `products.search` and `useProductSearch` accept a built filter (or a raw
  string); a new `resolveQuery` normalizer enforces the `compoundLogicalQuery`
  (OR) capability gate per service.

- [#137](https://github.com/viuteam/emporix-sdk/pull/137) [`de6e8b8`](https://github.com/viuteam/emporix-sdk/commit/de6e8b8727c5150f9fe3df77820dd13b6cf37e24) Thanks [@amnael1](https://github.com/amnael1)! - Wire the mixin filter builder into more services. `categories.search`,
  `orders.listMine({ q })`, `customerAdmin.searchCustomers({ q })` and
  `vendor.searchVendors({ q })` now accept a built mixin filter (or a raw `q`
  string), each entity-gated via `QueryFor<E>` and routed through `resolveQuery`
  (all are non-compound, so `or()` filters are rejected). New React hooks:
  `useCategorySearch` and a `q` option on `useMyOrders`.

## 2.17.0

### Minor Changes

- [#135](https://github.com/viuteam/emporix-sdk/pull/135) [`6fbf209`](https://github.com/viuteam/emporix-sdk/commit/6fbf20917d6d0fedc67e6ef290a5910f14ebe317) Thanks [@amnael1](https://github.com/amnael1)! - Add `useShippingZones` — lists the tenant's configured shipping zones with their
  active methods and fees in a single call (`expand=methods,fees`,
  `activeMethods=true`). Auto-detects auth (customer token if stored, otherwise
  anonymous), so storefronts can show delivery options to guests and customers
  alike. The site defaults to the provider's active `siteCode`.

## 2.16.0

### Minor Changes

- [#132](https://github.com/viuteam/emporix-sdk/pull/132) [`618ee65`](https://github.com/viuteam/emporix-sdk/commit/618ee65892bacae551c8790c2a13c302864e7d33) Thanks [@amnael1](https://github.com/amnael1)! - Add `createSessionStorage` — a per-tab `sessionStorage`-backed storage adapter
  (survives a page reload, cleared when the tab closes, not shared across tabs).
  Adds `createLocalStorage` as the preferred name for `createLocalStorageStorage`,
  which is now deprecated but still exported. Internally the `localStorage` and
  `sessionStorage` adapters share one `fromWebStorage` helper.

### Patch Changes

- [#134](https://github.com/viuteam/emporix-sdk/pull/134) [`7c1c97d`](https://github.com/viuteam/emporix-sdk/commit/7c1c97d7485acd92fb0931920d73b1bec39b1b13) Thanks [@amnael1](https://github.com/amnael1)! - `usePaymentModes` now works for anonymous (guest) sessions, not only logged-in
  customers. It auto-detects auth (customer token if stored, otherwise anonymous)
  and the query is keyed by the resolved auth kind.

## 2.15.0

### Patch Changes

- [#131](https://github.com/viuteam/emporix-sdk/pull/131) [`4c2862c`](https://github.com/viuteam/emporix-sdk/commit/4c2862c2a3394b04bbca8dfa2abafb529023920a) Thanks [@amnael1](https://github.com/amnael1)! - fix multiple checkouts per session: `useCheckout().placeOrder`/`placeOrderFromQuote` now reset the cart on success — they clear `storage.cartId` and drop the `["emporix","cart-bootstrap"]` query cache (held with `staleTime: Infinity`). Previously a placed order closed its cart server-side, but the bootstrap cache still re-served that closed cart on the next `useActiveCart({ create: true })`, so the second checkout re-adopted the dead cart and failed (cart reads 404, `placeOrder` 401). The next checkout now bootstraps a fresh cart.

- [#131](https://github.com/viuteam/emporix-sdk/pull/131) [`bcb35c4`](https://github.com/viuteam/emporix-sdk/commit/bcb35c41397a1c90b23ab866bb6f111159f02fef) Thanks [@amnael1](https://github.com/amnael1)! - fix customer checkout after a page reload: the `saasToken` (checkout `saas-token` header) is now persisted by the storage adapters (`getSaasToken`/`setSaasToken`, key `emporix.saasToken`) and re-hydrated into the customer-session store on load — alongside the already-persisted `refreshToken`. Previously it lived in memory only, so a reload mid-session dropped it and customer checkout 401'd with `"Saas TOKEN is invalid"` (the refresh endpoint cannot re-mint it). The storage methods are optional, so custom adapters are unaffected; the bundled memory/localStorage/cookie adapters all persist it.

- [#128](https://github.com/viuteam/emporix-sdk/pull/128) [`2d8a6cb`](https://github.com/viuteam/emporix-sdk/commit/2d8a6cb715e004cad3ab1a0652b4c77e330eb810) Thanks [@amnael1](https://github.com/amnael1)! - internal refactor: the standard read hooks now share a single `useEmporixQuery` factory that encapsulates auth-context resolution, site discriminators, query-key assembly, and default options. No observable behavior or API change — query keys, `enabled` gates, and `staleTime` values are identical; the existing hook test suites pass unchanged. Hooks with a non-standard auth shape (`useCustomerOnlyCtx` throw-on-missing — approvals/returns; caller-supplied `authCtx` — sales-order) and all infinite/bespoke-key hooks are intentionally left as-is.

## 2.14.0

### Patch Changes

- [#127](https://github.com/viuteam/emporix-sdk/pull/127) [`93e3b76`](https://github.com/viuteam/emporix-sdk/commit/93e3b76131abe9f8cdc29e010b99ddc92e575e91) Thanks [@amnael1](https://github.com/amnael1)! - default the cookie storage adapter's `Secure` attribute to on for https origins. Token cookies no longer ride plain http in production by default; localhost/http dev is unaffected (protocol-sniffed). Pass `secure: false` explicitly only for non-https deployments.

- [#125](https://github.com/viuteam/emporix-sdk/pull/125) [`c5f7a7d`](https://github.com/viuteam/emporix-sdk/commit/c5f7a7d89bdd6ac44ff92719f732fbd5d95b55ee) Thanks [@amnael1](https://github.com/amnael1)! - fix logout to purge the entire `["emporix"]` query-cache namespace. Previously only the `customer` and `cart` keys were removed, so customer-scoped caches without a user discriminator (payment modes, order lists) survived logout and could be served to the next logged-in customer straight from cache.

- [#125](https://github.com/viuteam/emporix-sdk/pull/125) [`48fed7a`](https://github.com/viuteam/emporix-sdk/commit/48fed7afccb3b7146dabfa3e7d86e384b2171689) Thanks [@amnael1](https://github.com/amnael1)! - ship a `"use client"` directive in the built client entries (`.`, `./provider`, `./hooks`, `./storage`) so they load as Client Components under the Next.js App Router without every consumer having to add their own `"use client"` wrapper file. `./ssr` stays directive-free and remains importable from Server Components — in server code, import `prefetchProduct`/`prefetchCart`/`prefetchOrder` from `@viu/emporix-sdk-react/ssr`, not from the package root.

- [#127](https://github.com/viuteam/emporix-sdk/pull/127) [`1a5e1f3`](https://github.com/viuteam/emporix-sdk/commit/1a5e1f3fb6b048b9732066ba2c02dfc871e47f3b) Thanks [@amnael1](https://github.com/amnael1)! - make auth/cart state reads reactive: all render-time `storage.getCustomerToken()`/`getCartId()` reads now go through `useSyncExternalStore`-backed snapshots. Login/logout and cart-id writes immediately re-render dependent hooks — previously `enabled` gates (e.g. `usePaymentModes`, `useMyCompanies`, order hooks) stayed stale until an unrelated re-render, and sibling components could tear under concurrent rendering. Storage adapters without `subscribe`/`subscribeAll` behave as before (non-reactive).

- [#127](https://github.com/viuteam/emporix-sdk/pull/127) [`31a8183`](https://github.com/viuteam/emporix-sdk/commit/31a8183329d17fe5247f62c9c39a74beb7a1a45e) Thanks [@amnael1](https://github.com/amnael1)! - apply the provider's balanced query defaults (`staleTime: 30s`, no focus refetch, `retry: 1`) to the `["emporix"]` namespace of any QueryClient — including consumer-supplied ones, which previously ran SDK queries with React-Query factory defaults (focus-refetch storms + retry amplification against the live tenant). The provider only fills gaps: a consumer's explicit defaults win, whether set globally (`defaultOptions.queries`) or emporix-scoped (`setQueryDefaults(["emporix"], …)`), and per-hook options always win; host-app queries outside the namespace are untouched.

- [#127](https://github.com/viuteam/emporix-sdk/pull/127) [`124532f`](https://github.com/viuteam/emporix-sdk/commit/124532fef11b1a4aadf2f80e979005b036168e1c) Thanks [@amnael1](https://github.com/amnael1)! - fix the RSC/SSR prefetch pipeline and StrictMode safety: `prefetchProduct`/`prefetchCart`/`prefetchOrder` now build their query keys through the same `emporixKey` builder the hooks use (previously the keys never matched — `siteCode`/`language`/company discriminators were missing — so hydration was always a cache miss and the client refetched); new `siteCode`/`language`/`activeCompanyId` options mirror the client context. The provider's anonymous-store wiring and `initialCustomerToken` seed now re-run when the `client`/`storage` props change and no longer execute inside `useMemo`; the fallback QueryClient is held in state (a dropped memo cache could previously discard the whole query cache). CompanyContext bootstrap is cancellation-safe under StrictMode and company switches are serialized — the token-rotating refresh can no longer double-fire with the same refresh token.

## 2.13.1

### Patch Changes

- [#124](https://github.com/viuteam/emporix-sdk/pull/124) [`3b4c796`](https://github.com/viuteam/emporix-sdk/commit/3b4c79640f2030edf7f2609a30d1546cc5f3cd0f) Thanks [@amnael1](https://github.com/amnael1)! - Clear the anonymous (guest) session from storage on customer login. Once a customer token is set the anonymous session is dormant — `useReadAuth` always prefers the customer token — but it lingered in storage (`emporix.anonymousSession`) for the whole authenticated session. `useCustomerSession.login` (and the shared `applySession` path used by `socialLogin` / `exchangeToken`) now call `storage.setAnonymousSession(null)`, so only the customer session remains after login.

## 2.13.0

### Minor Changes

- [#119](https://github.com/viuteam/emporix-sdk/pull/119) [`5580967`](https://github.com/viuteam/emporix-sdk/commit/5580967c83c8177b766e903cacdcbadc7a3c70a7) Thanks [@amnael1](https://github.com/amnael1)! - feat(react): add useActiveSite hook

  `useActiveSite()` returns the active site's DTO (the one matching
  `useSiteContext().siteCode`), derived from the shared `useSites()` query — so
  consumers no longer re-implement `sites.find(s => s.code === siteCode)`.

## 2.12.0

### Minor Changes

- [#116](https://github.com/viuteam/emporix-sdk/pull/116) [`5411502`](https://github.com/viuteam/emporix-sdk/commit/5411502fdde0737ad457812e28a86c505f938282) Thanks [@amnael1](https://github.com/amnael1)! - Add a runtime language switch. `client.setStorefrontContext({ language })` now sets an `Accept-Language` header on every read. React's `useSiteContext()` exposes `language` + `setLanguage(lang)` (modeled on `setCurrency`), persists the choice via `EmporixStorage` (`emporix.language`), mirrors it into the server session context, and keys localized reads (products, categories, segments, cart, shopping lists, orders) by language so the cache never serves stale-language text. A new `initialLanguage` provider prop seeds the active language.

## 2.11.0

### Minor Changes

- [#114](https://github.com/viuteam/emporix-sdk/pull/114) [`ac2b2c8`](https://github.com/viuteam/emporix-sdk/commit/ac2b2c890521da017b3ef44ff15bdf6b16d69bb9) Thanks [@amnael1](https://github.com/amnael1)! - feat: invoke Emporix cloud functions

  Adds `client.cloudFunctions.invoke<TRes, TReq>(functionId, { method?, path?,
body?, query?, headers? }, auth)` — a generic call to tenant cloud functions
  (`/cloud-functions/{tenant}/functions/{id}[/sub]`), with GET/POST/PUT/DELETE and
  service / customer / anonymous / raw auth (default anonymous). Adds the React
  hooks `useInvokeCloudFunction` (mutation, any method) and `useCloudFunction`
  (GET-style query with caching), both with auto-auth (customer-if-token-else-
  anonymous) and an optional override.

## 2.10.0

### Minor Changes

- [#112](https://github.com/viuteam/emporix-sdk/pull/112) [`1f87a9b`](https://github.com/viuteam/emporix-sdk/commit/1f87a9b54ddde591716eba7427e04573113b17f9) Thanks [@amnael1](https://github.com/amnael1)! - feat: runtime currency switching

  Adds `EmporixClient.setStorefrontContext({ currency, siteCode, targetLocation })`
  to re-bind the anonymous price context at runtime (invalidating the anon session
  so the next login re-mints with the new currency — covers the pre-cart guest
  case `sessionContext.patch` cannot). Adds `useSiteContext().setCurrency(code)`,
  which re-binds the context, clears the currency-bound guest cart, and PATCHes an
  existing server session context. The storefront-demo gains a currency dropdown
  populated from the active site's `availableCurrencies`.

  On reload, the site-context `currency` now seeds from the client's configured
  `context.currency` (instead of always deriving from the site default), so a
  persisted currency choice is respected.

## 2.9.0

### Minor Changes

- [#108](https://github.com/viuteam/emporix-sdk/pull/108) [`056cb62`](https://github.com/viuteam/emporix-sdk/commit/056cb622106fa5854ec9ebbee6e91c4820e62b29) Thanks [@amnael1](https://github.com/amnael1)! - feat(sdk): generate customer-management types from the real OpenAPI spec

  Replaces the hand-written customer-management mirror (B2B legal-entities /
  contact-assignments / locations) with codegen output from the vendored
  "Customer Management Service" spec, so Companies/Contacts/Locations return the
  real API shape. The `update` methods (and the matching `useUpdateCompany` /
  `useUpdateContactAssignment` / `useUpdateLocation` hooks) now type their PATCH
  body as `Partial<*Update>` to reflect the partial-update endpoint. `LegalEntity.id`
  and sibling ids are optional in the generated shape, matching the wire contract.

- [#109](https://github.com/viuteam/emporix-sdk/pull/109) [`f90e05b`](https://github.com/viuteam/emporix-sdk/commit/f90e05b97f6c022660bc36ac3656e2f48bf78e69) Thanks [@amnael1](https://github.com/amnael1)! - feat(sdk): generate IAM types, add group member mutations

  Replaces the last hand-written `generated/` mirror (`iam`) with codegen from the
  vendored "IAM Service" spec, so `customerGroups.listForCompany` returns the real
  group shape (`GroupsQueryDocument` — note: the wire uses `code`/`userType`, not
  the previously-mirrored `role`, which never existed on the API). Ships the
  previously-deferred group member mutations now that the endpoints are confirmed:
  `customerGroups.addMember` / `removeMember`, plus the `useAddGroupMember` /
  `useRemoveGroupMember` React hooks. No hand-written generated mirrors remain.

- [#107](https://github.com/viuteam/emporix-sdk/pull/107) [`975290c`](https://github.com/viuteam/emporix-sdk/commit/975290c7bd6129754d82e131186cade633394836) Thanks [@amnael1](https://github.com/amnael1)! - feat(product): add searchByName free-text helper + useProductNameSearch

  `products.searchByName(term)` builds the Emporix `name:(~<term>)` regex filter
  (escaping metacharacters) and delegates to `search`, so consumers no longer
  hand-build the `q` DSL — a bare free-text term otherwise 400s with
  "No value for key …". Adds the `useProductNameSearch` React hook (disabled on
  empty/whitespace).

### Patch Changes

- [#106](https://github.com/viuteam/emporix-sdk/pull/106) [`04b95ea`](https://github.com/viuteam/emporix-sdk/commit/04b95eab1fbf6b09ca29b0e3a98605e5ef938c6c) Thanks [@amnael1](https://github.com/amnael1)! - feat(sdk): generate order-v2 types from the real OpenAPI spec

  Replaces the hand-written `order-v2` type mirror (which invented `items`,
  `{amount,currency}` totals and a top-level `orderNumber`) with codegen output
  from the vendored Emporix Order Service spec. `OrdersService` and
  `SalesOrdersService` now return the real API shape:
  - line items are `entries` (not `items`); each entry has `itemYrn`,
    `orderedAmount`/`amount`, and a nested `product`
  - `totalPrice`/`subTotalPrice` are numbers + a top-level `currency`; rich
    net/gross/tax lives in `calculatedPrice`
  - `orderNumber` is under `mixins.generalAttributes`
  - `SalesOrderPatch` is now `Partial<OrderUpdateDto>` (the real PATCH body)

  Public type surface: `Order`, `OrderEntry`, `OrderStatus`, `SalesOrder`,
  `Transition`, `SalesOrderPatch`. The unused fictional re-exports (`OrderItem`,
  `OrderMoney`, `OrderCustomer`, `OrderAddress`, `OrderPayment`, `OrderDelivery`,
  `OrderTaxLine`, `OrderMetadata`, `OrderTransition`) are removed — they had no
  runtime counterpart.

  `useReorder` now reads `entries` and re-adds each with its `itemYrn` + price row
  (`priceId`/amounts/currency) — the cart requires a price, so the previous
  `{ product: { id } }` body always failed; reorder now actually works.

## 2.8.0

### Minor Changes

- [#98](https://github.com/viuteam/emporix-sdk/pull/98) [`108a724`](https://github.com/viuteam/emporix-sdk/commit/108a724f1d4342532ae8d575faa501d54d8c591f) Thanks [@amnael1](https://github.com/amnael1)! - Support partial cart-item updates. `client.carts.updateItem(cartId, itemId,
patch, auth, { partial: true })` now sends `?partial=true`, so a quantity-only
  change can be `{ quantity }` instead of a full item replace (which otherwise
  requires re-sending `itemYrn` + the `price` row). The React
  `useCartMutations().updateItem` mutation accepts an optional `partial` flag in
  its variables. Default behavior is unchanged.

- [#100](https://github.com/viuteam/emporix-sdk/pull/100) [`b4be158`](https://github.com/viuteam/emporix-sdk/commit/b4be1589b2fb0db44852233efe2a5d575a2e2795) Thanks [@amnael1](https://github.com/amnael1)! - `useCustomerSession()` now exposes the current `saasToken`. It was already
  tracked internally (from `login` / `exchangeToken`) but not returned — so
  consumers couldn't pass it to `useCheckout().placeOrder({ ..., saasToken })` for
  customer checkout, or to saas-token-gated order reads.

### Patch Changes

- [#101](https://github.com/viuteam/emporix-sdk/pull/101) [`e010a5a`](https://github.com/viuteam/emporix-sdk/commit/e010a5ab8f35c92ed946522558db33b2febff5de) Thanks [@amnael1](https://github.com/amnael1)! - fix(react): refresh the cart after a 204-only mutation

  `useCartMutations` assumed every cart write echoes the full updated cart.
  A partial quantity update (`updateItem(..., { partial: true })`) returns
  `204 No Content`, which the SDK resolves to `undefined` — and
  `setQueryData(key, undefined)` is a no-op in React Query, so the cart cache
  stayed stale and the UI did not reflect the change. The mutation now adopts
  a real cart body when one is returned and otherwise invalidates the cart
  query so it refetches. This also makes coupon/address/remove mutations
  reconcile with the server when they return no body.

- [#103](https://github.com/viuteam/emporix-sdk/pull/103) [`2e5c767`](https://github.com/viuteam/emporix-sdk/commit/2e5c76715f5d08358dd9342ef65d7c4c0d8b9aef) Thanks [@amnael1](https://github.com/amnael1)! - fix(react): drop the cart on logout and react to cart-id clearing

  Two related cleanup gaps caused follow-up errors after logout and checkout:
  - `useCustomerSession().logout()` cleared the customer token but left the
    stored `cartId`. The cart belonged to the customer and isn't accessible
    anonymously, so the cart query immediately refetched it and got a `403`.
    Logout now clears `cartId` too.
  - `useActiveCart` cached the cart id in local state and never reacted to
    external `storage.setCartId(null)` (logout, or the post-order cleanup that
    closes the cart). It kept fetching the dead cart id — a `403` after logout,
    a `404` after checkout. It now subscribes to storage cart-id changes and
    syncs, so clearing the id stops the fetch (and a logged-out cart page
    bootstraps a fresh anonymous cart on demand).

- [#102](https://github.com/viuteam/emporix-sdk/pull/102) [`020722b`](https://github.com/viuteam/emporix-sdk/commit/020722b2cb696bdc347538205ea4fad884451d88) Thanks [@amnael1](https://github.com/amnael1)! - fix(react): share the customer session across hook instances

  `useCustomerSession` kept its session in a per-instance `useState`. The
  `token` slot was mirrored from storage (so `isAuthenticated` was consistent),
  but the in-memory `saasToken` and `refreshToken` lived only in the component
  instance that called `login()`. A different consumer — e.g. the checkout page
  reading `saasToken` for the `saas-token` header — saw `null`, so customer
  checkout failed with `401 "Saas TOKEN is invalid"`.

  The session now lives in a shared, per-storage store consumed via
  `useSyncExternalStore`, so every `useCustomerSession()` reads the same
  `{ token, refreshToken, saasToken }`. A login in one component is immediately
  visible to all others. The tokens remain in-memory only (still cleared on a
  full reload, by design).

## 2.7.0

### Minor Changes

- [#96](https://github.com/viuteam/emporix-sdk/pull/96) [`da1113a`](https://github.com/viuteam/emporix-sdk/commit/da1113a07f70dceb9f1cb732b28462ccb3671f4a) Thanks [@amnael1](https://github.com/amnael1)! - Fix and extend the Category service for catalogue + hierarchy browsing. Several
  methods targeted routes that don't exist on the deployed category service
  (verified against a live tenant):
  - **`categories.productsIn(...)`** requested a non-existent
    `/categories/{id}/products` route (always 404). It now resolves products via
    category **assignments** (`/categories/{id}/assignments` → keep `PRODUCT`
    refs → `/products/search`), preserving its `PaginatedItems<Product>` contract;
    categories with no products return an empty page instead of throwing.
  - **`categories.tree()`** pointed at a non-existent `/categories/{...}Tree`
    route. It now reads `/category-trees` and returns the catalogue's **root
    categories** (`Promise<Category[]>`) for top-level navigation. (Return type
    changed from the previous nested-node shape; the `rootId` argument is removed.)
  - **New `categories.subcategories(categoryId)`** (+ React `useSubcategories`):
    a category's direct child categories, resolved from `CATEGORY` assignment refs
    (mirrors `productsIn`). Returns `[]` when there are none.

  React `useCategoryTree()` now returns `Category[]` (root categories) and takes no
  `rootId`.

## 2.6.0

### Minor Changes

- [#92](https://github.com/viuteam/emporix-sdk/pull/92) [`45a2bd8`](https://github.com/viuteam/emporix-sdk/commit/45a2bd8d83cb46d775301790cb2efc60805efc90) Thanks [@amnael1](https://github.com/amnael1)! - Add opt-in reactive customer-token auto-refresh.

  Core: `EmporixClient.setCustomerTokenRefresher(refresher)` registers a
  single-flight `CustomerTokenRefresher`; on a `customer`-kind 401 the HTTP layer
  refreshes once and retries. Off by default — the customer token stays
  caller-owned.

  React: `EmporixProvider` gains `autoRefreshCustomerToken` and
  `onCustomerSessionExpired`. When enabled, a customer 401 is transparently
  refreshed via the stored refresh token (anonymous-authorized
  `GET /refreshauthtoken`) and the request is retried; B2B `legalEntityId` is
  preserved.

## 2.5.1

### Patch Changes

- [#90](https://github.com/viuteam/emporix-sdk/pull/90) [`3db7978`](https://github.com/viuteam/emporix-sdk/commit/3db79789f90ed8e5134fde809fe689375f03cfa4) Thanks [@amnael1](https://github.com/amnael1)! - Document analytics integration (Google Tag Manager / GA4 ecommerce) via the
  telemetry channel. Adds `docs/analytics.md` — the `dataLayer` bridge, the GA4
  ecommerce event mapping, a `useTrackedCart` wrapper, and SSR + consent notes —
  plus an "Analytics & tracking" pointer in the package README. Docs-only; no API
  changes.

## 2.5.0

### Minor Changes

- [#87](https://github.com/viuteam/emporix-sdk/pull/87) [`83f5797`](https://github.com/viuteam/emporix-sdk/commit/83f5797ed8f38b63d67b9d392c0410be7a75997b) Thanks [@amnael1](https://github.com/amnael1)! - Add Emporix Approval Service bindings for B2B cart/quote approval workflows.

  Core `client.approvals` (`ApprovalService`): `listApprovals`, `getApproval`,
  `createApproval`, `updateApproval` (JSON-Patch approve/reject), `deleteApproval`,
  `checkPermitted`, and `searchApprovers`. Every endpoint is customer-token-only.

  React: `useApprovals`, `useApproval`, `useCreateApproval`, and `useUpdateApproval`
  (customer-only) for B2B approval self-service.

### Patch Changes

- [#88](https://github.com/viuteam/emporix-sdk/pull/88) [`ea9fc34`](https://github.com/viuteam/emporix-sdk/commit/ea9fc34c78e4620f3da2bf17040739f3dfd19669) Thanks [@amnael1](https://github.com/amnael1)! - Refresh package READMEs to reflect the full service and hook surface. The
  `@viu/emporix-sdk` README now lists all 44 services (grouped by area) and the
  correct published subpath exports; the `@viu/emporix-sdk-react` README documents
  every exported hook (orders, availability, coupon, reward-points, returns,
  approvals, shopping-lists, and the chunked price hook). Docs-only — no API
  changes.

## 2.4.0

### Minor Changes

- [#75](https://github.com/viuteam/emporix-sdk/pull/75) [`2174664`](https://github.com/viuteam/emporix-sdk/commit/21746648f890410a46c95c37b218bb6bbc98ebe7) Thanks [@amnael1](https://github.com/amnael1)! - Add Emporix Coupon Service bindings via `client.coupons`: coupon CRUD
  (`listCoupons`, `getCoupon`, `createCoupon`, `updateCoupon`, `patchCoupon`,
  `deleteCoupon`), validation (`validateCoupon`), redemptions (`listRedemptions`,
  `redeemCoupon`, `getRedemption`, `deleteRedemption`), and referral coupons
  (`getReferralCoupon`, `createReferralCoupon`). Methods default to the service
  token and are auth-overridable. Adds React hooks `useValidateCoupon` and
  `useRedeemCoupon` for storefront validate/redeem (browser auth context).

- [#81](https://github.com/viuteam/emporix-sdk/pull/81) [`f626ef6`](https://github.com/viuteam/emporix-sdk/commit/f626ef6ef25c6856c027402b854bf81bb14fe864) Thanks [@amnael1](https://github.com/amnael1)! - Add Emporix Returns Service bindings via `client.returns`: CRUD over returns
  (`listReturns`, `getReturn`, `createReturn`, `updateReturn`, `patchReturn`,
  `deleteReturn`). Methods default to the service token and are auth-overridable;
  `patchReturn` takes a JSON-Patch op-array. Adds React hooks `useMyReturns`,
  `useReturn`, and `useCreateReturn` for customer self-service (browser customer
  token).

- [#76](https://github.com/viuteam/emporix-sdk/pull/76) [`2dddd6a`](https://github.com/viuteam/emporix-sdk/commit/2dddd6a213fa9d70bbaf0acc790eee51a7d813a8) Thanks [@amnael1](https://github.com/amnael1)! - Add Emporix Reward Points Service bindings via `client.rewardPoints`: admin
  customer-points management (`listAllSummaries`, `getCustomerPoints`,
  `createCustomerPoints`, `deleteCustomerPoints`, `getCustomerSummary`,
  `addPoints`, `redeemPoints`), the signed-in customer's own points
  (`getMyPoints`, `getMySummary`, `redeemMyPoints` → coupon code), and redeem
  options (`listRedeemOptions`, `createRedeemOption`, `updateRedeemOption`,
  `deleteRedeemOption`). Admin methods default to the service token; the
  `/public/*` methods require a customer token. Adds React hooks
  `useMyRewardPoints`, `useMyRewardPointsSummary`, `useRedeemRewardPoints` and
  `useRedeemOptions`.

## 2.3.0

### Minor Changes

- [#65](https://github.com/viuteam/emporix-sdk/pull/65) [`dca34d0`](https://github.com/viuteam/emporix-sdk/commit/dca34d044e54c305ea2a310ba349dc800ced331a) Thanks [@amnael1](https://github.com/amnael1)! - Add Shopping List bindings: `client.shoppingLists` (per-customer named lists —
  list/create/replace/delete plus read-modify-write item helpers, last-write-wins)
  and React hooks (`useShoppingLists`, `useCreateShoppingList`, `useAddToShoppingList`,
  `useRemoveFromShoppingList`, `useSetShoppingListItemQuantity`, `useDeleteShoppingList`).

## 2.2.0

### Minor Changes

- [#63](https://github.com/viuteam/emporix-sdk/pull/63) [`bb2ce4f`](https://github.com/viuteam/emporix-sdk/commit/bb2ce4f891e50e07cee02e03340d2abe1133fdc0) Thanks [@amnael1](https://github.com/amnael1)! - Add `products.searchByCodes(codes, { chunkSize? })` — bulk-fetch products by
  `code` via `POST /products/search` (`q="code:(…)"`), chunked at 100, analogous
  to `searchByIds`. Codes with query-delimiter characters are dropped with a
  warning. Adds the `useProductsByCodes` React hook (30s stale-time).

## 2.1.0

### Minor Changes

- [#56](https://github.com/viuteam/emporix-sdk/pull/56) [`939a1b0`](https://github.com/viuteam/emporix-sdk/commit/939a1b0a24063db38545dc81f88c319f93e81833) Thanks [@amnael1](https://github.com/amnael1)! - Add AvailabilityService (`client.availability.get` / `.getMany`) and the
  `useAvailability` / `useAvailabilities` React hooks for site-aware product
  availability. `getMany` uses the batch `POST .../search` endpoint and returns
  results in input order; an opt-in `defaultAvailableOnNotFound` treats products
  with no stock record as available. New `@viu/emporix-sdk/availability` subpath export.

- [#58](https://github.com/viuteam/emporix-sdk/pull/58) [`caaff28`](https://github.com/viuteam/emporix-sdk/commit/caaff2819e64cf42e4c58dfe4c04fa994312f901) Thanks [@amnael1](https://github.com/amnael1)! - Add PriceService.matchByContextChunked and the useMatchPricesChunked React hook:
  split large match-prices-by-context requests into bounded-concurrency chunks
  (default 50 items, 4 in flight) with per-chunk error handling.

- [#57](https://github.com/viuteam/emporix-sdk/pull/57) [`0302ea3`](https://github.com/viuteam/emporix-sdk/commit/0302ea368e6d7feb0a064aac71a6f5314380deb3) Thanks [@amnael1](https://github.com/amnael1)! - Add ProductService.listVariantChildren / listVariantChildrenAll and the
  useVariantChildren React hook to resolve the VARIANT children of a
  PARENT_VARIANT product without hand-building the search query.

## 2.0.0

### Patch Changes

- Updated dependencies [[`26640fe`](https://github.com/viuteam/emporix-sdk/commit/26640fe281083e6ce0475a547e292ac82ba7d9bf)]:
  - @viu/emporix-sdk@2.0.0

## 1.0.0

### Minor Changes

- [#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 }`)
  is now sent at anonymous-login so `prices.matchByContext` resolves prices
  from the session. Adds the `useMatchPrices` React hook. The next-app-router
  and vite-spa examples now include an anonymous guest-checkout flow.

  BREAKING: `CartService.create` now returns the generated `CartCreated`
  (`{ cartId, yrn }`) — the actual create-endpoint response — instead of the
  `Cart` GET model. Read `cart.cartId` (not `cart.id`) from the result.

- [#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.

  **QueryClient defaults** (only applied when no `queryClient` prop is passed):
  - `staleTime: 30s` — fresh-within-30s policy reduces refetch-on-mount churn.
  - `refetchOnWindowFocus: false` — tabbing back no longer refetches all queries.
  - `retry: 1` — single retry on failure instead of three (caps failed-request
    cost at 2× per query).

  **Per-hook staleTime overrides:**
  - `useSites`, `useDefaultSite`, `usePaymentModes` — 10 min.
  - `useCategory(ies)`, `useCategoryTree`, `useProductsInCategory(Infinite)`,
    `useMySegment*` — 5 min.
  - `useProducts(Infinite)`, `useProduct`, `useProductByCode`, `useProductSearch`,
    `useMatchPrices` — 60 s.
  - `useCustomerSession.customer` (meQuery) — 30 s.
  - Cart, Addresses keep the 30s default (or 0 where freshness matters).

  **Bootstrap dedup:**
  - `useActiveCart({ create: true })` and `useCustomerSession.login` cart
    onboarding share a single `bootstrapCart` cache entry — parallel mounts
    trigger one server call instead of N.
  - `useCustomerSession.login` honours `customer.preferredSite` via the same
    `meQuery` cache key — login fires 1 `GET /customer/me` when the cache hits,
    2 in the worst-case timing race (vs always 2 before).

  No breaking changes. Consumers passing their own `queryClient` to
  `EmporixProvider` keep their existing defaults.

- [#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:
  - New `CompanyContextProvider` (auto-mounted inside `EmporixProvider`) and `useActiveCompany()` hook.
  - New B2B read hooks: `useMyCompanies`, `useCompany`, `useCompanyContacts`, `useCompanyLocations`, `useCompanyGroups`.
  - New admin mutation hooks: `useCreateCompany`/`useUpdateCompany`/`useDeleteCompany`, `useAssignContact`/`useUpdateContactAssignment`/`useUnassignContact`, `useCreateLocation`/`useUpdateLocation`/`useDeleteLocation`.
  - Convenience hook `useCompanySwitcher()`.
  - 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).
  - New SSR prop `EmporixProvider.initialActiveLegalEntityId` for hydration.
  - New telemetry event `{ type: "company:switched", from, to, durationMs }`.
  - `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.

  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.

- [#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`:
  - `useProductByCode(code)` — single-product lookup via the `code` field. For slug-based routes (`/products/[slug]`).
  - `useProductSearch(query, params?)` — full-text product search. Disabled on empty query; pair with consumer-side debouncing.
  - `useProductsInCategory(categoryId, params?)` — paginated products for a category landing page.
  - `useProductsInCategoryInfinite(categoryId, params?)` — infinite-scroll variant of the same.

  All four follow the established `useReadAuth` + `enabled`-gate patterns. No SDK change.

- [#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`:
  - `useUpdateCustomer()` — mutation for profile updates, invalidates `useCustomerSession.customer`.
  - `useChangePassword()` — mutation for password change. Customer-only.
  - `useCustomerAddresses()` — query for the customer's address list.
  - `useAddressMutations()` — `{ add, update, remove }` mutations following the `useCartMutations` shape.
  - `usePasswordReset()` — 2-step anonymous flow: `{ request, confirm }`.

  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`).

  No SDK change.

- [#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.

  **SDK (`@viu/emporix-sdk`)**
  - `EmporixClient.config` is now a public read-only field, so hosts can read static settings such as `storefront.context.siteCode` without re-plumbing.
  - **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.
  - **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: […] }`).

  **React (`@viu/emporix-sdk-react`)**
  - `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.

  **Migration**

  ```ts
  // SDK getCurrent:
  - const cart = await client.carts.getCurrent(auth.customer(token));
  + const cart = await client.carts.getCurrent(auth.customer(token), { siteCode: "main" });

  // SDK merge:
  - await client.carts.merge(anonCartId, auth.customer(token));
  + await client.carts.merge(customerCartId, [anonCartId], auth.customer(token));
  ```

  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).

- [#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
  `GET /customer/{tenant}/logout?accessToken=…` authorized with the customer
  token, invalidating it server-side (204). `useCustomerSession().logout()` is
  now async: it performs the server logout best-effort (ignoring failures, e.g.
  an already-expired token) and then clears the local session. The token is
  sent only as a query param the SDK never logs (the logger uses the path, not
  the full URL).

- [#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`,
  `listSegmentItems`, `getCategoryTree`, plus the hydrate helpers
  `listMyProductIds` / `listMyCategoryIds` / `listMyProducts` /
  `listMyCategories` that map segment-item ids to real `Product` /
  `Category` objects via parallel `products.get` / `categories.get` calls.
  All methods require a customer/raw `AuthContext` and use the shared
  `requireCustomer` guard (also adopted by `customer.ts` and `payment.ts`).

  React adds three lightweight hooks: `useMySegments`, `useMySegmentItems`,
  `useMySegmentCategoryTree`. Each reads the customer token from the
  storage and is `enabled: false` when there is no token (no network call
  for guests). Exposed on the `@viu/emporix-sdk/segment` subpath.

- [#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?,
legalEntityId? }, auth?)` calls `GET /customer/{tenant}/refreshauthtoken`
  (authorized with an anonymous token, default), returning a new
  `CustomerSession` with the **same `sessionId`**. The refresh endpoint does
  not return a `saas_token`, so the original is carried forward via the
  `saasToken` input. `useCustomerSession` now captures the refresh/saas tokens
  at `login`, exposes `refreshToken`, and adds a `refreshSession()` action
  that exchanges the refresh token and updates the stored customer token.

- [#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
  type. `carts.create` takes `CreateCart`, `carts.addItem` takes
  `CartItemRequest` (now requires `product`/`quantity`/`price`),
  `carts.updateItem` takes `UpdateCartItem`, `checkout.placeOrder` takes
  `RequestCheckout`, `checkout.placeOrderFromQuote` takes
  `RequestFromQuoteCheckout`, `payments.authorize` takes
  `AuthorizePaymentRequest` (`{ order: { id }, … }`),
  `customers.changePassword` takes `{ currentPassword, newPassword }`,
  `customers.confirmPasswordReset` takes `{ token, password }`,
  `customers.signup`/`update`/`addresses.*` take the generated DTOs. All
  ergonomic input wrappers and input transformations are removed — callers
  send the exact wire shape. `useCartMutations.addItem`/`updateItem` mutation
  variables change accordingly. `CustomerService.login` keeps its literal
  `{ email, password }` input and snake_case `CustomerSession` response (no
  generated request type exists for it).

- [#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", ... })`
  posts to `POST /media/{tenant}/assets` (multipart for BLOB, JSON for LINK);
  convenience helpers `uploadFile`, `link`, `attachToProduct`,
  `detachFromProduct`, `listForProduct` wrap the common product-attachment
  flows. `HttpClient` now passes `FormData` bodies through `fetch` verbatim
  (no Content-Type/JSON-stringify). React adds a thin `useProductMedia(id)`
  hook that reads `productMedia` from the existing product query (no
  service-token call in the browser).

  BREAKING: `ProductService.media` is removed — it called a path
  (`/product/{tenant}/products/{id}/media`) that does not exist in the
  Emporix Product API. Migrate to `client.media.listForProduct(productId)`
  (admin/server) or read `product.productMedia` from `client.products.get`
  (storefront).

- [#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.

  **SDK**
  - `client.sites.list()` — list active sites for the tenant.
  - `client.sites.get(code)` — retrieve one site by code.
  - `client.sites.current()` — convenience for the `default: true` site.
  - New `Site` type mirroring the `SiteDto` schema (code, name, active,
    default, currency, languages, homeBase, shipToCountries, …).

  **React**
  - `useSites()` — list active sites.
  - `useDefaultSite()` — the default site.

  No breaking changes. The active-site runtime context (provider state,
  `setSite`, cache-key migration) follows in MS-2.

- [#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.

  **Provider**
  - `<EmporixProvider initialSiteCode>` prop — resolution order: prop →
    `storage.getSiteCode()` → static `client.config.…context.siteCode` →
    `null`.

  **Hooks**
  - `useSiteContext()` — returns `{ siteCode, currency, targetLocation,
setSite }` for the active site. In MS-2 `currency` and `targetLocation`
    are `null` (populated in MS-4). `setSite(code)` writes storage, clears
    `storage.cartId` (carts are site-aware), and invalidates all
    `["emporix"]` queries.

  **Storage**
  - `EmporixStorage.{get,set}SiteCode` across all three backends (memory,
    localStorage, cookie). localStorage key: `emporix.siteCode`.

  **Cache keys**
  - All site-aware query keys (`useProducts`, `useCategories`, `useCart`,
    `useActiveCart`, `useCartMutations`, `useMatchPrices`, `useMySegment*`,
    `usePaymentModes`, etc.) now include `siteCode`. Different sites =
    separate cache entries. Internal change — no consumer subscribed
    directly to query keys.

  No breaking changes. Existing single-site apps work unchanged — they
  implicitly run with the static config's `siteCode` (or `null`).

- [#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.

  **SDK**
  - `client.sessionContext.get()` — `GET /session-context/{tenant}/me/context`.
    Returns `null` (not throws) when the server returns 404 — i.e. when the
    user has not created a cart yet and no session-context exists.
  - `client.sessionContext.patch(input)` — `PATCH /session-context/{tenant}/me/context`
    with optimistic-locking. Looks up `metadata.version` via GET first
    unless caller provides one. Returns `true` when applied, `false` when
    there is no session context yet (404 on the GET → patch skipped).
  - New `SessionContext` and `SessionContextPatch` types.

  **React**
  - `setSite()` is now async. It flips local state + storage + cart-id
    - cache-invalidation synchronously (optimistic UI), then PATCHes the
      server. Skips the PATCH when no session exists yet (404 on GET).
  - `useSiteContext()` gains `isSwitching: boolean` and
    `switchError: Error | null`. The optimistic state is NOT rolled back
    on PATCH failure — surface the error in UI; the next user interaction
    retries.

  No breaking changes. Existing call sites continue to work — `setSite("X")`
  without `await` still flips the UI; awaiting it blocks until the
  server-side sync completes.

- [#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.

  **Provider**
  - `useSiteContext().currency` and `useSiteContext().targetLocation` are no
    longer always `null`. They derive from the active site's DTO
    (`site.currency` and `site.homeBase.address.country`), cached for 5
    minutes via React-Query.
  - `setSite(code)` fetches the site DTO, populates `currency` /
    `targetLocation`, and includes all three fields in the
    `sessionContext.patch` body so the server is fully in sync.
  - On provider mount with a pre-resolved `siteCode` (from `initialSiteCode`
    prop, storage, or static config), the site DTO is fetched once so
    `currency` and `targetLocation` populate without a user-driven switch.

  **Login**
  - `useCustomerSession.login` (and `socialLogin` / `exchangeToken`) now read
    `customer.preferredSite`. If it's set and differs from the active site,
    the SDK calls `setSite(preferredSite)` — same flow as a user-driven
    switch. Best-effort: a failure here never blocks login.

  No breaking changes. Storefronts without `preferredSite` set on their
  customers see no behavior change.

- [#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.

  **SDK (additive)**
  - `TokenProvider.onRefresh(listener)` — optional subscription to
    token-refresh events. `DefaultTokenProvider` implements it (anonymous
    refresh path).

  **React (additive)**
  - `<EmporixProvider onTelemetry={fn}>` — receives a typed event stream
    covering cache hit/miss, refetches, errors, mutations, auth refreshes,
    and storage writes.
  - `useEmporixTelemetry()` — returns `{ emit }` for consumer-side custom
    events on the same channel.
  - `EmporixStorage.subscribeAll(listener)` — optional subscription to all
    storage write events. Implemented in all three built-in adapters
    (memory, localStorage, cookie).

  **Event types:**
  - `cache.hit`, `cache.miss`, `query.refetch`, `query.error`
  - `mutation.success`, `mutation.error`
  - `auth.refresh`
  - `storage.write`
  - `custom`

  No breaking changes. The entire telemetry layer is no-op when
  `onTelemetry` is not passed. Existing `TokenProvider` / `EmporixStorage`
  implementations continue to work without implementing the new optional
  methods.

- [#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:
  - Customer-facing: `useMyOrders`, `useMyOrdersInfinite`, `useOrder`, `useCancelOrder`, `useOrderTransition`, `useReorder`.
  - Service-account (backoffice): `useSalesOrder`, `useUpdateSalesOrder` — inert when `auth` is undefined so storefront apps can import them for types without unexpected backend traffic.
  - New `prefetchOrder` SSR helper alongside `prefetchProduct` / `prefetchCart`.
  - `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.
  - `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).

- [#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
  legacy `Page<T>` shape (whose `total` was always `NaN`, since the HTTP
  client never exposed `X-Total-Count`) and the `paginate()` async
  iterator.

  **BREAKING:**
  - `ProductService.list` / `ProductService.search` now return
    `PaginatedItems<Product>` (`{ items, pageNumber, pageSize, hasNextPage }`)
    instead of `Page<Product>` (`{ items, total, offset, limit }`).
  - `CategoryService.list` returns `PaginatedItems<Category>`;
    `CategoryService.productsIn` returns `PaginatedItems<Product>`.
  - `useProducts` / `useCategories` now resolve to `PaginatedItems<T>`.
  - `Page<T>` and `paginate()` are no longer exported from `@viu/emporix-sdk`.

  **Fixed:**
  - `useProductsInfinite` previously over-fetched a trailing empty page
    before terminating, and its `getNextPageParam` was tied to the
    fetched-page count rather than the cursor. It now derives the next
    page from `last.hasNextPage` / `last.pageNumber + 1` — same pattern as
    the segment-hydrate infinite hooks.

  **Added:**
  - `useCategoriesInfinite` — mirror of `useProductsInfinite`.
  - `iterateAll<T>(fetchPage, start?)` async iterator over
    `PaginatedItems<T>`. Replaces `paginate()` for "iterate every item
    across pages" use cases.

  **Migration:**

  ```ts
  // Before
  const { items, total } = await client.products.list({
    pageNumber: 1,
    pageSize: 50,
  });
  // total was always NaN.

  // After
  const { items, hasNextPage } = await client.products.list({
    pageNumber: 1,
    pageSize: 50,
  });
  ```

  ```ts
  // Before
  for await (const p of paginate((offset, limit) => svc.list(...), 50)) { ... }

  // After
  for await (const p of svc.listAll({ pageSize: 50 })) { ... }
  // or, for custom sources:
  for await (const x of iterateAll<X>((pageNumber) => fetchPage(pageNumber))) { ... }
  ```

- [#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.

- [#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.

  No code changes — no SDK feature uses a Node API beyond what Node 20 provides. Browser consumers are unaffected.

- [#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
  session, query hooks, cart mutations with optimistic updates, error helpers and
  SSR prefetch helpers. Core: expose EmporixClient.tenant for query-key namespacing.

- [#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
  of N+1 `GET /products/{id}` calls. New
  `ProductService.searchByIds(ids, { chunkSize? }, auth?)` and
  `CategoryService.searchByIds(...)` POST `/search` with
  `q="id:(id1,id2,…)"`, chunking at 100 IDs by default. Adds the generic
  `PaginatedItems<T>` (`{ items, pageNumber, pageSize, hasNextPage }`) in
  `core/context.ts`.

  **BREAKING:** `SegmentService.listMyProducts` and
  `SegmentService.listMyCategories` now return `PaginatedItems<Product>` /
  `PaginatedItems<Category>` instead of a flat `Product[]` / `Category[]`.
  `SegmentService.listItems` gains optional `pageNumber` / `pageSize`
  params (additive). `listMyProductIds` / `listMyCategoryIds` are
  unchanged.

  React adds four new hooks: `useMySegmentProducts` /
  `useMySegmentProductsInfinite` and `useMySegmentCategories` /
  `useMySegmentCategoriesInfinite`. The infinite variants use
  `useInfiniteQuery` with a `pageNumber` cursor and `hasNextPage`-driven
  `getNextPageParam`. All four are disabled when no customer token is in
  storage.

- [#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,
codeVerifier?, sessionId? })` performs the Authorization-Code code exchange
  (`POST /customer/{tenant}/socialLogin`); `customers.exchangeToken({
subjectToken, config? })` performs the RFC 8693 token exchange
  (`POST /customer/{tenant}/exchangeauthtoken`). Both default to anonymous auth
  and return a `CustomerSession` (now with optional `socialAccessToken` /
  `socialIdToken` from socialLogin); `expires_in` is normalized to a number
  across both flows. `useCustomerSession` gains `socialLogin` and
  `exchangeToken` actions that store the session like `login`.

- [#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`
  when their `cartId` argument is omitted. Pair with `useActiveCart` to drop
  the `useCartMutations(cartId ?? "")` boilerplate:
  - `useCart()` — disabled until storage has a cartId, then auto-resolves.
  - `useCartMutations()` — resolves cartId at mutate-time; throws
    `EmporixError("no cartId available…")` if storage is empty when a
    mutation runs.

  `useActiveCart` is now a thin wrapper around `useCart` and shares the same
  React-Query cache key. Optimistic updates from `useCartMutations` now
  propagate to every cart-aware view in one place.

  `useCreateCart` additionally invalidates `["emporix","cart"]` on success so
  `useActiveCart` picks up the new storage cartId on the next render.

  `useActiveCart`'s `data` now correctly returns `null` (not `undefined`)
  when storage has no cartId and `create` was not requested — matches the
  documented empty-state signal.

  No breaking changes — every old call signature still works.

- [#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.

  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.

  Useful for:
  - Cart-page mounts: `useActiveCart({ create: true })`.
  - Header mini-cart: `useActiveCart()` (read-only, no auto-create).
  - B2B quote carts in parallel to shopping carts: `useActiveCart({ create: true, type: "quote" })`.

  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.

- [#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.

  **SDK (`@viu/emporix-sdk`)**
  - 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.
  - `invalidateAnonymous()` now also clears the attached store (`write(null)`).
  - `EmporixClient.tokenProvider` is now a public, read-only field — so hosts can call `attachAnonymousStore` after construction.

  **React (`@viu/emporix-sdk-react`)**
  - `TokenStorage` renamed to `EmporixStorage` (alias `TokenStorage` is kept). New methods: `getCartId / setCartId`, `getAnonymousSession / setAnonymousSession`. All three storage backends — memory, `localStorage`, cookie — implement them.
  - `EmporixProvider` wires the storage's anonymous-session accessors to the SDK's `attachAnonymousStore` so the anonymous cart can survive a browser reload.
  - New `useCreateCart` mutation hook: auto-detects customer vs anonymous auth and persists `cartId` via `storage.setCartId`.
  - `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.

  **Migration**

  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()`).

### Patch Changes

- [#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
  return type and from `bootstrapCart`'s parameter list. Both duplicated
  `ctx.kind` (the discriminator of `AuthContext`) — callers now compose
  `ctx.kind` directly into query keys.

  No public API changes. No cache-key shape changes (`authKind` values stay
  identical: `"customer"`, `"anonymous"`, etc.). All 151 React tests stay
  green.

- [#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:
  - **`catalog.spec.ts`** — anonymous catalog renders 12 products; only `GET /anonymous/login` + `GET /product/viu/products` hit Emporix on `/`.
  - **`customer-session.spec.ts`** — login resolves the customer profile + stores the token; logout clears the token.
  - **`guest-checkout.spec.ts`** — `useCreateCart` → `useCartMutations.addItem` → `useCheckout.placeOrder` (anonymous) → real order `EONxxxx` placed on `viu`.
  - **`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.

  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.

  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.

- [#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
  TypeScript resolver evaluate `exports` conditions in declaration order;
  with `import`/`require` listed before `types`, the `types` condition was
  never reached, emitting build warnings and preventing consumers from
  picking up the generated `.d.ts` entry points. Every subpath in both
  packages now uses `{ types, import, require }` order.

- [#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
  declared `LICENSE` but the file was only present at the repo root; npm
  publishes per-package, so a copy now lives inside each package directory.
  Fixes "License: not specified" on npmjs.com and unblocks corporate
  license-compliance scanners (Snyk, Black Duck).

- [#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:
  - **`@viu/emporix-sdk-react`**: tighten the `@tanstack/react-query` peer
    range from `^5.0.0` to `^5.51.0`. This matches the version the package
    is developed and tested against. The previous range claimed support
    for v5.0–v5.50 that was never exercised in CI; tightening avoids a
    silent runtime mismatch for consumers who happen to be on those older
    patch versions.
  - **Both packages**: replace the bare-string `author: "viuteam"` with an
    `author` object — `{ "name": "viu", "url": "https://github.com/viuteam" }`
    — so the npm package page shows "viu" (our display name) and links
    back to the GitHub org page (`viuteam`, the actual org slug).
  - **`LICENSE` (root and per-package)**: the MIT copyright holder is now
    `VIU AG` (the legal entity) instead of the GitHub org slug `viuteam`,
    so license-compliance scanners attribute the package correctly.

- [#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
  unchanged, all 151 React tests stay green.

  **Storage**
  - Extract `createListenerSet<T>()` helper used by all three backends'
    `subscribeAll` — single try/catch wrapper instead of three copies.
  - Extract `parseAnonymousSession()` helper for the JSON-parse-with-fallback
    shared by localStorage and cookie backends.

  **Hooks**
  - `emporixKey(resource, args, ctx)` helper centralizes the
    `["emporix", resource, …args, { tenant, authKind, siteCode? }]` cache
    key shape used by 15+ Read hooks.
  - `useEmporixInfinite()` helper centralizes the `initialPageParam: 1` +
    `getNextPageParam` cursor logic shared by 6 infinite-scroll hooks
    (products, categories, segments).

  **Auth**
  - `useCheckout` now uses the central `useReadAuth` hook instead of a
    local `checkoutCtx` helper.
  - `usePaymentModes` cache key gains a stable `authKind: "customer"`
    component for consistency with other hooks.

  **Customer session**
  - `useCustomerSession` bundles the three separate `useState` calls
    (token / refreshToken / saasToken) into a single `SessionState` object.
    Login / logout / refresh / SSO flows now flip the session atomically
    via one `setSession(...)` call instead of three. Same public API,
    same behaviour — only an internal state-shape consolidation.

- [#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).

  **Fix:** `useCategoriesInfinite` is now re-exported from the package root. It was defined but not exported in the prior release.

  No public hook name, behavior, or query-key changed. Consumer imports from `@viu/emporix-sdk-react` continue to work.

- 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)]:
  - @viu/emporix-sdk@1.0.0
