@scayle/storefront-product-detail 1.8.0 → 1.9.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,19 @@
1
1
  # @scayle/storefront-product-detail
2
2
 
3
+ ## 1.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - **\[Security\]** Use the patched Nuxt for build — `nuxt@3.21.8` (3.x) / `nuxt@4.4.8` (4.x), with matching `@nuxt/kit` and `@nuxt/schema` — which includes the fix for [CVE-2026-53721](https://nvd.nist.gov/vuln/detail/CVE-2026-53721) ([GHSA-mm7m-92g8-7m47](https://github.com/nuxt/nuxt/security/advisories/GHSA-mm7m-92g8-7m47)), a route-rule middleware bypass.
8
+
9
+ Compatibility with previously-supported Nuxt versions is unchanged and remains specified via each package's `peerDependencies` and the Nuxt compatibility flag. `@scayle/storefront-core` is released in lockstep with `@scayle/storefront-nuxt`.
10
+
11
+ ## 1.9.0
12
+
13
+ ### Minor Changes
14
+
15
+ - **[RPC]** Updated built-in RPC methods to use `GET` for safe, cacheable reads and `PUT`/`POST`/`DELETE` for mutations, enabling CDN and browser caching for public data fetches.
16
+
3
17
  ## 1.8.0
4
18
 
5
19
  ### Minor Changes
@@ -34,7 +48,6 @@
34
48
 
35
49
  - Introduced support for Nuxt 4 while maintaining full backward compatibility with Nuxt 3.
36
50
  This enables consumers to migrate to Nuxt 4 ahead of the Nuxt 3 end of support on 31 Jan 2026.
37
-
38
51
  - **Version Requirements:**
39
52
  - Nuxt 3: `v3.13.0+`
40
53
  - Nuxt 4: `v4.2.0+`
@@ -139,7 +152,6 @@
139
152
  - Enhanced data fetching composables to support reactive keys, aligning with [Nuxt 3.17 data fetching improvements](https://nuxt.com/blog/v3-17#data-fetching-improvements).
140
153
 
141
154
  You can now pass a `ref`, `computed`, or getter function as the `key` parameter to `useRpc` and derived composables (like `useProducts`, `useBrand` etc.). When the reactive key changes, the data will automatically re-fetch.
142
-
143
155
  - **Before:** Keys were static strings.
144
156
  - **After:** Keys can be reactive, enabling dynamic caching and automatic updates.
145
157
 
@@ -269,9 +281,8 @@
269
281
  images: images.value,
270
282
  productId: product.value?.id || 0,
271
283
  color: formatColors(colors.value),
272
- variesBy: variants.value.length > 1
273
- ? ['https://schema.org/size']
274
- : undefined,
284
+ variesBy:
285
+ variants.value.length > 1 ? ['https://schema.org/size'] : undefined,
275
286
  })
276
287
 
277
288
  // Will become
@@ -291,9 +302,8 @@
291
302
  }),
292
303
  productId: product.value?.id || 0,
293
304
  color: formatColors(colors.value),
294
- variesBy: variants.value.length > 1
295
- ? ['https://schema.org/size']
296
- : undefined,
305
+ variesBy:
306
+ variants.value.length > 1 ? ['https://schema.org/size'] : undefined,
297
307
  })
298
308
  ```
299
309
 
@@ -351,7 +361,6 @@
351
361
 
352
362
  - This release introduces the `@scayle/storefront-product-detail` package, decoupling composables and utilities of the Product Detail Page (PDP) functionality from the SCAYLE Storefront Boilerplate for enhanced modularity and integration.
353
363
  This separation empowers developers with greater control over PDP updates and simplifies its integration into existing Storefront-based projects.
354
-
355
364
  - [Discover more about the Product Detail Page for SCAYLE Storefront in our SCAYLE Resource Center.](https://scayle.dev/en/core-documentation/storefront-guide/storefront-application/features/product-detail-page)
356
365
 
357
366
  - This release requires `@scayle/storefront-nuxt@8.x` or higher. Support for `@scayle/storefront-nuxt@7.x` has been discontinued. Please update your dependencies accordingly. The `peerDependency` range has been updated to `^8.0.0`.
@@ -359,7 +368,6 @@
359
368
  ### Minor Changes
360
369
 
361
370
  - Compared to the original implementation within the SCAYLE Storefront Boilerplate, the `@scayle/storefront-product-detail` package has received some improvements and refactoring:
362
-
363
371
  - Introduced the `getCombineWithProductIds` function within the product utility, enabling efficient retrieval of product combinations within product listings.
364
372
  - Added the `getFilteredAttributeGroups` function to the attribute utility, simplifying the retrieval and formatting of filtered product attributes for display in listings.
365
373
  - Introduced the `useProductSeoData` composable, automating the generation of SEO-critical metadata for product pages. This includes dynamically generated canonical links, robots meta tags, product titles, and structured data (`JSON-LD`) for product details and breadcrumbs, enhancing search engine visibility.
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@scayle/storefront-product-detail",
3
3
  "configKey": "product-detail",
4
- "version": "1.8.0",
4
+ "version": "1.9.1",
5
5
  "compatibility": {
6
6
  "bridge": false,
7
7
  "nuxt": ">=3.13"
package/dist/module.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defineNuxtModule, createResolver, addImportsDir } from '@nuxt/kit';
2
2
 
3
3
  const PACKAGE_NAME = "@scayle/storefront-product-detail";
4
- const PACKAGE_VERSION = "1.8.0";
4
+ const PACKAGE_VERSION = "1.9.1";
5
5
  const module$1 = defineNuxtModule({
6
6
  meta: {
7
7
  name: PACKAGE_NAME,
@@ -3,10 +3,5 @@ export function useAllShopProductsForId({
3
3
  params,
4
4
  options
5
5
  } = {}, key = "useAllShopProductsById") {
6
- return useRpc(
7
- "getAllShopProductsForId",
8
- key,
9
- params,
10
- options
11
- );
6
+ return useRpc("getAllShopProductsForId", key, params, options);
12
7
  }
@@ -65,9 +65,7 @@ export function useRecentlyViewedProducts(options) {
65
65
  products.value.push(...result);
66
66
  }
67
67
  const sortOrderMap = new Map(
68
- recentlyViewedProductsIds.value.map(
69
- (id, index) => [id, index]
70
- )
68
+ recentlyViewedProductsIds.value.map((id, index) => [id, index])
71
69
  );
72
70
  products.value = products.value.sort((a, b) => {
73
71
  const aIndex = sortOrderMap.get(a.id);
@@ -1,33 +1,36 @@
1
1
  import { defineRpcHandler } from "@scayle/storefront-nuxt";
2
- export const getAllShopProductsForId = defineRpcHandler(async (options, context) => {
3
- const { runtimeConfiguration, sapiClient, log, cached } = context;
4
- const fetchAllProducts = async () => {
5
- const products = [];
6
- const shops = Object.values(
7
- runtimeConfiguration.storefront.shops
8
- );
9
- await Promise.all(
10
- shops.map(async (shop) => {
11
- try {
12
- const product = await sapiClient.clone({ shopId: shop.shopId }).products.getById(options.id, {
13
- with: options.with
14
- });
15
- products.push({
16
- locale: shop.locale,
17
- path: Array.isArray(shop.path) ? shop.path[0] : shop.path,
18
- product
19
- });
20
- } catch (error) {
21
- log.error(error instanceof Error ? error : String(error));
22
- }
23
- })
24
- );
25
- return products;
26
- };
27
- const result = await cached(fetchAllProducts, {
28
- cacheKeyPrefix: `getAllShopProductsForId-${options.id}`,
29
- ttl: 12 * 60 * 60
30
- // 12 hours
31
- })();
32
- return result;
33
- });
2
+ export const getAllShopProductsForId = defineRpcHandler(
3
+ async (options, context) => {
4
+ const { runtimeConfiguration, sapiClient, log, cached } = context;
5
+ const fetchAllProducts = async () => {
6
+ const products = [];
7
+ const shops = Object.values(
8
+ runtimeConfiguration.storefront.shops
9
+ );
10
+ await Promise.all(
11
+ shops.map(async (shop) => {
12
+ try {
13
+ const product = await sapiClient.clone({ shopId: shop.shopId }).products.getById(options.id, {
14
+ with: options.with
15
+ });
16
+ products.push({
17
+ locale: shop.locale,
18
+ path: Array.isArray(shop.path) ? shop.path[0] : shop.path,
19
+ product
20
+ });
21
+ } catch (error) {
22
+ log.error(error instanceof Error ? error : String(error));
23
+ }
24
+ })
25
+ );
26
+ return products;
27
+ };
28
+ const result = await cached(fetchAllProducts, {
29
+ cacheKeyPrefix: `getAllShopProductsForId-${options.id}`,
30
+ ttl: 12 * 60 * 60
31
+ // 12 hours
32
+ })();
33
+ return result;
34
+ },
35
+ { method: "GET" }
36
+ );
@@ -2,36 +2,37 @@ import {
2
2
  ErrorResponse,
3
3
  FetchError,
4
4
  MINUTE,
5
- MIN_WITH_PARAMS_PRODUCT
5
+ MIN_WITH_PARAMS_PRODUCT,
6
+ defineRpcHandler
6
7
  } from "@scayle/storefront-nuxt";
7
- export const getSimilarProducts = async function getProductsByReferenceKeys(options, context) {
8
- const { sapiClient, cached, withParams } = context;
9
- const campaignKey = await context.callRpc?.("getCampaignKey");
10
- const { productId, with: withParameter, ...rest } = options;
11
- const fetch = async (productId2, params) => {
12
- try {
13
- return sapiClient.recommendations.getSimilarProducts(productId2, params);
14
- } catch (e) {
15
- if (e instanceof FetchError) {
16
- const response = e.response;
17
- return new ErrorResponse(
18
- response.status,
19
- response.statusText,
20
- "Failed to fetch similar products"
21
- );
8
+ export const getSimilarProducts = defineRpcHandler(
9
+ async (options, context) => {
10
+ const { sapiClient, cached, withParams } = context;
11
+ const campaignKey = await context.callRpc?.("getCampaignKey");
12
+ const { productId, with: withParameter, ...rest } = options;
13
+ const fetch = async (productId2, params) => {
14
+ try {
15
+ return sapiClient.recommendations.getSimilarProducts(productId2, params);
16
+ } catch (e) {
17
+ if (e instanceof FetchError) {
18
+ const response = e.response;
19
+ return new ErrorResponse(
20
+ response.status,
21
+ response.statusText,
22
+ "Failed to fetch similar products"
23
+ );
24
+ }
25
+ throw e;
22
26
  }
23
- throw e;
24
- }
25
- };
26
- return await cached(
27
- fetch,
28
- {
27
+ };
28
+ return await cached(fetch, {
29
29
  cacheKeyPrefix: "getSimilarProducts-products",
30
30
  ttl: 5 * MINUTE
31
- }
32
- )(productId, {
33
- with: withParameter ?? withParams?.product ?? MIN_WITH_PARAMS_PRODUCT,
34
- campaignKey,
35
- ...rest
36
- });
37
- };
31
+ })(productId, {
32
+ with: withParameter ?? withParams?.product ?? MIN_WITH_PARAMS_PRODUCT,
33
+ campaignKey,
34
+ ...rest
35
+ });
36
+ },
37
+ { method: "GET" }
38
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scayle/storefront-product-detail",
3
- "version": "1.8.0",
3
+ "version": "1.9.1",
4
4
  "description": "Collection of essential composables and utilities to work with product detail",
5
5
  "author": "SCAYLE Commerce Engine",
6
6
  "license": "MIT",
@@ -24,6 +24,9 @@
24
24
  "CHANGELOG.md",
25
25
  "dist"
26
26
  ],
27
+ "engines": {
28
+ "node": ">= 22.0.0"
29
+ },
27
30
  "imports": {
28
31
  "#storefront-product-detail": "./dist/runtime/index.js"
29
32
  },
@@ -37,29 +40,28 @@
37
40
  },
38
41
  "devDependencies": {
39
42
  "@arethetypeswrong/cli": "0.18.2",
40
- "@nuxt/kit": "3.20.0",
43
+ "@nuxt/kit": "^3.21.7",
41
44
  "@nuxt/module-builder": "1.0.2",
42
- "@nuxt/schema": "3.20.0",
43
- "@nuxt/test-utils": "3.23.0",
44
- "@types/node": "22.19.7",
45
- "@vitest/coverage-v8": "4.0.17",
46
- "@vueuse/core": "14.1.0",
47
- "dprint": "0.51.1",
48
- "eslint-formatter-gitlab": "7.0.1",
49
- "eslint": "9.39.2",
50
- "happy-dom": "20.3.4",
51
- "nuxt": "3.20.0",
52
- "publint": "0.3.16",
45
+ "@nuxt/schema": "^3.21.7",
46
+ "@nuxt/test-utils": "4.0.0",
47
+ "@scayle/eslint-config-storefront": "^4.8.0",
48
+ "@types/node": "24.12.2",
49
+ "@vitest/coverage-v8": "4.1.9",
50
+ "@vueuse/core": "14.3.0",
51
+ "eslint-formatter-gitlab": "7.1.0",
52
+ "eslint": "10.5.0",
53
+ "happy-dom": "20.10.6",
54
+ "nuxt": "^3.21.7",
55
+ "publint": "0.3.21",
53
56
  "schema-dts": "1.1.5",
54
- "typescript": "5.9.3",
57
+ "typescript": "6.0.3",
55
58
  "unbuild": "3.6.1",
56
- "vitest": "4.0.17",
59
+ "vitest": "4.1.9",
57
60
  "vue-router": "4.6.4",
58
- "vue-tsc": "3.2.2",
59
- "vue": "3.5.26",
60
- "@scayle/vitest-config-storefront": "1.0.0",
61
- "@scayle/eslint-config-storefront": "4.7.21",
62
- "@scayle/storefront-nuxt": "8.59.0"
61
+ "vue-tsc": "3.3.5",
62
+ "vue": "3.5.38",
63
+ "@scayle/storefront-nuxt": "8.62.1",
64
+ "@scayle/vitest-config-storefront": "1.0.0"
63
65
  },
64
66
  "scripts": {
65
67
  "build": "nuxt-module-build build",
@@ -69,11 +71,6 @@
69
71
  "lint": "eslint .",
70
72
  "lint:ci": "eslint . --format gitlab",
71
73
  "lint:fix": "eslint . --fix",
72
- "format": "dprint check",
73
- "format:fix": "dprint fmt",
74
- "test": "vitest --run",
75
- "test:watch": "vitest",
76
- "test:ci": "vitest --run --coverage --reporter=default --reporter=junit --outputFile=./coverage/junit.xml",
77
74
  "typecheck": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit",
78
75
  "package:lint": "publint",
79
76
  "verify-packaging": "attw --pack . --profile esm-only"