@viu/emporix-sdk 2.16.0 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @viu/emporix-sdk
2
2
 
3
+ ## 2.18.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#137](https://github.com/viuteam/emporix-sdk/pull/137) [`9ef7c51`](https://github.com/viuteam/emporix-sdk/commit/9ef7c51d933d9b78be1880ce19d6f7312ffcd20e) Thanks [@amnael1](https://github.com/amnael1)! - Add a type-safe mixin filter builder. `@viu/emporix-mixins` now exports
8
+ `mixinQuery`/`and`/`or`/`raw` to build Emporix `q` filters from generated
9
+ `MixinDescriptor`s, with attribute names and value types checked at compile
10
+ time and the entity carried through `MixinDescriptor<T, E>` / `MixinFilter<E>`.
11
+ Localized attributes are supported via a `{ lang, ... }` operator.
12
+ `products.search` and `useProductSearch` accept a built filter (or a raw
13
+ string); a new `resolveQuery` normalizer enforces the `compoundLogicalQuery`
14
+ (OR) capability gate per service.
15
+
16
+ - [#137](https://github.com/viuteam/emporix-sdk/pull/137) [`de6e8b8`](https://github.com/viuteam/emporix-sdk/commit/de6e8b8727c5150f9fe3df77820dd13b6cf37e24) Thanks [@amnael1](https://github.com/amnael1)! - Wire the mixin filter builder into more services. `categories.search`,
17
+ `orders.listMine({ q })`, `customerAdmin.searchCustomers({ q })` and
18
+ `vendor.searchVendors({ q })` now accept a built mixin filter (or a raw `q`
19
+ string), each entity-gated via `QueryFor<E>` and routed through `resolveQuery`
20
+ (all are non-compound, so `or()` filters are rejected). New React hooks:
21
+ `useCategorySearch` and a `q` option on `useMyOrders`.
22
+
3
23
  ## 2.16.0
4
24
 
5
25
  ### Patch Changes
package/README.md CHANGED
@@ -60,6 +60,25 @@ property on the `EmporixClient` instance:
60
60
  The sections below highlight the most-used services; per-service guides live in
61
61
  [`../../docs/`](../../docs).
62
62
 
63
+ ## Searching by mixin (custom) fields
64
+
65
+ `products.search`, `categories.search`, `orders.listMine({ q })`,
66
+ `customerAdmin.searchCustomers({ q })` and `vendor.searchVendors({ q })` accept a
67
+ raw Emporix `q` string **or** a type-safe filter built with `mixinQuery` from
68
+ [`@viu/emporix-mixins`](../mixins). The filter is entity-gated (a filter built for
69
+ one entity is a compile error on another) and an `or()` filter is rejected on
70
+ services that don't support `compoundLogicalQuery`.
71
+
72
+ ```ts
73
+ import { mixinQuery } from "@viu/emporix-mixins";
74
+ import { mixins } from "./mixins/generated/registry";
75
+
76
+ const q = mixinQuery(mixins.attrs, { color: "Blue", qty: { gte: 10 } });
77
+ const page = await sdk.products.search(q);
78
+ ```
79
+
80
+ See [`../../docs/mixin-search.md`](../../docs/mixin-search.md) for the capability matrix.
81
+
63
82
  ## Configuration
64
83
 
65
84
  | Option | Default | Notes |
package/dist/category.cjs CHANGED
@@ -11,6 +11,17 @@ async function* iterateAll(fetchPage, start = 1) {
11
11
  }
12
12
  }
13
13
 
14
+ // src/core/query.ts
15
+ function resolveQuery(q, cap) {
16
+ if (typeof q === "string") return q;
17
+ if (q.usesCompound === true && true) {
18
+ throw new Error(
19
+ "This filter uses or()/compoundLogicalQuery, which the target service does not support. Combine conditions with and() (space-separated AND) instead."
20
+ );
21
+ }
22
+ return q.toString();
23
+ }
24
+
14
25
  // src/services/category.ts
15
26
  var ANON = { kind: "anonymous" };
16
27
  var CategoryService = class {
@@ -39,6 +50,23 @@ var CategoryService = class {
39
50
  });
40
51
  return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
41
52
  }
53
+ /**
54
+ * Searches categories by a `q` filter — a raw Emporix DSL string or a built
55
+ * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Category does not
56
+ * support `compoundLogicalQuery`, so `or()` filters are rejected.
57
+ */
58
+ async search(query, params = {}, auth = ANON) {
59
+ const q = resolveQuery(query);
60
+ const pageNumber = params.pageNumber ?? 1;
61
+ const pageSize = params.pageSize ?? 50;
62
+ const items = await this.ctx.http.request({
63
+ method: "GET",
64
+ path: `/category/${this.ctx.tenant}/categories`,
65
+ query: { q, pageNumber, pageSize },
66
+ auth
67
+ });
68
+ return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
69
+ }
42
70
  /** Async-iterates every category across pages. */
43
71
  listAll(params = {}, auth = ANON) {
44
72
  const pageSize = params.pageSize ?? 50;
@@ -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 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
+ {"version":3,"sources":["../src/core/context.ts","../src/core/query.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","/**\n * A built query filter — structurally matched, NOT imported, so the SDK stays\n * decoupled from `@viu/emporix-mixins` (which lists the SDK as an optional\n * peer; a real import would be circular). `@viu/emporix-mixins`' `MixinFilter<E>`\n * satisfies this shape.\n */\nexport interface BuiltQuery<E extends string = string> {\n toString(): string;\n readonly usesCompound?: boolean;\n /** Phantom entity tag — gates which service a filter may be passed to. */\n readonly __entity?: E;\n}\n\n/** A `q` value a service method accepts: a raw DSL string or a built filter for entity `E`. */\nexport type QueryFor<E extends string = string> = string | BuiltQuery<E>;\n\n/** Per-service capability flags consulted by {@link resolveQuery}. */\nexport interface QueryCapability {\n /** Whether the target service supports the `compoundLogicalQuery` (OR) operator. */\n compoundLogicalQuery: boolean;\n}\n\n/**\n * Normalizes a `string | BuiltQuery` into a `q` string and enforces the\n * `compoundLogicalQuery` capability gate: an `or()`-built filter passed to a\n * service that does not support it throws rather than silently producing a\n * query the backend cannot execute.\n */\nexport function resolveQuery(q: QueryFor, cap: QueryCapability): string {\n if (typeof q === \"string\") return q;\n if (q.usesCompound === true && !cap.compoundLogicalQuery) {\n throw new Error(\n \"This filter uses or()/compoundLogicalQuery, which the target service does not support. \" +\n \"Combine conditions with and() (space-separated AND) instead.\",\n );\n }\n return q.toString();\n}\n","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 { 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 /**\n * Searches categories by a `q` filter — a raw Emporix DSL string or a built\n * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Category does not\n * support `compoundLogicalQuery`, so `or()` filters are rejected.\n */\n async search(\n query: QueryFor<\"CATEGORY\">,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Category>> {\n const q = resolveQuery(query, { compoundLogicalQuery: false });\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: { q, 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,5 @@
1
1
  import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-DhoFgoKf.cjs';
2
+ import { a as QueryFor } from './query-mLQs2WFr.cjs';
2
3
  import { Product } from './product.cjs';
3
4
 
4
5
  /**
@@ -289,6 +290,15 @@ declare class CategoryService {
289
290
  pageNumber?: number;
290
291
  pageSize?: number;
291
292
  }, auth?: AuthContext): Promise<PaginatedItems<Category>>;
293
+ /**
294
+ * Searches categories by a `q` filter — a raw Emporix DSL string or a built
295
+ * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Category does not
296
+ * support `compoundLogicalQuery`, so `or()` filters are rejected.
297
+ */
298
+ search(query: QueryFor<"CATEGORY">, params?: {
299
+ pageNumber?: number;
300
+ pageSize?: number;
301
+ }, auth?: AuthContext): Promise<PaginatedItems<Category>>;
292
302
  /** Async-iterates every category across pages. */
293
303
  listAll(params?: {
294
304
  pageSize?: number;
@@ -1,4 +1,5 @@
1
1
  import { C as ClientContext, b as AuthContext, P as PaginatedItems } from './context-DhoFgoKf.js';
2
+ import { a as QueryFor } from './query-mLQs2WFr.js';
2
3
  import { Product } from './product.js';
3
4
 
4
5
  /**
@@ -289,6 +290,15 @@ declare class CategoryService {
289
290
  pageNumber?: number;
290
291
  pageSize?: number;
291
292
  }, auth?: AuthContext): Promise<PaginatedItems<Category>>;
293
+ /**
294
+ * Searches categories by a `q` filter — a raw Emporix DSL string or a built
295
+ * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Category does not
296
+ * support `compoundLogicalQuery`, so `or()` filters are rejected.
297
+ */
298
+ search(query: QueryFor<"CATEGORY">, params?: {
299
+ pageNumber?: number;
300
+ pageSize?: number;
301
+ }, auth?: AuthContext): Promise<PaginatedItems<Category>>;
292
302
  /** Async-iterates every category across pages. */
293
303
  listAll(params?: {
294
304
  pageSize?: number;
package/dist/category.js CHANGED
@@ -1,4 +1,5 @@
1
- export { CategoryService } from './chunk-GIZS4BY3.js';
1
+ export { CategoryService } from './chunk-6XND7SYV.js';
2
2
  import './chunk-GWJ2U2CP.js';
3
+ import './chunk-S5HEIQDK.js';
3
4
  //# sourceMappingURL=category.js.map
4
5
  //# sourceMappingURL=category.js.map
@@ -1,3 +1,5 @@
1
+ import { resolveQuery } from './chunk-S5HEIQDK.js';
2
+
1
3
  // src/services/orders.ts
2
4
  function setIfDefined(q, key, value) {
3
5
  if (value !== void 0 && value !== "") {
@@ -27,6 +29,9 @@ var OrdersService = class {
27
29
  setIfDefined(query, "status", opts.status);
28
30
  setIfDefined(query, "legalEntityId", opts.legalEntityId);
29
31
  setIfDefined(query, "siteCode", opts.siteCode);
32
+ if (opts.q !== void 0) {
33
+ setIfDefined(query, "q", resolveQuery(opts.q, { compoundLogicalQuery: false }));
34
+ }
30
35
  const headers = this.saasHeader(opts.saasToken);
31
36
  const items = await this.ctx.http.request({
32
37
  method: "GET",
@@ -97,5 +102,5 @@ var SalesOrdersService = class {
97
102
  };
98
103
 
99
104
  export { OrdersService, SalesOrdersService };
100
- //# sourceMappingURL=chunk-62PCCHZ2.js.map
101
- //# sourceMappingURL=chunk-62PCCHZ2.js.map
105
+ //# sourceMappingURL=chunk-36T4YPQJ.js.map
106
+ //# sourceMappingURL=chunk-36T4YPQJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/orders.ts"],"names":[],"mappings":";;;AAyCA,SAAS,YAAA,CACP,CAAA,EACA,GAAA,EACA,KAAA,EACM;AACN,EAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,EAAA,EAAI;AACvC,IAAA,CAAA,CAAE,GAAG,CAAA,GAAI,KAAA;AAAA,EACX;AACF;AASO,IAAM,gBAAN,MAAoB;AAAA,EAEzB,YAA6B,GAAA,EAAoB;AAApB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAqB;AAAA,EAArB,GAAA;AAAA,EAD7B,OAAgB,OAAA,GAAU,QAAA;AAAA,EAGlB,IAAA,GAAe;AACrB,IAAA,OAAO,CAAA,UAAA,EAAa,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,OAAA,CAAA;AAAA,EACrC;AAAA,EAEQ,WAAW,SAAA,EAAmE;AACpF,IAAA,OAAO,SAAA,GAAY,EAAE,YAAA,EAAc,SAAA,EAAU,GAAI,MAAA;AAAA,EACnD;AAAA;AAAA,EAGA,MAAM,QAAA,CACJ,IAAA,EACA,IAAA,GAA4B,EAAC,EACG;AAChC,IAAA,MAAM,UAAA,GAAa,KAAK,UAAA,IAAc,CAAA;AACtC,IAAA,MAAM,QAAA,GAAW,KAAK,QAAA,IAAY,EAAA;AAClC,IAAA,MAAM,KAAA,GAAqD;AAAA,MACzD,UAAA;AAAA,MACA;AAAA,KACF;AACA,IAAA,YAAA,CAAa,KAAA,EAAO,QAAA,EAAU,IAAA,CAAK,MAAM,CAAA;AACzC,IAAA,YAAA,CAAa,KAAA,EAAO,eAAA,EAAiB,IAAA,CAAK,aAAa,CAAA;AACvD,IAAA,YAAA,CAAa,KAAA,EAAO,UAAA,EAAY,IAAA,CAAK,QAAQ,CAAA;AAC7C,IAAA,IAAI,IAAA,CAAK,MAAM,MAAA,EAAW;AACxB,MAAA,YAAA,CAAa,KAAA,EAAO,KAAK,YAAA,CAAa,IAAA,CAAK,GAAG,EAAE,oBAAA,EAAsB,KAAA,EAAO,CAAC,CAAA;AAAA,IAChF;AACA,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,SAAS,CAAA;AAK9C,IAAA,MAAM,KAAA,GAAQ,MAAM,IAAA,CAAK,GAAA,CAAI,KAAK,OAAA,CAAiB;AAAA,MACjD,MAAA,EAAQ,KAAA;AAAA,MACR,IAAA,EAAM,KAAK,IAAA,EAAK;AAAA,MAChB,KAAA;AAAA,MACA,IAAA;AAAA,MACA,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY;AAAC,KAC9B,CAAA;AACD,IAAA,OAAO,EAAE,KAAA,EAAO,UAAA,EAAY,UAAU,WAAA,EAAa,KAAA,CAAM,WAAW,QAAA,EAAS;AAAA,EAC/E;AAAA;AAAA,EAGA,MAAM,GAAA,CACJ,OAAA,EACA,IAAA,EACA,IAAA,GAAwB,EAAC,EACT;AAChB,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,SAAS,CAAA;AAC9C,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAe;AAAA,MAClC,MAAA,EAAQ,KAAA;AAAA,MACR,MAAM,CAAA,EAAG,IAAA,CAAK,IAAA,EAAM,IAAI,OAAO,CAAA,CAAA;AAAA,MAC/B,IAAA;AAAA,MACA,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY;AAAC,KAC9B,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,UAAA,CACJ,OAAA,EACA,QACA,IAAA,EACA,IAAA,GAA+B,EAAC,EACjB;AACf,IAAA,MAAM,IAAA,GAAkD,EAAE,MAAA,EAAO;AACjE,IAAA,IAAI,IAAA,CAAK,OAAA,KAAY,MAAA,EAAW,IAAA,CAAK,UAAU,IAAA,CAAK,OAAA;AACpD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,UAAA,CAAW,IAAA,CAAK,SAAS,CAAA;AAC9C,IAAA,MAAM,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAc;AAAA,MAChC,MAAA,EAAQ,MAAA;AAAA,MACR,MAAM,CAAA,EAAG,IAAA,CAAK,IAAA,EAAM,IAAI,OAAO,CAAA,YAAA,CAAA;AAAA,MAC/B,IAAA;AAAA,MACA,IAAA;AAAA,MACA,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY;AAAC,KAC9B,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,MAAA,CACJ,OAAA,EACA,IAAA,EACA,IAAA,GAA+B,EAAC,EACjB;AACf,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,OAAA,EAAS,UAAA,EAAY,MAAM,IAAI,CAAA;AAAA,EACvD;AACF;AAWO,IAAM,qBAAN,MAAyB;AAAA,EAE9B,YAA6B,GAAA,EAAoB;AAApB,IAAA,IAAA,CAAA,GAAA,GAAA,GAAA;AAAA,EAAqB;AAAA,EAArB,GAAA;AAAA,EAD7B,OAAgB,OAAA,GAAU,cAAA;AAAA,EAGlB,IAAA,GAAe;AACrB,IAAA,OAAO,CAAA,UAAA,EAAa,IAAA,CAAK,GAAA,CAAI,MAAM,CAAA,YAAA,CAAA;AAAA,EACrC;AAAA;AAAA,EAGA,MAAM,GAAA,CAAI,OAAA,EAAiB,IAAA,EAAmC;AAC5D,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAe;AAAA,MAClC,MAAA,EAAQ,KAAA;AAAA,MACR,MAAM,CAAA,EAAG,IAAA,CAAK,IAAA,EAAM,IAAI,OAAO,CAAA,CAAA;AAAA,MAC/B;AAAA,KACD,CAAA;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,MAAA,CACJ,OAAA,EACA,OACA,IAAA,EACA,IAAA,GAAgC,EAAC,EACjB;AAChB,IAAA,MAAM,QAAgC,EAAC;AACvC,IAAA,IAAI,IAAA,CAAK,WAAA,KAAgB,KAAA,EAAO,KAAA,CAAM,WAAA,GAAc,OAAA;AACpD,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,OAAA,CAAe;AAAA,MAClC,MAAA,EAAQ,OAAA;AAAA,MACR,MAAM,CAAA,EAAG,IAAA,CAAK,IAAA,EAAM,IAAI,OAAO,CAAA,CAAA;AAAA,MAC/B,IAAA;AAAA,MACA,GAAI,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,CAAE,SAAS,CAAA,GAAI,EAAE,KAAA,EAAM,GAAI,EAAC;AAAA,MACjD,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AACF","file":"chunk-36T4YPQJ.js","sourcesContent":["import type { ClientContext, PaginatedItems } from \"../core/context\";\nimport type { AuthContext } from \"../core/auth\";\nimport { resolveQuery, type QueryFor } from \"../core/query\";\nimport type { Order, OrderStatus, OrderUpdateDto } from \"../generated/order-v2\";\n\n/**\n * Salesorder PATCH body. The generated `orderUpdateDto` models a full order\n * (it requires `entries`/`customer`), but `PATCH /salesorders/{id}` accepts a\n * subset — so the SDK exposes a partial of it.\n */\nexport type SalesOrderPatch = Partial<OrderUpdateDto>;\n\n/** Optional fields supported by the order-v2 list endpoint. */\nexport interface ListMyOrdersOptions {\n pageNumber?: number;\n pageSize?: number;\n status?: OrderStatus;\n legalEntityId?: string;\n siteCode?: string;\n saasToken?: string;\n /** A `q` filter — raw DSL string or a built filter (e.g. mixinQuery for entity \"ORDER\"). */\n q?: QueryFor<\"ORDER\">;\n}\n\n/** Options for single-order reads (saas-token only). */\nexport interface GetOrderOptions {\n saasToken?: string;\n}\n\n/** Options for status transitions. */\nexport interface OrderTransitionOptions {\n saasToken?: string;\n comment?: string;\n}\n\n/** Options for `salesOrders.update`. */\nexport interface UpdateSalesOrderOptions {\n /** Forwarded as `?recalculate=`. Omit to use the server default (true). */\n recalculate?: boolean;\n}\n\nfunction setIfDefined<V>(\n q: Record<string, string | number | undefined>,\n key: string,\n value: V | undefined,\n): void {\n if (value !== undefined && value !== \"\") {\n q[key] = value as unknown as string | number;\n }\n}\n\n/**\n * Storefront-customer access to Order-v2's customer endpoints.\n *\n * `listMine`/`get` require `order.order_read_own`; transitions require\n * `order.order_manage_own`. All methods accept an optional `saasToken` that\n * is passed as the `saas-token` header (mirrors `checkout.placeOrder`).\n */\nexport class OrdersService {\n static readonly channel = \"orders\" as const;\n constructor(private readonly ctx: ClientContext) {}\n\n private base(): string {\n return `/order-v2/${this.ctx.tenant}/orders`;\n }\n\n private saasHeader(saasToken: string | undefined): Record<string, string> | undefined {\n return saasToken ? { \"saas-token\": saasToken } : undefined;\n }\n\n /** Lists the calling customer's orders. */\n async listMine(\n auth: AuthContext,\n opts: ListMyOrdersOptions = {},\n ): Promise<PaginatedItems<Order>> {\n const pageNumber = opts.pageNumber ?? 1;\n const pageSize = opts.pageSize ?? 50;\n const query: Record<string, string | number | undefined> = {\n pageNumber,\n pageSize,\n };\n setIfDefined(query, \"status\", opts.status);\n setIfDefined(query, \"legalEntityId\", opts.legalEntityId);\n setIfDefined(query, \"siteCode\", opts.siteCode);\n if (opts.q !== undefined) {\n setIfDefined(query, \"q\", resolveQuery(opts.q, { compoundLogicalQuery: false }));\n }\n const headers = this.saasHeader(opts.saasToken);\n // order-v2 returns a bare JSON array (the total count lives in the\n // X-Total-Count header), not a {items,...} envelope — so wrap it into the\n // shared PaginatedItems shape like every other paginated service.\n // hasNextPage is inferred from the page being full.\n const items = await this.ctx.http.request<Order[]>({\n method: \"GET\",\n path: this.base(),\n query,\n auth,\n ...(headers ? { headers } : {}),\n });\n return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };\n }\n\n /** Fetches one of the calling customer's orders by id. */\n async get(\n orderId: string,\n auth: AuthContext,\n opts: GetOrderOptions = {},\n ): Promise<Order> {\n const headers = this.saasHeader(opts.saasToken);\n return this.ctx.http.request<Order>({\n method: \"GET\",\n path: `${this.base()}/${orderId}`,\n auth,\n ...(headers ? { headers } : {}),\n });\n }\n\n /** Transitions an order to a new status. Server enforces legal transitions. */\n async transition(\n orderId: string,\n status: OrderStatus,\n auth: AuthContext,\n opts: OrderTransitionOptions = {},\n ): Promise<void> {\n const body: { status: OrderStatus; comment?: string } = { status };\n if (opts.comment !== undefined) body.comment = opts.comment;\n const headers = this.saasHeader(opts.saasToken);\n await this.ctx.http.request<void>({\n method: \"POST\",\n path: `${this.base()}/${orderId}/transitions`,\n auth,\n body,\n ...(headers ? { headers } : {}),\n });\n }\n\n /** Convenience: transitions to `DECLINED` (customer cancel). */\n async cancel(\n orderId: string,\n auth: AuthContext,\n opts: { saasToken?: string } = {},\n ): Promise<void> {\n await this.transition(orderId, \"DECLINED\", auth, opts);\n }\n}\n\n/**\n * Backend / service-account access to the merchant-facing\n * `/salesorders/{id}` resource. Requires `order.order_read` (read) /\n * `order.order_manage` (update) scopes on a service token.\n *\n * The full admin list + filter surface is deferred to a follow-up\n * sub-spec. This service ships only single-resource read + patch — the\n * common backend use case (e.g. mixin updates after fulfilment).\n */\nexport class SalesOrdersService {\n static readonly channel = \"sales-orders\" as const;\n constructor(private readonly ctx: ClientContext) {}\n\n private base(): string {\n return `/order-v2/${this.ctx.tenant}/salesorders`;\n }\n\n /** Fetches a single sales-order by id. */\n async get(orderId: string, auth: AuthContext): Promise<Order> {\n return this.ctx.http.request<Order>({\n method: \"GET\",\n path: `${this.base()}/${orderId}`,\n auth,\n });\n }\n\n /** Patches an existing order (status, mixins, customAttributes, metadata). */\n async update(\n orderId: string,\n patch: SalesOrderPatch,\n auth: AuthContext,\n opts: UpdateSalesOrderOptions = {},\n ): Promise<Order> {\n const query: Record<string, string> = {};\n if (opts.recalculate === false) query.recalculate = \"false\";\n return this.ctx.http.request<Order>({\n method: \"PATCH\",\n path: `${this.base()}/${orderId}`,\n auth,\n ...(Object.keys(query).length > 0 ? { query } : {}),\n body: patch,\n });\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import { iterateAll } from './chunk-GWJ2U2CP.js';
2
+ import { resolveQuery } from './chunk-S5HEIQDK.js';
2
3
 
3
4
  // src/services/category.ts
4
5
  var ANON = { kind: "anonymous" };
@@ -28,6 +29,23 @@ var CategoryService = class {
28
29
  });
29
30
  return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
30
31
  }
32
+ /**
33
+ * Searches categories by a `q` filter — a raw Emporix DSL string or a built
34
+ * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Category does not
35
+ * support `compoundLogicalQuery`, so `or()` filters are rejected.
36
+ */
37
+ async search(query, params = {}, auth = ANON) {
38
+ const q = resolveQuery(query, { compoundLogicalQuery: false });
39
+ const pageNumber = params.pageNumber ?? 1;
40
+ const pageSize = params.pageSize ?? 50;
41
+ const items = await this.ctx.http.request({
42
+ method: "GET",
43
+ path: `/category/${this.ctx.tenant}/categories`,
44
+ query: { q, pageNumber, pageSize },
45
+ auth
46
+ });
47
+ return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
48
+ }
31
49
  /** Async-iterates every category across pages. */
32
50
  listAll(params = {}, auth = ANON) {
33
51
  const pageSize = params.pageSize ?? 50;
@@ -126,5 +144,5 @@ var CategoryService = class {
126
144
  };
127
145
 
128
146
  export { CategoryService };
129
- //# sourceMappingURL=chunk-GIZS4BY3.js.map
130
- //# sourceMappingURL=chunk-GIZS4BY3.js.map
147
+ //# sourceMappingURL=chunk-6XND7SYV.js.map
148
+ //# sourceMappingURL=chunk-6XND7SYV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/category.ts"],"names":[],"mappings":"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","file":"chunk-6XND7SYV.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 { 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 /**\n * Searches categories by a `q` filter — a raw Emporix DSL string or a built\n * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Category does not\n * support `compoundLogicalQuery`, so `or()` filters are rejected.\n */\n async search(\n query: QueryFor<\"CATEGORY\">,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Category>> {\n const q = resolveQuery(query, { compoundLogicalQuery: false });\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: { q, 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,5 @@
1
1
  import { iterateAll } from './chunk-GWJ2U2CP.js';
2
+ import { resolveQuery } from './chunk-S5HEIQDK.js';
2
3
 
3
4
  // src/services/product.ts
4
5
  var ANON = { kind: "anonymous" };
@@ -45,14 +46,19 @@ var ProductService = class {
45
46
  const pageSize = params.pageSize ?? 50;
46
47
  return iterateAll((pageNumber) => this.list({ pageNumber, pageSize }, auth));
47
48
  }
48
- /** Searches products by free-text query. */
49
+ /**
50
+ * Searches products by a `q` filter — a raw Emporix DSL string or a built
51
+ * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Product supports
52
+ * `compoundLogicalQuery`, so `or()` filters are allowed.
53
+ */
49
54
  async search(query, params = {}, auth = ANON) {
55
+ const q = resolveQuery(query, { compoundLogicalQuery: true });
50
56
  const pageNumber = params.pageNumber ?? 1;
51
57
  const pageSize = params.pageSize ?? 50;
52
58
  const items = await this.ctx.http.request({
53
59
  method: "GET",
54
60
  path: `/product/${this.ctx.tenant}/products`,
55
- query: { q: query, pageNumber, pageSize },
61
+ query: { q, pageNumber, pageSize },
56
62
  auth
57
63
  });
58
64
  return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
@@ -163,5 +169,5 @@ var ProductService = class {
163
169
  };
164
170
 
165
171
  export { ProductService };
166
- //# sourceMappingURL=chunk-5VTBLBXJ.js.map
167
- //# sourceMappingURL=chunk-5VTBLBXJ.js.map
172
+ //# sourceMappingURL=chunk-FZMP5NQZ.js.map
173
+ //# sourceMappingURL=chunk-FZMP5NQZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/services/product.ts"],"names":[],"mappings":"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","file":"chunk-FZMP5NQZ.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} from \"../generated/product\";\n\nconst ANON: AuthContext = { kind: \"anonymous\" };\n\n/** A product as returned by the Product service (all generated fields). */\nexport type Product = BasicProductWithId | BundleProductWithId | ParentVariantProductWithId;\n\n/** A single product media entry (generated; `ProductMedia` is the list type). */\nexport type Media = ProductMedia[number];\n\n/** Catalog reads. Default auth: anonymous; pass customer for personalized pricing. */\nexport class ProductService {\n 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 async searchByName(\n query: string,\n params: { pageNumber?: number; pageSize?: number } = {},\n auth: AuthContext = ANON,\n ): Promise<PaginatedItems<Product>> {\n const escaped = query.trim().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"]}
@@ -0,0 +1,14 @@
1
+ // src/core/query.ts
2
+ function resolveQuery(q, cap) {
3
+ if (typeof q === "string") return q;
4
+ if (q.usesCompound === true && !cap.compoundLogicalQuery) {
5
+ throw new Error(
6
+ "This filter uses or()/compoundLogicalQuery, which the target service does not support. Combine conditions with and() (space-separated AND) instead."
7
+ );
8
+ }
9
+ return q.toString();
10
+ }
11
+
12
+ export { resolveQuery };
13
+ //# sourceMappingURL=chunk-S5HEIQDK.js.map
14
+ //# sourceMappingURL=chunk-S5HEIQDK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/query.ts"],"names":[],"mappings":";AA4BO,SAAS,YAAA,CAAa,GAAa,GAAA,EAA8B;AACtE,EAAA,IAAI,OAAO,CAAA,KAAM,QAAA,EAAU,OAAO,CAAA;AAClC,EAAA,IAAI,CAAA,CAAE,YAAA,KAAiB,IAAA,IAAQ,CAAC,IAAI,oBAAA,EAAsB;AACxD,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AACA,EAAA,OAAO,EAAE,QAAA,EAAS;AACpB","file":"chunk-S5HEIQDK.js","sourcesContent":["/**\n * A built query filter — structurally matched, NOT imported, so the SDK stays\n * decoupled from `@viu/emporix-mixins` (which lists the SDK as an optional\n * peer; a real import would be circular). `@viu/emporix-mixins`' `MixinFilter<E>`\n * satisfies this shape.\n */\nexport interface BuiltQuery<E extends string = string> {\n toString(): string;\n readonly usesCompound?: boolean;\n /** Phantom entity tag — gates which service a filter may be passed to. */\n readonly __entity?: E;\n}\n\n/** A `q` value a service method accepts: a raw DSL string or a built filter for entity `E`. */\nexport type QueryFor<E extends string = string> = string | BuiltQuery<E>;\n\n/** Per-service capability flags consulted by {@link resolveQuery}. */\nexport interface QueryCapability {\n /** Whether the target service supports the `compoundLogicalQuery` (OR) operator. */\n compoundLogicalQuery: boolean;\n}\n\n/**\n * Normalizes a `string | BuiltQuery` into a `q` string and enforces the\n * `compoundLogicalQuery` capability gate: an `or()`-built filter passed to a\n * service that does not support it throws rather than silently producing a\n * query the backend cannot execute.\n */\nexport function resolveQuery(q: QueryFor, cap: QueryCapability): string {\n if (typeof q === \"string\") return q;\n if (q.usesCompound === true && !cap.compoundLogicalQuery) {\n throw new Error(\n \"This filter uses or()/compoundLogicalQuery, which the target service does not support. \" +\n \"Combine conditions with and() (space-separated AND) instead.\",\n );\n }\n return q.toString();\n}\n"]}
package/dist/index.cjs CHANGED
@@ -783,7 +783,7 @@ var HttpClient = class {
783
783
  };
784
784
 
785
785
  // package.json
786
- var version = "2.16.0";
786
+ var version = "2.18.0";
787
787
 
788
788
  // src/version.ts
789
789
  var SDK_VERSION = version;
@@ -1071,6 +1071,17 @@ async function* iterateAll(fetchPage, start = 1) {
1071
1071
  }
1072
1072
  }
1073
1073
 
1074
+ // src/core/query.ts
1075
+ function resolveQuery(q, cap) {
1076
+ if (typeof q === "string") return q;
1077
+ if (q.usesCompound === true && !cap.compoundLogicalQuery) {
1078
+ throw new Error(
1079
+ "This filter uses or()/compoundLogicalQuery, which the target service does not support. Combine conditions with and() (space-separated AND) instead."
1080
+ );
1081
+ }
1082
+ return q.toString();
1083
+ }
1084
+
1074
1085
  // src/services/product.ts
1075
1086
  var ANON = { kind: "anonymous" };
1076
1087
  var ProductService = class {
@@ -1116,14 +1127,19 @@ var ProductService = class {
1116
1127
  const pageSize = params.pageSize ?? 50;
1117
1128
  return iterateAll((pageNumber) => this.list({ pageNumber, pageSize }, auth2));
1118
1129
  }
1119
- /** Searches products by free-text query. */
1130
+ /**
1131
+ * Searches products by a `q` filter — a raw Emporix DSL string or a built
1132
+ * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Product supports
1133
+ * `compoundLogicalQuery`, so `or()` filters are allowed.
1134
+ */
1120
1135
  async search(query, params = {}, auth2 = ANON) {
1136
+ const q = resolveQuery(query, { compoundLogicalQuery: true });
1121
1137
  const pageNumber = params.pageNumber ?? 1;
1122
1138
  const pageSize = params.pageSize ?? 50;
1123
1139
  const items = await this.ctx.http.request({
1124
1140
  method: "GET",
1125
1141
  path: `/product/${this.ctx.tenant}/products`,
1126
- query: { q: query, pageNumber, pageSize },
1142
+ query: { q, pageNumber, pageSize },
1127
1143
  auth: auth2
1128
1144
  });
1129
1145
  return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
@@ -1261,6 +1277,23 @@ var CategoryService = class {
1261
1277
  });
1262
1278
  return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
1263
1279
  }
1280
+ /**
1281
+ * Searches categories by a `q` filter — a raw Emporix DSL string or a built
1282
+ * filter (e.g. `@viu/emporix-mixins`' `mixinQuery(...)`). Category does not
1283
+ * support `compoundLogicalQuery`, so `or()` filters are rejected.
1284
+ */
1285
+ async search(query, params = {}, auth2 = ANON2) {
1286
+ const q = resolveQuery(query, { compoundLogicalQuery: false });
1287
+ const pageNumber = params.pageNumber ?? 1;
1288
+ const pageSize = params.pageSize ?? 50;
1289
+ const items = await this.ctx.http.request({
1290
+ method: "GET",
1291
+ path: `/category/${this.ctx.tenant}/categories`,
1292
+ query: { q, pageNumber, pageSize },
1293
+ auth: auth2
1294
+ });
1295
+ return { items, pageNumber, pageSize, hasNextPage: items.length === pageSize };
1296
+ }
1264
1297
  /** Async-iterates every category across pages. */
1265
1298
  listAll(params = {}, auth2 = ANON2) {
1266
1299
  const pageSize = params.pageSize ?? 50;
@@ -2436,6 +2469,9 @@ var OrdersService = class {
2436
2469
  setIfDefined2(query, "status", opts.status);
2437
2470
  setIfDefined2(query, "legalEntityId", opts.legalEntityId);
2438
2471
  setIfDefined2(query, "siteCode", opts.siteCode);
2472
+ if (opts.q !== void 0) {
2473
+ setIfDefined2(query, "q", resolveQuery(opts.q, { compoundLogicalQuery: false }));
2474
+ }
2439
2475
  const headers = this.saasHeader(opts.saasToken);
2440
2476
  const items = await this.ctx.http.request({
2441
2477
  method: "GET",
@@ -4951,13 +4987,14 @@ var VendorService = class {
4951
4987
  auth: auth2
4952
4988
  });
4953
4989
  }
4954
- /** Search vendors (`POST /vendors/search`). */
4990
+ /** Search vendors (`POST /vendors/search`). A built filter in `q` is resolved to a string. */
4955
4991
  async searchVendors(query, auth2 = SERVICE24) {
4992
+ const body = query.q !== void 0 ? { ...query, q: resolveQuery(query.q, { compoundLogicalQuery: false }) } : query;
4956
4993
  return this.ctx.http.request({
4957
4994
  method: "POST",
4958
4995
  path: `${this.base()}/vendors/search`,
4959
4996
  auth: auth2,
4960
- body: query
4997
+ body
4961
4998
  });
4962
4999
  }
4963
5000
  /** Create a vendor. */
@@ -5141,9 +5178,10 @@ var CustomerAdminService = class {
5141
5178
  ...Object.keys(query).length ? { query } : {}
5142
5179
  });
5143
5180
  }
5144
- /** Search customers (`POST /customers/search`). */
5181
+ /** Search customers (`POST /customers/search`). A built filter in `q` is resolved to a string. */
5145
5182
  async searchCustomers(query, auth2 = SERVICE26) {
5146
- return this.ctx.http.request({ method: "POST", path: `${this.base()}/search`, auth: auth2, body: query });
5183
+ const body = query.q !== void 0 ? { ...query, q: resolveQuery(query.q, { compoundLogicalQuery: false }) } : query;
5184
+ return this.ctx.http.request({ method: "POST", path: `${this.base()}/search`, auth: auth2, body });
5147
5185
  }
5148
5186
  /** Retrieve a customer profile by number. */
5149
5187
  async getCustomer(customerNumber, auth2 = SERVICE26) {
@@ -5545,6 +5583,7 @@ exports.errorFromResponse = errorFromResponse;
5545
5583
  exports.iterateAll = iterateAll;
5546
5584
  exports.productIdFromYrn = productIdFromYrn;
5547
5585
  exports.redact = redact;
5586
+ exports.resolveQuery = resolveQuery;
5548
5587
  exports.resolveToken = resolveToken;
5549
5588
  exports.validateConfig = validateConfig;
5550
5589
  //# sourceMappingURL=index.cjs.map