@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.
- package/CHANGELOG.md +407 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/dist/cart.cjs +214 -0
- package/dist/cart.cjs.map +1 -0
- package/dist/cart.d.cts +1807 -0
- package/dist/cart.d.ts +1807 -0
- package/dist/cart.js +4 -0
- package/dist/cart.js.map +1 -0
- package/dist/category.cjs +97 -0
- package/dist/category.cjs.map +1 -0
- package/dist/category.d.cts +313 -0
- package/dist/category.d.ts +313 -0
- package/dist/category.js +4 -0
- package/dist/category.js.map +1 -0
- package/dist/checkout.cjs +98 -0
- package/dist/checkout.cjs.map +1 -0
- package/dist/checkout.d.cts +252 -0
- package/dist/checkout.d.ts +252 -0
- package/dist/checkout.js +4 -0
- package/dist/checkout.js.map +1 -0
- package/dist/chunk-4BJUCCKJ.js +86 -0
- package/dist/chunk-4BJUCCKJ.js.map +1 -0
- package/dist/chunk-6UNFMCHZ.js +95 -0
- package/dist/chunk-6UNFMCHZ.js.map +1 -0
- package/dist/chunk-B56JZNI7.js +49 -0
- package/dist/chunk-B56JZNI7.js.map +1 -0
- package/dist/chunk-BFQ732VZ.js +11 -0
- package/dist/chunk-BFQ732VZ.js.map +1 -0
- package/dist/chunk-F3WAQH2E.js +120 -0
- package/dist/chunk-F3WAQH2E.js.map +1 -0
- package/dist/chunk-GWJ2U2CP.js +14 -0
- package/dist/chunk-GWJ2U2CP.js.map +1 -0
- package/dist/chunk-HEO34WLO.js +47 -0
- package/dist/chunk-HEO34WLO.js.map +1 -0
- package/dist/chunk-HKPZI7Y5.js +30 -0
- package/dist/chunk-HKPZI7Y5.js.map +1 -0
- package/dist/chunk-IEZHGU7F.js +171 -0
- package/dist/chunk-IEZHGU7F.js.map +1 -0
- package/dist/chunk-KTODT6DV.js +86 -0
- package/dist/chunk-KTODT6DV.js.map +1 -0
- package/dist/chunk-NM7VDXBR.js +57 -0
- package/dist/chunk-NM7VDXBR.js.map +1 -0
- package/dist/chunk-OMYVOOSX.js +57 -0
- package/dist/chunk-OMYVOOSX.js.map +1 -0
- package/dist/chunk-QDABAMVN.js +89 -0
- package/dist/chunk-QDABAMVN.js.map +1 -0
- package/dist/chunk-RXVVFNYN.js +56 -0
- package/dist/chunk-RXVVFNYN.js.map +1 -0
- package/dist/chunk-SKX7KCMH.js +23 -0
- package/dist/chunk-SKX7KCMH.js.map +1 -0
- package/dist/chunk-TLH6SLAF.js +215 -0
- package/dist/chunk-TLH6SLAF.js.map +1 -0
- package/dist/chunk-Z6PNI5VT.js +142 -0
- package/dist/chunk-Z6PNI5VT.js.map +1 -0
- package/dist/companies.cjs +58 -0
- package/dist/companies.cjs.map +1 -0
- package/dist/companies.d.cts +31 -0
- package/dist/companies.d.ts +31 -0
- package/dist/companies.js +3 -0
- package/dist/companies.js.map +1 -0
- package/dist/contacts.cjs +51 -0
- package/dist/contacts.cjs.map +1 -0
- package/dist/contacts.d.cts +28 -0
- package/dist/contacts.d.ts +28 -0
- package/dist/contacts.js +3 -0
- package/dist/contacts.js.map +1 -0
- package/dist/context-vKgzZ-5f.d.cts +329 -0
- package/dist/context-vKgzZ-5f.d.ts +329 -0
- package/dist/customer-groups.cjs +25 -0
- package/dist/customer-groups.cjs.map +1 -0
- package/dist/customer-groups.d.cts +36 -0
- package/dist/customer-groups.d.ts +36 -0
- package/dist/customer-groups.js +3 -0
- package/dist/customer-groups.js.map +1 -0
- package/dist/customer.cjs +262 -0
- package/dist/customer.cjs.map +1 -0
- package/dist/customer.d.cts +386 -0
- package/dist/customer.d.ts +386 -0
- package/dist/customer.js +5 -0
- package/dist/customer.js.map +1 -0
- package/dist/index.cjs +2075 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +218 -0
- package/dist/index.d.ts +218 -0
- package/dist/index.js +791 -0
- package/dist/index.js.map +1 -0
- package/dist/locations.cjs +59 -0
- package/dist/locations.cjs.map +1 -0
- package/dist/locations.d.cts +30 -0
- package/dist/locations.d.ts +30 -0
- package/dist/locations.js +3 -0
- package/dist/locations.js.map +1 -0
- package/dist/media.cjs +123 -0
- package/dist/media.cjs.map +1 -0
- package/dist/media.d.cts +219 -0
- package/dist/media.d.ts +219 -0
- package/dist/media.js +3 -0
- package/dist/media.js.map +1 -0
- package/dist/orders.cjs +98 -0
- package/dist/orders.cjs.map +1 -0
- package/dist/orders.d.cts +167 -0
- package/dist/orders.d.ts +167 -0
- package/dist/orders.js +3 -0
- package/dist/orders.js.map +1 -0
- package/dist/payment.cjs +77 -0
- package/dist/payment.cjs.map +1 -0
- package/dist/payment.d.cts +110 -0
- package/dist/payment.d.ts +110 -0
- package/dist/payment.js +5 -0
- package/dist/payment.js.map +1 -0
- package/dist/price.cjs +88 -0
- package/dist/price.cjs.map +1 -0
- package/dist/price.d.cts +445 -0
- package/dist/price.d.ts +445 -0
- package/dist/price.js +4 -0
- package/dist/price.js.map +1 -0
- package/dist/product.cjs +100 -0
- package/dist/product.cjs.map +1 -0
- package/dist/product.d.cts +400 -0
- package/dist/product.d.ts +400 -0
- package/dist/product.js +4 -0
- package/dist/product.js.map +1 -0
- package/dist/segment.cjs +189 -0
- package/dist/segment.cjs.map +1 -0
- package/dist/segment.d.cts +241 -0
- package/dist/segment.d.ts +241 -0
- package/dist/segment.js +5 -0
- package/dist/segment.js.map +1 -0
- package/dist/types.gen-D1QuqPLs.d.cts +166 -0
- package/dist/types.gen-D1QuqPLs.d.ts +166 -0
- 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.
|