@viu/emporix-sdk 2.13.1 → 2.15.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 (136) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +1 -1
  3. package/dist/availability.cjs +4 -1
  4. package/dist/availability.cjs.map +1 -1
  5. package/dist/availability.d.cts +2 -1
  6. package/dist/availability.d.ts +2 -1
  7. package/dist/availability.js +2 -2
  8. package/dist/cart.cjs +1 -0
  9. package/dist/cart.cjs.map +1 -1
  10. package/dist/cart.d.cts +2 -1
  11. package/dist/cart.d.ts +2 -1
  12. package/dist/cart.js +2 -2
  13. package/dist/category.cjs +4 -1
  14. package/dist/category.cjs.map +1 -1
  15. package/dist/category.d.cts +2 -1
  16. package/dist/category.d.ts +2 -1
  17. package/dist/category.js +1 -1
  18. package/dist/checkout.cjs +1 -0
  19. package/dist/checkout.cjs.map +1 -1
  20. package/dist/checkout.d.cts +2 -1
  21. package/dist/checkout.d.ts +2 -1
  22. package/dist/checkout.js +2 -2
  23. package/dist/{chunk-BFQ732VZ.js → chunk-3BDDYX3V.js} +3 -3
  24. package/dist/{chunk-BFQ732VZ.js.map → chunk-3BDDYX3V.js.map} +1 -1
  25. package/dist/{chunk-LGOC5XOI.js → chunk-3BTPX7IJ.js} +3 -2
  26. package/dist/chunk-3BTPX7IJ.js.map +1 -0
  27. package/dist/{chunk-B4UOYJKW.js → chunk-5VTBLBXJ.js} +9 -4
  28. package/dist/chunk-5VTBLBXJ.js.map +1 -0
  29. package/dist/{chunk-WW3H7VJ5.js → chunk-62PCCHZ2.js} +4 -2
  30. package/dist/chunk-62PCCHZ2.js.map +1 -0
  31. package/dist/{chunk-BK25FDTH.js → chunk-A3LYJUD4.js} +10 -5
  32. package/dist/chunk-A3LYJUD4.js.map +1 -0
  33. package/dist/{chunk-MM6JF3Z7.js → chunk-BNX3JULF.js} +3 -2
  34. package/dist/chunk-BNX3JULF.js.map +1 -0
  35. package/dist/{chunk-LGLFJL3S.js → chunk-DUVIIWFG.js} +3 -2
  36. package/dist/chunk-DUVIIWFG.js.map +1 -0
  37. package/dist/{chunk-HAEVK6IS.js → chunk-EAWGUZHS.js} +3 -2
  38. package/dist/chunk-EAWGUZHS.js.map +1 -0
  39. package/dist/{chunk-AUVJ5V47.js → chunk-GIZS4BY3.js} +6 -3
  40. package/dist/chunk-GIZS4BY3.js.map +1 -0
  41. package/dist/{chunk-XMEP74BU.js → chunk-K3VQLW3P.js} +7 -4
  42. package/dist/chunk-K3VQLW3P.js.map +1 -0
  43. package/dist/{chunk-Z6PNI5VT.js → chunk-NE7MIZPR.js} +5 -3
  44. package/dist/chunk-NE7MIZPR.js.map +1 -0
  45. package/dist/{chunk-TLH6SLAF.js → chunk-NEXUWPHW.js} +28 -34
  46. package/dist/chunk-NEXUWPHW.js.map +1 -0
  47. package/dist/{chunk-WSXRUYBE.js → chunk-OTG2S2L4.js} +4 -3
  48. package/dist/chunk-OTG2S2L4.js.map +1 -0
  49. package/dist/{chunk-I3YYGSN2.js → chunk-PAT4CS36.js} +4 -3
  50. package/dist/chunk-PAT4CS36.js.map +1 -0
  51. package/dist/{chunk-OMYVOOSX.js → chunk-RYQVMR26.js} +4 -3
  52. package/dist/chunk-RYQVMR26.js.map +1 -0
  53. package/dist/{chunk-KTODT6DV.js → chunk-UWGGF4MC.js} +7 -3
  54. package/dist/chunk-UWGGF4MC.js.map +1 -0
  55. package/dist/{chunk-HKPZI7Y5.js → chunk-XEJPH6UN.js} +4 -3
  56. package/dist/chunk-XEJPH6UN.js.map +1 -0
  57. package/dist/companies.cjs +1 -0
  58. package/dist/companies.cjs.map +1 -1
  59. package/dist/companies.d.cts +2 -1
  60. package/dist/companies.d.ts +2 -1
  61. package/dist/companies.js +1 -1
  62. package/dist/contacts.cjs +1 -0
  63. package/dist/contacts.cjs.map +1 -1
  64. package/dist/contacts.d.cts +2 -1
  65. package/dist/contacts.d.ts +2 -1
  66. package/dist/contacts.js +1 -1
  67. package/dist/{context-CD7F_LHT.d.cts → context-DhoFgoKf.d.cts} +13 -0
  68. package/dist/{context-CD7F_LHT.d.ts → context-DhoFgoKf.d.ts} +13 -0
  69. package/dist/customer-groups.cjs +1 -0
  70. package/dist/customer-groups.cjs.map +1 -1
  71. package/dist/customer-groups.d.cts +2 -1
  72. package/dist/customer-groups.d.ts +2 -1
  73. package/dist/customer-groups.js +1 -1
  74. package/dist/customer.cjs +24 -31
  75. package/dist/customer.cjs.map +1 -1
  76. package/dist/customer.d.cts +2 -1
  77. package/dist/customer.d.ts +2 -1
  78. package/dist/customer.js +3 -3
  79. package/dist/index.cjs +310 -151
  80. package/dist/index.cjs.map +1 -1
  81. package/dist/index.d.cts +84 -6
  82. package/dist/index.d.ts +84 -6
  83. package/dist/index.js +278 -148
  84. package/dist/index.js.map +1 -1
  85. package/dist/locations.cjs +1 -0
  86. package/dist/locations.cjs.map +1 -1
  87. package/dist/locations.d.cts +2 -1
  88. package/dist/locations.d.ts +2 -1
  89. package/dist/locations.js +1 -1
  90. package/dist/media.cjs +1 -0
  91. package/dist/media.cjs.map +1 -1
  92. package/dist/media.d.cts +2 -1
  93. package/dist/media.d.ts +2 -1
  94. package/dist/media.js +2 -2
  95. package/dist/orders.cjs +2 -0
  96. package/dist/orders.cjs.map +1 -1
  97. package/dist/orders.d.cts +3 -1
  98. package/dist/orders.d.ts +3 -1
  99. package/dist/orders.js +1 -1
  100. package/dist/payment.cjs +1 -0
  101. package/dist/payment.cjs.map +1 -1
  102. package/dist/payment.d.cts +2 -1
  103. package/dist/payment.d.ts +2 -1
  104. package/dist/payment.js +3 -3
  105. package/dist/price.cjs +7 -2
  106. package/dist/price.cjs.map +1 -1
  107. package/dist/price.d.cts +2 -1
  108. package/dist/price.d.ts +2 -1
  109. package/dist/price.js +2 -2
  110. package/dist/product.cjs +7 -2
  111. package/dist/product.cjs.map +1 -1
  112. package/dist/product.d.cts +2 -1
  113. package/dist/product.d.ts +2 -1
  114. package/dist/product.js +1 -1
  115. package/dist/segment.cjs +2 -0
  116. package/dist/segment.cjs.map +1 -1
  117. package/dist/segment.d.cts +3 -1
  118. package/dist/segment.d.ts +3 -1
  119. package/dist/segment.js +3 -3
  120. package/package.json +2 -1
  121. package/dist/chunk-AUVJ5V47.js.map +0 -1
  122. package/dist/chunk-B4UOYJKW.js.map +0 -1
  123. package/dist/chunk-BK25FDTH.js.map +0 -1
  124. package/dist/chunk-HAEVK6IS.js.map +0 -1
  125. package/dist/chunk-HKPZI7Y5.js.map +0 -1
  126. package/dist/chunk-I3YYGSN2.js.map +0 -1
  127. package/dist/chunk-KTODT6DV.js.map +0 -1
  128. package/dist/chunk-LGLFJL3S.js.map +0 -1
  129. package/dist/chunk-LGOC5XOI.js.map +0 -1
  130. package/dist/chunk-MM6JF3Z7.js.map +0 -1
  131. package/dist/chunk-OMYVOOSX.js.map +0 -1
  132. package/dist/chunk-TLH6SLAF.js.map +0 -1
  133. package/dist/chunk-WSXRUYBE.js.map +0 -1
  134. package/dist/chunk-WW3H7VJ5.js.map +0 -1
  135. package/dist/chunk-XMEP74BU.js.map +0 -1
  136. package/dist/chunk-Z6PNI5VT.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @viu/emporix-sdk
2
2
 
3
+ ## 2.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#129](https://github.com/viuteam/emporix-sdk/pull/129) [`05cf47c`](https://github.com/viuteam/emporix-sdk/commit/05cf47ca6f21dfb6e14b2bfdda2d88d11e55eaf9) Thanks [@amnael1](https://github.com/amnael1)! - add `createEmporixClient(config, services)` — a tree-shakeable, opt-in client factory that instantiates only the service classes you pass (e.g. `{ products: ProductService, carts: CartService }`), so bundlers drop every service you don't use. Service classes now carry static `channel`/`deps` metadata; `createCore(config)` exposes the shared infrastructure. `EmporixClient` is unchanged — it stays the batteries-included default that bundles everything — so this is purely additive.
8
+
9
+ ## 2.14.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#127](https://github.com/viuteam/emporix-sdk/pull/127) [`5769d9e`](https://github.com/viuteam/emporix-sdk/commit/5769d9e5e4a7fb9dc468968785c190c2f4fd8944) Thanks [@amnael1](https://github.com/amnael1)! - harden the HTTP and token layers: timeouts and connection failures now throw typed `EmporixTimeoutError`/`EmporixNetworkError` (previously raw `AbortError`/`TypeError` escaped the SDK's error taxonomy); the response body read is bounded by the timeout (a stalled stream no longer hangs forever); `timeouts.connectMs` is now actually enforced as the time-to-headers budget; `/oauth/token` and anonymous-login fetches are bounded by `timeouts.readMs` (one hung token call no longer blocks every request behind the single-flight lock); `login`/`refresh`/`socialLogin`/`exchangeToken` now throw `EmporixAuthError` on a 2xx response missing `access_token` instead of fabricating an empty session; read-only POST search endpoints (`products.searchByIds`/`searchByCodes`, `price.match`/`matchByContext`, `availability.getMany`, category product search) are marked `idempotent: true` and retry on 5xx/429 again.
14
+
15
+ - [#125](https://github.com/viuteam/emporix-sdk/pull/125) [`236caa3`](https://github.com/viuteam/emporix-sdk/commit/236caa3d42565852ce2240498794accc6c897f67) Thanks [@amnael1](https://github.com/amnael1)! - fix the HTTP retry to never replay non-idempotent requests: POST/PATCH responses with 5xx/429 are no longer retried automatically (a 5xx can arrive after the server committed — retrying `placeOrder` could duplicate orders/charges). Read-only POST endpoints can opt back in via the new `RequestOptions.idempotent: true` flag. Numeric `Retry-After` waits are now capped at 8s. 5xx responses without a `Retry-After` header now back off exponentially instead of retrying immediately.
16
+
3
17
  ## 2.13.1
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -70,7 +70,7 @@ The sections below highlight the most-used services; per-service guides live in
70
70
  | `credentials.custom` | — | `Record<name, { clientId, secret, scope? }>` |
71
71
  | `host` | `https://api.emporix.io` | |
72
72
  | `timeouts` | `{ connectMs: 10000, readMs: 60000 }` | |
73
- | `retry` | `{ maxAttempts: 3 }` | 5xx/429 backoff + jitter, respects `Retry-After` |
73
+ | `retry` | `{ maxAttempts: 3 }` | 5xx/429 backoff + jitter for idempotent methods (GET/PUT/DELETE; POST/PATCH opt in via `idempotent: true`); numeric `Retry-After` honoured, capped at 8s |
74
74
  | `cache` | `{ expirationBufferSeconds: 60, maxLifetimeSeconds: 3600 }` | token cache |
75
75
  | `logger` | console @ `warn` | `false`, a `Logger`, or `{ level, services, pretty, redact }` |
76
76
  | `tokenProvider` | built-in | inject for SSO/token-exchange |
@@ -48,6 +48,7 @@ var AvailabilityService = class {
48
48
  this.ctx = ctx;
49
49
  }
50
50
  ctx;
51
+ static channel = "availability";
51
52
  /**
52
53
  * Single product. Resolves the availability record, or — when
53
54
  * `opts.defaultAvailableOnNotFound` is set — a default available record on 404.
@@ -82,7 +83,9 @@ var AvailabilityService = class {
82
83
  path: `/availability/${this.ctx.tenant}/availability/search`,
83
84
  auth,
84
85
  query: { site: siteCode, pageSize: productIds.length },
85
- body: productIds
86
+ body: productIds,
87
+ idempotent: true
88
+ // pure read over POST — safe to replay on 5xx/429
86
89
  });
87
90
  const byId = /* @__PURE__ */ new Map();
88
91
  for (const a of found) if (a.productId) byId.set(a.productId, a);
@@ -1 +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"]}
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/** Request aborted by the configured connect/read timeout. No HTTP status. */\nexport class EmporixTimeoutError extends EmporixError {}\n/** DNS/TLS/connection-level failure before or during the exchange. No HTTP status. */\nexport class EmporixNetworkError 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 static readonly channel = \"availability\" as const;\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 idempotent: true, // pure read over POST — safe to replay on 5xx/429\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"]}
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-CD7F_LHT.cjs';
1
+ import { C as ClientContext, b as AuthContext } from './context-DhoFgoKf.cjs';
2
2
 
3
3
  type DistributionChannel = 'ASSORTMENT' | 'HOME_DELIVERY' | 'PICKUP';
4
4
  type Availability$1 = {
@@ -68,6 +68,7 @@ interface AvailabilityOptions {
68
68
  */
69
69
  declare class AvailabilityService {
70
70
  private readonly ctx;
71
+ static readonly channel: "availability";
71
72
  constructor(ctx: ClientContext);
72
73
  /**
73
74
  * Single product. Resolves the availability record, or — when
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-CD7F_LHT.js';
1
+ import { C as ClientContext, b as AuthContext } from './context-DhoFgoKf.js';
2
2
 
3
3
  type DistributionChannel = 'ASSORTMENT' | 'HOME_DELIVERY' | 'PICKUP';
4
4
  type Availability$1 = {
@@ -68,6 +68,7 @@ interface AvailabilityOptions {
68
68
  */
69
69
  declare class AvailabilityService {
70
70
  private readonly ctx;
71
+ static readonly channel: "availability";
71
72
  constructor(ctx: ClientContext);
72
73
  /**
73
74
  * Single product. Resolves the availability record, or — when
@@ -1,4 +1,4 @@
1
- export { AvailabilityService } from './chunk-XMEP74BU.js';
2
- import './chunk-KTODT6DV.js';
1
+ export { AvailabilityService } from './chunk-K3VQLW3P.js';
2
+ import './chunk-UWGGF4MC.js';
3
3
  //# sourceMappingURL=availability.js.map
4
4
  //# sourceMappingURL=availability.js.map
package/dist/cart.cjs CHANGED
@@ -59,6 +59,7 @@ var CartService = class {
59
59
  this.ctx = ctx;
60
60
  }
61
61
  ctx;
62
+ static channel = "cart";
62
63
  base() {
63
64
  return `/cart/${this.ctx.tenant}/carts`;
64
65
  }
package/dist/cart.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/errors.ts","../src/services/cart.ts"],"names":[],"mappings":"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","file":"cart.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, EmporixValidationError } from \"../core/errors\";\nimport type {\n Cart as GeneratedCart,\n CreateCart,\n CreatedCart,\n CartItemRequest,\n UpdateCartItem,\n AddressRequest,\n BatchResponse as GeneratedBatchResponse,\n SingleBatchResponse as GeneratedSingleBatchResponse,\n} from \"../generated/cart\";\n\n/** A cart as returned by the Cart service (all generated fields). */\nexport type Cart = GeneratedCart;\n\n/** The cart-create response (`{ cartId, yrn }`, generated). */\nexport type CartCreated = CreatedCart;\n\n/** Generated request bodies (caller sends the exact wire shape). */\nexport type CreateCartInput = CreateCart;\nexport type CartItemInput = CartItemRequest;\nexport type CartItemUpdate = UpdateCartItem;\n\n/** Per-entry result of `addItemsBatch`. `status` is HTTP-style per entry (e.g. 201 = added, 4xx = failed). */\nexport type CartItemBatchEntry = GeneratedSingleBatchResponse;\n/** Response shape of `addItemsBatch` — one entry per input item, matching by `index`. */\nexport type CartItemsBatchResponse = GeneratedBatchResponse;\n\n/** An address payload for cart shipping/billing (generated). */\nexport type CartAddress = AddressRequest;\n\nfunction requireCartAuth(auth: AuthContext | undefined): AuthContext {\n if (auth && (auth.kind === \"customer\" || auth.kind === \"anonymous\")) return auth;\n throw new EmporixValidationError(\n \"CartService requires an explicit { kind: 'customer' } or { kind: 'anonymous' } AuthContext\",\n );\n}\n\nfunction requireCustomerAuth(auth: AuthContext | undefined): AuthContext {\n if (auth && auth.kind === \"customer\") return auth;\n throw new EmporixValidationError(\"cart.merge requires a { kind: 'customer' } AuthContext\");\n}\n\n/** Cart operations. Every method requires an explicit customer/anonymous context. */\nexport class CartService {\n constructor(private readonly ctx: ClientContext) {}\n\n private base(): string {\n return `/cart/${this.ctx.tenant}/carts`;\n }\n\n /** Creates a cart. */\n async create(\n input: CreateCartInput | undefined,\n auth: AuthContext,\n ): Promise<CartCreated> {\n return this.ctx.http.request<CartCreated>({\n method: \"POST\",\n path: this.base(),\n auth: requireCartAuth(auth),\n body: input ?? {},\n });\n }\n\n /** Fetches a cart by id. */\n async get(cartId: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"GET\",\n path: `${this.base()}/${cartId}`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Returns the current cart for the session, or null if none. */\n /**\n * Get the customer / anonymous cart matching the given criteria. Per Emporix:\n * uniqueness is defined by `siteCode` + `type` + `legalEntityId` +\n * (`customerId` derived from a customer token, or `sessionId` derived from an\n * anonymous token). With `create: true`, Emporix creates a new cart if none\n * matches.\n *\n * Returns `null` on 404 (no cart found and `create` was not set). All other\n * errors are propagated.\n */\n async getCurrent(\n auth: AuthContext,\n opts: { siteCode: string; type?: string; legalEntityId?: string; create?: boolean },\n ): Promise<Cart | null> {\n const query: Record<string, string | number> = { siteCode: opts.siteCode };\n if (opts.type !== undefined) query.type = opts.type;\n if (opts.legalEntityId !== undefined) query.legalEntityId = opts.legalEntityId;\n if (opts.create) query.create = \"true\";\n try {\n return await this.ctx.http.request<Cart>({\n method: \"GET\",\n path: this.base(),\n query,\n auth: requireCartAuth(auth),\n });\n } catch (e) {\n if (e instanceof EmporixNotFoundError) return null;\n throw e;\n }\n }\n\n /** Adds an item. */\n async addItem(\n cartId: string,\n item: CartItemInput,\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"POST\",\n path: `${this.base()}/${cartId}/items`,\n auth: requireCartAuth(auth),\n body: item,\n });\n }\n\n /**\n * Adds multiple items in a single request via `POST /carts/{id}/itemsBatch`.\n * Server-side limit is 200 entries per call — callers handling larger sets\n * must chunk. The response carries a per-entry `status` (HTTP-style: 201\n * for added, 4xx/5xx for failed) and `index` matching the input position;\n * partial failures do **not** throw — inspect `status` per entry.\n */\n async addItemsBatch(\n cartId: string,\n items: CartItemInput[],\n auth: AuthContext,\n ): Promise<CartItemsBatchResponse> {\n return this.ctx.http.request<CartItemsBatchResponse>({\n method: \"POST\",\n path: `${this.base()}/${cartId}/itemsBatch`,\n auth: requireCartAuth(auth),\n body: items,\n });\n }\n\n /**\n * Updates an item. By default this is a **full replace** — the server expects\n * the complete item (e.g. `itemYrn` + the `price` row). Pass\n * `{ partial: true }` to send a **partial update** (`?partial=true`), e.g. a\n * quantity-only change with just `{ quantity }`.\n */\n async updateItem(\n cartId: string,\n itemId: string,\n patch: CartItemUpdate,\n auth: AuthContext,\n opts: { partial?: boolean } = {},\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"PUT\",\n path: `${this.base()}/${cartId}/items/${itemId}`,\n auth: requireCartAuth(auth),\n body: patch,\n ...(opts.partial ? { query: { partial: \"true\" } } : {}),\n });\n }\n\n /** Removes an item. */\n async removeItem(cartId: string, itemId: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"DELETE\",\n path: `${this.base()}/${cartId}/items/${itemId}`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Empties the cart. */\n async clear(cartId: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"DELETE\",\n path: `${this.base()}/${cartId}/items`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Applies a coupon. */\n async applyCoupon(cartId: string, code: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"POST\",\n path: `${this.base()}/${cartId}/coupons`,\n auth: requireCartAuth(auth),\n body: { code },\n });\n }\n\n /** Removes a coupon. */\n async removeCoupon(cartId: string, code: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"DELETE\",\n path: `${this.base()}/${cartId}/coupons/${code}`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Sets the shipping address. */\n async setShippingAddress(\n cartId: string,\n address: CartAddress,\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"PUT\",\n path: `${this.base()}/${cartId}/shipping-address`,\n auth: requireCartAuth(auth),\n body: address,\n });\n }\n\n /** Sets the billing address. */\n async setBillingAddress(\n cartId: string,\n address: CartAddress,\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"PUT\",\n path: `${this.base()}/${cartId}/billing-address`,\n auth: requireCartAuth(auth),\n body: address,\n });\n }\n\n /**\n * Merges one or more anonymous carts into the specified customer cart.\n * Per Emporix: the target cart in the path **must belong to the logged-in\n * customer**, and each id in `anonymousCartIds` must belong to an anonymous\n * customer. Anonymous carts go `CLOSED` on success.\n *\n * Requires a customer `AuthContext`.\n */\n async merge(\n customerCartId: string,\n anonymousCartIds: string[],\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"POST\",\n path: `${this.base()}/${customerCartId}/merge`,\n auth: requireCustomerAuth(auth),\n body: { carts: anonymousCartIds },\n });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/core/errors.ts","../src/services/cart.ts"],"names":[],"mappings":"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","file":"cart.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/** Request aborted by the configured connect/read timeout. No HTTP status. */\nexport class EmporixTimeoutError extends EmporixError {}\n/** DNS/TLS/connection-level failure before or during the exchange. No HTTP status. */\nexport class EmporixNetworkError 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, EmporixValidationError } from \"../core/errors\";\nimport type {\n Cart as GeneratedCart,\n CreateCart,\n CreatedCart,\n CartItemRequest,\n UpdateCartItem,\n AddressRequest,\n BatchResponse as GeneratedBatchResponse,\n SingleBatchResponse as GeneratedSingleBatchResponse,\n} from \"../generated/cart\";\n\n/** A cart as returned by the Cart service (all generated fields). */\nexport type Cart = GeneratedCart;\n\n/** The cart-create response (`{ cartId, yrn }`, generated). */\nexport type CartCreated = CreatedCart;\n\n/** Generated request bodies (caller sends the exact wire shape). */\nexport type CreateCartInput = CreateCart;\nexport type CartItemInput = CartItemRequest;\nexport type CartItemUpdate = UpdateCartItem;\n\n/** Per-entry result of `addItemsBatch`. `status` is HTTP-style per entry (e.g. 201 = added, 4xx = failed). */\nexport type CartItemBatchEntry = GeneratedSingleBatchResponse;\n/** Response shape of `addItemsBatch` — one entry per input item, matching by `index`. */\nexport type CartItemsBatchResponse = GeneratedBatchResponse;\n\n/** An address payload for cart shipping/billing (generated). */\nexport type CartAddress = AddressRequest;\n\nfunction requireCartAuth(auth: AuthContext | undefined): AuthContext {\n if (auth && (auth.kind === \"customer\" || auth.kind === \"anonymous\")) return auth;\n throw new EmporixValidationError(\n \"CartService requires an explicit { kind: 'customer' } or { kind: 'anonymous' } AuthContext\",\n );\n}\n\nfunction requireCustomerAuth(auth: AuthContext | undefined): AuthContext {\n if (auth && auth.kind === \"customer\") return auth;\n throw new EmporixValidationError(\"cart.merge requires a { kind: 'customer' } AuthContext\");\n}\n\n/** Cart operations. Every method requires an explicit customer/anonymous context. */\nexport class CartService {\n static readonly channel = \"cart\" as const;\n constructor(private readonly ctx: ClientContext) {}\n\n private base(): string {\n return `/cart/${this.ctx.tenant}/carts`;\n }\n\n /** Creates a cart. */\n async create(\n input: CreateCartInput | undefined,\n auth: AuthContext,\n ): Promise<CartCreated> {\n return this.ctx.http.request<CartCreated>({\n method: \"POST\",\n path: this.base(),\n auth: requireCartAuth(auth),\n body: input ?? {},\n });\n }\n\n /** Fetches a cart by id. */\n async get(cartId: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"GET\",\n path: `${this.base()}/${cartId}`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Returns the current cart for the session, or null if none. */\n /**\n * Get the customer / anonymous cart matching the given criteria. Per Emporix:\n * uniqueness is defined by `siteCode` + `type` + `legalEntityId` +\n * (`customerId` derived from a customer token, or `sessionId` derived from an\n * anonymous token). With `create: true`, Emporix creates a new cart if none\n * matches.\n *\n * Returns `null` on 404 (no cart found and `create` was not set). All other\n * errors are propagated.\n */\n async getCurrent(\n auth: AuthContext,\n opts: { siteCode: string; type?: string; legalEntityId?: string; create?: boolean },\n ): Promise<Cart | null> {\n const query: Record<string, string | number> = { siteCode: opts.siteCode };\n if (opts.type !== undefined) query.type = opts.type;\n if (opts.legalEntityId !== undefined) query.legalEntityId = opts.legalEntityId;\n if (opts.create) query.create = \"true\";\n try {\n return await this.ctx.http.request<Cart>({\n method: \"GET\",\n path: this.base(),\n query,\n auth: requireCartAuth(auth),\n });\n } catch (e) {\n if (e instanceof EmporixNotFoundError) return null;\n throw e;\n }\n }\n\n /** Adds an item. */\n async addItem(\n cartId: string,\n item: CartItemInput,\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"POST\",\n path: `${this.base()}/${cartId}/items`,\n auth: requireCartAuth(auth),\n body: item,\n });\n }\n\n /**\n * Adds multiple items in a single request via `POST /carts/{id}/itemsBatch`.\n * Server-side limit is 200 entries per call — callers handling larger sets\n * must chunk. The response carries a per-entry `status` (HTTP-style: 201\n * for added, 4xx/5xx for failed) and `index` matching the input position;\n * partial failures do **not** throw — inspect `status` per entry.\n */\n async addItemsBatch(\n cartId: string,\n items: CartItemInput[],\n auth: AuthContext,\n ): Promise<CartItemsBatchResponse> {\n return this.ctx.http.request<CartItemsBatchResponse>({\n method: \"POST\",\n path: `${this.base()}/${cartId}/itemsBatch`,\n auth: requireCartAuth(auth),\n body: items,\n });\n }\n\n /**\n * Updates an item. By default this is a **full replace** — the server expects\n * the complete item (e.g. `itemYrn` + the `price` row). Pass\n * `{ partial: true }` to send a **partial update** (`?partial=true`), e.g. a\n * quantity-only change with just `{ quantity }`.\n */\n async updateItem(\n cartId: string,\n itemId: string,\n patch: CartItemUpdate,\n auth: AuthContext,\n opts: { partial?: boolean } = {},\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"PUT\",\n path: `${this.base()}/${cartId}/items/${itemId}`,\n auth: requireCartAuth(auth),\n body: patch,\n ...(opts.partial ? { query: { partial: \"true\" } } : {}),\n });\n }\n\n /** Removes an item. */\n async removeItem(cartId: string, itemId: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"DELETE\",\n path: `${this.base()}/${cartId}/items/${itemId}`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Empties the cart. */\n async clear(cartId: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"DELETE\",\n path: `${this.base()}/${cartId}/items`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Applies a coupon. */\n async applyCoupon(cartId: string, code: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"POST\",\n path: `${this.base()}/${cartId}/coupons`,\n auth: requireCartAuth(auth),\n body: { code },\n });\n }\n\n /** Removes a coupon. */\n async removeCoupon(cartId: string, code: string, auth: AuthContext): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"DELETE\",\n path: `${this.base()}/${cartId}/coupons/${code}`,\n auth: requireCartAuth(auth),\n });\n }\n\n /** Sets the shipping address. */\n async setShippingAddress(\n cartId: string,\n address: CartAddress,\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"PUT\",\n path: `${this.base()}/${cartId}/shipping-address`,\n auth: requireCartAuth(auth),\n body: address,\n });\n }\n\n /** Sets the billing address. */\n async setBillingAddress(\n cartId: string,\n address: CartAddress,\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"PUT\",\n path: `${this.base()}/${cartId}/billing-address`,\n auth: requireCartAuth(auth),\n body: address,\n });\n }\n\n /**\n * Merges one or more anonymous carts into the specified customer cart.\n * Per Emporix: the target cart in the path **must belong to the logged-in\n * customer**, and each id in `anonymousCartIds` must belong to an anonymous\n * customer. Anonymous carts go `CLOSED` on success.\n *\n * Requires a customer `AuthContext`.\n */\n async merge(\n customerCartId: string,\n anonymousCartIds: string[],\n auth: AuthContext,\n ): Promise<Cart> {\n return this.ctx.http.request<Cart>({\n method: \"POST\",\n path: `${this.base()}/${customerCartId}/merge`,\n auth: requireCustomerAuth(auth),\n body: { carts: anonymousCartIds },\n });\n }\n}\n"]}
package/dist/cart.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-CD7F_LHT.cjs';
1
+ import { C as ClientContext, b as AuthContext } from './context-DhoFgoKf.cjs';
2
2
 
3
3
  /**
4
4
  * Access control identifier that determines who can view or manage the cart.
@@ -1746,6 +1746,7 @@ type CartAddress = AddressRequest;
1746
1746
  /** Cart operations. Every method requires an explicit customer/anonymous context. */
1747
1747
  declare class CartService {
1748
1748
  private readonly ctx;
1749
+ static readonly channel: "cart";
1749
1750
  constructor(ctx: ClientContext);
1750
1751
  private base;
1751
1752
  /** Creates a cart. */
package/dist/cart.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-CD7F_LHT.js';
1
+ import { C as ClientContext, b as AuthContext } from './context-DhoFgoKf.js';
2
2
 
3
3
  /**
4
4
  * Access control identifier that determines who can view or manage the cart.
@@ -1746,6 +1746,7 @@ type CartAddress = AddressRequest;
1746
1746
  /** Cart operations. Every method requires an explicit customer/anonymous context. */
1747
1747
  declare class CartService {
1748
1748
  private readonly ctx;
1749
+ static readonly channel: "cart";
1749
1750
  constructor(ctx: ClientContext);
1750
1751
  private base;
1751
1752
  /** Creates a cart. */
package/dist/cart.js CHANGED
@@ -1,4 +1,4 @@
1
- export { CartService } from './chunk-I3YYGSN2.js';
2
- import './chunk-KTODT6DV.js';
1
+ export { CartService } from './chunk-PAT4CS36.js';
2
+ import './chunk-UWGGF4MC.js';
3
3
  //# sourceMappingURL=cart.js.map
4
4
  //# sourceMappingURL=cart.js.map
package/dist/category.cjs CHANGED
@@ -18,6 +18,7 @@ var CategoryService = class {
18
18
  this.ctx = ctx;
19
19
  }
20
20
  ctx;
21
+ static channel = "category";
21
22
  /** Fetches one category by id. */
22
23
  async get(categoryId, auth = ANON) {
23
24
  return this.ctx.http.request({
@@ -101,7 +102,9 @@ var CategoryService = class {
101
102
  path: `/product/${this.ctx.tenant}/products/search`,
102
103
  query: { pageSize: productIds.length },
103
104
  auth,
104
- body: { q: `id:(${productIds.join(",")})` }
105
+ body: { q: `id:(${productIds.join(",")})` },
106
+ idempotent: true
107
+ // pure read over POST — safe to replay on 5xx/429
105
108
  });
106
109
  return { items, pageNumber, pageSize, hasNextPage };
107
110
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/context.ts","../src/services/category.ts"],"names":[],"mappings":"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","file":"category.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 { Product } from \"./product\";\nimport type { Category as GeneratedCategory, CategoryTree } from \"../generated/category\";\n\nconst ANON: AuthContext = { kind: \"anonymous\" };\n\n/** A category as returned by the Category service (all generated fields). */\nexport type Category = GeneratedCategory;\n\n/** The category tree as returned by the Category service. */\nexport type CategoryNode = CategoryTree;\n\n/** Category reads. Default auth: anonymous. */\nexport class CategoryService {\n constructor(private readonly ctx: ClientContext) {}\n\n /** Fetches one category by id. */\n async get(categoryId: string, auth: AuthContext = ANON): Promise<Category> {\n return this.ctx.http.request<Category>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories/${categoryId}`,\n auth,\n });\n }\n\n /** One page of categories. */\n async list(\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Category>> {\n const pageNumber = params.pageNumber ?? 1;\n const pageSize = params.pageSize ?? 50;\n const items = await this.ctx.http.request<Category[]>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories`,\n query: { pageNumber, pageSize },\n auth,\n });\n return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };\n }\n\n /** Async-iterates every category across pages. */\n listAll(params: { pageSize?: number } = {}, auth: AuthContext = ANON): AsyncIterable<Category> {\n const pageSize = params.pageSize ?? 50;\n return iterateAll<Category>((pageNumber) => this.list({ pageNumber, pageSize }, auth));\n }\n\n /**\n * The catalogue's **root categories** — the published category trees\n * (`GET /category-trees`). Use these for top-level storefront navigation;\n * drill into a node's children with {@link subcategories}.\n */\n async tree(auth: AuthContext = ANON): Promise<Category[]> {\n return this.ctx.http.request<Category[]>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/category-trees`,\n auth,\n });\n }\n\n /**\n * Direct child categories of a category (for hierarchy drill-down). Mirrors\n * {@link productsIn}: reads the category's **assignments** and keeps the\n * `CATEGORY` references, resolving them to full categories. Returns an empty\n * array when the category has no child categories.\n */\n async subcategories(\n categoryId: string,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<Category[]> {\n const pageNumber = params.pageNumber ?? 1;\n const pageSize = params.pageSize ?? 50;\n const assignments = await this.ctx.http.request<Array<{ ref?: { id?: string; type?: string } }>>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories/${categoryId}/assignments`,\n query: { pageNumber, pageSize },\n auth,\n });\n const categoryIds = assignments\n .map((a) => (a.ref?.type?.toUpperCase() === \"CATEGORY\" ? a.ref.id : undefined))\n .filter((id): id is string => Boolean(id));\n if (categoryIds.length === 0) return [];\n return this.searchByIds(categoryIds, {}, auth);\n }\n\n /**\n * One page of products in a category. The category service exposes products\n * as **assignments** (`/categories/{id}/assignments`) — references, not full\n * products — so this fetches a page of assignments, keeps the `PRODUCT`\n * references, and resolves them to full products via `/products/search`.\n * `hasNextPage` reflects the assignments page (the source of truth for\n * pagination).\n */\n async productsIn(\n categoryId: 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 assignments = await this.ctx.http.request<Array<{ ref?: { id?: string; type?: string } }>>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories/${categoryId}/assignments`,\n query: { pageNumber, pageSize },\n auth,\n });\n const hasNextPage = assignments.length === pageSize;\n const productIds = assignments\n .map((a) => (a.ref?.type?.toUpperCase() === \"PRODUCT\" ? a.ref.id : undefined))\n .filter((id): id is string => Boolean(id));\n if (productIds.length === 0) {\n return { items: [], pageNumber, pageSize, hasNextPage };\n }\n const items = await this.ctx.http.request<Product[]>({\n method: \"POST\",\n path: `/product/${this.ctx.tenant}/products/search`,\n query: { pageSize: productIds.length },\n auth,\n body: { q: `id:(${productIds.join(\",\")})` },\n });\n return { items, pageNumber, pageSize, hasNextPage };\n }\n\n /**\n * Bulk fetch by id. POSTs `/categories/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<Category[]> {\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<Category[]>({\n method: \"POST\",\n path: `/category/${this.ctx.tenant}/categories/search`,\n query: { pageSize: chunk.length },\n auth,\n body: { q: `id:(${chunk.join(\",\")})` },\n }),\n ),\n );\n return pages.flat();\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/core/context.ts","../src/services/category.ts"],"names":[],"mappings":"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","file":"category.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 { Product } from \"./product\";\nimport type { Category as GeneratedCategory, CategoryTree } from \"../generated/category\";\n\nconst ANON: AuthContext = { kind: \"anonymous\" };\n\n/** A category as returned by the Category service (all generated fields). */\nexport type Category = GeneratedCategory;\n\n/** The category tree as returned by the Category service. */\nexport type CategoryNode = CategoryTree;\n\n/** Category reads. Default auth: anonymous. */\nexport class CategoryService {\n static readonly channel = \"category\" as const;\n constructor(private readonly ctx: ClientContext) {}\n\n /** Fetches one category by id. */\n async get(categoryId: string, auth: AuthContext = ANON): Promise<Category> {\n return this.ctx.http.request<Category>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories/${categoryId}`,\n auth,\n });\n }\n\n /** One page of categories. */\n async list(\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Category>> {\n const pageNumber = params.pageNumber ?? 1;\n const pageSize = params.pageSize ?? 50;\n const items = await this.ctx.http.request<Category[]>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories`,\n query: { pageNumber, pageSize },\n auth,\n });\n return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };\n }\n\n /** Async-iterates every category across pages. */\n listAll(params: { pageSize?: number } = {}, auth: AuthContext = ANON): AsyncIterable<Category> {\n const pageSize = params.pageSize ?? 50;\n return iterateAll<Category>((pageNumber) => this.list({ pageNumber, pageSize }, auth));\n }\n\n /**\n * The catalogue's **root categories** — the published category trees\n * (`GET /category-trees`). Use these for top-level storefront navigation;\n * drill into a node's children with {@link subcategories}.\n */\n async tree(auth: AuthContext = ANON): Promise<Category[]> {\n return this.ctx.http.request<Category[]>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/category-trees`,\n auth,\n });\n }\n\n /**\n * Direct child categories of a category (for hierarchy drill-down). Mirrors\n * {@link productsIn}: reads the category's **assignments** and keeps the\n * `CATEGORY` references, resolving them to full categories. Returns an empty\n * array when the category has no child categories.\n */\n async subcategories(\n categoryId: string,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<Category[]> {\n const pageNumber = params.pageNumber ?? 1;\n const pageSize = params.pageSize ?? 50;\n const assignments = await this.ctx.http.request<Array<{ ref?: { id?: string; type?: string } }>>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories/${categoryId}/assignments`,\n query: { pageNumber, pageSize },\n auth,\n });\n const categoryIds = assignments\n .map((a) => (a.ref?.type?.toUpperCase() === \"CATEGORY\" ? a.ref.id : undefined))\n .filter((id): id is string => Boolean(id));\n if (categoryIds.length === 0) return [];\n return this.searchByIds(categoryIds, {}, auth);\n }\n\n /**\n * One page of products in a category. The category service exposes products\n * as **assignments** (`/categories/{id}/assignments`) — references, not full\n * products — so this fetches a page of assignments, keeps the `PRODUCT`\n * references, and resolves them to full products via `/products/search`.\n * `hasNextPage` reflects the assignments page (the source of truth for\n * pagination).\n */\n async productsIn(\n categoryId: 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 assignments = await this.ctx.http.request<Array<{ ref?: { id?: string; type?: string } }>>({\n method: \"GET\",\n path: `/category/${this.ctx.tenant}/categories/${categoryId}/assignments`,\n query: { pageNumber, pageSize },\n auth,\n });\n const hasNextPage = assignments.length === pageSize;\n const productIds = assignments\n .map((a) => (a.ref?.type?.toUpperCase() === \"PRODUCT\" ? a.ref.id : undefined))\n .filter((id): id is string => Boolean(id));\n if (productIds.length === 0) {\n return { items: [], pageNumber, pageSize, hasNextPage };\n }\n const items = await this.ctx.http.request<Product[]>({\n method: \"POST\",\n path: `/product/${this.ctx.tenant}/products/search`,\n query: { pageSize: productIds.length },\n auth,\n body: { q: `id:(${productIds.join(\",\")})` },\n idempotent: true, // pure read over POST — safe to replay on 5xx/429\n });\n return { items, pageNumber, pageSize, hasNextPage };\n }\n\n /**\n * Bulk fetch by id. POSTs `/categories/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<Category[]> {\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<Category[]>({\n method: \"POST\",\n path: `/category/${this.ctx.tenant}/categories/search`,\n query: { pageSize: chunk.length },\n auth,\n body: { q: `id:(${chunk.join(\",\")})` },\n }),\n ),\n );\n return pages.flat();\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CD7F_LHT.cjs';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-DhoFgoKf.cjs';
2
2
  import { Product } from './product.cjs';
3
3
 
4
4
  /**
@@ -280,6 +280,7 @@ type CategoryNode = CategoryTree;
280
280
  /** Category reads. Default auth: anonymous. */
281
281
  declare class CategoryService {
282
282
  private readonly ctx;
283
+ static readonly channel: "category";
283
284
  constructor(ctx: ClientContext);
284
285
  /** Fetches one category by id. */
285
286
  get(categoryId: string, auth?: AuthContext): Promise<Category>;
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-CD7F_LHT.js';
1
+ import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-DhoFgoKf.js';
2
2
  import { Product } from './product.js';
3
3
 
4
4
  /**
@@ -280,6 +280,7 @@ type CategoryNode = CategoryTree;
280
280
  /** Category reads. Default auth: anonymous. */
281
281
  declare class CategoryService {
282
282
  private readonly ctx;
283
+ static readonly channel: "category";
283
284
  constructor(ctx: ClientContext);
284
285
  /** Fetches one category by id. */
285
286
  get(categoryId: string, auth?: AuthContext): Promise<Category>;
package/dist/category.js CHANGED
@@ -1,4 +1,4 @@
1
- export { CategoryService } from './chunk-AUVJ5V47.js';
1
+ export { CategoryService } from './chunk-GIZS4BY3.js';
2
2
  import './chunk-GWJ2U2CP.js';
3
3
  //# sourceMappingURL=category.js.map
4
4
  //# sourceMappingURL=category.js.map
package/dist/checkout.cjs CHANGED
@@ -57,6 +57,7 @@ var CheckoutService = class {
57
57
  this.ctx = ctx;
58
58
  }
59
59
  ctx;
60
+ static channel = "checkout";
60
61
  headers(opts, guest) {
61
62
  if (guest || !opts.saasToken) return void 0;
62
63
  return { "saas-token": opts.saasToken };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/errors.ts","../src/services/checkout.ts"],"names":[],"mappings":"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","file":"checkout.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 {\n ResponseCheckout,\n RequestCheckout,\n RequestFromQuoteCheckout,\n CustomerJson,\n} from \"../generated/checkout\";\n\n/** `requestCheckout` body (cart-based, generated). */\nexport type CheckoutInput = RequestCheckout;\n\n/** `requestFromQuoteCheckout` body (quote-based, generated). */\nexport type QuoteCheckoutInput = RequestFromQuoteCheckout;\n\n/** `responseCheckout` — the full generated checkout response. */\nexport type CheckoutResult = ResponseCheckout;\n\n/** Options for a checkout call. */\nexport interface CheckoutOptions {\n /** Customer `saasToken` from `customers.login()` — required for logged-in checkout. */\n saasToken?: string;\n /** Site code (`?siteCode=`). */\n siteCode?: string;\n}\n\nfunction isGuest(customer: CustomerJson | undefined): boolean {\n return customer?.guest === true;\n}\n\nfunction resolveAuth(auth: AuthContext | undefined, guest: boolean): AuthContext {\n if (auth && (auth.kind === \"customer\" || auth.kind === \"raw\")) return auth;\n if (guest && auth && auth.kind === \"anonymous\") return auth;\n throw new EmporixAuthError(\n \"checkout requires a customer/raw AuthContext (or anonymous for a guest checkout)\",\n );\n}\n\n/** Triggers Emporix checkout (atomic: validate → order → payment → close cart). */\nexport class CheckoutService {\n constructor(private readonly ctx: ClientContext) {}\n\n private headers(opts: CheckoutOptions, guest: boolean): Record<string, string> | undefined {\n if (guest || !opts.saasToken) return undefined;\n return { \"saas-token\": opts.saasToken };\n }\n\n private query(opts: CheckoutOptions): Record<string, string> | undefined {\n return opts.siteCode ? { siteCode: opts.siteCode } : undefined;\n }\n\n /** Checkout from a cart. Requires customer/raw auth (or anonymous for guest). */\n async placeOrder(\n input: CheckoutInput,\n auth?: AuthContext,\n opts: CheckoutOptions = {},\n ): Promise<CheckoutResult> {\n const guest = isGuest(input.customer);\n const headers = this.headers(opts, guest);\n const query = this.query(opts);\n return this.ctx.http.request<CheckoutResult>({\n method: \"POST\",\n path: `/checkout/${this.ctx.tenant}/checkouts/order`,\n auth: resolveAuth(auth, guest),\n ...(query ? { query } : {}),\n ...(headers ? { headers } : {}),\n body: input,\n });\n }\n\n /** Checkout from a quote. */\n async placeOrderFromQuote(\n input: QuoteCheckoutInput,\n auth?: AuthContext,\n opts: CheckoutOptions = {},\n ): Promise<CheckoutResult> {\n const headers = this.headers(opts, false);\n const query = this.query(opts);\n return this.ctx.http.request<CheckoutResult>({\n method: \"POST\",\n path: `/checkout/${this.ctx.tenant}/checkouts/order`,\n auth: resolveAuth(auth, false),\n ...(query ? { query } : {}),\n ...(headers ? { headers } : {}),\n body: input,\n });\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/core/errors.ts","../src/services/checkout.ts"],"names":[],"mappings":"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","file":"checkout.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/** Request aborted by the configured connect/read timeout. No HTTP status. */\nexport class EmporixTimeoutError extends EmporixError {}\n/** DNS/TLS/connection-level failure before or during the exchange. No HTTP status. */\nexport class EmporixNetworkError 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 {\n ResponseCheckout,\n RequestCheckout,\n RequestFromQuoteCheckout,\n CustomerJson,\n} from \"../generated/checkout\";\n\n/** `requestCheckout` body (cart-based, generated). */\nexport type CheckoutInput = RequestCheckout;\n\n/** `requestFromQuoteCheckout` body (quote-based, generated). */\nexport type QuoteCheckoutInput = RequestFromQuoteCheckout;\n\n/** `responseCheckout` — the full generated checkout response. */\nexport type CheckoutResult = ResponseCheckout;\n\n/** Options for a checkout call. */\nexport interface CheckoutOptions {\n /** Customer `saasToken` from `customers.login()` — required for logged-in checkout. */\n saasToken?: string;\n /** Site code (`?siteCode=`). */\n siteCode?: string;\n}\n\nfunction isGuest(customer: CustomerJson | undefined): boolean {\n return customer?.guest === true;\n}\n\nfunction resolveAuth(auth: AuthContext | undefined, guest: boolean): AuthContext {\n if (auth && (auth.kind === \"customer\" || auth.kind === \"raw\")) return auth;\n if (guest && auth && auth.kind === \"anonymous\") return auth;\n throw new EmporixAuthError(\n \"checkout requires a customer/raw AuthContext (or anonymous for a guest checkout)\",\n );\n}\n\n/** Triggers Emporix checkout (atomic: validate → order → payment → close cart). */\nexport class CheckoutService {\n static readonly channel = \"checkout\" as const;\n constructor(private readonly ctx: ClientContext) {}\n\n private headers(opts: CheckoutOptions, guest: boolean): Record<string, string> | undefined {\n if (guest || !opts.saasToken) return undefined;\n return { \"saas-token\": opts.saasToken };\n }\n\n private query(opts: CheckoutOptions): Record<string, string> | undefined {\n return opts.siteCode ? { siteCode: opts.siteCode } : undefined;\n }\n\n /** Checkout from a cart. Requires customer/raw auth (or anonymous for guest). */\n async placeOrder(\n input: CheckoutInput,\n auth?: AuthContext,\n opts: CheckoutOptions = {},\n ): Promise<CheckoutResult> {\n const guest = isGuest(input.customer);\n const headers = this.headers(opts, guest);\n const query = this.query(opts);\n return this.ctx.http.request<CheckoutResult>({\n method: \"POST\",\n path: `/checkout/${this.ctx.tenant}/checkouts/order`,\n auth: resolveAuth(auth, guest),\n ...(query ? { query } : {}),\n ...(headers ? { headers } : {}),\n body: input,\n });\n }\n\n /** Checkout from a quote. */\n async placeOrderFromQuote(\n input: QuoteCheckoutInput,\n auth?: AuthContext,\n opts: CheckoutOptions = {},\n ): Promise<CheckoutResult> {\n const headers = this.headers(opts, false);\n const query = this.query(opts);\n return this.ctx.http.request<CheckoutResult>({\n method: \"POST\",\n path: `/checkout/${this.ctx.tenant}/checkouts/order`,\n auth: resolveAuth(auth, false),\n ...(query ? { query } : {}),\n ...(headers ? { headers } : {}),\n body: input,\n });\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-CD7F_LHT.cjs';
1
+ import { C as ClientContext, b as AuthContext } from './context-DhoFgoKf.cjs';
2
2
 
3
3
  /**
4
4
  * Information about the customer's address.
@@ -240,6 +240,7 @@ interface CheckoutOptions {
240
240
  /** Triggers Emporix checkout (atomic: validate → order → payment → close cart). */
241
241
  declare class CheckoutService {
242
242
  private readonly ctx;
243
+ static readonly channel: "checkout";
243
244
  constructor(ctx: ClientContext);
244
245
  private headers;
245
246
  private query;
@@ -1,4 +1,4 @@
1
- import { C as ClientContext, b as AuthContext } from './context-CD7F_LHT.js';
1
+ import { C as ClientContext, b as AuthContext } from './context-DhoFgoKf.js';
2
2
 
3
3
  /**
4
4
  * Information about the customer's address.
@@ -240,6 +240,7 @@ interface CheckoutOptions {
240
240
  /** Triggers Emporix checkout (atomic: validate → order → payment → close cart). */
241
241
  declare class CheckoutService {
242
242
  private readonly ctx;
243
+ static readonly channel: "checkout";
243
244
  constructor(ctx: ClientContext);
244
245
  private headers;
245
246
  private query;
package/dist/checkout.js CHANGED
@@ -1,4 +1,4 @@
1
- export { CheckoutService } from './chunk-OMYVOOSX.js';
2
- import './chunk-KTODT6DV.js';
1
+ export { CheckoutService } from './chunk-RYQVMR26.js';
2
+ import './chunk-UWGGF4MC.js';
3
3
  //# sourceMappingURL=checkout.js.map
4
4
  //# sourceMappingURL=checkout.js.map
@@ -1,4 +1,4 @@
1
- import { EmporixAuthError } from './chunk-KTODT6DV.js';
1
+ import { EmporixAuthError } from './chunk-UWGGF4MC.js';
2
2
 
3
3
  // src/core/require-customer.ts
4
4
  function requireCustomer(auth) {
@@ -7,5 +7,5 @@ function requireCustomer(auth) {
7
7
  }
8
8
 
9
9
  export { requireCustomer };
10
- //# sourceMappingURL=chunk-BFQ732VZ.js.map
11
- //# sourceMappingURL=chunk-BFQ732VZ.js.map
10
+ //# sourceMappingURL=chunk-3BDDYX3V.js.map
11
+ //# sourceMappingURL=chunk-3BDDYX3V.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/require-customer.ts"],"names":[],"mappings":";;;AASO,SAAS,gBAAgB,IAAA,EAA4C;AAC1E,EAAA,IAAI,SAAS,IAAA,CAAK,IAAA,KAAS,cAAc,IAAA,CAAK,IAAA,KAAS,QAAQ,OAAO,IAAA;AACtE,EAAA,MAAM,IAAI,iBAAiB,uDAAuD,CAAA;AACpF","file":"chunk-BFQ732VZ.js","sourcesContent":["import type { AuthContext } from \"./auth\";\nimport { EmporixAuthError } from \"./errors\";\n\n/**\n * Enforces a caller-owned customer or raw token. Throws `EmporixAuthError`\n * for any other `AuthContext` kind (or when `auth` is missing). Shared by\n * services whose endpoints require a customer scope (`/me`, payments,\n * customer-segments…).\n */\nexport function requireCustomer(auth: AuthContext | undefined): AuthContext {\n if (auth && (auth.kind === \"customer\" || auth.kind === \"raw\")) return auth;\n throw new EmporixAuthError(\"This operation requires a customer or raw AuthContext\");\n}\n"]}
1
+ {"version":3,"sources":["../src/core/require-customer.ts"],"names":[],"mappings":";;;AASO,SAAS,gBAAgB,IAAA,EAA4C;AAC1E,EAAA,IAAI,SAAS,IAAA,CAAK,IAAA,KAAS,cAAc,IAAA,CAAK,IAAA,KAAS,QAAQ,OAAO,IAAA;AACtE,EAAA,MAAM,IAAI,iBAAiB,uDAAuD,CAAA;AACpF","file":"chunk-3BDDYX3V.js","sourcesContent":["import type { AuthContext } from \"./auth\";\nimport { EmporixAuthError } from \"./errors\";\n\n/**\n * Enforces a caller-owned customer or raw token. Throws `EmporixAuthError`\n * for any other `AuthContext` kind (or when `auth` is missing). Shared by\n * services whose endpoints require a customer scope (`/me`, payments,\n * customer-segments…).\n */\nexport function requireCustomer(auth: AuthContext | undefined): AuthContext {\n if (auth && (auth.kind === \"customer\" || auth.kind === \"raw\")) return auth;\n throw new EmporixAuthError(\"This operation requires a customer or raw AuthContext\");\n}\n"]}
@@ -4,6 +4,7 @@ var CustomerGroupsService = class {
4
4
  this.ctx = ctx;
5
5
  }
6
6
  ctx;
7
+ static channel = "iam";
7
8
  base() {
8
9
  return `/iam/${this.ctx.tenant}/groups`;
9
10
  }
@@ -36,5 +37,5 @@ var CustomerGroupsService = class {
36
37
  };
37
38
 
38
39
  export { CustomerGroupsService };
39
- //# sourceMappingURL=chunk-LGOC5XOI.js.map
40
- //# sourceMappingURL=chunk-LGOC5XOI.js.map
40
+ //# sourceMappingURL=chunk-3BTPX7IJ.js.map
41
+ //# sourceMappingURL=chunk-3BTPX7IJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/customer-groups.ts"],"names":[],"mappings":";AAeO,IAAM,wBAAN,MAA4B;AAAA,EAEjC,YAA6B,GAAA,EAAoB;AAApB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAqB;AAAA,EAArB,GAAA;AAAA,EAD7B,OAAgB,OAAA,GAAU,KAAA;AAAA,EAGlB,IAAA,GAAe;AACrB,IAAA,OAAO,CAAA,KAAA,EAAQ,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,OAAA,CAAA;AAAA,EAChC;AAAA;AAAA,EAGA,MAAM,cAAA,CAAe,aAAA,EAAuB,IAAA,EAAmD;AAC7F,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAA+B;AAAA,MAClD,MAAA,EAAQ,KAAA;AAAA,MACR,IAAA,EAAM,KAAK,IAAA,EAAK;AAAA,MAChB,KAAA,EAAO,EAAE,mBAAA,EAAqB,aAAA,EAAc;AAAA,MAC5C;AAAA,KACD,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,SAAA,CACJ,OAAA,EACA,MAAA,EACA,IAAA,EACyB;AACzB,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAwB;AAAA,MAC3C,MAAA,EAAQ,MAAA;AAAA,MACR,MAAM,CAAA,EAAG,IAAA,CAAK,IAAA,EAAM,IAAI,OAAO,CAAA,MAAA,CAAA;AAAA,MAC/B,IAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,YAAA,CAAa,OAAA,EAAiB,MAAA,EAAgB,IAAA,EAAkC;AACpF,IAAA,MAAM,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAc;AAAA,MAChC,MAAA,EAAQ,QAAA;AAAA,MACR,IAAA,EAAM,GAAG,IAAA,CAAK,IAAA,EAAM,CAAA,CAAA,EAAI,OAAO,UAAU,MAAM,CAAA,CAAA;AAAA,MAC/C;AAAA,KACD,CAAA;AAAA,EACH;AACF","file":"chunk-3BTPX7IJ.js","sourcesContent":["import type { ClientContext } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport type {\n GroupsQueryDocument,\n AssignmentCreateRequest,\n} from \"../generated/iam\";\n\n/**\n * Access to IAM customer groups for a legal entity.\n *\n * `listForCompany` requires `iam.group_read`; the member mutations\n * (`addMember`/`removeMember`) require `iam.group_manage` — typically only\n * granted to Admin-group customers; a 403 surfaces as\n * `EmporixInsufficientScopeError`.\n */\nexport class CustomerGroupsService {\n static readonly channel = \"iam\" as const;\n constructor(private readonly ctx: ClientContext) {}\n\n private base(): string {\n return `/iam/${this.ctx.tenant}/groups`;\n }\n\n /** Lists customer groups belonging to one legal entity. */\n async listForCompany(legalEntityId: string, auth: AuthContext): Promise<GroupsQueryDocument[]> {\n return this.ctx.http.request<GroupsQueryDocument[]>({\n method: \"GET\",\n path: this.base(),\n query: { \"b2b.legalEntityId\": legalEntityId },\n auth,\n });\n }\n\n /** Adds a user (customer or employee) to a group. */\n async addMember(\n groupId: string,\n member: AssignmentCreateRequest,\n auth: AuthContext,\n ): Promise<{ id: string }> {\n return this.ctx.http.request<{ id: string }>({\n method: \"POST\",\n path: `${this.base()}/${groupId}/users`,\n auth,\n body: member,\n });\n }\n\n /** Removes a user from a group. */\n async removeMember(groupId: string, userId: string, auth: AuthContext): Promise<void> {\n await this.ctx.http.request<void>({\n method: \"DELETE\",\n path: `${this.base()}/${groupId}/users/${userId}`,\n auth,\n });\n }\n}\n"]}
@@ -7,6 +7,7 @@ var ProductService = class {
7
7
  this.ctx = ctx;
8
8
  }
9
9
  ctx;
10
+ static channel = "product";
10
11
  /** Fetches one product by id. */
11
12
  async get(productId, _opts, auth = ANON) {
12
13
  return this.ctx.http.request({
@@ -86,7 +87,9 @@ var ProductService = class {
86
87
  path: `/product/${this.ctx.tenant}/products/search`,
87
88
  query: { pageSize: chunk.length },
88
89
  auth,
89
- body: { q: `id:(${chunk.join(",")})` }
90
+ body: { q: `id:(${chunk.join(",")})` },
91
+ idempotent: true
92
+ // pure read over POST — safe to replay on 5xx/429
90
93
  })
91
94
  )
92
95
  );
@@ -126,7 +129,9 @@ var ProductService = class {
126
129
  path: `/product/${this.ctx.tenant}/products/search`,
127
130
  query: { pageSize: chunk.length },
128
131
  auth,
129
- body: { q: `code:(${chunk.join(",")})` }
132
+ body: { q: `code:(${chunk.join(",")})` },
133
+ idempotent: true
134
+ // pure read over POST — safe to replay on 5xx/429
130
135
  })
131
136
  )
132
137
  );
@@ -158,5 +163,5 @@ var ProductService = class {
158
163
  };
159
164
 
160
165
  export { ProductService };
161
- //# sourceMappingURL=chunk-B4UOYJKW.js.map
162
- //# sourceMappingURL=chunk-B4UOYJKW.js.map
166
+ //# sourceMappingURL=chunk-5VTBLBXJ.js.map
167
+ //# sourceMappingURL=chunk-5VTBLBXJ.js.map