@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +26 -1
  3. package/dist/availability.cjs +101 -0
  4. package/dist/availability.cjs.map +1 -0
  5. package/dist/availability.d.cts +87 -0
  6. package/dist/availability.d.ts +87 -0
  7. package/dist/availability.js +4 -0
  8. package/dist/availability.js.map +1 -0
  9. package/dist/cart.d.cts +1 -1
  10. package/dist/cart.d.ts +1 -1
  11. package/dist/category.d.cts +1 -1
  12. package/dist/category.d.ts +1 -1
  13. package/dist/checkout.d.cts +1 -1
  14. package/dist/checkout.d.ts +1 -1
  15. package/dist/{chunk-QDABAMVN.js → chunk-JTJV7WUW.js} +25 -2
  16. package/dist/chunk-JTJV7WUW.js.map +1 -0
  17. package/dist/chunk-SCI7D74B.js +91 -0
  18. package/dist/chunk-SCI7D74B.js.map +1 -0
  19. package/dist/chunk-WSXRUYBE.js +248 -0
  20. package/dist/chunk-WSXRUYBE.js.map +1 -0
  21. package/dist/chunk-XMEP74BU.js +60 -0
  22. package/dist/chunk-XMEP74BU.js.map +1 -0
  23. package/dist/companies.d.cts +1 -1
  24. package/dist/companies.d.ts +1 -1
  25. package/dist/contacts.d.cts +1 -1
  26. package/dist/contacts.d.ts +1 -1
  27. package/dist/{context-vKgzZ-5f.d.cts → context-CGuxSb56.d.cts} +16 -1
  28. package/dist/{context-vKgzZ-5f.d.ts → context-CGuxSb56.d.ts} +16 -1
  29. package/dist/customer-groups.d.cts +1 -1
  30. package/dist/customer-groups.d.ts +1 -1
  31. package/dist/customer.d.cts +1 -1
  32. package/dist/customer.d.ts +1 -1
  33. package/dist/index.cjs +311 -9
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +8 -5
  36. package/dist/index.d.ts +8 -5
  37. package/dist/index.js +61 -6
  38. package/dist/index.js.map +1 -1
  39. package/dist/locations.d.cts +1 -1
  40. package/dist/locations.d.ts +1 -1
  41. package/dist/media.cjs +218 -9
  42. package/dist/media.cjs.map +1 -1
  43. package/dist/media.d.cts +103 -7
  44. package/dist/media.d.ts +103 -7
  45. package/dist/media.js +2 -1
  46. package/dist/orders.d.cts +1 -1
  47. package/dist/orders.d.ts +1 -1
  48. package/dist/payment.d.cts +1 -1
  49. package/dist/payment.d.ts +1 -1
  50. package/dist/price.cjs +44 -0
  51. package/dist/price.cjs.map +1 -1
  52. package/dist/price.d.cts +27 -2
  53. package/dist/price.d.ts +27 -2
  54. package/dist/price.js +1 -1
  55. package/dist/product.cjs +23 -0
  56. package/dist/product.cjs.map +1 -1
  57. package/dist/product.d.cts +18 -1
  58. package/dist/product.d.ts +18 -1
  59. package/dist/product.js +1 -1
  60. package/dist/segment.d.cts +1 -1
  61. package/dist/segment.d.ts +1 -1
  62. package/package.json +7 -2
  63. package/dist/chunk-F3WAQH2E.js +0 -120
  64. package/dist/chunk-F3WAQH2E.js.map +0 -1
  65. package/dist/chunk-HEO34WLO.js +0 -47
  66. package/dist/chunk-HEO34WLO.js.map +0 -1
  67. package/dist/chunk-QDABAMVN.js.map +0 -1
package/dist/media.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-vKgzZ-5f.js';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CGuxSb56.js';
2
2
 
3
3
  type MetadataUpdate = {
4
4
  /**
@@ -156,9 +156,44 @@ type AssetUpdateBlob = Asset$1 & {
156
156
  /** Generated media types (caller sends the exact wire shape). */
157
157
  type AssetCreateBlobInput = AssetCreateBlob;
158
158
  type AssetCreateLinkInput = AssetCreateLink;
159
+ type AssetUpdateBlobInput = AssetUpdateBlob;
160
+ type AssetUpdateLinkInput = AssetUpdateLink;
159
161
  type AssetUpdateInput = AssetUpdateBlob | AssetUpdateLink;
160
162
  type Asset = GetAsset;
161
163
  type AssetRefId = RefId;
164
+ /**
165
+ * Filter / pagination options for {@link MediaService.list}. The explicit
166
+ * fields are typed for autocomplete; the index signature stays open so
167
+ * Emporix `q`-syntax filters like `"refIds.id"` can be passed verbatim.
168
+ */
169
+ interface ListAssetsQuery {
170
+ pageNumber?: number;
171
+ pageSize?: number;
172
+ /** Emporix sort syntax, e.g. `"name,metadata.createdAt:desc"`. */
173
+ sort?: string;
174
+ /** Emporix `q`-syntax filter, e.g. `"name:hero"`. */
175
+ q?: string;
176
+ [key: string]: string | number | undefined;
177
+ }
178
+ /**
179
+ * Result of {@link MediaService.download}. The endpoint's behaviour depends
180
+ * on the asset's `access`:
181
+ * - `PUBLIC` assets respond with a 30x redirect whose `Location` header
182
+ * carries the externally-cacheable storage URL — useful when the
183
+ * storefront wants to send the user there directly.
184
+ * - `PRIVATE` assets respond with the raw bytes (plus an `ETag` header
185
+ * for caching). The asset is delivered through Cloudinary behind the
186
+ * server-side service token, never publicly addressable.
187
+ */
188
+ type DownloadResult = {
189
+ kind: "redirect";
190
+ url: string;
191
+ } | {
192
+ kind: "bytes";
193
+ data: ArrayBuffer;
194
+ etag?: string;
195
+ contentType?: string;
196
+ };
162
197
  declare function isProductRef(r: AssetRefId, productId: string): boolean;
163
198
  /**
164
199
  * Media assets (BLOB/LINK). All endpoints require a backend-only scope
@@ -179,14 +214,61 @@ declare class MediaService {
179
214
  }, auth?: AuthContext): Promise<{
180
215
  id: string;
181
216
  }>;
182
- /** List assets. Optional `query` is passed through to Emporix verbatim. */
183
- list(query?: Record<string, string | number | undefined>, auth?: AuthContext): Promise<Asset[]>;
217
+ /**
218
+ * List assets, wrapped in the shared {@link PaginatedItems} envelope so
219
+ * callers can iterate over pages consistently with the rest of the SDK.
220
+ * `hasNextPage` is the standard SDK heuristic: `true` when the returned
221
+ * page is full (`items.length === requested pageSize`). Pagination
222
+ * defaults match Emporix's server defaults (`pageNumber: 1`,
223
+ * `pageSize: 60`) so an empty `query` produces stable expectations.
224
+ */
225
+ list(query?: ListAssetsQuery, auth?: AuthContext): Promise<PaginatedItems<Asset>>;
184
226
  /** Fetch an asset by id. */
185
227
  get(assetId: string, auth?: AuthContext): Promise<Asset>;
186
- /** Update an asset (e.g. swap `refIds` or `access`). */
187
- update(assetId: string, patch: AssetUpdateInput, auth?: AuthContext): Promise<Asset>;
228
+ /**
229
+ * Update an asset. The input is a discriminated union mirroring
230
+ * {@link create}:
231
+ * - `{ kind: "json", body }` — metadata-only patch (BLOB or LINK).
232
+ * Sends `application/json`. Used for `refIds`, `details`, `metadata`,
233
+ * or `url` changes.
234
+ * - `{ kind: "blob", file, body }` — replaces the BLOB file content
235
+ * (max 10MB) AND patches metadata in the same request. Sends
236
+ * `multipart/form-data`.
237
+ *
238
+ * `type` and `access` are immutable per Emporix — they must match the
239
+ * existing asset's values. Optimistic-locking is via `body.metadata.version`;
240
+ * the server returns 409 Conflict on a stale version.
241
+ */
242
+ update(assetId: string, input: {
243
+ kind: "json";
244
+ body: AssetUpdateInput;
245
+ } | {
246
+ kind: "blob";
247
+ file: Blob;
248
+ body: AssetUpdateBlobInput;
249
+ }, auth?: AuthContext): Promise<Asset>;
188
250
  /** Remove an asset. */
189
251
  remove(assetId: string, auth?: AuthContext): Promise<void>;
252
+ /**
253
+ * Download an asset by id. Returns a discriminated union:
254
+ * - `{ kind: "redirect", url }` for `PUBLIC` assets (server-side 30x with
255
+ * the storage URL in `Location`).
256
+ * - `{ kind: "bytes", data }` for `PRIVATE` assets (server-side 200 with
257
+ * the asset bytes; `etag` + `contentType` headers are exposed for
258
+ * caching). When the server reports `text/plain` Content-Type (the
259
+ * OpenAPI-documented format), the SDK decodes the base64 stream into
260
+ * an `ArrayBuffer` transparently; binary Content-Types are passed
261
+ * through verbatim.
262
+ *
263
+ * Implementation note: uses `redirect: "manual"` so the redirect-location
264
+ * is observable. In Node.js this works; in a browser the redirect Location
265
+ * is intentionally hidden by fetch — there, `PUBLIC` downloads will
266
+ * surface as an opaque-redirect and the SDK throws. Browser code should
267
+ * use the asset's `url` field from `get()` for `LINK` assets, or the
268
+ * direct storage URL for `PUBLIC` `BLOB` assets (typically delivered via
269
+ * an `<img>` tag rather than `download()`).
270
+ */
271
+ download(assetId: string, auth?: AuthContext): Promise<DownloadResult>;
190
272
  /** Multipart upload sugar: builds `AssetCreateBlob` from input. */
191
273
  uploadFile(input: {
192
274
  file: Blob;
@@ -197,6 +279,20 @@ declare class MediaService {
197
279
  }, auth?: AuthContext): Promise<{
198
280
  id: string;
199
281
  }>;
282
+ /**
283
+ * Replace the file content of an existing BLOB asset. Sugar over
284
+ * {@link update} with `kind: "blob"`. `access` is required because the
285
+ * field is immutable and the server validates that the patch matches.
286
+ * Pass `version` from `asset.metadata.version` if you want optimistic
287
+ * locking (recommended when concurrent writers are possible).
288
+ */
289
+ replaceFile(assetId: string, input: {
290
+ file: Blob;
291
+ access: "PUBLIC" | "PRIVATE";
292
+ filename?: string;
293
+ mimeType?: string;
294
+ version?: number;
295
+ }, auth?: AuthContext): Promise<Asset>;
200
296
  /** External-URL sugar: builds `AssetCreateLink`. */
201
297
  link(input: {
202
298
  url: string;
@@ -210,10 +306,10 @@ declare class MediaService {
210
306
  /** Remove a PRODUCT refId from an asset (no-op if absent). */
211
307
  detachFromProduct(assetId: string, productId: string, auth?: AuthContext): Promise<Asset>;
212
308
  /** Convenience: list assets attached to a product (server-side filter). */
213
- listForProduct(productId: string, auth?: AuthContext): Promise<Asset[]>;
309
+ listForProduct(productId: string, auth?: AuthContext): Promise<PaginatedItems<Asset>>;
214
310
  }
215
311
  declare const _internalMedia: {
216
312
  isProductRef: typeof isProductRef;
217
313
  };
218
314
 
219
- export { type Asset, type AssetCreateBlobInput, type AssetCreateLinkInput, type AssetRefId, type AssetUpdateInput, MediaService, _internalMedia };
315
+ export { type Asset, type AssetCreateBlobInput, type AssetCreateLinkInput, type AssetRefId, type AssetUpdateBlobInput, type AssetUpdateInput, type AssetUpdateLinkInput, type DownloadResult, type ListAssetsQuery, MediaService, _internalMedia };
package/dist/media.js CHANGED
@@ -1,3 +1,4 @@
1
- export { MediaService, _internalMedia } from './chunk-F3WAQH2E.js';
1
+ export { MediaService, _internalMedia } from './chunk-WSXRUYBE.js';
2
+ import './chunk-KTODT6DV.js';
2
3
  //# sourceMappingURL=media.js.map
3
4
  //# sourceMappingURL=media.js.map
package/dist/orders.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-vKgzZ-5f.cjs';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CGuxSb56.cjs';
2
2
 
3
3
  /**
4
4
  * Hand-written mirror of the Emporix Order-v2 schemas (storefront-relevant
package/dist/orders.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-vKgzZ-5f.js';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CGuxSb56.js';
2
2
 
3
3
  /**
4
4
  * Hand-written mirror of the Emporix Order-v2 schemas (storefront-relevant
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-vKgzZ-5f.cjs';
1
+ import { C as ClientContext, b as AuthContext } from './context-CGuxSb56.cjs';
2
2
 
3
3
  type PaymentModeFrontendResponse = {
4
4
  /**
package/dist/payment.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-vKgzZ-5f.js';
1
+ import { C as ClientContext, b as AuthContext } from './context-CGuxSb56.js';
2
2
 
3
3
  type PaymentModeFrontendResponse = {
4
4
  /**
package/dist/price.cjs CHANGED
@@ -81,6 +81,50 @@ var PriceService = class {
81
81
  body: input
82
82
  });
83
83
  }
84
+ /**
85
+ * Chunked variant of {@link matchByContext} for large `items` arrays. The
86
+ * Emporix backend handles only a limited number of items per request
87
+ * (production limit ~50), so this splits `input.items` into chunks and runs
88
+ * `matchByContext` with bounded concurrency.
89
+ *
90
+ * By default a failing chunk is skipped (its items are absent from the
91
+ * result) and `opts.onChunkError` is called once for it; pass
92
+ * `throwOnAnyChunkError: true` to reject on the first failure instead.
93
+ *
94
+ * **Result order is not guaranteed** — match entries back to your items by
95
+ * `priceId` / `itemRef.id`.
96
+ */
97
+ async matchByContextChunked(input, opts = {}, auth) {
98
+ const chunkSize = opts.chunkSize ?? 50;
99
+ const concurrency = opts.concurrency ?? 4;
100
+ if (chunkSize < 1) throw new Error("chunkSize must be >= 1");
101
+ if (concurrency < 1) throw new Error("concurrency must be >= 1");
102
+ const items = input.items ?? [];
103
+ if (items.length === 0) return [];
104
+ const chunks = [];
105
+ for (let i = 0; i < items.length; i += chunkSize) {
106
+ chunks.push({ ...input, items: items.slice(i, i + chunkSize) });
107
+ }
108
+ const results = new Array(chunks.length);
109
+ let cursor = 0;
110
+ const worker = async () => {
111
+ for (; ; ) {
112
+ const idx = cursor++;
113
+ const chunk = chunks[idx];
114
+ if (chunk === void 0) return;
115
+ try {
116
+ results[idx] = await this.matchByContext(chunk, auth);
117
+ } catch (err) {
118
+ if (opts.throwOnAnyChunkError) throw err;
119
+ results[idx] = [];
120
+ opts.onChunkError?.(err, idx);
121
+ }
122
+ }
123
+ };
124
+ const workerCount = Math.min(concurrency, chunks.length);
125
+ await Promise.all(Array.from({ length: workerCount }, () => worker()));
126
+ return results.flat();
127
+ }
84
128
  };
85
129
 
86
130
  exports.PriceService = PriceService;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/errors.ts","../src/services/price.ts"],"names":[],"mappings":"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","file":"price.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 { 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\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"]}
1
+ {"version":3,"sources":["../src/core/errors.ts","../src/services/price.ts"],"names":[],"mappings":"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","file":"price.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 { 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"]}
package/dist/price.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-vKgzZ-5f.cjs';
1
+ import { C as ClientContext, b as AuthContext } from './context-CGuxSb56.cjs';
2
2
 
3
3
  type Metadata = {
4
4
  /**
@@ -421,6 +421,17 @@ type PriceMatchByContextInput = MatchByContext;
421
421
  type PriceMatchInput = Match;
422
422
  /** A resolved price (full generated match-response schema). */
423
423
  type PriceMatch = MatchResponse;
424
+ /** Options for {@link PriceService.matchByContextChunked}. */
425
+ interface MatchByContextChunkedOptions {
426
+ /** Items per request. Default 50. Must be >= 1. */
427
+ chunkSize?: number;
428
+ /** Maximum number of requests in flight at once. Default 4. Must be >= 1. */
429
+ concurrency?: number;
430
+ /** Invoked once per failed chunk (default mode only — not when throwing). */
431
+ onChunkError?: (err: unknown, chunkIndex: number) => void;
432
+ /** When true, the first failing chunk rejects the whole call. Default false. */
433
+ throwOnAnyChunkError?: boolean;
434
+ }
424
435
  /**
425
436
  * Price matching. The Cart service does not resolve prices — call this
426
437
  * explicitly before rendering money and again right before placing an order.
@@ -440,6 +451,20 @@ declare class PriceService {
440
451
  * (requires `price.price_read` / `price.price_manage`).
441
452
  */
442
453
  match(input: PriceMatchInput, auth?: AuthContext): Promise<PriceMatch[]>;
454
+ /**
455
+ * Chunked variant of {@link matchByContext} for large `items` arrays. The
456
+ * Emporix backend handles only a limited number of items per request
457
+ * (production limit ~50), so this splits `input.items` into chunks and runs
458
+ * `matchByContext` with bounded concurrency.
459
+ *
460
+ * By default a failing chunk is skipped (its items are absent from the
461
+ * result) and `opts.onChunkError` is called once for it; pass
462
+ * `throwOnAnyChunkError: true` to reject on the first failure instead.
463
+ *
464
+ * **Result order is not guaranteed** — match entries back to your items by
465
+ * `priceId` / `itemRef.id`.
466
+ */
467
+ matchByContextChunked(input: PriceMatchByContextInput, opts?: MatchByContextChunkedOptions, auth?: AuthContext): Promise<PriceMatch[]>;
443
468
  }
444
469
 
445
- export { type PriceMatch, type PriceMatchByContextInput, type PriceMatchInput, PriceService };
470
+ export { type MatchByContextChunkedOptions, type PriceMatch, type PriceMatchByContextInput, type PriceMatchInput, PriceService };
package/dist/price.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-vKgzZ-5f.js';
1
+ import { C as ClientContext, b as AuthContext } from './context-CGuxSb56.js';
2
2
 
3
3
  type Metadata = {
4
4
  /**
@@ -421,6 +421,17 @@ type PriceMatchByContextInput = MatchByContext;
421
421
  type PriceMatchInput = Match;
422
422
  /** A resolved price (full generated match-response schema). */
423
423
  type PriceMatch = MatchResponse;
424
+ /** Options for {@link PriceService.matchByContextChunked}. */
425
+ interface MatchByContextChunkedOptions {
426
+ /** Items per request. Default 50. Must be >= 1. */
427
+ chunkSize?: number;
428
+ /** Maximum number of requests in flight at once. Default 4. Must be >= 1. */
429
+ concurrency?: number;
430
+ /** Invoked once per failed chunk (default mode only — not when throwing). */
431
+ onChunkError?: (err: unknown, chunkIndex: number) => void;
432
+ /** When true, the first failing chunk rejects the whole call. Default false. */
433
+ throwOnAnyChunkError?: boolean;
434
+ }
424
435
  /**
425
436
  * Price matching. The Cart service does not resolve prices — call this
426
437
  * explicitly before rendering money and again right before placing an order.
@@ -440,6 +451,20 @@ declare class PriceService {
440
451
  * (requires `price.price_read` / `price.price_manage`).
441
452
  */
442
453
  match(input: PriceMatchInput, auth?: AuthContext): Promise<PriceMatch[]>;
454
+ /**
455
+ * Chunked variant of {@link matchByContext} for large `items` arrays. The
456
+ * Emporix backend handles only a limited number of items per request
457
+ * (production limit ~50), so this splits `input.items` into chunks and runs
458
+ * `matchByContext` with bounded concurrency.
459
+ *
460
+ * By default a failing chunk is skipped (its items are absent from the
461
+ * result) and `opts.onChunkError` is called once for it; pass
462
+ * `throwOnAnyChunkError: true` to reject on the first failure instead.
463
+ *
464
+ * **Result order is not guaranteed** — match entries back to your items by
465
+ * `priceId` / `itemRef.id`.
466
+ */
467
+ matchByContextChunked(input: PriceMatchByContextInput, opts?: MatchByContextChunkedOptions, auth?: AuthContext): Promise<PriceMatch[]>;
443
468
  }
444
469
 
445
- export { type PriceMatch, type PriceMatchByContextInput, type PriceMatchInput, PriceService };
470
+ export { type MatchByContextChunkedOptions, type PriceMatch, type PriceMatchByContextInput, type PriceMatchInput, PriceService };
package/dist/price.js CHANGED
@@ -1,4 +1,4 @@
1
- export { PriceService } from './chunk-HEO34WLO.js';
1
+ export { PriceService } from './chunk-SCI7D74B.js';
2
2
  import './chunk-KTODT6DV.js';
3
3
  //# sourceMappingURL=price.js.map
4
4
  //# sourceMappingURL=price.js.map
package/dist/product.cjs CHANGED
@@ -93,6 +93,29 @@ var ProductService = class {
93
93
  );
94
94
  return pages.flat();
95
95
  }
96
+ /**
97
+ * Streams the VARIANT children of a PARENT_VARIANT product, page by page,
98
+ * via the search query `productType:VARIANT parentVariantId:<id>`. Default
99
+ * pageSize 200. The query syntax (space-separated fields = implicit AND) is
100
+ * encapsulated here so consumers don't build it themselves.
101
+ */
102
+ listVariantChildrenAll(parentVariantId, params = {}, auth = ANON) {
103
+ const pageSize = params.pageSize ?? 200;
104
+ const q = `productType:VARIANT parentVariantId:${parentVariantId}`;
105
+ return iterateAll((pageNumber) => this.search(q, { pageNumber, pageSize }, auth));
106
+ }
107
+ /**
108
+ * Resolves ALL VARIANT children of a PARENT_VARIANT product into a flat
109
+ * array (loads every page). Default pageSize 200. Returns `[]` when there are
110
+ * no children — never throws.
111
+ */
112
+ async listVariantChildren(parentVariantId, params = {}, auth = ANON) {
113
+ const out = [];
114
+ for await (const child of this.listVariantChildrenAll(parentVariantId, params, auth)) {
115
+ out.push(child);
116
+ }
117
+ return out;
118
+ }
96
119
  };
97
120
 
98
121
  exports.ProductService = ProductService;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/context.ts","../src/services/product.ts"],"names":[],"mappings":";;;AAkCA,gBAAuB,UAAA,CACrB,SAAA,EACA,KAAA,GAAQ,CAAA,EACU;AAClB,EAAA,IAAI,CAAA,GAAI,KAAA;AACR,EAAA,WAAS;AACP,IAAA,MAAM,IAAA,GAAO,MAAM,SAAA,CAAU,CAAC,CAAA;AAC9B,IAAA,KAAA,MAAW,EAAA,IAAM,IAAA,CAAK,KAAA,EAAO,MAAM,EAAA;AACnC,IAAA,IAAI,CAAC,KAAK,WAAA,EAAa;AACvB,IAAA,CAAA,IAAK,CAAA;AAAA,EACP;AACF;;;ACnCA,IAAM,IAAA,GAAoB,EAAE,IAAA,EAAM,WAAA,EAAY;AASvC,IAAM,iBAAN,MAAqB;AAAA,EAC1B,YAA6B,GAAA,EAAoB;AAApB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAqB;AAAA,EAArB,GAAA;AAAA;AAAA,EAG7B,MAAM,GAAA,CACJ,SAAA,EACA,KAAA,EACA,OAAoB,IAAA,EACF;AAClB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAiB;AAAA,MACpC,MAAA,EAAQ,KAAA;AAAA,MACR,MAAM,CAAA,SAAA,EAAY,IAAA,CAAK,GAAA,CAAI,MAAM,aAAa,SAAS,CAAA,CAAA;AAAA,MACvD;AAAA,KACD,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,SAAA,CAAU,IAAA,EAAc,IAAA,GAAoB,IAAA,EAAwB;AACxE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,OAAA,CAAmB;AAAA,MAClD,MAAA,EAAQ,KAAA;AAAA,MACR,IAAA,EAAM,CAAA,SAAA,EAAY,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,SAAA,CAAA;AAAA,MACjC,KAAA,EAAO,EAAE,CAAA,EAAG,CAAA,KAAA,EAAQ,IAAI,CAAA,CAAA,EAAG;AAAA,MAC3B;AAAA,KACD,CAAA;AACD,IAAA,MAAM,KAAA,GAAQ,KAAK,CAAC,CAAA;AACpB,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,KAAA,CAAM,CAAA,sBAAA,EAAyB,IAAI,CAAA,CAAA,CAAG,CAAA;AAC5D,IAAA,OAAO,KAAA;AAAA,EACT;AAAA;AAAA,EAGA,MAAM,IAAA,CACJ,MAAA,GAAqD,EAAC,EACtD,OAAoB,IAAA,EACc;AAClC,IAAA,MAAM,UAAA,GAAa,OAAO,UAAA,IAAc,CAAA;AACxC,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AACpC,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,OAAA,CAAmB;AAAA,MACnD,MAAA,EAAQ,KAAA;AAAA,MACR,IAAA,EAAM,CAAA,SAAA,EAAY,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,SAAA,CAAA;AAAA,MACjC,KAAA,EAAO,EAAE,UAAA,EAAY,QAAA,EAAS;AAAA,MAC9B;AAAA,KACD,CAAA;AACD,IAAA,OAAO,EAAE,KAAA,EAAO,UAAA,EAAY,UAAU,WAAA,EAAa,KAAA,CAAM,WAAW,QAAA,EAAS;AAAA,EAC/E;AAAA;AAAA,EAGA,OAAA,CAAQ,MAAA,GAAgC,EAAC,EAAG,OAAoB,IAAA,EAA8B;AAC5F,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AACpC,IAAA,OAAO,UAAA,CAAoB,CAAC,UAAA,KAAe,IAAA,CAAK,IAAA,CAAK,EAAE,UAAA,EAAY,QAAA,EAAS,EAAG,IAAI,CAAC,CAAA;AAAA,EACtF;AAAA;AAAA,EAGA,MAAM,MAAA,CACJ,KAAA,EACA,SAAqD,EAAC,EACtD,OAAoB,IAAA,EACc;AAClC,IAAA,MAAM,UAAA,GAAa,OAAO,UAAA,IAAc,CAAA;AACxC,IAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,EAAA;AACpC,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,OAAA,CAAmB;AAAA,MACnD,MAAA,EAAQ,KAAA;AAAA,MACR,IAAA,EAAM,CAAA,SAAA,EAAY,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,SAAA,CAAA;AAAA,MACjC,KAAA,EAAO,EAAE,CAAA,EAAG,KAAA,EAAO,YAAY,QAAA,EAAS;AAAA,MACxC;AAAA,KACD,CAAA;AACD,IAAA,OAAO,EAAE,KAAA,EAAO,UAAA,EAAY,UAAU,WAAA,EAAa,KAAA,CAAM,WAAW,QAAA,EAAS;AAAA,EAC/E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAA,CACJ,GAAA,EACA,UAAkC,EAAC,EACnC,OAAoB,IAAA,EACA;AACpB,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,CAAA,EAAG,OAAO,EAAC;AAC9B,IAAA,MAAM,SAAA,GAAY,QAAQ,SAAA,IAAa,GAAA;AACvC,IAAA,MAAM,SAAqB,EAAC;AAC5B,IAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,MAAA,EAAQ,KAAK,SAAA,EAAW;AAC9C,MAAA,MAAA,CAAO,KAAK,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,CAAA,GAAI,SAAS,CAAC,CAAA;AAAA,IACzC;AACA,IAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,GAAA;AAAA,MAC1B,MAAA,CAAO,GAAA;AAAA,QAAI,CAAC,KAAA,KACV,IAAA,CAAK,GAAA,CAAI,KAAK,OAAA,CAAmB;AAAA,UAC/B,MAAA,EAAQ,MAAA;AAAA,UACR,IAAA,EAAM,CAAA,SAAA,EAAY,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,gBAAA,CAAA;AAAA,UACjC,KAAA,EAAO,EAAE,QAAA,EAAU,KAAA,CAAM,MAAA,EAAO;AAAA,UAChC,IAAA;AAAA,UACA,IAAA,EAAM,EAAE,CAAA,EAAG,CAAA,IAAA,EAAO,MAAM,IAAA,CAAK,GAAG,CAAC,CAAA,CAAA,CAAA;AAAI,SACtC;AAAA;AACH,KACF;AACA,IAAA,OAAO,MAAM,IAAA,EAAK;AAAA,EACpB;AAEF","file":"product.cjs","sourcesContent":["import type { HttpClient } from \"./http\";\nimport type { TokenProvider, AuthContext } from \"./auth\";\nimport type { Logger } from \"./logger\";\n\n/** Shared dependencies every service facade receives. */\nexport interface ClientContext {\n tenant: string;\n http: HttpClient;\n tokenProvider: TokenProvider;\n logger: Logger;\n}\n\n/**\n * A cursor-paged collection — `pageNumber`/`pageSize` indexing with a\n * `hasNextPage` signal driven by the source page being full. Used by all\n * paginated services and hooks across the SDK.\n */\nexport interface PaginatedItems<T> {\n items: T[];\n pageNumber: number;\n pageSize: number;\n hasNextPage: boolean;\n}\n\n/** Default `AuthContext` applied by a service when the caller passes none. */\nexport type DefaultAuth = AuthContext | undefined;\n\n/**\n * Async-iterates every item across pages of a `PaginatedItems<T>` source.\n * Stops when the source reports `hasNextPage: false`.\n *\n * @param fetchPage - given a 1-based page number, returns the page.\n * @param start - first page to fetch (default `1`).\n */\nexport async function* iterateAll<T>(\n fetchPage: (pageNumber: number) => Promise<PaginatedItems<T>>,\n start = 1,\n): AsyncIterable<T> {\n let p = start;\n for (;;) {\n const page = await fetchPage(p);\n for (const it of page.items) yield it;\n if (!page.hasNextPage) return;\n p += 1;\n }\n}\n\n","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"]}
1
+ {"version":3,"sources":["../src/core/context.ts","../src/services/product.ts"],"names":[],"mappings":"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","file":"product.cjs","sourcesContent":["import type { HttpClient } from \"./http\";\nimport type { TokenProvider, AuthContext } from \"./auth\";\nimport type { Logger } from \"./logger\";\n\n/** Shared dependencies every service facade receives. */\nexport interface ClientContext {\n tenant: string;\n http: HttpClient;\n tokenProvider: TokenProvider;\n logger: Logger;\n}\n\n/**\n * A cursor-paged collection — `pageNumber`/`pageSize` indexing with a\n * `hasNextPage` signal driven by the source page being full. Used by all\n * paginated services and hooks across the SDK.\n */\nexport interface PaginatedItems<T> {\n items: T[];\n pageNumber: number;\n pageSize: number;\n hasNextPage: boolean;\n}\n\n/** Default `AuthContext` applied by a service when the caller passes none. */\nexport type DefaultAuth = AuthContext | undefined;\n\n/**\n * Async-iterates every item across pages of a `PaginatedItems<T>` source.\n * Stops when the source reports `hasNextPage: false`.\n *\n * @param fetchPage - given a 1-based page number, returns the page.\n * @param start - first page to fetch (default `1`).\n */\nexport async function* iterateAll<T>(\n fetchPage: (pageNumber: number) => Promise<PaginatedItems<T>>,\n start = 1,\n): AsyncIterable<T> {\n let p = start;\n for (;;) {\n const page = await fetchPage(p);\n for (const it of page.items) yield it;\n if (!page.hasNextPage) return;\n p += 1;\n }\n}\n\n","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"]}
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-vKgzZ-5f.cjs';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CGuxSb56.cjs';
2
2
 
3
3
  /**
4
4
  * Localized value which can have different structure which depends on provided `Content-Language` header.
@@ -395,6 +395,23 @@ declare class ProductService {
395
395
  searchByIds(ids: string[], options?: {
396
396
  chunkSize?: number;
397
397
  }, auth?: AuthContext): Promise<Product[]>;
398
+ /**
399
+ * Streams the VARIANT children of a PARENT_VARIANT product, page by page,
400
+ * via the search query `productType:VARIANT parentVariantId:<id>`. Default
401
+ * pageSize 200. The query syntax (space-separated fields = implicit AND) is
402
+ * encapsulated here so consumers don't build it themselves.
403
+ */
404
+ listVariantChildrenAll(parentVariantId: string, params?: {
405
+ pageSize?: number;
406
+ }, auth?: AuthContext): AsyncIterable<Product>;
407
+ /**
408
+ * Resolves ALL VARIANT children of a PARENT_VARIANT product into a flat
409
+ * array (loads every page). Default pageSize 200. Returns `[]` when there are
410
+ * no children — never throws.
411
+ */
412
+ listVariantChildren(parentVariantId: string, params?: {
413
+ pageSize?: number;
414
+ }, auth?: AuthContext): Promise<Product[]>;
398
415
  }
399
416
 
400
417
  export { type Media, type Product, ProductService };
package/dist/product.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-vKgzZ-5f.js';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CGuxSb56.js';
2
2
 
3
3
  /**
4
4
  * Localized value which can have different structure which depends on provided `Content-Language` header.
@@ -395,6 +395,23 @@ declare class ProductService {
395
395
  searchByIds(ids: string[], options?: {
396
396
  chunkSize?: number;
397
397
  }, auth?: AuthContext): Promise<Product[]>;
398
+ /**
399
+ * Streams the VARIANT children of a PARENT_VARIANT product, page by page,
400
+ * via the search query `productType:VARIANT parentVariantId:<id>`. Default
401
+ * pageSize 200. The query syntax (space-separated fields = implicit AND) is
402
+ * encapsulated here so consumers don't build it themselves.
403
+ */
404
+ listVariantChildrenAll(parentVariantId: string, params?: {
405
+ pageSize?: number;
406
+ }, auth?: AuthContext): AsyncIterable<Product>;
407
+ /**
408
+ * Resolves ALL VARIANT children of a PARENT_VARIANT product into a flat
409
+ * array (loads every page). Default pageSize 200. Returns `[]` when there are
410
+ * no children — never throws.
411
+ */
412
+ listVariantChildren(parentVariantId: string, params?: {
413
+ pageSize?: number;
414
+ }, auth?: AuthContext): Promise<Product[]>;
398
415
  }
399
416
 
400
417
  export { type Media, type Product, ProductService };
package/dist/product.js CHANGED
@@ -1,4 +1,4 @@
1
- export { ProductService } from './chunk-QDABAMVN.js';
1
+ export { ProductService } from './chunk-JTJV7WUW.js';
2
2
  import './chunk-GWJ2U2CP.js';
3
3
  //# sourceMappingURL=product.js.map
4
4
  //# sourceMappingURL=product.js.map
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-vKgzZ-5f.cjs';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CGuxSb56.cjs';
2
2
  import { ProductService } from './product.cjs';
3
3
  import { CategoryService } from './category.cjs';
4
4
 
package/dist/segment.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-vKgzZ-5f.js';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CGuxSb56.js';
2
2
  import { ProductService } from './product.js';
3
3
  import { CategoryService } from './category.js';
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viu/emporix-sdk",
3
- "version": "1.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "TypeScript SDK for the Emporix Commerce Engine",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -110,11 +110,16 @@
110
110
  "types": "./dist/orders.d.ts",
111
111
  "import": "./dist/orders.js",
112
112
  "require": "./dist/orders.cjs"
113
+ },
114
+ "./availability": {
115
+ "types": "./dist/availability.d.ts",
116
+ "import": "./dist/availability.js",
117
+ "require": "./dist/availability.cjs"
113
118
  }
114
119
  },
115
120
  "publishConfig": {
116
121
  "access": "public",
117
- "provenance": false
122
+ "provenance": true
118
123
  },
119
124
  "devDependencies": {
120
125
  "@hey-api/openapi-ts": "^0.64.0",