@viu/emporix-sdk 2.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 +18 -0
- package/README.md +13 -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-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-mMTqsW1Z.d.cts → context-CGuxSb56.d.cts} +1 -1
- package/dist/{context-mMTqsW1Z.d.ts → context-CGuxSb56.d.ts} +1 -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 +125 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/locations.d.cts +1 -1
- package/dist/locations.d.ts +1 -1
- package/dist/media.d.cts +1 -1
- package/dist/media.d.ts +1 -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 +6 -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,23 @@
|
|
|
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
|
+
|
|
3
21
|
## 2.0.0
|
|
4
22
|
|
|
5
23
|
### Major 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
|
|
|
@@ -111,11 +115,19 @@ require a server-only scope — every call defaults to a `service`
|
|
|
111
115
|
on the product) or the `useProductMedia(productId)` hook, not by calling
|
|
112
116
|
the Media service directly. See [`../../docs/media.md`](../../docs/media.md).
|
|
113
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
|
+
|
|
114
126
|
## Subpath exports
|
|
115
127
|
|
|
116
128
|
`@viu/emporix-sdk` (everything) plus `./customer`, `./product`, `./category`,
|
|
117
129
|
`./cart`, `./checkout`, `./payment`, `./price`, `./media`, `./segment`,
|
|
118
|
-
`./companies`, `./contacts`, `./locations`, `./customer-groups` for tree-shaking.
|
|
130
|
+
`./companies`, `./contacts`, `./locations`, `./customer-groups`, `./availability` for tree-shaking.
|
|
119
131
|
|
|
120
132
|
## Authors
|
|
121
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":"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","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"]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { EmporixNotFoundError } from './chunk-KTODT6DV.js';
|
|
2
|
+
|
|
3
|
+
// src/services/availability.ts
|
|
4
|
+
var ANON = { kind: "anonymous" };
|
|
5
|
+
var AvailabilityService = class {
|
|
6
|
+
constructor(ctx) {
|
|
7
|
+
this.ctx = ctx;
|
|
8
|
+
}
|
|
9
|
+
ctx;
|
|
10
|
+
/**
|
|
11
|
+
* Single product. Resolves the availability record, or — when
|
|
12
|
+
* `opts.defaultAvailableOnNotFound` is set — a default available record on 404.
|
|
13
|
+
*/
|
|
14
|
+
async get(productId, siteCode, auth = ANON, opts = {}) {
|
|
15
|
+
try {
|
|
16
|
+
return await this.ctx.http.request({
|
|
17
|
+
method: "GET",
|
|
18
|
+
path: `/availability/${this.ctx.tenant}/availability/${encodeURIComponent(
|
|
19
|
+
productId
|
|
20
|
+
)}/${encodeURIComponent(siteCode)}`,
|
|
21
|
+
auth
|
|
22
|
+
});
|
|
23
|
+
} catch (err) {
|
|
24
|
+
if (err instanceof EmporixNotFoundError && opts.defaultAvailableOnNotFound) {
|
|
25
|
+
return { productId, site: siteCode, available: true };
|
|
26
|
+
}
|
|
27
|
+
throw err;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Batch read via `POST .../availability/search` (one request). Products with
|
|
32
|
+
* no availability record are absent from the response; each is synthesized as
|
|
33
|
+
* `{ available: false }` (or `{ available: true }` when
|
|
34
|
+
* `opts.defaultAvailableOnNotFound` is set). The result preserves input order
|
|
35
|
+
* and length. An empty `productIds` resolves to `[]` without a request.
|
|
36
|
+
*/
|
|
37
|
+
async getMany(productIds, siteCode, auth = ANON, opts = {}) {
|
|
38
|
+
if (productIds.length === 0) return [];
|
|
39
|
+
const found = await this.ctx.http.request({
|
|
40
|
+
method: "POST",
|
|
41
|
+
path: `/availability/${this.ctx.tenant}/availability/search`,
|
|
42
|
+
auth,
|
|
43
|
+
query: { site: siteCode, pageSize: productIds.length },
|
|
44
|
+
body: productIds
|
|
45
|
+
});
|
|
46
|
+
const byId = /* @__PURE__ */ new Map();
|
|
47
|
+
for (const a of found) if (a.productId) byId.set(a.productId, a);
|
|
48
|
+
return productIds.map(
|
|
49
|
+
(id) => byId.get(id) ?? {
|
|
50
|
+
productId: id,
|
|
51
|
+
site: siteCode,
|
|
52
|
+
available: Boolean(opts.defaultAvailableOnNotFound)
|
|
53
|
+
}
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export { AvailabilityService };
|
|
59
|
+
//# sourceMappingURL=chunk-XMEP74BU.js.map
|
|
60
|
+
//# sourceMappingURL=chunk-XMEP74BU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/services/availability.ts"],"names":[],"mappings":";;;AAuBA,IAAM,IAAA,GAAoB,EAAE,IAAA,EAAM,WAAA,EAAY;AAQvC,IAAM,sBAAN,MAA0B;AAAA,EAC/B,YAA6B,GAAA,EAAoB;AAApB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAqB;AAAA,EAArB,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM7B,MAAM,IACJ,SAAA,EACA,QAAA,EACA,OAAoB,IAAA,EACpB,IAAA,GAA4B,EAAC,EACN;AACvB,IAAA,IAAI;AACF,MAAA,OAAO,MAAM,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAsB;AAAA,QAC/C,MAAA,EAAQ,KAAA;AAAA,QACR,IAAA,EAAM,CAAA,cAAA,EAAiB,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,cAAA,EAAiB,kBAAA;AAAA,UACrD;AAAA,SACD,CAAA,CAAA,EAAI,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAA;AAAA,QACjC;AAAA,OACD,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AACZ,MAAA,IAAI,GAAA,YAAe,oBAAA,IAAwB,IAAA,CAAK,0BAAA,EAA4B;AAC1E,QAAA,OAAO,EAAE,SAAA,EAAW,IAAA,EAAM,QAAA,EAAU,WAAW,IAAA,EAAK;AAAA,MACtD;AACA,MAAA,MAAM,GAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,QACJ,UAAA,EACA,QAAA,EACA,OAAoB,IAAA,EACpB,IAAA,GAA4B,EAAC,EACJ;AACzB,IAAA,IAAI,UAAA,CAAW,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AACrC,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,OAAA,CAAwB;AAAA,MACxD,MAAA,EAAQ,MAAA;AAAA,MACR,IAAA,EAAM,CAAA,cAAA,EAAiB,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,oBAAA,CAAA;AAAA,MACtC,IAAA;AAAA,MACA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAU,QAAA,EAAU,WAAW,MAAA,EAAO;AAAA,MACrD,IAAA,EAAM;AAAA,KACP,CAAA;AACD,IAAA,MAAM,IAAA,uBAAW,GAAA,EAA0B;AAC3C,IAAA,KAAA,MAAW,CAAA,IAAK,OAAO,IAAI,CAAA,CAAE,WAAW,IAAA,CAAK,GAAA,CAAI,CAAA,CAAE,SAAA,EAAW,CAAC,CAAA;AAC/D,IAAA,OAAO,UAAA,CAAW,GAAA;AAAA,MAChB,CAAC,EAAA,KACC,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA,IAAK;AAAA,QACd,SAAA,EAAW,EAAA;AAAA,QACX,IAAA,EAAM,QAAA;AAAA,QACN,SAAA,EAAW,OAAA,CAAQ,IAAA,CAAK,0BAA0B;AAAA;AACpD,KACJ;AAAA,EACF;AACF","file":"chunk-XMEP74BU.js","sourcesContent":["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"]}
|