@viu/emporix-sdk 1.0.0 → 2.1.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 +56 -0
- package/README.md +26 -1
- package/dist/availability.cjs +101 -0
- package/dist/availability.cjs.map +1 -0
- package/dist/availability.d.cts +87 -0
- package/dist/availability.d.ts +87 -0
- package/dist/availability.js +4 -0
- package/dist/availability.js.map +1 -0
- package/dist/cart.d.cts +1 -1
- package/dist/cart.d.ts +1 -1
- package/dist/category.d.cts +1 -1
- package/dist/category.d.ts +1 -1
- package/dist/checkout.d.cts +1 -1
- package/dist/checkout.d.ts +1 -1
- package/dist/{chunk-QDABAMVN.js → chunk-JTJV7WUW.js} +25 -2
- package/dist/chunk-JTJV7WUW.js.map +1 -0
- package/dist/chunk-SCI7D74B.js +91 -0
- package/dist/chunk-SCI7D74B.js.map +1 -0
- package/dist/chunk-WSXRUYBE.js +248 -0
- package/dist/chunk-WSXRUYBE.js.map +1 -0
- package/dist/chunk-XMEP74BU.js +60 -0
- package/dist/chunk-XMEP74BU.js.map +1 -0
- package/dist/companies.d.cts +1 -1
- package/dist/companies.d.ts +1 -1
- package/dist/contacts.d.cts +1 -1
- package/dist/contacts.d.ts +1 -1
- package/dist/{context-vKgzZ-5f.d.cts → context-CGuxSb56.d.cts} +16 -1
- package/dist/{context-vKgzZ-5f.d.ts → context-CGuxSb56.d.ts} +16 -1
- package/dist/customer-groups.d.cts +1 -1
- package/dist/customer-groups.d.ts +1 -1
- package/dist/customer.d.cts +1 -1
- package/dist/customer.d.ts +1 -1
- package/dist/index.cjs +311 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -5
- package/dist/index.d.ts +8 -5
- package/dist/index.js +61 -6
- package/dist/index.js.map +1 -1
- package/dist/locations.d.cts +1 -1
- package/dist/locations.d.ts +1 -1
- package/dist/media.cjs +218 -9
- package/dist/media.cjs.map +1 -1
- package/dist/media.d.cts +103 -7
- package/dist/media.d.ts +103 -7
- package/dist/media.js +2 -1
- package/dist/orders.d.cts +1 -1
- package/dist/orders.d.ts +1 -1
- package/dist/payment.d.cts +1 -1
- package/dist/payment.d.ts +1 -1
- package/dist/price.cjs +44 -0
- package/dist/price.cjs.map +1 -1
- package/dist/price.d.cts +27 -2
- package/dist/price.d.ts +27 -2
- package/dist/price.js +1 -1
- package/dist/product.cjs +23 -0
- package/dist/product.cjs.map +1 -1
- package/dist/product.d.cts +18 -1
- package/dist/product.d.ts +18 -1
- package/dist/product.js +1 -1
- package/dist/segment.d.cts +1 -1
- package/dist/segment.d.ts +1 -1
- package/package.json +7 -2
- package/dist/chunk-F3WAQH2E.js +0 -120
- package/dist/chunk-F3WAQH2E.js.map +0 -1
- package/dist/chunk-HEO34WLO.js +0 -47
- package/dist/chunk-HEO34WLO.js.map +0 -1
- package/dist/chunk-QDABAMVN.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,61 @@
|
|
|
1
1
|
# @viu/emporix-sdk
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#56](https://github.com/viuteam/emporix-sdk/pull/56) [`939a1b0`](https://github.com/viuteam/emporix-sdk/commit/939a1b0a24063db38545dc81f88c319f93e81833) Thanks [@amnael1](https://github.com/amnael1)! - Add AvailabilityService (`client.availability.get` / `.getMany`) and the
|
|
8
|
+
`useAvailability` / `useAvailabilities` React hooks for site-aware product
|
|
9
|
+
availability. `getMany` uses the batch `POST .../search` endpoint and returns
|
|
10
|
+
results in input order; an opt-in `defaultAvailableOnNotFound` treats products
|
|
11
|
+
with no stock record as available. New `@viu/emporix-sdk/availability` subpath export.
|
|
12
|
+
|
|
13
|
+
- [#58](https://github.com/viuteam/emporix-sdk/pull/58) [`caaff28`](https://github.com/viuteam/emporix-sdk/commit/caaff2819e64cf42e4c58dfe4c04fa994312f901) Thanks [@amnael1](https://github.com/amnael1)! - Add PriceService.matchByContextChunked and the useMatchPricesChunked React hook:
|
|
14
|
+
split large match-prices-by-context requests into bounded-concurrency chunks
|
|
15
|
+
(default 50 items, 4 in flight) with per-chunk error handling.
|
|
16
|
+
|
|
17
|
+
- [#57](https://github.com/viuteam/emporix-sdk/pull/57) [`0302ea3`](https://github.com/viuteam/emporix-sdk/commit/0302ea368e6d7feb0a064aac71a6f5314380deb3) Thanks [@amnael1](https://github.com/amnael1)! - Add ProductService.listVariantChildren / listVariantChildrenAll and the
|
|
18
|
+
useVariantChildren React hook to resolve the VARIANT children of a
|
|
19
|
+
PARENT_VARIANT product without hand-building the search query.
|
|
20
|
+
|
|
21
|
+
## 2.0.0
|
|
22
|
+
|
|
23
|
+
### Major Changes
|
|
24
|
+
|
|
25
|
+
- [#52](https://github.com/viuteam/emporix-sdk/pull/52) [`26640fe`](https://github.com/viuteam/emporix-sdk/commit/26640fe281083e6ce0475a547e292ac82ba7d9bf) Thanks [@amnael1](https://github.com/amnael1)! - Add `client.media.download(assetId, auth?)` for retrieving asset content via `GET /media/{tenant}/assets/{id}/download`. Returns a discriminated union:
|
|
26
|
+
- `{ kind: "redirect", url }` for `PUBLIC` assets — the SDK captures the server's 30x `Location` header so the caller can redirect the user to the storage URL without proxying bytes.
|
|
27
|
+
- `{ kind: "bytes", data, etag?, contentType? }` for `PRIVATE` assets — the SDK returns the response body as an `ArrayBuffer`. When the server uses the OpenAPI-documented `text/plain` + base64 wire format, the SDK decodes it transparently; binary content types are passed through.
|
|
28
|
+
|
|
29
|
+
Also adds a low-level `HttpClient.requestRaw` escape hatch (used internally by `download`) for endpoints whose responses are not JSON. Auth resolution + timeout + logging are applied; the retry-on-5xx and 401-reauth-once paths from `request` are intentionally skipped (callers of `requestRaw` handle their own response shape).
|
|
30
|
+
|
|
31
|
+
Browser note: `download()` uses `redirect: "manual"` so it can observe `PUBLIC` redirect URLs. In Node this works as documented; in a browser the redirect `Location` is hidden by the fetch spec — there, `PUBLIC` downloads will surface as an error. Browser code should use the asset's `url` field (for `LINK` assets) or render `PUBLIC` `BLOB` assets via the storage URL directly.
|
|
32
|
+
|
|
33
|
+
**Breaking — `media.list()`** now returns `PaginatedItems<Asset>` instead of `Asset[]`. Callers must read `.items` for the array. The new shape includes `pageNumber`, `pageSize`, and the standard `hasNextPage` heuristic so paginated listings behave like every other list endpoint in the SDK. The previous shape silently truncated at the server-default page size (60); the new shape makes pagination explicit.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// before
|
|
37
|
+
const assets = await client.media.list(); // Asset[], page 1 only
|
|
38
|
+
// after
|
|
39
|
+
const { items, hasNextPage } = await client.media.list({ pageSize: 100 });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
`client.media.listForProduct(productId)` shares the same envelope change.
|
|
43
|
+
|
|
44
|
+
**Breaking — `media.update()`** now takes a discriminated input matching `create()`:
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
// before
|
|
48
|
+
await client.media.update(id, patch);
|
|
49
|
+
// after — JSON path (refIds, details, url, metadata)
|
|
50
|
+
await client.media.update(id, { kind: "json", body: patch });
|
|
51
|
+
// after — BLOB file-replacement (multipart, up to 10MB)
|
|
52
|
+
await client.media.update(id, { kind: "blob", file, body });
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The previous signature only supported the JSON path; the new BLOB path closes the gap where re-uploading bytes required `remove` + `create` (and lost the asset id). A new `client.media.replaceFile(assetId, { file, access, ... })` sugar wraps the common case.
|
|
56
|
+
|
|
57
|
+
New named type exports: `AssetUpdateBlobInput`, `AssetUpdateLinkInput`, `ListAssetsQuery`.
|
|
58
|
+
|
|
3
59
|
## 1.0.0
|
|
4
60
|
|
|
5
61
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @viu/emporix-sdk
|
|
2
2
|
|
|
3
|
+
[](https://github.com/viuteam/emporix-sdk/actions/workflows/pr-check.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@viu/emporix-sdk)
|
|
5
|
+
[](https://bundlephobia.com/package/@viu/emporix-sdk)
|
|
6
|
+
|
|
3
7
|
Framework-agnostic TypeScript SDK for the Emporix Commerce Engine. Native
|
|
4
8
|
`fetch` only (Node 20.19+), zero runtime dependencies.
|
|
5
9
|
|
|
@@ -70,6 +74,7 @@ await sdk.customers.me(auth.raw(externalJwt));
|
|
|
70
74
|
| `carts.*` | — | explicit `customer` or `anonymous` |
|
|
71
75
|
| `carts.merge` | — | `customer` |
|
|
72
76
|
| `companies.*` / `contacts.*` / `locations.*` / `customerGroups.*` (B2B) | — | `customer` (reads need `*_read_own`; mutations need `*_manage`) |
|
|
77
|
+
| `media.*` (Asset CRUD + download) | `service` | `service` — server-only (`media.asset_read` / `media.asset_manage`); never call from the browser |
|
|
73
78
|
|
|
74
79
|
`AuthContext` is **per call, never stored** — one client safely serves many
|
|
75
80
|
concurrent shoppers (SSR/edge/multi-tenant). SDK-managed (`service`/`anonymous`)
|
|
@@ -98,11 +103,31 @@ customer's role lacks scope for surface as `EmporixInsufficientScopeError`
|
|
|
98
103
|
(extends `EmporixForbiddenError`, carries `requiredScope`). See
|
|
99
104
|
[`../../docs/b2b.md`](../../docs/b2b.md).
|
|
100
105
|
|
|
106
|
+
## Media
|
|
107
|
+
|
|
108
|
+
`sdk.media` covers the full `/media/{tenant}/assets/*` surface: create
|
|
109
|
+
(`uploadFile` / `link` / `create`), list (paginated, `PaginatedItems<Asset>`),
|
|
110
|
+
get, update (JSON metadata or BLOB multipart file-replacement via the
|
|
111
|
+
`replaceFile` sugar), remove, and download (resolves to either a redirect
|
|
112
|
+
URL for `PUBLIC` assets or an `ArrayBuffer` for `PRIVATE`). All endpoints
|
|
113
|
+
require a server-only scope — every call defaults to a `service`
|
|
114
|
+
`AuthContext`. Storefronts read media via `product.productMedia` (denormalised
|
|
115
|
+
on the product) or the `useProductMedia(productId)` hook, not by calling
|
|
116
|
+
the Media service directly. See [`../../docs/media.md`](../../docs/media.md).
|
|
117
|
+
|
|
118
|
+
## Availability
|
|
119
|
+
|
|
120
|
+
`sdk.availability` reads site-aware product availability: `get(productId, siteCode)`
|
|
121
|
+
for one product and `getMany(productIds, siteCode)` for a batch (single
|
|
122
|
+
`POST .../search` request, result in input order). The opt-in
|
|
123
|
+
`defaultAvailableOnNotFound` returns `{ available: true }` for products without a
|
|
124
|
+
stock record. There is no restock-date field. See [`../../docs/availability.md`](../../docs/availability.md).
|
|
125
|
+
|
|
101
126
|
## Subpath exports
|
|
102
127
|
|
|
103
128
|
`@viu/emporix-sdk` (everything) plus `./customer`, `./product`, `./category`,
|
|
104
129
|
`./cart`, `./checkout`, `./payment`, `./price`, `./media`, `./segment`,
|
|
105
|
-
`./companies`, `./contacts`, `./locations`, `./customer-groups` for tree-shaking.
|
|
130
|
+
`./companies`, `./contacts`, `./locations`, `./customer-groups`, `./availability` for tree-shaking.
|
|
106
131
|
|
|
107
132
|
## Authors
|
|
108
133
|
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/core/errors.ts
|
|
4
|
+
var REDACTED_BODY_KEYS = /* @__PURE__ */ new Set([
|
|
5
|
+
"access_token",
|
|
6
|
+
"refresh_token",
|
|
7
|
+
"token",
|
|
8
|
+
"customertoken",
|
|
9
|
+
"saastoken",
|
|
10
|
+
"secret",
|
|
11
|
+
"client_secret",
|
|
12
|
+
"authorization",
|
|
13
|
+
"password"
|
|
14
|
+
]);
|
|
15
|
+
function scrub(body) {
|
|
16
|
+
if (Array.isArray(body)) return body.map(scrub);
|
|
17
|
+
if (body && typeof body === "object") {
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [k, v] of Object.entries(body)) {
|
|
20
|
+
out[k] = REDACTED_BODY_KEYS.has(k.toLowerCase()) ? "***redacted***" : scrub(v);
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
return body;
|
|
25
|
+
}
|
|
26
|
+
var EmporixError = class extends Error {
|
|
27
|
+
status;
|
|
28
|
+
body;
|
|
29
|
+
constructor(message, status, body) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = new.target.name;
|
|
32
|
+
this.status = status;
|
|
33
|
+
this.body = body;
|
|
34
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
35
|
+
}
|
|
36
|
+
/** Safe serialisation — token-like body fields are redacted. */
|
|
37
|
+
toJSON() {
|
|
38
|
+
return { name: this.name, message: this.message, status: this.status, body: scrub(this.body) };
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var EmporixNotFoundError = class extends EmporixError {
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/services/availability.ts
|
|
45
|
+
var ANON = { kind: "anonymous" };
|
|
46
|
+
var AvailabilityService = class {
|
|
47
|
+
constructor(ctx) {
|
|
48
|
+
this.ctx = ctx;
|
|
49
|
+
}
|
|
50
|
+
ctx;
|
|
51
|
+
/**
|
|
52
|
+
* Single product. Resolves the availability record, or — when
|
|
53
|
+
* `opts.defaultAvailableOnNotFound` is set — a default available record on 404.
|
|
54
|
+
*/
|
|
55
|
+
async get(productId, siteCode, auth = ANON, opts = {}) {
|
|
56
|
+
try {
|
|
57
|
+
return await this.ctx.http.request({
|
|
58
|
+
method: "GET",
|
|
59
|
+
path: `/availability/${this.ctx.tenant}/availability/${encodeURIComponent(
|
|
60
|
+
productId
|
|
61
|
+
)}/${encodeURIComponent(siteCode)}`,
|
|
62
|
+
auth
|
|
63
|
+
});
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (err instanceof EmporixNotFoundError && opts.defaultAvailableOnNotFound) {
|
|
66
|
+
return { productId, site: siteCode, available: true };
|
|
67
|
+
}
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Batch read via `POST .../availability/search` (one request). Products with
|
|
73
|
+
* no availability record are absent from the response; each is synthesized as
|
|
74
|
+
* `{ available: false }` (or `{ available: true }` when
|
|
75
|
+
* `opts.defaultAvailableOnNotFound` is set). The result preserves input order
|
|
76
|
+
* and length. An empty `productIds` resolves to `[]` without a request.
|
|
77
|
+
*/
|
|
78
|
+
async getMany(productIds, siteCode, auth = ANON, opts = {}) {
|
|
79
|
+
if (productIds.length === 0) return [];
|
|
80
|
+
const found = await this.ctx.http.request({
|
|
81
|
+
method: "POST",
|
|
82
|
+
path: `/availability/${this.ctx.tenant}/availability/search`,
|
|
83
|
+
auth,
|
|
84
|
+
query: { site: siteCode, pageSize: productIds.length },
|
|
85
|
+
body: productIds
|
|
86
|
+
});
|
|
87
|
+
const byId = /* @__PURE__ */ new Map();
|
|
88
|
+
for (const a of found) if (a.productId) byId.set(a.productId, a);
|
|
89
|
+
return productIds.map(
|
|
90
|
+
(id) => byId.get(id) ?? {
|
|
91
|
+
productId: id,
|
|
92
|
+
site: siteCode,
|
|
93
|
+
available: Boolean(opts.defaultAvailableOnNotFound)
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
exports.AvailabilityService = AvailabilityService;
|
|
100
|
+
//# sourceMappingURL=availability.cjs.map
|
|
101
|
+
//# sourceMappingURL=availability.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/errors.ts","../src/services/availability.ts"],"names":[],"mappings":"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","file":"availability.cjs","sourcesContent":["/** Keys whose values are stripped from any serialised error body. */\nconst REDACTED_BODY_KEYS = new Set([\n \"access_token\", \"refresh_token\", \"token\", \"customertoken\", \"saastoken\",\n \"secret\", \"client_secret\", \"authorization\", \"password\",\n]);\n\nfunction scrub(body: unknown): unknown {\n if (Array.isArray(body)) return body.map(scrub);\n if (body && typeof body === \"object\") {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(body as Record<string, unknown>)) {\n out[k] = REDACTED_BODY_KEYS.has(k.toLowerCase()) ? \"***redacted***\" : scrub(v);\n }\n return out;\n }\n return body;\n}\n\n/** Base class for every error thrown by the SDK. */\nexport class EmporixError extends Error {\n readonly status: number | undefined;\n readonly body: unknown;\n constructor(message: string, status?: number, body?: unknown) {\n super(message);\n this.name = new.target.name;\n this.status = status;\n this.body = body;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n /** Safe serialisation — token-like body fields are redacted. */\n toJSON(): Record<string, unknown> {\n return { name: this.name, message: this.message, status: this.status, body: scrub(this.body) };\n }\n}\n\n/** 401 — authentication failed or token expired. */\nexport class EmporixAuthError extends EmporixError {}\n/** 403 — authenticated but not permitted. */\nexport class EmporixForbiddenError extends EmporixError {}\n/** 404 — resource not found. */\nexport class EmporixNotFoundError extends EmporixError {}\n/** 400/422 — request validation failed. */\nexport class EmporixValidationError extends EmporixError {}\n/** 5xx — server-side failure. */\nexport class EmporixServerError extends EmporixError {}\n\n/**\n * 403 with a `missing scope: <name>` hint in the body's `details` array.\n * Subclasses {@link EmporixForbiddenError} so existing 403 catches still work.\n */\nexport class EmporixInsufficientScopeError extends EmporixForbiddenError {\n readonly requiredScope: string | undefined;\n constructor(message: string, status: number, body: unknown, requiredScope?: string) {\n super(message, status, body);\n this.requiredScope = requiredScope;\n }\n}\n\nconst SCOPE_HINT_RE = /missing scope[: ]+([a-z0-9._-]+)/i;\n\nfunction extractRequiredScope(body: unknown): string | undefined {\n if (body && typeof body === \"object\" && \"details\" in body) {\n const details = (body as { details?: unknown }).details;\n if (Array.isArray(details)) {\n for (const d of details) {\n if (typeof d === \"string\") {\n const m = d.match(SCOPE_HINT_RE);\n if (m) return m[1];\n }\n }\n }\n }\n return undefined;\n}\n\n/** Maps an HTTP status to the matching {@link EmporixError} subclass. */\nexport function errorFromResponse(status: number, message: string, body: unknown): EmporixError {\n if (status === 401) return new EmporixAuthError(message, status, body);\n if (status === 403) {\n const scope = extractRequiredScope(body);\n if (scope) return new EmporixInsufficientScopeError(message, status, body, scope);\n return new EmporixForbiddenError(message, status, body);\n }\n if (status === 404) return new EmporixNotFoundError(message, status, body);\n if (status === 400 || status === 422) return new EmporixValidationError(message, status, body);\n if (status >= 500) return new EmporixServerError(message, status, body);\n return new EmporixError(message, status, body);\n}\n","import type { ClientContext } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport { EmporixNotFoundError } from \"../core/errors\";\nimport type { AvailabilityWithBundle } from \"../generated/availability\";\n\n/**\n * Site-aware product availability. Mirrors the Emporix Availability Service\n * `AvailabilityWithBundle` shape (the single-product GET and the batch search\n * both return the bundle-aware variant). There is no restock-date field.\n */\nexport type Availability = AvailabilityWithBundle;\n\n/** Shared options for {@link AvailabilityService} reads. */\nexport interface AvailabilityOptions {\n /**\n * When `true`, a product with no availability record resolves to a default\n * `{ available: true }` instead of throwing (single `get`) / being marked\n * unavailable (`getMany`). Off by default — opt in for tenants that sell\n * without stock management.\n */\n defaultAvailableOnNotFound?: boolean;\n}\n\nconst ANON: AuthContext = { kind: \"anonymous\" };\n\n/**\n * Reads product availability per site. Default auth is anonymous (like\n * `PriceService.matchByContext`); pass a customer/raw/service context to use a\n * different token. Requires the `availability.availability_view` scope on\n * whichever token is used.\n */\nexport class AvailabilityService {\n constructor(private readonly ctx: ClientContext) {}\n\n /**\n * Single product. Resolves the availability record, or — when\n * `opts.defaultAvailableOnNotFound` is set — a default available record on 404.\n */\n async get(\n productId: string,\n siteCode: string,\n auth: AuthContext = ANON,\n opts: AvailabilityOptions = {},\n ): Promise<Availability> {\n try {\n return await this.ctx.http.request<Availability>({\n method: \"GET\",\n path: `/availability/${this.ctx.tenant}/availability/${encodeURIComponent(\n productId,\n )}/${encodeURIComponent(siteCode)}`,\n auth,\n });\n } catch (err) {\n if (err instanceof EmporixNotFoundError && opts.defaultAvailableOnNotFound) {\n return { productId, site: siteCode, available: true };\n }\n throw err;\n }\n }\n\n /**\n * Batch read via `POST .../availability/search` (one request). Products with\n * no availability record are absent from the response; each is synthesized as\n * `{ available: false }` (or `{ available: true }` when\n * `opts.defaultAvailableOnNotFound` is set). The result preserves input order\n * and length. An empty `productIds` resolves to `[]` without a request.\n */\n async getMany(\n productIds: string[],\n siteCode: string,\n auth: AuthContext = ANON,\n opts: AvailabilityOptions = {},\n ): Promise<Availability[]> {\n if (productIds.length === 0) return [];\n const found = await this.ctx.http.request<Availability[]>({\n method: \"POST\",\n path: `/availability/${this.ctx.tenant}/availability/search`,\n auth,\n query: { site: siteCode, pageSize: productIds.length },\n body: productIds,\n });\n const byId = new Map<string, Availability>();\n for (const a of found) if (a.productId) byId.set(a.productId, a);\n return productIds.map(\n (id) =>\n byId.get(id) ?? {\n productId: id,\n site: siteCode,\n available: Boolean(opts.defaultAvailableOnNotFound),\n },\n );\n }\n}\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { C as ClientContext, b as AuthContext } from './context-CGuxSb56.cjs';
|
|
2
|
+
|
|
3
|
+
type DistributionChannel = 'ASSORTMENT' | 'HOME_DELIVERY' | 'PICKUP';
|
|
4
|
+
type Availability$1 = {
|
|
5
|
+
id?: string;
|
|
6
|
+
site?: string;
|
|
7
|
+
stockLevel?: number;
|
|
8
|
+
available?: boolean;
|
|
9
|
+
productId?: string;
|
|
10
|
+
vendorId?: string;
|
|
11
|
+
popularity?: number;
|
|
12
|
+
distributionChannel?: DistributionChannel;
|
|
13
|
+
mixins?: {
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
16
|
+
metadata?: {
|
|
17
|
+
createdAt?: string;
|
|
18
|
+
modifiedAt?: string;
|
|
19
|
+
mixins?: {
|
|
20
|
+
[key: string]: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
type AvailabilityWithBundle = {
|
|
25
|
+
id?: string;
|
|
26
|
+
site?: string;
|
|
27
|
+
stockLevel?: number;
|
|
28
|
+
available?: boolean;
|
|
29
|
+
productId?: string;
|
|
30
|
+
vendorId?: string;
|
|
31
|
+
popularity?: number;
|
|
32
|
+
distributionChannel?: DistributionChannel;
|
|
33
|
+
bundleAvailabilities?: AvailabilityList;
|
|
34
|
+
mixins?: {
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
metadata?: {
|
|
38
|
+
createdAt?: string;
|
|
39
|
+
modifiedAt?: string;
|
|
40
|
+
mixins?: {
|
|
41
|
+
[key: string]: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
type AvailabilityList = Array<Availability$1>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Site-aware product availability. Mirrors the Emporix Availability Service
|
|
49
|
+
* `AvailabilityWithBundle` shape (the single-product GET and the batch search
|
|
50
|
+
* both return the bundle-aware variant). There is no restock-date field.
|
|
51
|
+
*/
|
|
52
|
+
type Availability = AvailabilityWithBundle;
|
|
53
|
+
/** Shared options for {@link AvailabilityService} reads. */
|
|
54
|
+
interface AvailabilityOptions {
|
|
55
|
+
/**
|
|
56
|
+
* When `true`, a product with no availability record resolves to a default
|
|
57
|
+
* `{ available: true }` instead of throwing (single `get`) / being marked
|
|
58
|
+
* unavailable (`getMany`). Off by default — opt in for tenants that sell
|
|
59
|
+
* without stock management.
|
|
60
|
+
*/
|
|
61
|
+
defaultAvailableOnNotFound?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Reads product availability per site. Default auth is anonymous (like
|
|
65
|
+
* `PriceService.matchByContext`); pass a customer/raw/service context to use a
|
|
66
|
+
* different token. Requires the `availability.availability_view` scope on
|
|
67
|
+
* whichever token is used.
|
|
68
|
+
*/
|
|
69
|
+
declare class AvailabilityService {
|
|
70
|
+
private readonly ctx;
|
|
71
|
+
constructor(ctx: ClientContext);
|
|
72
|
+
/**
|
|
73
|
+
* Single product. Resolves the availability record, or — when
|
|
74
|
+
* `opts.defaultAvailableOnNotFound` is set — a default available record on 404.
|
|
75
|
+
*/
|
|
76
|
+
get(productId: string, siteCode: string, auth?: AuthContext, opts?: AvailabilityOptions): Promise<Availability>;
|
|
77
|
+
/**
|
|
78
|
+
* Batch read via `POST .../availability/search` (one request). Products with
|
|
79
|
+
* no availability record are absent from the response; each is synthesized as
|
|
80
|
+
* `{ available: false }` (or `{ available: true }` when
|
|
81
|
+
* `opts.defaultAvailableOnNotFound` is set). The result preserves input order
|
|
82
|
+
* and length. An empty `productIds` resolves to `[]` without a request.
|
|
83
|
+
*/
|
|
84
|
+
getMany(productIds: string[], siteCode: string, auth?: AuthContext, opts?: AvailabilityOptions): Promise<Availability[]>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { type Availability, type AvailabilityOptions, AvailabilityService };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { C as ClientContext, b as AuthContext } from './context-CGuxSb56.js';
|
|
2
|
+
|
|
3
|
+
type DistributionChannel = 'ASSORTMENT' | 'HOME_DELIVERY' | 'PICKUP';
|
|
4
|
+
type Availability$1 = {
|
|
5
|
+
id?: string;
|
|
6
|
+
site?: string;
|
|
7
|
+
stockLevel?: number;
|
|
8
|
+
available?: boolean;
|
|
9
|
+
productId?: string;
|
|
10
|
+
vendorId?: string;
|
|
11
|
+
popularity?: number;
|
|
12
|
+
distributionChannel?: DistributionChannel;
|
|
13
|
+
mixins?: {
|
|
14
|
+
[key: string]: unknown;
|
|
15
|
+
};
|
|
16
|
+
metadata?: {
|
|
17
|
+
createdAt?: string;
|
|
18
|
+
modifiedAt?: string;
|
|
19
|
+
mixins?: {
|
|
20
|
+
[key: string]: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
type AvailabilityWithBundle = {
|
|
25
|
+
id?: string;
|
|
26
|
+
site?: string;
|
|
27
|
+
stockLevel?: number;
|
|
28
|
+
available?: boolean;
|
|
29
|
+
productId?: string;
|
|
30
|
+
vendorId?: string;
|
|
31
|
+
popularity?: number;
|
|
32
|
+
distributionChannel?: DistributionChannel;
|
|
33
|
+
bundleAvailabilities?: AvailabilityList;
|
|
34
|
+
mixins?: {
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
};
|
|
37
|
+
metadata?: {
|
|
38
|
+
createdAt?: string;
|
|
39
|
+
modifiedAt?: string;
|
|
40
|
+
mixins?: {
|
|
41
|
+
[key: string]: string;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
type AvailabilityList = Array<Availability$1>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Site-aware product availability. Mirrors the Emporix Availability Service
|
|
49
|
+
* `AvailabilityWithBundle` shape (the single-product GET and the batch search
|
|
50
|
+
* both return the bundle-aware variant). There is no restock-date field.
|
|
51
|
+
*/
|
|
52
|
+
type Availability = AvailabilityWithBundle;
|
|
53
|
+
/** Shared options for {@link AvailabilityService} reads. */
|
|
54
|
+
interface AvailabilityOptions {
|
|
55
|
+
/**
|
|
56
|
+
* When `true`, a product with no availability record resolves to a default
|
|
57
|
+
* `{ available: true }` instead of throwing (single `get`) / being marked
|
|
58
|
+
* unavailable (`getMany`). Off by default — opt in for tenants that sell
|
|
59
|
+
* without stock management.
|
|
60
|
+
*/
|
|
61
|
+
defaultAvailableOnNotFound?: boolean;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Reads product availability per site. Default auth is anonymous (like
|
|
65
|
+
* `PriceService.matchByContext`); pass a customer/raw/service context to use a
|
|
66
|
+
* different token. Requires the `availability.availability_view` scope on
|
|
67
|
+
* whichever token is used.
|
|
68
|
+
*/
|
|
69
|
+
declare class AvailabilityService {
|
|
70
|
+
private readonly ctx;
|
|
71
|
+
constructor(ctx: ClientContext);
|
|
72
|
+
/**
|
|
73
|
+
* Single product. Resolves the availability record, or — when
|
|
74
|
+
* `opts.defaultAvailableOnNotFound` is set — a default available record on 404.
|
|
75
|
+
*/
|
|
76
|
+
get(productId: string, siteCode: string, auth?: AuthContext, opts?: AvailabilityOptions): Promise<Availability>;
|
|
77
|
+
/**
|
|
78
|
+
* Batch read via `POST .../availability/search` (one request). Products with
|
|
79
|
+
* no availability record are absent from the response; each is synthesized as
|
|
80
|
+
* `{ available: false }` (or `{ available: true }` when
|
|
81
|
+
* `opts.defaultAvailableOnNotFound` is set). The result preserves input order
|
|
82
|
+
* and length. An empty `productIds` resolves to `[]` without a request.
|
|
83
|
+
*/
|
|
84
|
+
getMany(productIds: string[], siteCode: string, auth?: AuthContext, opts?: AvailabilityOptions): Promise<Availability[]>;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { type Availability, type AvailabilityOptions, AvailabilityService };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"availability.js"}
|
package/dist/cart.d.cts
CHANGED
package/dist/cart.d.ts
CHANGED
package/dist/category.d.cts
CHANGED
package/dist/category.d.ts
CHANGED
package/dist/checkout.d.cts
CHANGED
package/dist/checkout.d.ts
CHANGED
|
@@ -82,8 +82,31 @@ var ProductService = class {
|
|
|
82
82
|
);
|
|
83
83
|
return pages.flat();
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Streams the VARIANT children of a PARENT_VARIANT product, page by page,
|
|
87
|
+
* via the search query `productType:VARIANT parentVariantId:<id>`. Default
|
|
88
|
+
* pageSize 200. The query syntax (space-separated fields = implicit AND) is
|
|
89
|
+
* encapsulated here so consumers don't build it themselves.
|
|
90
|
+
*/
|
|
91
|
+
listVariantChildrenAll(parentVariantId, params = {}, auth = ANON) {
|
|
92
|
+
const pageSize = params.pageSize ?? 200;
|
|
93
|
+
const q = `productType:VARIANT parentVariantId:${parentVariantId}`;
|
|
94
|
+
return iterateAll((pageNumber) => this.search(q, { pageNumber, pageSize }, auth));
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Resolves ALL VARIANT children of a PARENT_VARIANT product into a flat
|
|
98
|
+
* array (loads every page). Default pageSize 200. Returns `[]` when there are
|
|
99
|
+
* no children — never throws.
|
|
100
|
+
*/
|
|
101
|
+
async listVariantChildren(parentVariantId, params = {}, auth = ANON) {
|
|
102
|
+
const out = [];
|
|
103
|
+
for await (const child of this.listVariantChildrenAll(parentVariantId, params, auth)) {
|
|
104
|
+
out.push(child);
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
85
108
|
};
|
|
86
109
|
|
|
87
110
|
export { ProductService };
|
|
88
|
-
//# sourceMappingURL=chunk-
|
|
89
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-JTJV7WUW.js.map
|
|
112
|
+
//# sourceMappingURL=chunk-JTJV7WUW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/product.ts"],"names":[],"mappings":"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","file":"chunk-JTJV7WUW.js","sourcesContent":["import type { ClientContext, PaginatedItems } from \"../core/context\";\nimport { iterateAll } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport type {\n BasicProductWithId,\n BundleProductWithId,\n ParentVariantProductWithId,\n ProductMedia,\n} from \"../generated/product\";\n\nconst ANON: AuthContext = { kind: \"anonymous\" };\n\n/** A product as returned by the Product service (all generated fields). */\nexport type Product = BasicProductWithId | BundleProductWithId | ParentVariantProductWithId;\n\n/** A single product media entry (generated; `ProductMedia` is the list type). */\nexport type Media = ProductMedia[number];\n\n/** Catalog reads. Default auth: anonymous; pass customer for personalized pricing. */\nexport class ProductService {\n constructor(private readonly ctx: ClientContext) {}\n\n /** Fetches one product by id. */\n async get(\n productId: string,\n _opts?: Record<string, never>,\n auth: AuthContext = ANON,\n ): Promise<Product> {\n return this.ctx.http.request<Product>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/products/${productId}`,\n auth,\n });\n }\n\n /** Fetches one product by its code. */\n async getByCode(code: string, auth: AuthContext = ANON): Promise<Product> {\n const rows = await this.ctx.http.request<Product[]>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/products`,\n query: { q: `code:${code}` },\n auth,\n });\n const first = rows[0];\n if (!first) throw new Error(`No product with code \"${code}\"`);\n return first;\n }\n\n /** One page of products. */\n async list(\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Product>> {\n const pageNumber = params.pageNumber ?? 1;\n const pageSize = params.pageSize ?? 50;\n const items = await this.ctx.http.request<Product[]>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/products`,\n query: { pageNumber, pageSize },\n auth,\n });\n return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };\n }\n\n /** Async-iterates every product across pages. */\n listAll(params: { pageSize?: number } = {}, auth: AuthContext = ANON): AsyncIterable<Product> {\n const pageSize = params.pageSize ?? 50;\n return iterateAll<Product>((pageNumber) => this.list({ pageNumber, pageSize }, auth));\n }\n\n /** Searches products by free-text query. */\n async search(\n query: string,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Product>> {\n const pageNumber = params.pageNumber ?? 1;\n const pageSize = params.pageSize ?? 50;\n const items = await this.ctx.http.request<Product[]>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/products`,\n query: { q: query, pageNumber, pageSize },\n auth,\n });\n return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };\n }\n\n /**\n * Bulk fetch by id. POSTs `/products/search` with `q=\"id:(id1,id2,…)\"`,\n * chunking when the list is larger than `options.chunkSize` (default\n * 100). An empty list short-circuits with no HTTP call. **Order is not\n * guaranteed** across chunks — re-index by `id` if order matters.\n */\n async searchByIds(\n ids: string[],\n options: { chunkSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<Product[]> {\n if (ids.length === 0) return [];\n const chunkSize = options.chunkSize ?? 100;\n const chunks: string[][] = [];\n for (let i = 0; i < ids.length; i += chunkSize) {\n chunks.push(ids.slice(i, i + chunkSize));\n }\n const pages = await Promise.all(\n chunks.map((chunk) =>\n this.ctx.http.request<Product[]>({\n method: \"POST\",\n path: `/product/${this.ctx.tenant}/products/search`,\n query: { pageSize: chunk.length },\n auth,\n body: { q: `id:(${chunk.join(\",\")})` },\n }),\n ),\n );\n return pages.flat();\n }\n\n /**\n * Streams the VARIANT children of a PARENT_VARIANT product, page by page,\n * via the search query `productType:VARIANT parentVariantId:<id>`. Default\n * pageSize 200. The query syntax (space-separated fields = implicit AND) is\n * encapsulated here so consumers don't build it themselves.\n */\n listVariantChildrenAll(\n parentVariantId: string,\n params: { pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): AsyncIterable<Product> {\n const pageSize = params.pageSize ?? 200;\n const q = `productType:VARIANT parentVariantId:${parentVariantId}`;\n return iterateAll<Product>((pageNumber) => this.search(q, { pageNumber, pageSize }, auth));\n }\n\n /**\n * Resolves ALL VARIANT children of a PARENT_VARIANT product into a flat\n * array (loads every page). Default pageSize 200. Returns `[]` when there are\n * no children — never throws.\n */\n async listVariantChildren(\n parentVariantId: string,\n params: { pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<Product[]> {\n const out: Product[] = [];\n for await (const child of this.listVariantChildrenAll(parentVariantId, params, auth)) {\n out.push(child);\n }\n return out;\n }\n}\n"]}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { EmporixAuthError } from './chunk-KTODT6DV.js';
|
|
2
|
+
|
|
3
|
+
// src/services/price.ts
|
|
4
|
+
var ANON = { kind: "anonymous" };
|
|
5
|
+
var SERVICE = { kind: "service" };
|
|
6
|
+
function requireContextAuth(auth) {
|
|
7
|
+
const a = auth ?? ANON;
|
|
8
|
+
if (a.kind === "anonymous" || a.kind === "customer" || a.kind === "raw") return a;
|
|
9
|
+
throw new EmporixAuthError(
|
|
10
|
+
"match-prices-by-context requires an anonymous, customer, or raw AuthContext"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
var PriceService = class {
|
|
14
|
+
constructor(ctx) {
|
|
15
|
+
this.ctx = ctx;
|
|
16
|
+
}
|
|
17
|
+
ctx;
|
|
18
|
+
/**
|
|
19
|
+
* Resolves prices using the session context bound to the bearer token
|
|
20
|
+
* (currency/site/country were set at anonymous-login time). Default auth:
|
|
21
|
+
* anonymous; pass a customer/raw context for personalized pricing.
|
|
22
|
+
*/
|
|
23
|
+
async matchByContext(input, auth) {
|
|
24
|
+
return this.ctx.http.request({
|
|
25
|
+
method: "POST",
|
|
26
|
+
path: `/price/${this.ctx.tenant}/match-prices-by-context`,
|
|
27
|
+
auth: requireContextAuth(auth),
|
|
28
|
+
body: input
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves prices from an explicit context. Default auth: service
|
|
33
|
+
* (requires `price.price_read` / `price.price_manage`).
|
|
34
|
+
*/
|
|
35
|
+
async match(input, auth = SERVICE) {
|
|
36
|
+
return this.ctx.http.request({
|
|
37
|
+
method: "POST",
|
|
38
|
+
path: `/price/${this.ctx.tenant}/match-prices`,
|
|
39
|
+
auth,
|
|
40
|
+
body: input
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Chunked variant of {@link matchByContext} for large `items` arrays. The
|
|
45
|
+
* Emporix backend handles only a limited number of items per request
|
|
46
|
+
* (production limit ~50), so this splits `input.items` into chunks and runs
|
|
47
|
+
* `matchByContext` with bounded concurrency.
|
|
48
|
+
*
|
|
49
|
+
* By default a failing chunk is skipped (its items are absent from the
|
|
50
|
+
* result) and `opts.onChunkError` is called once for it; pass
|
|
51
|
+
* `throwOnAnyChunkError: true` to reject on the first failure instead.
|
|
52
|
+
*
|
|
53
|
+
* **Result order is not guaranteed** — match entries back to your items by
|
|
54
|
+
* `priceId` / `itemRef.id`.
|
|
55
|
+
*/
|
|
56
|
+
async matchByContextChunked(input, opts = {}, auth) {
|
|
57
|
+
const chunkSize = opts.chunkSize ?? 50;
|
|
58
|
+
const concurrency = opts.concurrency ?? 4;
|
|
59
|
+
if (chunkSize < 1) throw new Error("chunkSize must be >= 1");
|
|
60
|
+
if (concurrency < 1) throw new Error("concurrency must be >= 1");
|
|
61
|
+
const items = input.items ?? [];
|
|
62
|
+
if (items.length === 0) return [];
|
|
63
|
+
const chunks = [];
|
|
64
|
+
for (let i = 0; i < items.length; i += chunkSize) {
|
|
65
|
+
chunks.push({ ...input, items: items.slice(i, i + chunkSize) });
|
|
66
|
+
}
|
|
67
|
+
const results = new Array(chunks.length);
|
|
68
|
+
let cursor = 0;
|
|
69
|
+
const worker = async () => {
|
|
70
|
+
for (; ; ) {
|
|
71
|
+
const idx = cursor++;
|
|
72
|
+
const chunk = chunks[idx];
|
|
73
|
+
if (chunk === void 0) return;
|
|
74
|
+
try {
|
|
75
|
+
results[idx] = await this.matchByContext(chunk, auth);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (opts.throwOnAnyChunkError) throw err;
|
|
78
|
+
results[idx] = [];
|
|
79
|
+
opts.onChunkError?.(err, idx);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const workerCount = Math.min(concurrency, chunks.length);
|
|
84
|
+
await Promise.all(Array.from({ length: workerCount }, () => worker()));
|
|
85
|
+
return results.flat();
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export { PriceService };
|
|
90
|
+
//# sourceMappingURL=chunk-SCI7D74B.js.map
|
|
91
|
+
//# sourceMappingURL=chunk-SCI7D74B.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/price.ts"],"names":[],"mappings":";;;AA0BA,IAAM,IAAA,GAAoB,EAAE,IAAA,EAAM,WAAA,EAAY;AAC9C,IAAM,OAAA,GAAuB,EAAE,IAAA,EAAM,SAAA,EAAU;AAE/C,SAAS,mBAAmB,IAAA,EAA4C;AACtE,EAAA,MAAM,IAAI,IAAA,IAAQ,IAAA;AAClB,EAAA,IAAI,CAAA,CAAE,SAAS,WAAA,IAAe,CAAA,CAAE,SAAS,UAAA,IAAc,CAAA,CAAE,IAAA,KAAS,KAAA,EAAO,OAAO,CAAA;AAChF,EAAA,MAAM,IAAI,gBAAA;AAAA,IACR;AAAA,GACF;AACF;AAOO,IAAM,eAAN,MAAmB;AAAA,EACxB,YAA6B,GAAA,EAAoB;AAApB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAqB;AAAA,EAArB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO7B,MAAM,cAAA,CACJ,KAAA,EACA,IAAA,EACuB;AACvB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAsB;AAAA,MACzC,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,CAAA,OAAA,EAAU,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,wBAAA,CAAA;AAAA,MAC/B,IAAA,EAAM,mBAAmB,IAAI,CAAA;AAAA,MAC7B,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,KAAA,CAAM,KAAA,EAAwB,IAAA,GAAoB,OAAA,EAAgC;AACtF,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAsB;AAAA,MACzC,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,CAAA,OAAA,EAAU,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,aAAA,CAAA;AAAA,MAC/B,IAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,MAAM,qBAAA,CACJ,KAAA,EACA,IAAA,GAAqC,IACrC,IAAA,EACuB;AACvB,IAAA,MAAM,SAAA,GAAY,KAAK,SAAA,IAAa,EAAA;AACpC,IAAA,MAAM,WAAA,GAAc,KAAK,WAAA,IAAe,CAAA;AACxC,IAAA,IAAI,SAAA,GAAY,CAAA,EAAG,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAC3D,IAAA,IAAI,WAAA,GAAc,CAAA,EAAG,MAAM,IAAI,MAAM,0BAA0B,CAAA;AAE/D,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,IAAS,EAAC;AAC9B,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAEhC,IAAA,MAAM,SAAqC,EAAC;AAC5C,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,MAAA,EAAQ,KAAK,SAAA,EAAW;AAChD,MAAA,MAAA,CAAO,IAAA,CAAK,EAAE,GAAG,KAAA,EAAO,KAAA,EAAO,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAA,GAAI,SAAS,CAAA,EAAG,CAAA;AAAA,IAChE;AAEA,IAAA,MAAM,OAAA,GAA0B,IAAI,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA;AACvD,IAAA,IAAI,MAAA,GAAS,CAAA;AAEb,IAAA,MAAM,SAAS,YAA2B;AACxC,MAAA,WAAS;AACP,QAAA,MAAM,GAAA,GAAM,MAAA,EAAA;AACZ,QAAA,MAAM,KAAA,GAAQ,OAAO,GAAG,CAAA;AACxB,QAAA,IAAI,UAAU,MAAA,EAAW;AACzB,QAAA,IAAI;AACF,UAAA,OAAA,CAAQ,GAAG,CAAA,GAAI,MAAM,IAAA,CAAK,cAAA,CAAe,OAAO,IAAI,CAAA;AAAA,QACtD,SAAS,GAAA,EAAK;AACZ,UAAA,IAAI,IAAA,CAAK,sBAAsB,MAAM,GAAA;AACrC,UAAA,OAAA,CAAQ,GAAG,IAAI,EAAC;AAChB,UAAA,IAAA,CAAK,YAAA,GAAe,KAAK,GAAG,CAAA;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,CAAA;AAEA,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,OAAO,MAAM,CAAA;AACvD,IAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,WAAA,EAAY,EAAG,MAAM,MAAA,EAAQ,CAAC,CAAA;AAErE,IAAA,OAAO,QAAQ,IAAA,EAAK;AAAA,EACtB;AACF","file":"chunk-SCI7D74B.js","sourcesContent":["import type { ClientContext } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport { EmporixAuthError } from \"../core/errors\";\nimport type { Match, MatchByContext, MatchResponse } from \"../generated/price\";\n\n/** Session-context match request body (generated). */\nexport type PriceMatchByContextInput = MatchByContext;\n\n/** Explicit-context match request body (generated). */\nexport type PriceMatchInput = Match;\n\n/** A resolved price (full generated match-response schema). */\nexport type PriceMatch = MatchResponse;\n\n/** Options for {@link PriceService.matchByContextChunked}. */\nexport interface MatchByContextChunkedOptions {\n /** Items per request. Default 50. Must be >= 1. */\n chunkSize?: number;\n /** Maximum number of requests in flight at once. Default 4. Must be >= 1. */\n concurrency?: number;\n /** Invoked once per failed chunk (default mode only — not when throwing). */\n onChunkError?: (err: unknown, chunkIndex: number) => void;\n /** When true, the first failing chunk rejects the whole call. Default false. */\n throwOnAnyChunkError?: boolean;\n}\n\nconst ANON: AuthContext = { kind: \"anonymous\" };\nconst SERVICE: AuthContext = { kind: \"service\" };\n\nfunction requireContextAuth(auth: AuthContext | undefined): AuthContext {\n const a = auth ?? ANON;\n if (a.kind === \"anonymous\" || a.kind === \"customer\" || a.kind === \"raw\") return a;\n throw new EmporixAuthError(\n \"match-prices-by-context requires an anonymous, customer, or raw AuthContext\",\n );\n}\n\n/**\n * Price matching. The Cart service does not resolve prices — call this\n * explicitly before rendering money and again right before placing an order.\n * The SDK is stateless: it never caches or revalidates prices.\n */\nexport class PriceService {\n constructor(private readonly ctx: ClientContext) {}\n\n /**\n * Resolves prices using the session context bound to the bearer token\n * (currency/site/country were set at anonymous-login time). Default auth:\n * anonymous; pass a customer/raw context for personalized pricing.\n */\n async matchByContext(\n input: PriceMatchByContextInput,\n auth?: AuthContext,\n ): Promise<PriceMatch[]> {\n return this.ctx.http.request<PriceMatch[]>({\n method: \"POST\",\n path: `/price/${this.ctx.tenant}/match-prices-by-context`,\n auth: requireContextAuth(auth),\n body: input,\n });\n }\n\n /**\n * Resolves prices from an explicit context. Default auth: service\n * (requires `price.price_read` / `price.price_manage`).\n */\n async match(input: PriceMatchInput, auth: AuthContext = SERVICE): Promise<PriceMatch[]> {\n return this.ctx.http.request<PriceMatch[]>({\n method: \"POST\",\n path: `/price/${this.ctx.tenant}/match-prices`,\n auth,\n body: input,\n });\n }\n\n /**\n * Chunked variant of {@link matchByContext} for large `items` arrays. The\n * Emporix backend handles only a limited number of items per request\n * (production limit ~50), so this splits `input.items` into chunks and runs\n * `matchByContext` with bounded concurrency.\n *\n * By default a failing chunk is skipped (its items are absent from the\n * result) and `opts.onChunkError` is called once for it; pass\n * `throwOnAnyChunkError: true` to reject on the first failure instead.\n *\n * **Result order is not guaranteed** — match entries back to your items by\n * `priceId` / `itemRef.id`.\n */\n async matchByContextChunked(\n input: PriceMatchByContextInput,\n opts: MatchByContextChunkedOptions = {},\n auth?: AuthContext,\n ): Promise<PriceMatch[]> {\n const chunkSize = opts.chunkSize ?? 50;\n const concurrency = opts.concurrency ?? 4;\n if (chunkSize < 1) throw new Error(\"chunkSize must be >= 1\");\n if (concurrency < 1) throw new Error(\"concurrency must be >= 1\");\n\n const items = input.items ?? [];\n if (items.length === 0) return [];\n\n const chunks: PriceMatchByContextInput[] = [];\n for (let i = 0; i < items.length; i += chunkSize) {\n chunks.push({ ...input, items: items.slice(i, i + chunkSize) });\n }\n\n const results: PriceMatch[][] = new Array(chunks.length);\n let cursor = 0;\n\n const worker = async (): Promise<void> => {\n for (;;) {\n const idx = cursor++;\n const chunk = chunks[idx];\n if (chunk === undefined) return; // past the end\n try {\n results[idx] = await this.matchByContext(chunk, auth);\n } catch (err) {\n if (opts.throwOnAnyChunkError) throw err;\n results[idx] = [];\n opts.onChunkError?.(err, idx);\n }\n }\n };\n\n const workerCount = Math.min(concurrency, chunks.length);\n await Promise.all(Array.from({ length: workerCount }, () => worker()));\n\n return results.flat();\n }\n}\n"]}
|