@scayle/storefront-core 8.28.6 → 8.29.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,47 @@
1
1
  # @scayle/storefront-core
2
2
 
3
+ ## 8.29.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added an `includeProductSorting` boolean parameter to all category RPC method endpoints (`getRootCategories`, `getCategoryByPath`, `getCategoriesByPath`, `getCategoryById`, `getCategoryTree`).
8
+ This flag allows consumers to retrieve product sorting data, including the `smartSortingKey` and `customSortingKey` properties.
9
+ Developers can leverage this data to seamlessly apply smart sorting keys on the product listing page by passing those parameters when fetching products.
10
+ - Added an optional `hideEmptyCategories` parameter to `getCategoryTree`. When enabled, the product count for each category is retrieved, and categories (including their children) without any products are removed. Enabling this option may increase response times, especially for large category trees.
11
+
12
+ ```ts
13
+ import { rpcMethods } from '@scayle/storefront-core'
14
+
15
+ rpcMethods.getCategoryTree({ hideEmptyCategories: true }, rpcContext)
16
+ ```
17
+
18
+ or
19
+
20
+ ```ts
21
+ import { useCategoryTree } from '#storefront/composables'
22
+
23
+ const { data: rootCategories, status } = useCategoryTree(
24
+ {
25
+ params: {
26
+ hideEmptyCategories: true,
27
+ },
28
+ },
29
+ 'category-navigation-tree',
30
+ )
31
+ ```
32
+
33
+ ### Patch Changes
34
+
35
+ **Dependencies**
36
+
37
+ - Updated dependency to @scayle/storefront-api@18.9.0
38
+
39
+ ## 8.28.7
40
+
41
+ ### Patch Changes
42
+
43
+ - Fixed the return type of `flattenFieldSet` to reflect the flattened field set.
44
+
3
45
  ## 8.28.6
4
46
 
5
47
  ### Patch Changes
@@ -137,7 +179,7 @@ No changes in this release.
137
179
  - Deprecated attribute group specific utilities `getFlattenedVariantCrosssellings` and
138
180
  `getFlattenedMaterialComposition`.
139
181
 
140
- These functions should be a part of the Storefront application.
182
+ These functions should be a part of the Storefront Application.
141
183
 
142
184
  ## 8.22.0
143
185
 
@@ -6,7 +6,7 @@ import type { FieldSet, GroupSet } from '../types';
6
6
  *
7
7
  * @returns An array of flattened objects.
8
8
  */
9
- export declare const flattenFieldSet: (fieldSet: FieldSet) => FieldSet;
9
+ export declare const flattenFieldSet: (fieldSet: FieldSet) => FieldSet[number];
10
10
  interface FlattenedMaterialComposition {
11
11
  materialGroupName?: string;
12
12
  values: {
@@ -1,7 +1,8 @@
1
1
  import { flattenDeep } from "./arrayHelpers.mjs";
2
- import { flattenObject } from "./objectHelpers.mjs";
3
2
  export const flattenFieldSet = (fieldSet) => {
4
- return fieldSet.map(flattenObject);
3
+ return fieldSet.map((list) => {
4
+ return Object.assign({}, ...list);
5
+ });
5
6
  };
6
7
  export const getFlattenedMaterialComposition = (materialCompositions) => {
7
8
  return materialCompositions.map((materialComposition) => ({
@@ -7,6 +7,7 @@ import type { Category, ProductCategoryPropertyWith, RpcHandler } from '../../ty
7
7
  *
8
8
  * @param params The parameters for retrieving the root category.
9
9
  * @param params.children The number of child categories to include.
10
+ * @param params.includeProductSorting Whether to include the product sorting data.
10
11
  * @param params.includeHidden Whether to include hidden categories.
11
12
  * @param params.properties The properties to include for each category.
12
13
  * @param context The RPC context.
@@ -17,6 +18,7 @@ export declare const getRootCategories: RpcHandler<{
17
18
  children?: number;
18
19
  includeHidden?: true;
19
20
  properties?: ProductCategoryPropertyWith;
21
+ includeProductSorting?: boolean;
20
22
  }, {
21
23
  categories: Category[];
22
24
  activeNode: undefined;
@@ -30,6 +32,7 @@ export declare const getRootCategories: RpcHandler<{
30
32
  * @param params The parameters for retrieving a category by its path.
31
33
  * @param params.path The path of the category.
32
34
  * @param params.children The number of child categories to include, default to `1`.
35
+ * @param params.includeProductSorting Whether to include the product sorting data.
33
36
  * @param params.includeHidden Whether to include hidden categories.
34
37
  * @param params.properties The properties to include for each category.
35
38
  * @param context The RPC context.
@@ -41,6 +44,7 @@ export declare const getCategoryByPath: RpcHandler<{
41
44
  path: string;
42
45
  children?: number;
43
46
  includeHidden?: true;
47
+ includeProductSorting?: boolean;
44
48
  properties?: ProductCategoryPropertyWith;
45
49
  }, Category | undefined>;
46
50
  /**
@@ -52,6 +56,7 @@ export declare const getCategoryByPath: RpcHandler<{
52
56
  * @param params The parameters for retrieving multiple categories by path.
53
57
  * @param params.path The path of the categories.
54
58
  * @param params.children The number of children categories to include, default o `1`.
59
+ * @param params.includeProductSorting Whether to include the product sorting data.
55
60
  * @param params.includeHidden Whether to include hidden categories.
56
61
  * @param params.properties The properties to include for each category.
57
62
  * @param context The RPC context.
@@ -62,6 +67,7 @@ export declare const getCategoriesByPath: RpcHandler<{
62
67
  path: string;
63
68
  children?: number;
64
69
  includeHidden?: true;
70
+ includeProductSorting?: boolean;
65
71
  properties?: ProductCategoryPropertyWith;
66
72
  }, {
67
73
  categories: Category[] | Category;
@@ -76,6 +82,7 @@ export declare const getCategoriesByPath: RpcHandler<{
76
82
  * @param params The parameters for retrieving a category by ID.
77
83
  * @param params.id The category ID.
78
84
  * @param params.children The number of child categories to include.
85
+ * @param params.includeProductSorting Whether to include the product sorting data.
79
86
  * @param params.includeHidden Whether to include hidden categories.
80
87
  * @param params.properties The properties to include for each category.
81
88
  * @param context The RPC context.
@@ -86,10 +93,13 @@ export declare const getCategoryById: RpcHandler<{
86
93
  id: number;
87
94
  children?: number;
88
95
  includeHidden?: true;
96
+ includeProductSorting?: boolean;
89
97
  properties?: ProductCategoryPropertyWith;
90
98
  }, Category>;
91
99
  export declare const getCategoryTree: RpcHandler<{
92
100
  children?: number;
93
101
  includeHidden?: true;
102
+ includeProductSorting?: boolean;
94
103
  properties?: ProductCategoryPropertyWith;
104
+ hideEmptyCategories?: boolean;
95
105
  }, Category[]>;
@@ -1,11 +1,11 @@
1
1
  import { splitAndRemoveEmpty } from "../../helpers/index.mjs";
2
2
  import { mapSAPIFetchErrorToResponse } from "../../utils/sapi.mjs";
3
- export const getRootCategories = async function getRootCategories2({ children = 1, includeHidden, properties }, context) {
3
+ export const getRootCategories = async function getRootCategories2({ children = 1, includeHidden, properties, includeProductSorting }, context) {
4
4
  const { cached, sapiClient } = context;
5
5
  const result = await cached(sapiClient.categories.getRoots, {
6
6
  cacheKeyPrefix: "root-categories"
7
7
  })({
8
- with: { children, properties },
8
+ with: { children, properties, includeProductSorting },
9
9
  includeHidden
10
10
  });
11
11
  return {
@@ -13,7 +13,7 @@ export const getRootCategories = async function getRootCategories2({ children =
13
13
  activeNode: void 0
14
14
  };
15
15
  };
16
- export const getCategoryByPath = async function getCategoryByPath2({ path, children = 1, includeHidden, properties }, context) {
16
+ export const getCategoryByPath = async function getCategoryByPath2({ path, children = 1, includeHidden, properties, includeProductSorting }, context) {
17
17
  const { cached, sapiClient } = context;
18
18
  const sanitizedPath = splitAndRemoveEmpty(path);
19
19
  return await cached(
@@ -21,12 +21,18 @@ export const getCategoryByPath = async function getCategoryByPath2({ path, child
21
21
  {
22
22
  cacheKeyPrefix: `getByPath-category-${sanitizedPath}`
23
23
  }
24
- )(sanitizedPath, { with: { children, properties }, includeHidden });
24
+ )(sanitizedPath, {
25
+ with: { children, properties, includeProductSorting },
26
+ includeHidden
27
+ });
25
28
  };
26
- export const getCategoriesByPath = async function getCategoriesByPath2({ path, children = 1, includeHidden, properties }, context) {
29
+ export const getCategoriesByPath = async function getCategoriesByPath2({ path, children = 1, includeHidden, properties, includeProductSorting }, context) {
27
30
  const { cached, sapiClient } = context;
28
31
  if (path === "/") {
29
- return getRootCategories({ children, includeHidden, properties }, context);
32
+ return getRootCategories(
33
+ { children, includeHidden, properties, includeProductSorting },
34
+ context
35
+ );
30
36
  }
31
37
  const sanitizedPath = splitAndRemoveEmpty(path);
32
38
  const result = await cached(
@@ -35,7 +41,7 @@ export const getCategoriesByPath = async function getCategoriesByPath2({ path, c
35
41
  cacheKeyPrefix: `getByPath-categories-${sanitizedPath}`
36
42
  }
37
43
  )(sanitizedPath, {
38
- with: { children, properties },
44
+ with: { children, properties, includeProductSorting },
39
45
  includeHidden
40
46
  });
41
47
  if (result instanceof Response) {
@@ -49,7 +55,8 @@ export const getCategoriesByPath = async function getCategoriesByPath2({ path, c
49
55
  with: {
50
56
  children,
51
57
  parents: "all",
52
- properties
58
+ properties,
59
+ includeProductSorting
53
60
  },
54
61
  includeHidden
55
62
  });
@@ -72,7 +79,7 @@ export const getCategoriesByPath = async function getCategoriesByPath2({ path, c
72
79
  }
73
80
  return { categories: tree, activeNode: result };
74
81
  };
75
- export const getCategoryById = async function getCategoryById2({ id, children = 1, includeHidden, properties }, context) {
82
+ export const getCategoryById = async function getCategoryById2({ id, children = 1, includeHidden, properties, includeProductSorting }, context) {
76
83
  const { cached, sapiClient } = context;
77
84
  return await cached(
78
85
  mapSAPIFetchErrorToResponse(sapiClient.categories.getById),
@@ -83,17 +90,56 @@ export const getCategoryById = async function getCategoryById2({ id, children =
83
90
  with: {
84
91
  children,
85
92
  parents: "all",
86
- properties
93
+ properties,
94
+ includeProductSorting
87
95
  },
88
96
  includeHidden
89
97
  });
90
98
  };
91
- export const getCategoryTree = async function getCategoryTree2({ children, includeHidden, properties }, context) {
92
- const { cached, sapiClient } = context;
93
- return await cached(sapiClient.categories.getRoots, {
99
+ export const getCategoryTree = async function getCategoryTree2({
100
+ children,
101
+ includeHidden,
102
+ properties,
103
+ hideEmptyCategories,
104
+ includeProductSorting
105
+ }, context) {
106
+ const { cached, sapiClient, callRpc } = context;
107
+ const categoryTree = await cached(sapiClient.categories.getRoots, {
94
108
  cacheKeyPrefix: "root-categories"
95
109
  })({
96
- with: { children, properties },
110
+ with: { children, properties, includeProductSorting },
97
111
  includeHidden
98
112
  });
113
+ if (!hideEmptyCategories) {
114
+ return categoryTree;
115
+ }
116
+ const campaignKey = await callRpc?.("getCampaignKey");
117
+ const categoryProductCountTable = await cached(async () => {
118
+ const productCount = await sapiClient.filters.getValues("categoryids", {
119
+ campaignKey
120
+ });
121
+ return Object.fromEntries(
122
+ productCount.map(({ id, productCount: productCount2 }) => [id, productCount2])
123
+ );
124
+ }, {
125
+ cacheKey: `category-product-count-table-${campaignKey || "no-campaign"}`,
126
+ ttl: 3600
127
+ // 1 hour
128
+ })();
129
+ const removeEmptyCategories = (category) => {
130
+ if (category.children) {
131
+ category.children = category.children.filter(
132
+ (child) => categoryProductCountTable[child.id] > 0
133
+ );
134
+ category.children.forEach((child) => {
135
+ removeEmptyCategories(child);
136
+ });
137
+ }
138
+ };
139
+ categoryTree.forEach((category) => {
140
+ removeEmptyCategories(category);
141
+ });
142
+ return categoryTree.filter(
143
+ (category) => categoryProductCountTable[category.id] > 0
144
+ );
99
145
  };
@@ -36,7 +36,7 @@ export const getCheckoutToken = async function getCheckoutToken2(jwtPayload = {}
36
36
  carrier,
37
37
  basketId: context.basketKey,
38
38
  campaignKey
39
- }).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"8.28.6"}`).setProtectedHeader({ alg: "HS256", typ: "JWT" }).sign(secret);
39
+ }).setIssuedAt(now).setNotBefore(now).setExpirationTime("1h").setIssuer(`${"@scayle/storefront-core"}@${"8.29.0"}`).setProtectedHeader({ alg: "HS256", typ: "JWT" }).sign(secret);
40
40
  return {
41
41
  accessToken: refreshedAccessToken,
42
42
  checkoutJwt
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-core",
3
- "version": "8.28.6",
3
+ "version": "8.29.0",
4
4
  "description": "Collection of essential utilities to work with the Storefront API",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -64,7 +64,7 @@
64
64
  "fishery": "^2.2.3"
65
65
  },
66
66
  "dependencies": {
67
- "@scayle/storefront-api": "18.8.0",
67
+ "@scayle/storefront-api": "18.9.0",
68
68
  "@scayle/unstorage-scayle-kv-driver": "1.0.1",
69
69
  "crypto-js": "^4.2.0",
70
70
  "hookable": "^5.5.3",
@@ -75,21 +75,21 @@
75
75
  "utility-types": "^3.11.0"
76
76
  },
77
77
  "devDependencies": {
78
- "@scayle/eslint-config-storefront": "4.5.4",
78
+ "@scayle/eslint-config-storefront": "4.5.5",
79
79
  "@types/crypto-js": "4.2.2",
80
- "@types/node": "22.15.30",
80
+ "@types/node": "22.15.32",
81
81
  "@types/webpack-env": "1.18.8",
82
- "@vitest/coverage-v8": "3.2.2",
82
+ "@vitest/coverage-v8": "3.2.3",
83
83
  "dprint": "0.50.0",
84
- "eslint-formatter-gitlab": "6.0.0",
85
- "eslint": "9.28.0",
84
+ "eslint-formatter-gitlab": "6.0.1",
85
+ "eslint": "9.29.0",
86
86
  "fishery": "2.3.1",
87
87
  "publint": "0.2.12",
88
88
  "rimraf": "6.0.1",
89
89
  "typescript": "5.8.3",
90
90
  "unbuild": "3.5.0",
91
91
  "unstorage": "1.16.0",
92
- "vitest": "3.2.2"
92
+ "vitest": "3.2.3"
93
93
  },
94
94
  "volta": {
95
95
  "node": "22.16.0"