@viu/emporix-sdk 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.
Files changed (132) hide show
  1. package/CHANGELOG.md +407 -0
  2. package/LICENSE +21 -0
  3. package/README.md +115 -0
  4. package/dist/cart.cjs +214 -0
  5. package/dist/cart.cjs.map +1 -0
  6. package/dist/cart.d.cts +1807 -0
  7. package/dist/cart.d.ts +1807 -0
  8. package/dist/cart.js +4 -0
  9. package/dist/cart.js.map +1 -0
  10. package/dist/category.cjs +97 -0
  11. package/dist/category.cjs.map +1 -0
  12. package/dist/category.d.cts +313 -0
  13. package/dist/category.d.ts +313 -0
  14. package/dist/category.js +4 -0
  15. package/dist/category.js.map +1 -0
  16. package/dist/checkout.cjs +98 -0
  17. package/dist/checkout.cjs.map +1 -0
  18. package/dist/checkout.d.cts +252 -0
  19. package/dist/checkout.d.ts +252 -0
  20. package/dist/checkout.js +4 -0
  21. package/dist/checkout.js.map +1 -0
  22. package/dist/chunk-4BJUCCKJ.js +86 -0
  23. package/dist/chunk-4BJUCCKJ.js.map +1 -0
  24. package/dist/chunk-6UNFMCHZ.js +95 -0
  25. package/dist/chunk-6UNFMCHZ.js.map +1 -0
  26. package/dist/chunk-B56JZNI7.js +49 -0
  27. package/dist/chunk-B56JZNI7.js.map +1 -0
  28. package/dist/chunk-BFQ732VZ.js +11 -0
  29. package/dist/chunk-BFQ732VZ.js.map +1 -0
  30. package/dist/chunk-F3WAQH2E.js +120 -0
  31. package/dist/chunk-F3WAQH2E.js.map +1 -0
  32. package/dist/chunk-GWJ2U2CP.js +14 -0
  33. package/dist/chunk-GWJ2U2CP.js.map +1 -0
  34. package/dist/chunk-HEO34WLO.js +47 -0
  35. package/dist/chunk-HEO34WLO.js.map +1 -0
  36. package/dist/chunk-HKPZI7Y5.js +30 -0
  37. package/dist/chunk-HKPZI7Y5.js.map +1 -0
  38. package/dist/chunk-IEZHGU7F.js +171 -0
  39. package/dist/chunk-IEZHGU7F.js.map +1 -0
  40. package/dist/chunk-KTODT6DV.js +86 -0
  41. package/dist/chunk-KTODT6DV.js.map +1 -0
  42. package/dist/chunk-NM7VDXBR.js +57 -0
  43. package/dist/chunk-NM7VDXBR.js.map +1 -0
  44. package/dist/chunk-OMYVOOSX.js +57 -0
  45. package/dist/chunk-OMYVOOSX.js.map +1 -0
  46. package/dist/chunk-QDABAMVN.js +89 -0
  47. package/dist/chunk-QDABAMVN.js.map +1 -0
  48. package/dist/chunk-RXVVFNYN.js +56 -0
  49. package/dist/chunk-RXVVFNYN.js.map +1 -0
  50. package/dist/chunk-SKX7KCMH.js +23 -0
  51. package/dist/chunk-SKX7KCMH.js.map +1 -0
  52. package/dist/chunk-TLH6SLAF.js +215 -0
  53. package/dist/chunk-TLH6SLAF.js.map +1 -0
  54. package/dist/chunk-Z6PNI5VT.js +142 -0
  55. package/dist/chunk-Z6PNI5VT.js.map +1 -0
  56. package/dist/companies.cjs +58 -0
  57. package/dist/companies.cjs.map +1 -0
  58. package/dist/companies.d.cts +31 -0
  59. package/dist/companies.d.ts +31 -0
  60. package/dist/companies.js +3 -0
  61. package/dist/companies.js.map +1 -0
  62. package/dist/contacts.cjs +51 -0
  63. package/dist/contacts.cjs.map +1 -0
  64. package/dist/contacts.d.cts +28 -0
  65. package/dist/contacts.d.ts +28 -0
  66. package/dist/contacts.js +3 -0
  67. package/dist/contacts.js.map +1 -0
  68. package/dist/context-vKgzZ-5f.d.cts +329 -0
  69. package/dist/context-vKgzZ-5f.d.ts +329 -0
  70. package/dist/customer-groups.cjs +25 -0
  71. package/dist/customer-groups.cjs.map +1 -0
  72. package/dist/customer-groups.d.cts +36 -0
  73. package/dist/customer-groups.d.ts +36 -0
  74. package/dist/customer-groups.js +3 -0
  75. package/dist/customer-groups.js.map +1 -0
  76. package/dist/customer.cjs +262 -0
  77. package/dist/customer.cjs.map +1 -0
  78. package/dist/customer.d.cts +386 -0
  79. package/dist/customer.d.ts +386 -0
  80. package/dist/customer.js +5 -0
  81. package/dist/customer.js.map +1 -0
  82. package/dist/index.cjs +2075 -0
  83. package/dist/index.cjs.map +1 -0
  84. package/dist/index.d.cts +218 -0
  85. package/dist/index.d.ts +218 -0
  86. package/dist/index.js +791 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/locations.cjs +59 -0
  89. package/dist/locations.cjs.map +1 -0
  90. package/dist/locations.d.cts +30 -0
  91. package/dist/locations.d.ts +30 -0
  92. package/dist/locations.js +3 -0
  93. package/dist/locations.js.map +1 -0
  94. package/dist/media.cjs +123 -0
  95. package/dist/media.cjs.map +1 -0
  96. package/dist/media.d.cts +219 -0
  97. package/dist/media.d.ts +219 -0
  98. package/dist/media.js +3 -0
  99. package/dist/media.js.map +1 -0
  100. package/dist/orders.cjs +98 -0
  101. package/dist/orders.cjs.map +1 -0
  102. package/dist/orders.d.cts +167 -0
  103. package/dist/orders.d.ts +167 -0
  104. package/dist/orders.js +3 -0
  105. package/dist/orders.js.map +1 -0
  106. package/dist/payment.cjs +77 -0
  107. package/dist/payment.cjs.map +1 -0
  108. package/dist/payment.d.cts +110 -0
  109. package/dist/payment.d.ts +110 -0
  110. package/dist/payment.js +5 -0
  111. package/dist/payment.js.map +1 -0
  112. package/dist/price.cjs +88 -0
  113. package/dist/price.cjs.map +1 -0
  114. package/dist/price.d.cts +445 -0
  115. package/dist/price.d.ts +445 -0
  116. package/dist/price.js +4 -0
  117. package/dist/price.js.map +1 -0
  118. package/dist/product.cjs +100 -0
  119. package/dist/product.cjs.map +1 -0
  120. package/dist/product.d.cts +400 -0
  121. package/dist/product.d.ts +400 -0
  122. package/dist/product.js +4 -0
  123. package/dist/product.js.map +1 -0
  124. package/dist/segment.cjs +189 -0
  125. package/dist/segment.cjs.map +1 -0
  126. package/dist/segment.d.cts +241 -0
  127. package/dist/segment.d.ts +241 -0
  128. package/dist/segment.js +5 -0
  129. package/dist/segment.js.map +1 -0
  130. package/dist/types.gen-D1QuqPLs.d.cts +166 -0
  131. package/dist/types.gen-D1QuqPLs.d.ts +166 -0
  132. package/package.json +141 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,407 @@
1
+ # @viu/emporix-sdk
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
+ - [#17](https://github.com/viuteam/emporix-sdk/pull/17) [`bda4bd8`](https://github.com/viuteam/emporix-sdk/commit/bda4bd8b5b02e2b397f3a0751a45ac204b8572a0) Thanks [@amnael1](https://github.com/amnael1)! - Anonymous session continuity: when the cached anonymous access token is
17
+ expired (or rejected with a 401), the SDK now **refreshes via the refresh
18
+ token first**, preserving the same `sessionId` (and thus the anonymous
19
+ cart), and only falls back to a brand-new anonymous login if the refresh
20
+ fails. Previously every expiry/401 started a fresh session with a new
21
+ `sessionId`. Adds an optional `expireAnonymous()` to the `TokenProvider`
22
+ interface (used by the HTTP 401 path to keep the refresh token);
23
+ `invalidateAnonymous()` still performs a full reset.
24
+
25
+ - [#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:
26
+ - New `client.companies` / `client.contacts` / `client.locations` services over Customer Management (legal entities, contact assignments, locations).
27
+ - New `client.customerGroups` (read-only) over IAM (groups filtered by `b2b.legalEntityId`).
28
+ - New `EmporixInsufficientScopeError` subclass of `EmporixForbiddenError`, surfaced from 403 responses that carry a `missing scope: …` detail. Carries `requiredScope`.
29
+ - New `ServiceName` entries `"customer-management"` and `"iam"` for logger scoping.
30
+
31
+ No breaking changes. Existing `cart.getCurrent({ legalEntityId })` and `customer.refresh({ legalEntityId })` are now exercised in tests.
32
+
33
+ - [#49](https://github.com/viuteam/emporix-sdk/pull/49) [`f18e55c`](https://github.com/viuteam/emporix-sdk/commit/f18e55ceec9784e5aad6e95604e016c5858f9bdc) Thanks [@amnael1](https://github.com/amnael1)! - Expose B2B services as subpath imports: `@viu/emporix-sdk/companies`, `@viu/emporix-sdk/contacts`, `@viu/emporix-sdk/locations`, `@viu/emporix-sdk/customer-groups`. The services were already reachable via the package root; this adds the matching `exports` entries and `tsup` build artefacts so tree-shaking and selective imports work the same way they do for `./customer`, `./product`, etc.
34
+
35
+ - [#5](https://github.com/viuteam/emporix-sdk/pull/5) [`f312f22`](https://github.com/viuteam/emporix-sdk/commit/f312f228f17686476ce3458436758bd05af63fce) Thanks [@amnael1](https://github.com/amnael1)! - Fix two defects found by live verification of the example apps:
36
+ - **Browser compatibility:** the SDK read `process.env` unconditionally
37
+ (logger level resolution + console-logger `pretty` default), throwing
38
+ `ReferenceError: process is not defined` in browsers/edge runtimes. All env
39
+ reads now go through a guarded `readEnv()`. The SDK works in the browser
40
+ without `logger: false`.
41
+ - **`credentials.backend` is now optional.** Storefront/SPA apps use only
42
+ `credentials.storefront` (anonymous) plus caller-supplied customer tokens and
43
+ must never ship a backend secret. `validateConfig` no longer requires
44
+ `backend`; a missing backend is enforced lazily (clear `EmporixAuthError`)
45
+ only when a `service` AuthContext is actually used.
46
+
47
+ - [#7](https://github.com/viuteam/emporix-sdk/pull/7) [`959c6cc`](https://github.com/viuteam/emporix-sdk/commit/959c6cc3d0a4a37870cb72d5573b6fde9b0faa65) Thanks [@amnael1](https://github.com/amnael1)! - Add CheckoutService (cart and quote checkout, `saas-token` header, guest
48
+ checkout, `siteCode`) and PaymentGatewayService (frontend payment modes,
49
+ post-checkout deferred authorize). HttpClient gains per-request `headers`;
50
+ `saas-token` is added to the redaction floor. New subpath exports
51
+ `@viu/emporix-sdk/checkout` and `@viu/emporix-sdk/payment`.
52
+
53
+ - [#2](https://github.com/viuteam/emporix-sdk/pull/2) [`d52bcdc`](https://github.com/viuteam/emporix-sdk/commit/d52bcdc79433daaf143586264a409cad57e404a1) Thanks [@amnael1](https://github.com/amnael1)! - Add OpenAPI codegen pipeline and the Customer, Product, Category and Cart
54
+ service facades plus the EmporixClient aggregator with per-service subpath
55
+ exports.
56
+
57
+ - [#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.
58
+
59
+ **SDK (`@viu/emporix-sdk`)**
60
+ - `EmporixClient.config` is now a public read-only field, so hosts can read static settings such as `storefront.context.siteCode` without re-plumbing.
61
+ - **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.
62
+ - **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: […] }`).
63
+
64
+ **React (`@viu/emporix-sdk-react`)**
65
+ - `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.
66
+
67
+ **Migration**
68
+
69
+ ```ts
70
+ // SDK getCurrent:
71
+ - const cart = await client.carts.getCurrent(auth.customer(token));
72
+ + const cart = await client.carts.getCurrent(auth.customer(token), { siteCode: "main" });
73
+
74
+ // SDK merge:
75
+ - await client.carts.merge(anonCartId, auth.customer(token));
76
+ + await client.carts.merge(customerCartId, [anonCartId], auth.customer(token));
77
+ ```
78
+
79
+ 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).
80
+
81
+ - [#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
82
+ `GET /customer/{tenant}/logout?accessToken=…` authorized with the customer
83
+ token, invalidating it server-side (204). `useCustomerSession().logout()` is
84
+ now async: it performs the server logout best-effort (ignoring failures, e.g.
85
+ an already-expired token) and then clears the local session. The token is
86
+ sent only as a query param the SDK never logs (the logger uses the path, not
87
+ the full URL).
88
+
89
+ - [#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`,
90
+ `listSegmentItems`, `getCategoryTree`, plus the hydrate helpers
91
+ `listMyProductIds` / `listMyCategoryIds` / `listMyProducts` /
92
+ `listMyCategories` that map segment-item ids to real `Product` /
93
+ `Category` objects via parallel `products.get` / `categories.get` calls.
94
+ All methods require a customer/raw `AuthContext` and use the shared
95
+ `requireCustomer` guard (also adopted by `customer.ts` and `payment.ts`).
96
+
97
+ React adds three lightweight hooks: `useMySegments`, `useMySegmentItems`,
98
+ `useMySegmentCategoryTree`. Each reads the customer token from the
99
+ storage and is `enabled: false` when there is no token (no network call
100
+ for guests). Exposed on the `@viu/emporix-sdk/segment` subpath.
101
+
102
+ - [#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?,
103
+ legalEntityId? }, auth?)` calls `GET /customer/{tenant}/refreshauthtoken`
104
+ (authorized with an anonymous token, default), returning a new
105
+ `CustomerSession` with the **same `sessionId`**. The refresh endpoint does
106
+ not return a `saas_token`, so the original is carried forward via the
107
+ `saasToken` input. `useCustomerSession` now captures the refresh/saas tokens
108
+ at `login`, exposes `refreshToken`, and adds a `refreshSession()` action
109
+ that exchanges the refresh token and updates the stored customer token.
110
+
111
+ - [#1](https://github.com/viuteam/emporix-sdk/pull/1) [`4cdfa41`](https://github.com/viuteam/emporix-sdk/commit/4cdfa411ffb48b79510b0e98faa9ddf6f8c0600c) Thanks [@amnael1](https://github.com/amnael1)! - Add SDK foundation: config validation, EmporixError hierarchy, per-service
112
+ logger with redaction, TokenProvider (service + anonymous with
113
+ sessionId-preserving refresh), and the HTTP client with retry and 401
114
+ asymmetry.
115
+
116
+ - [#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
117
+ type. `carts.create` takes `CreateCart`, `carts.addItem` takes
118
+ `CartItemRequest` (now requires `product`/`quantity`/`price`),
119
+ `carts.updateItem` takes `UpdateCartItem`, `checkout.placeOrder` takes
120
+ `RequestCheckout`, `checkout.placeOrderFromQuote` takes
121
+ `RequestFromQuoteCheckout`, `payments.authorize` takes
122
+ `AuthorizePaymentRequest` (`{ order: { id }, … }`),
123
+ `customers.changePassword` takes `{ currentPassword, newPassword }`,
124
+ `customers.confirmPasswordReset` takes `{ token, password }`,
125
+ `customers.signup`/`update`/`addresses.*` take the generated DTOs. All
126
+ ergonomic input wrappers and input transformations are removed — callers
127
+ send the exact wire shape. `useCartMutations.addItem`/`updateItem` mutation
128
+ variables change accordingly. `CustomerService.login` keeps its literal
129
+ `{ email, password }` input and snake_case `CustomerSession` response (no
130
+ generated request type exists for it).
131
+
132
+ - [#12](https://github.com/viuteam/emporix-sdk/pull/12) [`693c58c`](https://github.com/viuteam/emporix-sdk/commit/693c58c5d148eeef746aef18a8f5dada766d7041) Thanks [@amnael1](https://github.com/amnael1)! - BREAKING: service methods now return the generated OpenAPI types instead of
133
+ the simplified hand-rolled interfaces. `Product`, `Cart`, `Category`,
134
+ `CategoryNode`, `CheckoutResult`, `PaymentMode`, `Customer`, and `Address`
135
+ are now type aliases over the generated schemas, so all API fields are typed
136
+ and available. Code that relied on the previous narrow shapes may need to
137
+ adjust field access — notably `Customer.email` is now `Customer.contactEmail`,
138
+ `Cart.items` / `Product.id` are optional per the spec, and product `name` is a
139
+ localized object. `CustomerService.login` / `CustomerSession` are unchanged
140
+ (the login wire is snake_case; camelCase is deprecated).
141
+
142
+ - [#6](https://github.com/viuteam/emporix-sdk/pull/6) [`59b78a8`](https://github.com/viuteam/emporix-sdk/commit/59b78a87d1dd56568e068c0a7738223714cb086b) Thanks [@amnael1](https://github.com/amnael1)! - Fix `CustomerService.login` wire mapping. The Emporix `CustomerToken` response
143
+ is snake_case (`access_token`, `saas_token`, `refresh_token`, `session_id`,
144
+ `expires_in`); the camelCase variants are deprecated in the spec and may be
145
+ absent on real tenants, so the previous camelCase-only mapping returned
146
+ `undefined` tokens. Mapping is now snake_case-first with a camelCase fallback,
147
+ and `CustomerSession` additionally exposes `sessionId` and `expiresIn`. The
148
+ `saasToken` (JWT) is documented as required for the checkout `saas-token`
149
+ header.
150
+
151
+ - [#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", ... })`
152
+ posts to `POST /media/{tenant}/assets` (multipart for BLOB, JSON for LINK);
153
+ convenience helpers `uploadFile`, `link`, `attachToProduct`,
154
+ `detachFromProduct`, `listForProduct` wrap the common product-attachment
155
+ flows. `HttpClient` now passes `FormData` bodies through `fetch` verbatim
156
+ (no Content-Type/JSON-stringify). React adds a thin `useProductMedia(id)`
157
+ hook that reads `productMedia` from the existing product query (no
158
+ service-token call in the browser).
159
+
160
+ BREAKING: `ProductService.media` is removed — it called a path
161
+ (`/product/{tenant}/products/{id}/media`) that does not exist in the
162
+ Emporix Product API. Migrate to `client.media.listForProduct(productId)`
163
+ (admin/server) or read `product.productMedia` from `client.products.get`
164
+ (storefront).
165
+
166
+ - [#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.
167
+
168
+ **SDK**
169
+ - `client.sites.list()` — list active sites for the tenant.
170
+ - `client.sites.get(code)` — retrieve one site by code.
171
+ - `client.sites.current()` — convenience for the `default: true` site.
172
+ - New `Site` type mirroring the `SiteDto` schema (code, name, active,
173
+ default, currency, languages, homeBase, shipToCountries, …).
174
+
175
+ **React**
176
+ - `useSites()` — list active sites.
177
+ - `useDefaultSite()` — the default site.
178
+
179
+ No breaking changes. The active-site runtime context (provider state,
180
+ `setSite`, cache-key migration) follows in MS-2.
181
+
182
+ - [#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.
183
+
184
+ **SDK**
185
+ - `client.sessionContext.get()` — `GET /session-context/{tenant}/me/context`.
186
+ Returns `null` (not throws) when the server returns 404 — i.e. when the
187
+ user has not created a cart yet and no session-context exists.
188
+ - `client.sessionContext.patch(input)` — `PATCH /session-context/{tenant}/me/context`
189
+ with optimistic-locking. Looks up `metadata.version` via GET first
190
+ unless caller provides one. Returns `true` when applied, `false` when
191
+ there is no session context yet (404 on the GET → patch skipped).
192
+ - New `SessionContext` and `SessionContextPatch` types.
193
+
194
+ **React**
195
+ - `setSite()` is now async. It flips local state + storage + cart-id
196
+ - cache-invalidation synchronously (optimistic UI), then PATCHes the
197
+ server. Skips the PATCH when no session exists yet (404 on GET).
198
+ - `useSiteContext()` gains `isSwitching: boolean` and
199
+ `switchError: Error | null`. The optimistic state is NOT rolled back
200
+ on PATCH failure — surface the error in UI; the next user interaction
201
+ retries.
202
+
203
+ No breaking changes. Existing call sites continue to work — `setSite("X")`
204
+ without `await` still flips the UI; awaiting it blocks until the
205
+ server-side sync completes.
206
+
207
+ - [#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.
208
+
209
+ **SDK (additive)**
210
+ - `TokenProvider.onRefresh(listener)` — optional subscription to
211
+ token-refresh events. `DefaultTokenProvider` implements it (anonymous
212
+ refresh path).
213
+
214
+ **React (additive)**
215
+ - `<EmporixProvider onTelemetry={fn}>` — receives a typed event stream
216
+ covering cache hit/miss, refetches, errors, mutations, auth refreshes,
217
+ and storage writes.
218
+ - `useEmporixTelemetry()` — returns `{ emit }` for consumer-side custom
219
+ events on the same channel.
220
+ - `EmporixStorage.subscribeAll(listener)` — optional subscription to all
221
+ storage write events. Implemented in all three built-in adapters
222
+ (memory, localStorage, cookie).
223
+
224
+ **Event types:**
225
+ - `cache.hit`, `cache.miss`, `query.refetch`, `query.error`
226
+ - `mutation.success`, `mutation.error`
227
+ - `auth.refresh`
228
+ - `storage.write`
229
+ - `custom`
230
+
231
+ No breaking changes. The entire telemetry layer is no-op when
232
+ `onTelemetry` is not passed. Existing `TokenProvider` / `EmporixStorage`
233
+ implementations continue to work without implementing the new optional
234
+ methods.
235
+
236
+ - [#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 (customer foundation):
237
+ - New `client.orders` — `listMine` / `get` / `transition` / `cancel` over the customer-facing `/order-v2/{tenant}/orders/*` endpoints. All methods accept an `opts.saasToken` forwarded as the `saas-token` header.
238
+ - New `client.salesOrders` — `get` / `update` over `/order-v2/{tenant}/salesorders/{id}` for backend / service-account use (status, mixins, custom attributes patches). `update` accepts `opts.recalculate` (server default `true`).
239
+ - New hand-rolled `Order`, `OrderItem`, `OrderStatus`, `OrderTransition`, `SalesOrderPatch` types (pending real codegen).
240
+ - New subpath export `@viu/emporix-sdk/orders`.
241
+ - New `client.carts.addItemsBatch(cartId, items, auth)` — wraps `POST /cart/{tenant}/carts/{cartId}/itemsBatch` (cap 200 items per call). Per-entry status surfaces partial failures via the generated `BatchResponse` shape.
242
+
243
+ No breaking changes. The full `/salesorders` admin list, order split, returns, and order events are deferred sub-specs.
244
+
245
+ - [#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
246
+ legacy `Page<T>` shape (whose `total` was always `NaN`, since the HTTP
247
+ client never exposed `X-Total-Count`) and the `paginate()` async
248
+ iterator.
249
+
250
+ **BREAKING:**
251
+ - `ProductService.list` / `ProductService.search` now return
252
+ `PaginatedItems<Product>` (`{ items, pageNumber, pageSize, hasNextPage }`)
253
+ instead of `Page<Product>` (`{ items, total, offset, limit }`).
254
+ - `CategoryService.list` returns `PaginatedItems<Category>`;
255
+ `CategoryService.productsIn` returns `PaginatedItems<Product>`.
256
+ - `useProducts` / `useCategories` now resolve to `PaginatedItems<T>`.
257
+ - `Page<T>` and `paginate()` are no longer exported from `@viu/emporix-sdk`.
258
+
259
+ **Fixed:**
260
+ - `useProductsInfinite` previously over-fetched a trailing empty page
261
+ before terminating, and its `getNextPageParam` was tied to the
262
+ fetched-page count rather than the cursor. It now derives the next
263
+ page from `last.hasNextPage` / `last.pageNumber + 1` — same pattern as
264
+ the segment-hydrate infinite hooks.
265
+
266
+ **Added:**
267
+ - `useCategoriesInfinite` — mirror of `useProductsInfinite`.
268
+ - `iterateAll<T>(fetchPage, start?)` async iterator over
269
+ `PaginatedItems<T>`. Replaces `paginate()` for "iterate every item
270
+ across pages" use cases.
271
+
272
+ **Migration:**
273
+
274
+ ```ts
275
+ // Before
276
+ const { items, total } = await client.products.list({ pageNumber: 1, pageSize: 50 });
277
+ // total was always NaN.
278
+
279
+ // After
280
+ const { items, hasNextPage } = await client.products.list({ pageNumber: 1, pageSize: 50 });
281
+ ```
282
+
283
+ ```ts
284
+ // Before
285
+ for await (const p of paginate((offset, limit) => svc.list(...), 50)) { ... }
286
+
287
+ // After
288
+ for await (const p of svc.listAll({ pageSize: 50 })) { ... }
289
+ // or, for custom sources:
290
+ for await (const x of iterateAll<X>((pageNumber) => fetchPage(pageNumber))) { ... }
291
+ ```
292
+
293
+ - [#13](https://github.com/viuteam/emporix-sdk/pull/13) [`dfabb02`](https://github.com/viuteam/emporix-sdk/commit/dfabb02882ca65e2a32e4a52082c0b14dc71faa8) Thanks [@amnael1](https://github.com/amnael1)! - Add `PriceService` (price matching only). `prices.matchByContext(input, auth?)`
294
+ resolves prices from the session context (default anonymous token);
295
+ `prices.match(input, auth?)` resolves from an explicit context (default
296
+ service token). Both the request and the response use the generated price
297
+ schema — `PriceMatchByContextInput` (`MatchByContext`), `PriceMatchInput`
298
+ (`Match`), `PriceMatch` (`MatchResponse`) — so every spec field is typed.
299
+ Exposed on `EmporixClient.prices` and via the `@viu/emporix-sdk/price`
300
+ subpath.
301
+
302
+ - [#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.
303
+
304
+ - [#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.
305
+
306
+ No code changes — no SDK feature uses a Node API beyond what Node 20 provides. Browser consumers are unaffected.
307
+
308
+ - [#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
309
+ of N+1 `GET /products/{id}` calls. New
310
+ `ProductService.searchByIds(ids, { chunkSize? }, auth?)` and
311
+ `CategoryService.searchByIds(...)` POST `/search` with
312
+ `q="id:(id1,id2,…)"`, chunking at 100 IDs by default. Adds the generic
313
+ `PaginatedItems<T>` (`{ items, pageNumber, pageSize, hasNextPage }`) in
314
+ `core/context.ts`.
315
+
316
+ **BREAKING:** `SegmentService.listMyProducts` and
317
+ `SegmentService.listMyCategories` now return `PaginatedItems<Product>` /
318
+ `PaginatedItems<Category>` instead of a flat `Product[]` / `Category[]`.
319
+ `SegmentService.listItems` gains optional `pageNumber` / `pageSize`
320
+ params (additive). `listMyProductIds` / `listMyCategoryIds` are
321
+ unchanged.
322
+
323
+ React adds four new hooks: `useMySegmentProducts` /
324
+ `useMySegmentProductsInfinite` and `useMySegmentCategories` /
325
+ `useMySegmentCategoriesInfinite`. The infinite variants use
326
+ `useInfiniteQuery` with a `pageNumber` cursor and `hasNextPage`-driven
327
+ `getNextPageParam`. All four are disabled when no customer token is in
328
+ storage.
329
+
330
+ - [#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,
331
+ codeVerifier?, sessionId? })` performs the Authorization-Code code exchange
332
+ (`POST /customer/{tenant}/socialLogin`); `customers.exchangeToken({
333
+ subjectToken, config? })` performs the RFC 8693 token exchange
334
+ (`POST /customer/{tenant}/exchangeauthtoken`). Both default to anonymous auth
335
+ and return a `CustomerSession` (now with optional `socialAccessToken` /
336
+ `socialIdToken` from socialLogin); `expires_in` is normalized to a number
337
+ across both flows. `useCustomerSession` gains `socialLogin` and
338
+ `exchangeToken` actions that store the session like `login`.
339
+
340
+ - [#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.
341
+
342
+ **SDK (`@viu/emporix-sdk`)**
343
+ - 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.
344
+ - `invalidateAnonymous()` now also clears the attached store (`write(null)`).
345
+ - `EmporixClient.tokenProvider` is now a public, read-only field — so hosts can call `attachAnonymousStore` after construction.
346
+
347
+ **React (`@viu/emporix-sdk-react`)**
348
+ - `TokenStorage` renamed to `EmporixStorage` (alias `TokenStorage` is kept). New methods: `getCartId / setCartId`, `getAnonymousSession / setAnonymousSession`. All three storage backends — memory, `localStorage`, cookie — implement them.
349
+ - `EmporixProvider` wires the storage's anonymous-session accessors to the SDK's `attachAnonymousStore` so the anonymous cart can survive a browser reload.
350
+ - New `useCreateCart` mutation hook: auto-detects customer vs anonymous auth and persists `cartId` via `storage.setCartId`.
351
+ - `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.
352
+
353
+ **Migration**
354
+
355
+ 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()`).
356
+
357
+ ### Patch Changes
358
+
359
+ - [#7](https://github.com/viuteam/emporix-sdk/pull/7) [`e10854f`](https://github.com/viuteam/emporix-sdk/commit/e10854fc9ef11fec74f24e65dedbe11c3ca09d22) Thanks [@amnael1](https://github.com/amnael1)! - Document the checkout & payment flow (docs/checkout.md, saas-token note in
360
+ docs/auth.md, README links) and add a Next.js checkout-step example. No API
361
+ changes.
362
+
363
+ - [#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:
364
+ - **`catalog.spec.ts`** — anonymous catalog renders 12 products; only `GET /anonymous/login` + `GET /product/viu/products` hit Emporix on `/`.
365
+ - **`customer-session.spec.ts`** — login resolves the customer profile + stores the token; logout clears the token.
366
+ - **`guest-checkout.spec.ts`** — `useCreateCart` → `useCartMutations.addItem` → `useCheckout.placeOrder` (anonymous) → real order `EONxxxx` placed on `viu`.
367
+ - **`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.
368
+
369
+ 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.
370
+
371
+ 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.
372
+
373
+ - [#4](https://github.com/viuteam/emporix-sdk/pull/4) [`5f6cb4a`](https://github.com/viuteam/emporix-sdk/commit/5f6cb4ad207f4a1c8562d1da1713255762b9c436) Thanks [@amnael1](https://github.com/amnael1)! - Add documentation (root + package READMEs, CONTRIBUTING, docs/logging,
374
+ docs/auth, docs/react) and runnable examples (node-server, vite-spa,
375
+ next-app-router). No API changes.
376
+
377
+ - [#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
378
+ TypeScript resolver evaluate `exports` conditions in declaration order;
379
+ with `import`/`require` listed before `types`, the `types` condition was
380
+ never reached, emitting build warnings and preventing consumers from
381
+ picking up the generated `.d.ts` entry points. Every subpath in both
382
+ packages now uses `{ types, import, require }` order.
383
+
384
+ - [#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
385
+ declared `LICENSE` but the file was only present at the repo root; npm
386
+ publishes per-package, so a copy now lives inside each package directory.
387
+ Fixes "License: not specified" on npmjs.com and unblocks corporate
388
+ license-compliance scanners (Snyk, Black Duck).
389
+
390
+ - [#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:
391
+ - **`@viu/emporix-sdk-react`**: tighten the `@tanstack/react-query` peer
392
+ range from `^5.0.0` to `^5.51.0`. This matches the version the package
393
+ is developed and tested against. The previous range claimed support
394
+ for v5.0–v5.50 that was never exercised in CI; tightening avoids a
395
+ silent runtime mismatch for consumers who happen to be on those older
396
+ patch versions.
397
+ - **Both packages**: replace the bare-string `author: "viuteam"` with an
398
+ `author` object — `{ "name": "viu", "url": "https://github.com/viuteam" }`
399
+ — so the npm package page shows "viu" (our display name) and links
400
+ back to the GitHub org page (`viuteam`, the actual org slug).
401
+ - **`LICENSE` (root and per-package)**: the MIT copyright holder is now
402
+ `VIU AG` (the legal entity) instead of the GitHub org slug `viuteam`,
403
+ so license-compliance scanners attribute the package correctly.
404
+
405
+ - [#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
406
+ session, query hooks, cart mutations with optimistic updates, error helpers and
407
+ SSR prefetch helpers. Core: expose EmporixClient.tenant for query-key namespacing.
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,115 @@
1
+ # @viu/emporix-sdk
2
+
3
+ Framework-agnostic TypeScript SDK for the Emporix Commerce Engine. Native
4
+ `fetch` only (Node 20.19+), zero runtime dependencies.
5
+
6
+ ## Install
7
+
8
+ ```bash
9
+ pnpm add @viu/emporix-sdk
10
+ ```
11
+
12
+ ## Quick start
13
+
14
+ ```ts
15
+ import { EmporixClient, auth } from "@viu/emporix-sdk";
16
+
17
+ const sdk = new EmporixClient({
18
+ tenant: "mytenant",
19
+ credentials: {
20
+ backend: { clientId: "...", secret: "..." },
21
+ storefront: { clientId: "..." },
22
+ custom: { partner: { clientId: "...", secret: "..." } },
23
+ },
24
+ });
25
+
26
+ // Anonymous browse
27
+ const products = await sdk.products.list();
28
+
29
+ // Authenticated shopper
30
+ const { customerToken } = await sdk.customers.login({ email, password });
31
+ const ctx = auth.customer(customerToken);
32
+ const me = await sdk.customers.me(ctx);
33
+ const cart = await sdk.carts.create({ currency: "EUR" }, ctx);
34
+ await sdk.carts.addItem(cart.id, { productId: "p_1", quantity: 2 }, ctx);
35
+
36
+ // Service account with a custom credential set
37
+ await sdk.products.list(undefined, auth.service("partner"));
38
+
39
+ // Externally-issued token (SSO/token-exchange done upstream)
40
+ await sdk.customers.me(auth.raw(externalJwt));
41
+ ```
42
+
43
+ ## Configuration
44
+
45
+ | Option | Default | Notes |
46
+ | --- | --- | --- |
47
+ | `tenant` (required) | — | lowercase, 3–16 chars, `^[a-z][a-z0-9]+$` |
48
+ | `credentials.backend` (required) | — | `{ clientId, secret, scope? }` — service token |
49
+ | `credentials.storefront` | — | `{ clientId }` — anonymous token (no secret) |
50
+ | `credentials.custom` | — | `Record<name, { clientId, secret, scope? }>` |
51
+ | `host` | `https://api.emporix.io` | |
52
+ | `timeouts` | `{ connectMs: 10000, readMs: 60000 }` | |
53
+ | `retry` | `{ maxAttempts: 3 }` | 5xx/429 backoff + jitter, respects `Retry-After` |
54
+ | `cache` | `{ expirationBufferSeconds: 60, maxLifetimeSeconds: 3600 }` | token cache |
55
+ | `logger` | console @ `warn` | `false`, a `Logger`, or `{ level, services, pretty, redact }` |
56
+ | `tokenProvider` | built-in | inject for SSO/token-exchange |
57
+
58
+ ## AuthContext per method
59
+
60
+ `auth.service(name?)`, `auth.anonymous()`, `auth.customer(token)`,
61
+ `auth.raw(token)`. The last argument of every service method is the
62
+ `AuthContext`; defaults below apply when omitted.
63
+
64
+ | Method | Default | Required kind |
65
+ | --- | --- | --- |
66
+ | `customers.signup` / `.login` / `.requestPasswordReset` / `.confirmPasswordReset` | `anonymous` | — |
67
+ | `customers.me` / `.update` / `.changePassword` / `.addresses.*` | — | `customer` or `raw` |
68
+ | `customers.anonymous()` | — | obtains anonymous session (token ignored) |
69
+ | `products.*` / `categories.*` (reads) | `anonymous` | — (pass `customer` for personalized pricing) |
70
+ | `carts.*` | — | explicit `customer` or `anonymous` |
71
+ | `carts.merge` | — | `customer` |
72
+ | `companies.*` / `contacts.*` / `locations.*` / `customerGroups.*` (B2B) | — | `customer` (reads need `*_read_own`; mutations need `*_manage`) |
73
+
74
+ `AuthContext` is **per call, never stored** — one client safely serves many
75
+ concurrent shoppers (SSR/edge/multi-tenant). SDK-managed (`service`/`anonymous`)
76
+ 401s refresh-and-retry once; caller-managed (`customer`/`raw`) 401s propagate as
77
+ `EmporixAuthError`. Full details in [`../../docs/auth.md`](../../docs/auth.md).
78
+
79
+ ## Logging
80
+
81
+ Structured, level-aware, per-service controllable, secret-redacting, zero
82
+ dependency. See [`../../docs/logging.md`](../../docs/logging.md).
83
+
84
+ ## Checkout & payment
85
+
86
+ `sdk.checkout.placeOrder(...)` / `.placeOrderFromQuote(...)` and
87
+ `sdk.payments.listPaymentModes(...)` / `.authorize(...)`. The `saas-token`
88
+ header, guest checkout and deferred payment are covered in
89
+ [`../../docs/checkout.md`](../../docs/checkout.md).
90
+
91
+ ## B2B
92
+
93
+ `sdk.companies` (legal entities), `sdk.contacts` (contact assignments),
94
+ `sdk.locations` (HQ/warehouse/office), `sdk.customerGroups` (IAM groups,
95
+ read-only for now). Switching company scope is a customer-token rescope via
96
+ `sdk.customers.refresh({ refreshToken, legalEntityId })`. Mutations that the
97
+ customer's role lacks scope for surface as `EmporixInsufficientScopeError`
98
+ (extends `EmporixForbiddenError`, carries `requiredScope`). See
99
+ [`../../docs/b2b.md`](../../docs/b2b.md).
100
+
101
+ ## Subpath exports
102
+
103
+ `@viu/emporix-sdk` (everything) plus `./customer`, `./product`, `./category`,
104
+ `./cart`, `./checkout`, `./payment`, `./price`, `./media`, `./segment`,
105
+ `./companies`, `./contacts`, `./locations`, `./customer-groups` for tree-shaking.
106
+
107
+ ## Authors
108
+
109
+ - **Dominic Fritschi** — _Maintainer_ — [VIU](https://www.viu.ch)
110
+ - **Andreas Nebiker** — _Contributor_ — [VIU](https://www.viu.ch)
111
+ - The **Team at VIU** — _Contributors_ — [VIU](https://www.viu.ch)
112
+
113
+ ## License
114
+
115
+ This project is licensed under the MIT License — see the [LICENSE](./LICENSE) file for details.