@viu/emporix-sdk 2.26.0 → 2.28.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # @viu/emporix-sdk
2
2
 
3
+ ## 2.28.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#199](https://github.com/viuteam/emporix-sdk/pull/199) [`5836902`](https://github.com/viuteam/emporix-sdk/commit/58369024a1446bf4213f36403467808fbb371cdd) Thanks [@amnael1](https://github.com/amnael1)! - `products.searchByName` no longer 400s on text a shopper actually types.
8
+
9
+ It escaped regex metacharacters, which is right for the regex but not enough for
10
+ the filter: Emporix parses the `q` DSL's own structure first, so an **escaped**
11
+ paren is still an unbalanced paren to it. `searchByName("Access (")` came back as
12
+
13
+ ```
14
+ 400 Missing closing parenthesis in value '(~Access \()' of key 'name'
15
+ ```
16
+
17
+ Any search box wired to this crashed on an opening bracket.
18
+
19
+ `(`, `)` and `"` are now **removed** rather than escaped — measured against a live
20
+ tenant on 2026-08-03, those three are the only metacharacters that break this way;
21
+ every other one survives escaping and stays escaped.
22
+
23
+ Two details that follow from it:
24
+ - Removed brackets become a **space**, then runs of whitespace collapse. Dropping
25
+ them outright would turn `Access(JIT)` into one run-together word, and leaving a
26
+ double space behind would put two _literal_ spaces in the regex — matching
27
+ nothing.
28
+ - A query made only of those characters leaves nothing to search for and returns
29
+ an **empty page without a request**, because `name:(~)` is itself a 400.
30
+
31
+ The escaping behaviour for every other character is unchanged, and one existing
32
+ test that asserted `name:(~a\.b\*\(c\))` was updated — it encoded the bug.
33
+
34
+ ## 2.28.0
35
+
36
+ ### Minor Changes
37
+
38
+ - [#195](https://github.com/viuteam/emporix-sdk/pull/195) [`bda9663`](https://github.com/viuteam/emporix-sdk/commit/bda9663040b3d6c8c33743ced6a3df4b79c81ddb) Thanks [@amnael1](https://github.com/amnael1)! - Export `resolveZone` and `pickFee` alongside the shipping service, plus the
39
+ `ShippingFee` type they use.
40
+
41
+ Both are pure functions — no client, no request — lifted verbatim from the
42
+ storefront demo, where they had no test coverage. They now have ten tests,
43
+ including the `<=` boundary that decides whether a cart total exactly meeting a
44
+ free-shipping threshold gets free shipping.
45
+ - `resolveZone(zones, country)` — the zone whose `shipTo` covers the country,
46
+ else the default zone, else the first.
47
+ - `pickFee(fees, cartTotal)` — the highest `minOrderValue` at or below the
48
+ total, else the first fee.
49
+
50
+ The fee type is exported as `ShippingFee` rather than `Fee`, because the Fee
51
+ service already owns that name at the package root.
52
+
3
53
  ## 2.26.0
4
54
 
5
55
  ### Patch Changes
@@ -69,9 +69,28 @@ var ProductService = class {
69
69
  * so a bare term (e.g. "in time") 400s with "No value for key …". This builds
70
70
  * a `name:(~<term>)` regex filter (regex metacharacters escaped) and delegates
71
71
  * to {@link search}.
72
+ *
73
+ * Safe for text a shopper typed: `( ) "` are **removed** rather than escaped,
74
+ * because Emporix parses the filter's own structure before it looks at the
75
+ * regex. `name:(~a \()` is rejected with «Missing closing parenthesis in value»
76
+ * even though the paren is escaped, so a search box would 400 on an opening
77
+ * bracket. Measured against a live tenant on 2026-08-03: of every regex
78
+ * metacharacter only those three break; the rest survive escaping.
79
+ *
80
+ * A query consisting solely of those characters leaves nothing to search for and
81
+ * returns an empty page without a request — `name:(~)` is a 400.
72
82
  */
73
83
  async searchByName(query, params = {}, auth = ANON) {
74
- const escaped = query.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
84
+ const term = query.replace(/[()"]/g, " ").replace(/\s+/g, " ").trim();
85
+ if (term === "") {
86
+ return {
87
+ items: [],
88
+ pageNumber: params.pageNumber ?? 1,
89
+ pageSize: params.pageSize ?? 50,
90
+ hasNextPage: false
91
+ };
92
+ }
93
+ const escaped = term.replace(/[.*+?^${}|[\]\\]/g, "\\$&");
75
94
  return this.search(`name:(~${escaped})`, params, auth);
76
95
  }
77
96
  /**
@@ -359,5 +378,5 @@ var ProductService = class {
359
378
  };
360
379
 
361
380
  export { ProductService };
362
- //# sourceMappingURL=chunk-UNW6SFSB.js.map
363
- //# sourceMappingURL=chunk-UNW6SFSB.js.map
381
+ //# sourceMappingURL=chunk-7W22KLUL.js.map
382
+ //# sourceMappingURL=chunk-7W22KLUL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/product.ts"],"names":[],"mappings":"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","file":"chunk-7W22KLUL.js","sourcesContent":["import type { ClientContext, PaginatedItems } from \"../core/context\";\nimport { iterateAll } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport { resolveQuery, type QueryFor } from \"../core/query\";\nimport type {\n BasicProductWithId,\n BundleProductWithId,\n ParentVariantProductWithId,\n ProductMedia,\n ProductCreateBody,\n ProductUpdateBody,\n ProductPartialUpdateBody,\n ProductBulkCreateBody,\n ProductBulkUpdateBody,\n BulkResponse,\n ResourceLocation,\n DynamicVariantRecalculationRequest,\n DynamicVariantRecalculationResponse,\n DynamicVariantRecalculationJobResponse,\n DynamicVariantRecalculationJobStatus,\n ProductTemplateResponse,\n ProductTemplateCreation,\n ProductTemplateUpdate,\n} from \"../generated/product\";\n\nconst ANON: AuthContext = { kind: \"anonymous\" };\nconst SERVICE: AuthContext = { kind: \"service\" };\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/** Body for creating a product (`POST /products`) — any of the 5 product shapes. */\nexport type ProductCreateInput = ProductCreateBody;\n\n/** Body for a full product replace (`PUT /products/{id}`). */\nexport type ProductUpdateInput = ProductUpdateBody;\n\n/** Body for a partial product update (`PATCH /products/{id}`). */\nexport type ProductPatchInput = ProductPartialUpdateBody;\n\n/** Id/location envelope returned when a product is created. */\nexport type ProductCreated = ResourceLocation;\n\n/** Body for bulk-creating products (`POST /products/bulk`). */\nexport type ProductBulkCreateInput = ProductBulkCreateBody;\n\n/** Body for bulk-updating products (`PUT /products/bulk`). */\nexport type ProductBulkUpdateInput = ProductBulkUpdateBody;\n\n/** Per-entry result of a bulk product operation (207 Multi-Status). */\nexport type ProductBulkResult = BulkResponse;\n\n/** Body for triggering a dynamic-variant recalculation. */\nexport type ProductRecalculationInput = DynamicVariantRecalculationRequest;\n\n/** Result of triggering a recalculation — created jobs plus skipped product ids. */\nexport type ProductRecalculationResult = DynamicVariantRecalculationResponse;\n\n/** A dynamic-variant recalculation job. */\nexport type ProductRecalculationJob = DynamicVariantRecalculationJobResponse;\n\n/** Status of a recalculation job. */\nexport type ProductRecalculationJobStatus = DynamicVariantRecalculationJobStatus;\n\n/** A product template as returned by the Product service. */\nexport type ProductTemplate = ProductTemplateResponse;\n\n/** Body for creating a product template. */\nexport type ProductTemplateCreateInput = ProductTemplateCreation;\n\n/** Body for updating a product template (`PUT`, full replace). */\nexport type ProductTemplateUpdateInput = ProductTemplateUpdate;\n\n/** Id envelope returned when a product template is created. */\nexport type ProductTemplateCreated = { id?: string };\n\n/** Query flags shared by the product write endpoints. */\nexport interface ProductWriteOptions {\n skipVariantGeneration?: boolean;\n doIndex?: boolean;\n skipRelatedItemsValidation?: boolean;\n}\n\n/** Catalog reads. Default auth: anonymous; pass customer for personalized pricing. */\nexport class ProductService {\n static readonly channel = \"product\" as const;\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 /**\n * Searches products by a `q` filter — a raw Emporix DSL string or a built\n * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Product supports\n * `compoundLogicalQuery`, so `or()` filters are allowed.\n */\n async search(\n query: QueryFor<\"PRODUCT\">,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Product>> {\n const q = resolveQuery(query, { compoundLogicalQuery: true });\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, pageNumber, pageSize },\n auth,\n });\n return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };\n }\n\n /**\n * Free-text product search by name. The product `q` is a `field:value` DSL,\n * so a bare term (e.g. \"in time\") 400s with \"No value for key …\". This builds\n * a `name:(~<term>)` regex filter (regex metacharacters escaped) and delegates\n * to {@link search}.\n *\n * Safe for text a shopper typed: `( ) \"` are **removed** rather than escaped,\n * because Emporix parses the filter's own structure before it looks at the\n * regex. `name:(~a \\()` is rejected with «Missing closing parenthesis in value»\n * even though the paren is escaped, so a search box would 400 on an opening\n * bracket. Measured against a live tenant on 2026-08-03: of every regex\n * metacharacter only those three break; the rest survive escaping.\n *\n * A query consisting solely of those characters leaves nothing to search for and\n * returns an empty page without a request — `name:(~)` is a 400.\n */\n async searchByName(\n query: string,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Product>> {\n // Replaced by a space rather than dropped, so «Access(JIT)» does not become\n // one run-together word — and then collapsed, because a leftover double space\n // is two LITERAL spaces in the regex and would match nothing.\n const term = query\n .replace(/[()\"]/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n if (term === \"\") {\n // Defaults mirror `search`, so the shape matches a real answer.\n return {\n items: [],\n pageNumber: params.pageNumber ?? 1,\n pageSize: params.pageSize ?? 50,\n hasNextPage: false,\n };\n }\n const escaped = term.replace(/[.*+?^${}|[\\]\\\\]/g, \"\\\\$&\");\n return this.search(`name:(~${escaped})`, params, auth);\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 idempotent: true, // pure read over POST — safe to replay on 5xx/429\n }),\n ),\n );\n return pages.flat();\n }\n\n /**\n * Bulk fetch by code. POSTs `/products/search` with `q=\"code:(c1,c2,…)\"`,\n * chunking when the list is larger than `options.chunkSize` (default 100).\n * Duplicate codes are de-duplicated. Codes containing query-delimiter\n * characters (`(`, `)`, `,`, whitespace, `\"`) are dropped with a logged\n * warning, because the Emporix `q` syntax uses them as delimiters and does\n * not support escaping them in a plain IN-list. An empty list — or one with\n * no safe codes — short-circuits with no HTTP call. **Order is not\n * guaranteed** across chunks — re-index by `code` if order matters.\n */\n async searchByCodes(\n codes: string[],\n options: { chunkSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<Product[]> {\n const unique = [...new Set(codes)];\n const unsafe = /[(),\"\\s]/;\n const safe = unique.filter((c) => !unsafe.test(c));\n const dropped = unique.filter((c) => unsafe.test(c));\n if (dropped.length > 0) {\n this.ctx.logger.warn(\n \"products.searchByCodes: dropped codes containing query-delimiter characters\",\n { dropped },\n );\n }\n if (safe.length === 0) return [];\n const chunkSize = options.chunkSize ?? 100;\n const chunks: string[][] = [];\n for (let i = 0; i < safe.length; i += chunkSize) {\n chunks.push(safe.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: `code:(${chunk.join(\",\")})` },\n idempotent: true, // pure read over POST — safe to replay on 5xx/429\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 // --- Admin writes. Default auth: service. ---\n\n /**\n * Creates a product (`POST /products`). Default auth: service. Accepts any of\n * the five product shapes (basic, bundle, parent-variant, variant,\n * dynamic-variant).\n */\n async create(\n input: ProductCreateInput,\n options: ProductWriteOptions = {},\n auth: AuthContext = SERVICE,\n ): Promise<ProductCreated> {\n const query = {\n ...(options.skipVariantGeneration === undefined ? {} : { skipVariantGeneration: String(options.skipVariantGeneration) }),\n ...(options.doIndex === undefined ? {} : { doIndex: String(options.doIndex) }),\n ...(options.skipRelatedItemsValidation === undefined ? {} : { skipRelatedItemsValidation: String(options.skipRelatedItemsValidation) }),\n };\n return this.ctx.http.request<ProductCreated>({\n method: \"POST\",\n path: `/product/${this.ctx.tenant}/products`,\n ...(Object.keys(query).length > 0 ? { query } : {}),\n body: input,\n auth,\n });\n }\n\n /**\n * Partially updates a product (`PATCH /products/{productId}`). Default auth:\n * service. Use {@link replace} for a full replace (PUT).\n */\n async update(\n productId: string,\n input: ProductPatchInput,\n options: ProductWriteOptions = {},\n auth: AuthContext = SERVICE,\n ): Promise<void> {\n const query = {\n ...(options.skipVariantGeneration === undefined ? {} : { skipVariantGeneration: String(options.skipVariantGeneration) }),\n ...(options.doIndex === undefined ? {} : { doIndex: String(options.doIndex) }),\n ...(options.skipRelatedItemsValidation === undefined ? {} : { skipRelatedItemsValidation: String(options.skipRelatedItemsValidation) }),\n };\n await this.ctx.http.request<void>({\n method: \"PATCH\",\n path: `/product/${this.ctx.tenant}/products/${productId}`,\n ...(Object.keys(query).length > 0 ? { query } : {}),\n body: input,\n auth,\n });\n }\n\n /**\n * Full-replaces a product (`PUT /products/{productId}`). Returns the created\n * resource on 201 (upsert) and nothing on 204. `options.partial` sends\n * `?partial=true` for a merge-style replace. Default auth: service.\n */\n async replace(\n productId: string,\n input: ProductUpdateInput,\n options: ProductWriteOptions & { partial?: boolean } = {},\n auth: AuthContext = SERVICE,\n ): Promise<ProductCreated | void> {\n const query = {\n ...(options.partial === undefined ? {} : { partial: String(options.partial) }),\n ...(options.skipVariantGeneration === undefined ? {} : { skipVariantGeneration: String(options.skipVariantGeneration) }),\n ...(options.doIndex === undefined ? {} : { doIndex: String(options.doIndex) }),\n ...(options.skipRelatedItemsValidation === undefined ? {} : { skipRelatedItemsValidation: String(options.skipRelatedItemsValidation) }),\n };\n return this.ctx.http.request<ProductCreated | void>({\n method: \"PUT\",\n path: `/product/${this.ctx.tenant}/products/${productId}`,\n ...(Object.keys(query).length > 0 ? { query } : {}),\n body: input,\n auth,\n });\n }\n\n /**\n * Deletes a product (`DELETE /products/{productId}`). `options.force` deletes\n * even when the product is referenced. Default auth: service.\n */\n async delete(\n productId: string,\n options: { force?: boolean; doIndex?: boolean } = {},\n auth: AuthContext = SERVICE,\n ): Promise<void> {\n const query = {\n ...(options.force === undefined ? {} : { force: String(options.force) }),\n ...(options.doIndex === undefined ? {} : { doIndex: String(options.doIndex) }),\n };\n await this.ctx.http.request<void>({\n method: \"DELETE\",\n path: `/product/${this.ctx.tenant}/products/${productId}`,\n ...(Object.keys(query).length > 0 ? { query } : {}),\n auth,\n });\n }\n\n /**\n * Bulk-creates products (`POST /products/bulk`). Responds 207 Multi-Status:\n * partial failures do **not** throw — inspect each entry. Default auth: service.\n */\n async bulkCreate(\n input: ProductBulkCreateInput,\n options: ProductWriteOptions = {},\n auth: AuthContext = SERVICE,\n ): Promise<ProductBulkResult[]> {\n const query = {\n ...(options.skipVariantGeneration === undefined ? {} : { skipVariantGeneration: String(options.skipVariantGeneration) }),\n ...(options.doIndex === undefined ? {} : { doIndex: String(options.doIndex) }),\n ...(options.skipRelatedItemsValidation === undefined ? {} : { skipRelatedItemsValidation: String(options.skipRelatedItemsValidation) }),\n };\n return this.ctx.http.request<ProductBulkResult[]>({\n method: \"POST\",\n path: `/product/${this.ctx.tenant}/products/bulk`,\n ...(Object.keys(query).length > 0 ? { query } : {}),\n body: input,\n auth,\n });\n }\n\n /**\n * Bulk-updates products (`PUT /products/bulk`). Responds 207 Multi-Status:\n * partial failures do **not** throw — inspect each entry. Default auth: service.\n */\n async bulkUpdate(\n input: ProductBulkUpdateInput,\n options: ProductWriteOptions = {},\n auth: AuthContext = SERVICE,\n ): Promise<ProductBulkResult[]> {\n const query = {\n ...(options.skipVariantGeneration === undefined ? {} : { skipVariantGeneration: String(options.skipVariantGeneration) }),\n ...(options.doIndex === undefined ? {} : { doIndex: String(options.doIndex) }),\n ...(options.skipRelatedItemsValidation === undefined ? {} : { skipRelatedItemsValidation: String(options.skipRelatedItemsValidation) }),\n };\n return this.ctx.http.request<ProductBulkResult[]>({\n method: \"PUT\",\n path: `/product/${this.ctx.tenant}/products/bulk`,\n ...(Object.keys(query).length > 0 ? { query } : {}),\n body: input,\n auth,\n });\n }\n\n /**\n * Triggers a dynamic-variant recalculation for the given product ids\n * (`POST /products/recalculate`, 202). The result carries the created or\n * already-referenced `jobs` plus `skippedProductIds` — products whose root\n * already has a PENDING/PROCESSING job. Default auth: service.\n */\n async recalculate(\n input: ProductRecalculationInput,\n auth: AuthContext = SERVICE,\n ): Promise<ProductRecalculationResult> {\n return this.ctx.http.request<ProductRecalculationResult>({\n method: \"POST\",\n path: `/product/${this.ctx.tenant}/products/recalculate`,\n body: input,\n auth,\n });\n }\n\n /** Lists dynamic-variant recalculation jobs, optionally filtered by status. Default auth: anonymous. */\n async listRecalculationJobs(\n params: { status?: ProductRecalculationJobStatus } = {},\n auth: AuthContext = ANON,\n ): Promise<ProductRecalculationJob[]> {\n return this.ctx.http.request<ProductRecalculationJob[]>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/products/recalculate/jobs`,\n ...(params.status === undefined ? {} : { query: { status: params.status } }),\n auth,\n });\n }\n\n /** Fetches one dynamic-variant recalculation job by id. Default auth: anonymous. */\n async getRecalculationJob(jobId: string, auth: AuthContext = ANON): Promise<ProductRecalculationJob> {\n return this.ctx.http.request<ProductRecalculationJob>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/products/recalculate/jobs/${jobId}`,\n auth,\n });\n }\n\n /**\n * Product templates (`/product-templates`). Reads default to anonymous,\n * writes to service auth.\n */\n readonly templates = {\n /** One page of product templates. Default auth: anonymous. */\n list: async (\n params: { pageNumber?: number; pageSize?: number; sort?: string; q?: string } = {},\n auth: AuthContext = ANON,\n ): Promise<ProductTemplate[]> =>\n this.ctx.http.request<ProductTemplate[]>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/product-templates`,\n query: {\n ...(params.pageNumber === undefined ? {} : { pageNumber: params.pageNumber }),\n ...(params.pageSize === undefined ? {} : { pageSize: params.pageSize }),\n ...(params.sort === undefined ? {} : { sort: params.sort }),\n ...(params.q === undefined ? {} : { q: params.q }),\n },\n auth,\n }),\n\n /** Fetches one product template by id. Default auth: anonymous. */\n get: async (templateId: string, auth: AuthContext = ANON): Promise<ProductTemplate> =>\n this.ctx.http.request<ProductTemplate>({\n method: \"GET\",\n path: `/product/${this.ctx.tenant}/product-templates/${templateId}`,\n auth,\n }),\n\n /** Creates a product template. Default auth: service. */\n create: async (\n input: ProductTemplateCreateInput,\n auth: AuthContext = SERVICE,\n ): Promise<ProductTemplateCreated> =>\n this.ctx.http.request<ProductTemplateCreated>({\n method: \"POST\",\n path: `/product/${this.ctx.tenant}/product-templates`,\n body: input,\n auth,\n }),\n\n /** Full-replaces a product template (`PUT`). Default auth: service. */\n update: async (\n templateId: string,\n input: ProductTemplateUpdateInput,\n auth: AuthContext = SERVICE,\n ): Promise<void> => {\n await this.ctx.http.request<void>({\n method: \"PUT\",\n path: `/product/${this.ctx.tenant}/product-templates/${templateId}`,\n body: input,\n auth,\n });\n },\n\n /** Deletes a product template. Default auth: service. */\n delete: async (templateId: string, auth: AuthContext = SERVICE): Promise<void> => {\n await this.ctx.http.request<void>({\n method: \"DELETE\",\n path: `/product/${this.ctx.tenant}/product-templates/${templateId}`,\n auth,\n });\n },\n };\n}\n"]}
package/dist/index.cjs CHANGED
@@ -890,7 +890,7 @@ var HttpClient = class {
890
890
  };
891
891
 
892
892
  // package.json
893
- var version = "2.26.0";
893
+ var version = "2.28.1";
894
894
 
895
895
  // src/version.ts
896
896
  var SDK_VERSION = version;
@@ -1320,9 +1320,28 @@ var ProductService = class {
1320
1320
  * so a bare term (e.g. "in time") 400s with "No value for key …". This builds
1321
1321
  * a `name:(~<term>)` regex filter (regex metacharacters escaped) and delegates
1322
1322
  * to {@link search}.
1323
+ *
1324
+ * Safe for text a shopper typed: `( ) "` are **removed** rather than escaped,
1325
+ * because Emporix parses the filter's own structure before it looks at the
1326
+ * regex. `name:(~a \()` is rejected with «Missing closing parenthesis in value»
1327
+ * even though the paren is escaped, so a search box would 400 on an opening
1328
+ * bracket. Measured against a live tenant on 2026-08-03: of every regex
1329
+ * metacharacter only those three break; the rest survive escaping.
1330
+ *
1331
+ * A query consisting solely of those characters leaves nothing to search for and
1332
+ * returns an empty page without a request — `name:(~)` is a 400.
1323
1333
  */
1324
1334
  async searchByName(query, params = {}, auth2 = ANON) {
1325
- const escaped = query.trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1335
+ const term = query.replace(/[()"]/g, " ").replace(/\s+/g, " ").trim();
1336
+ if (term === "") {
1337
+ return {
1338
+ items: [],
1339
+ pageNumber: params.pageNumber ?? 1,
1340
+ pageSize: params.pageSize ?? 50,
1341
+ hasNextPage: false
1342
+ };
1343
+ }
1344
+ const escaped = term.replace(/[.*+?^${}|[\]\\]/g, "\\$&");
1326
1345
  return this.search(`name:(~${escaped})`, params, auth2);
1327
1346
  }
1328
1347
  /**
@@ -6673,6 +6692,17 @@ var CurrencyService = class {
6673
6692
 
6674
6693
  // src/services/shipping.ts
6675
6694
  var SERVICE25 = { kind: "service" };
6695
+ function resolveZone(zones, country) {
6696
+ if (!zones || zones.length === 0) return void 0;
6697
+ const c = country.trim().toUpperCase();
6698
+ const byCountry = c ? zones.find((z) => (z.shipTo ?? []).some((s) => s.country?.toUpperCase() === c)) : void 0;
6699
+ return byCountry ?? zones.find((z) => z.default) ?? zones[0];
6700
+ }
6701
+ function pickFee(fees, cartTotal) {
6702
+ if (!fees || fees.length === 0) return void 0;
6703
+ const eligible = fees.filter((f) => (f.minOrderValue?.amount ?? 0) <= cartTotal).sort((a, b) => (b.minOrderValue?.amount ?? 0) - (a.minOrderValue?.amount ?? 0));
6704
+ return eligible[0] ?? fees[0];
6705
+ }
6676
6706
  var ShippingService = class {
6677
6707
  constructor(ctx) {
6678
6708
  this.ctx = ctx;
@@ -8256,10 +8286,12 @@ exports.createEmporixClient = createEmporixClient;
8256
8286
  exports.createNoopLogger = createNoopLogger;
8257
8287
  exports.errorFromResponse = errorFromResponse;
8258
8288
  exports.iterateAll = iterateAll;
8289
+ exports.pickFee = pickFee;
8259
8290
  exports.productIdFromYrn = productIdFromYrn;
8260
8291
  exports.redact = redact;
8261
8292
  exports.resolveQuery = resolveQuery;
8262
8293
  exports.resolveToken = resolveToken;
8294
+ exports.resolveZone = resolveZone;
8263
8295
  exports.validateConfig = validateConfig;
8264
8296
  //# sourceMappingURL=index.cjs.map
8265
8297
  //# sourceMappingURL=index.cjs.map