@akinon/next 1.99.0-snapshot-ZERO-3640-20250919140935 → 1.100.0-rc.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +1337 -51
  2. package/__tests__/next-config.test.ts +1 -10
  3. package/__tests__/redirect.test.ts +319 -0
  4. package/api/cache.ts +41 -5
  5. package/api/form.ts +84 -0
  6. package/api/image-proxy.ts +75 -0
  7. package/api/similar-product-list.ts +84 -0
  8. package/api/similar-products.ts +120 -0
  9. package/bin/pz-generate-routes.js +105 -0
  10. package/bin/pz-prebuild.js +1 -0
  11. package/bin/pz-predev.js +1 -0
  12. package/components/accordion.tsx +20 -5
  13. package/components/file-input.tsx +65 -3
  14. package/components/input.tsx +2 -0
  15. package/components/link.tsx +16 -12
  16. package/components/modal.tsx +32 -16
  17. package/components/plugin-module.tsx +32 -4
  18. package/data/client/checkout.ts +4 -2
  19. package/data/server/basket.ts +72 -0
  20. package/data/server/category.ts +48 -26
  21. package/data/server/flatpage.ts +20 -13
  22. package/data/server/form.ts +4 -1
  23. package/data/server/landingpage.ts +20 -13
  24. package/data/server/list.ts +25 -14
  25. package/data/server/menu.ts +4 -1
  26. package/data/server/product.ts +68 -40
  27. package/data/server/seo.ts +4 -1
  28. package/data/server/special-page.ts +18 -13
  29. package/data/server/widget.ts +4 -1
  30. package/data/urls.ts +6 -2
  31. package/hocs/server/with-segment-defaults.tsx +5 -2
  32. package/hooks/use-localization.ts +2 -3
  33. package/jest.config.js +7 -1
  34. package/lib/cache-handler.mjs +365 -87
  35. package/lib/cache.ts +254 -25
  36. package/middlewares/complete-gpay.ts +2 -1
  37. package/middlewares/complete-masterpass.ts +2 -1
  38. package/middlewares/default.ts +50 -13
  39. package/middlewares/locale.ts +9 -1
  40. package/middlewares/pretty-url.ts +21 -6
  41. package/middlewares/redirection-payment.ts +2 -1
  42. package/middlewares/saved-card-redirection.ts +2 -1
  43. package/middlewares/three-d-redirection.ts +2 -2
  44. package/middlewares/url-redirection.ts +8 -14
  45. package/package.json +4 -3
  46. package/plugins.d.ts +8 -5
  47. package/plugins.js +3 -1
  48. package/redux/middlewares/checkout.ts +5 -1
  49. package/types/commerce/order.ts +1 -0
  50. package/types/index.ts +35 -1
  51. package/utils/app-fetch.ts +7 -2
  52. package/utils/redirect-ignore.ts +35 -0
  53. package/utils/redirect.ts +31 -6
  54. package/with-pz-config.js +1 -5
@@ -5,7 +5,6 @@ import { category, product } from '../urls';
5
5
  import { Cache, CacheKey } from '../../lib/cache';
6
6
  import { parse } from 'lossless-json';
7
7
  import logger from '../../utils/log';
8
- import { headers as nHeaders } from 'next/headers';
9
8
  import { ServerVariables } from '../../utils/server-variables';
10
9
 
11
10
  function getCategoryDataHandler(
@@ -18,19 +17,30 @@ function getCategoryDataHandler(
18
17
  return async function () {
19
18
  const params = generateCommerceSearchParams(searchParams);
20
19
 
21
- const rawData = await appFetch<string>({
22
- url: `${category.getCategoryByPk(pk)}${params ? params : ''}`,
23
- locale,
24
- currency,
25
- init: {
26
- headers: {
27
- Accept: 'application/json',
28
- 'Content-Type': 'application/json',
29
- ...(headers ?? {})
30
- }
31
- },
32
- responseType: FetchResponseType.TEXT
33
- });
20
+ let rawData: string;
21
+
22
+ try {
23
+ rawData = await appFetch<string>({
24
+ url: `${category.getCategoryByPk(pk)}${params ? params : ''}`,
25
+ locale,
26
+ currency,
27
+ init: {
28
+ headers: {
29
+ Accept: 'application/json',
30
+ 'Content-Type': 'application/json',
31
+ ...(headers ?? {})
32
+ }
33
+ },
34
+ responseType: FetchResponseType.TEXT
35
+ });
36
+ } catch (error) {
37
+ logger.error('Failed to fetch category data', {
38
+ handler: 'getCategoryDataHandler',
39
+ pk,
40
+ error: error.message
41
+ });
42
+ return null;
43
+ }
34
44
 
35
45
  let data: GetCategoryResponse;
36
46
 
@@ -64,17 +74,27 @@ function getCategoryDataHandler(
64
74
  return { data, breadcrumbData: undefined };
65
75
  }
66
76
 
67
- const breadcrumbData = await appFetch<any>({
68
- url: product.breadcrumbUrl(menuItemModel),
69
- locale,
70
- currency,
71
- init: {
72
- headers: {
73
- Accept: 'application/json',
74
- 'Content-Type': 'application/json'
77
+ let breadcrumbData: { menu?: unknown } = {};
78
+
79
+ try {
80
+ breadcrumbData = await appFetch<{ menu?: unknown }>({
81
+ url: product.breadcrumbUrl(menuItemModel),
82
+ locale,
83
+ currency,
84
+ init: {
85
+ headers: {
86
+ Accept: 'application/json',
87
+ 'Content-Type': 'application/json'
88
+ }
75
89
  }
76
- }
77
- });
90
+ });
91
+ } catch (error) {
92
+ logger.warn('Failed to fetch breadcrumb data', {
93
+ handler: 'getCategoryDataHandler',
94
+ pk,
95
+ error: error.message
96
+ });
97
+ }
78
98
 
79
99
  return { data, breadcrumbData: breadcrumbData?.menu };
80
100
  };
@@ -98,7 +118,8 @@ export const getCategoryData = ({
98
118
  locale,
99
119
  getCategoryDataHandler(pk, locale, currency, searchParams, headers),
100
120
  {
101
- expire: 300
121
+ expire: 300,
122
+ compressed: true
102
123
  }
103
124
  );
104
125
  };
@@ -158,7 +179,8 @@ export const getCategoryBySlugData = async ({
158
179
  locale,
159
180
  getCategoryBySlugDataHandler(slug, locale, currency),
160
181
  {
161
- expire: 300
182
+ expire: 300,
183
+ compressed: true
162
184
  }
163
185
  );
164
186
  };
@@ -11,20 +11,24 @@ const getFlatPageDataHandler = (
11
11
  headers?: Record<string, string>
12
12
  ) => {
13
13
  return async function () {
14
- const data = await appFetch<FlatPage>({
15
- url: flatpage.getFlatPageByPk(pk),
16
- locale,
17
- currency,
18
- init: {
19
- headers: {
20
- Accept: 'application/json',
21
- 'Content-Type': 'application/json',
22
- ...(headers ?? {})
14
+ try {
15
+ const data = await appFetch<FlatPage>({
16
+ url: flatpage.getFlatPageByPk(pk),
17
+ locale,
18
+ currency,
19
+ init: {
20
+ headers: {
21
+ Accept: 'application/json',
22
+ 'Content-Type': 'application/json',
23
+ ...(headers ?? {})
24
+ }
23
25
  }
24
- }
25
- });
26
+ });
26
27
 
27
- return data;
28
+ return data;
29
+ } catch (error) {
30
+ return null;
31
+ }
28
32
  };
29
33
  };
30
34
 
@@ -42,6 +46,9 @@ export const getFlatPageData = ({
42
46
  return Cache.wrap(
43
47
  CacheKey.FlatPage(pk),
44
48
  locale,
45
- getFlatPageDataHandler(pk, locale, currency, headers)
49
+ getFlatPageDataHandler(pk, locale, currency, headers),
50
+ {
51
+ compressed: true
52
+ }
46
53
  );
47
54
  };
@@ -43,6 +43,9 @@ export const getFormData = ({
43
43
  return Cache.wrap(
44
44
  CacheKey.Form(pk),
45
45
  locale,
46
- getFormDataHandler(pk, locale, currency, headers)
46
+ getFormDataHandler(pk, locale, currency, headers),
47
+ {
48
+ compressed: true
49
+ }
47
50
  );
48
51
  };
@@ -11,20 +11,24 @@ const getLandingPageHandler = (
11
11
  headers?: Record<string, string>
12
12
  ) => {
13
13
  return async function () {
14
- const data = await appFetch<LandingPage>({
15
- url: landingpage.getLandingPageByPk(pk),
16
- locale,
17
- currency,
18
- init: {
19
- headers: {
20
- Accept: 'application/json',
21
- 'Content-Type': 'application/json',
22
- ...(headers ?? {})
14
+ try {
15
+ const data = await appFetch<LandingPage>({
16
+ url: landingpage.getLandingPageByPk(pk),
17
+ locale,
18
+ currency,
19
+ init: {
20
+ headers: {
21
+ Accept: 'application/json',
22
+ 'Content-Type': 'application/json',
23
+ ...(headers ?? {})
24
+ }
23
25
  }
24
- }
25
- });
26
+ });
26
27
 
27
- return data;
28
+ return data;
29
+ } catch (error) {
30
+ return null;
31
+ }
28
32
  };
29
33
  };
30
34
 
@@ -42,6 +46,9 @@ export const getLandingPageData = ({
42
46
  return Cache.wrap(
43
47
  CacheKey.LandingPage(pk),
44
48
  locale,
45
- getLandingPageHandler(pk, locale, currency, headers)
49
+ getLandingPageHandler(pk, locale, currency, headers),
50
+ {
51
+ compressed: true
52
+ }
46
53
  );
47
54
  };
@@ -16,19 +16,29 @@ const getListDataHandler = (
16
16
  return async function () {
17
17
  const params = generateCommerceSearchParams(searchParams);
18
18
 
19
- const rawData = await appFetch<string>({
20
- url: `${category.list}${params}`,
21
- locale,
22
- currency,
23
- init: {
24
- headers: {
25
- Accept: 'application/json',
26
- 'Content-Type': 'application/json',
27
- ...(headers ?? {})
28
- }
29
- },
30
- responseType: FetchResponseType.TEXT
31
- });
19
+ let rawData: string;
20
+
21
+ try {
22
+ rawData = await appFetch<string>({
23
+ url: `${category.list}${params}`,
24
+ locale,
25
+ currency,
26
+ init: {
27
+ headers: {
28
+ Accept: 'application/json',
29
+ 'Content-Type': 'application/json',
30
+ ...(headers ?? {})
31
+ }
32
+ },
33
+ responseType: FetchResponseType.TEXT
34
+ });
35
+ } catch (error) {
36
+ logger.error('Failed to fetch list data', {
37
+ handler: 'getListDataHandler',
38
+ error: error.message
39
+ });
40
+ return null;
41
+ }
32
42
 
33
43
  let data: GetCategoryResponse;
34
44
 
@@ -71,7 +81,8 @@ export const getListData = async ({
71
81
  locale,
72
82
  getListDataHandler(locale, currency, searchParams, headers),
73
83
  {
74
- expire: 300
84
+ expire: 300,
85
+ compressed: true
75
86
  }
76
87
  );
77
88
  };
@@ -48,6 +48,9 @@ export const getMenu = async (params?: MenuHandlerParams) => {
48
48
  return Cache.wrap(
49
49
  CacheKey.Menu(params?.depth ?? DEFAULT_DEPTH, params?.parent),
50
50
  params?.locale ?? ServerVariables.locale,
51
- getMenuHandler(params)
51
+ getMenuHandler(params),
52
+ {
53
+ compressed: true
54
+ }
52
55
  );
53
56
  };
@@ -35,57 +35,84 @@ const getProductDataHandler = ({
35
35
  .join('&');
36
36
  }
37
37
 
38
- const data = await appFetch<ProductResult>({
39
- url,
40
- locale,
41
- currency,
42
- init: {
43
- headers: {
44
- Accept: 'application/json',
45
- 'Content-Type': 'application/json',
46
- ...(headers ?? {})
38
+ let data: ProductResult;
39
+
40
+ try {
41
+ data = await appFetch<ProductResult>({
42
+ url,
43
+ locale,
44
+ currency,
45
+ init: {
46
+ headers: {
47
+ Accept: 'application/json',
48
+ 'Content-Type': 'application/json',
49
+ ...(headers ?? {})
50
+ }
47
51
  }
48
- }
49
- });
52
+ });
53
+ } catch (error) {
54
+ logger.error('Failed to fetch product data', {
55
+ handler: 'getProductDataHandler',
56
+ pk,
57
+ error: error.message,
58
+ url
59
+ });
60
+ return null;
61
+ }
50
62
 
51
63
  const categoryUrl = product.categoryUrl(data.product.pk);
52
64
 
53
- const productCategoryData = await appFetch<ProductCategoryResult>({
54
- url: categoryUrl,
55
- locale,
56
- currency,
57
- init: {
58
- headers: {
59
- Accept: 'application/json',
60
- 'Content-Type': 'application/json'
65
+ let productCategoryData: ProductCategoryResult;
66
+ let breadcrumbData: { menu?: unknown } = {};
67
+
68
+ try {
69
+ productCategoryData = await appFetch<ProductCategoryResult>({
70
+ url: categoryUrl,
71
+ locale,
72
+ currency,
73
+ init: {
74
+ headers: {
75
+ Accept: 'application/json',
76
+ 'Content-Type': 'application/json'
77
+ }
61
78
  }
79
+ });
80
+
81
+ const menuItemModel = productCategoryData?.results[0]?.menuitemmodel;
82
+
83
+ if (!menuItemModel) {
84
+ logger.warn(
85
+ 'menuItemModel is undefined, skipping breadcrumbData fetch',
86
+ {
87
+ handler: 'getProductDataHandler',
88
+ pk
89
+ }
90
+ );
91
+ return { data, breadcrumbData: undefined };
62
92
  }
63
- });
64
93
 
65
- const menuItemModel = productCategoryData?.results[0]?.menuitemmodel;
94
+ const breadcrumbUrl = product.breadcrumbUrl(menuItemModel);
66
95
 
67
- if (!menuItemModel) {
68
- logger.warn('menuItemModel is undefined, skipping breadcrumbData fetch', {
96
+ breadcrumbData = await appFetch<{ menu?: unknown }>({
97
+ url: breadcrumbUrl,
98
+ locale,
99
+ currency,
100
+ init: {
101
+ headers: {
102
+ Accept: 'application/json',
103
+ 'Content-Type': 'application/json'
104
+ }
105
+ }
106
+ });
107
+ } catch (error) {
108
+ logger.warn('Failed to fetch breadcrumb data', {
69
109
  handler: 'getProductDataHandler',
70
- pk
110
+ pk,
111
+ error: error.message
71
112
  });
72
- return { data, breadcrumbData: undefined };
113
+ // Continue without breadcrumb data
73
114
  }
74
115
 
75
- const breadcrumbUrl = product.breadcrumbUrl(menuItemModel);
76
-
77
- const breadcrumbData = await appFetch<any>({
78
- url: breadcrumbUrl,
79
- locale,
80
- currency,
81
- init: {
82
- headers: {
83
- Accept: 'application/json',
84
- 'Content-Type': 'application/json'
85
- }
86
- }
87
- });
88
-
89
116
  return {
90
117
  data,
91
118
  breadcrumbData: breadcrumbData?.menu
@@ -116,7 +143,8 @@ export const getProductData = async ({
116
143
  headers
117
144
  }),
118
145
  {
119
- expire: 300
146
+ expire: 300,
147
+ compressed: true
120
148
  }
121
149
  );
122
150
  };
@@ -43,6 +43,9 @@ export const getSeoData = async (
43
43
  return Cache.wrap(
44
44
  CacheKey.Seo(url),
45
45
  locale,
46
- getSeoDataHandler({ url, locale, currency, headers })
46
+ getSeoDataHandler({ url, locale, currency, headers }),
47
+ {
48
+ compressed: true
49
+ }
47
50
  );
48
51
  };
@@ -15,20 +15,24 @@ const getSpecialPageDataHandler = (
15
15
  return async function () {
16
16
  const params = generateCommerceSearchParams(searchParams);
17
17
 
18
- const data: GetCategoryResponse = await appFetch({
19
- url: `${category.getSpecialPageByPk(pk)}${params}`,
20
- locale,
21
- currency,
22
- init: {
23
- headers: {
24
- Accept: 'application/json',
25
- 'Content-Type': 'application/json',
26
- ...(headers ?? {})
18
+ try {
19
+ const data: GetCategoryResponse = await appFetch({
20
+ url: `${category.getSpecialPageByPk(pk)}${params}`,
21
+ locale,
22
+ currency,
23
+ init: {
24
+ headers: {
25
+ Accept: 'application/json',
26
+ 'Content-Type': 'application/json',
27
+ ...(headers ?? {})
28
+ }
27
29
  }
28
- }
29
- });
30
+ });
30
31
 
31
- return data;
32
+ return data;
33
+ } catch (error) {
34
+ return null;
35
+ }
32
36
  };
33
37
  };
34
38
 
@@ -50,7 +54,8 @@ export const getSpecialPageData = async ({
50
54
  locale,
51
55
  getSpecialPageDataHandler(pk, locale, currency, searchParams, headers),
52
56
  {
53
- expire: 300
57
+ expire: 300,
58
+ compressed: true
54
59
  }
55
60
  );
56
61
  };
@@ -44,6 +44,9 @@ export const getWidgetData = async <T>({
44
44
  CacheKey.Widget(slug),
45
45
  locale,
46
46
  getWidgetDataHandler(slug, locale, currency, headers),
47
- cacheOptions
47
+ {
48
+ compressed: true,
49
+ ...cacheOptions
50
+ }
48
51
  );
49
52
  };
package/data/urls.ts CHANGED
@@ -183,7 +183,11 @@ export const product = {
183
183
  breadcrumbUrl: (menuitemmodel: string) =>
184
184
  `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`,
185
185
  bundleProduct: (productPk: string, queryString: string) =>
186
- `/bundle-product/${productPk}/?${queryString}`
186
+ `/bundle-product/${productPk}/?${queryString}`,
187
+ similarProducts: (params?: string) =>
188
+ `/similar-products${params ? `?${params}` : ''}`,
189
+ similarProductsList: (params?: string) =>
190
+ `/similar-product-list${params ? `?${params}` : ''}`
187
191
  };
188
192
 
189
193
  export const wishlist = {
@@ -247,7 +251,7 @@ export const widgets = {
247
251
  };
248
252
 
249
253
  export const form = {
250
- getForm: (pk: number) => `/forms/${pk}/generate/`
254
+ getForm: (pk: number) => `/forms/${pk}/generate`
251
255
  };
252
256
 
253
257
  const URLS = {
@@ -72,10 +72,13 @@ const addRootLayoutProps = async (componentProps: RootLayoutProps) => {
72
72
  const checkRedisVariables = () => {
73
73
  const requiredVariableValues = [
74
74
  process.env.CACHE_HOST,
75
- process.env.CACHE_PORT,
76
- process.env.CACHE_SECRET
75
+ process.env.CACHE_PORT
77
76
  ];
78
77
 
78
+ if (!settings.usePrettyUrlRoute) {
79
+ requiredVariableValues.push(process.env.CACHE_SECRET);
80
+ }
81
+
79
82
  if (
80
83
  !requiredVariableValues.every((v) => v) &&
81
84
  process.env.NODE_ENV === 'production'
@@ -4,7 +4,6 @@ import { LocalizationContext } from '../localization/provider';
4
4
  import { useContext } from 'react';
5
5
  import { setCookie, urlLocaleMatcherRegex } from '../utils';
6
6
  import { LocaleUrlStrategy } from '../localization';
7
- import { useRouter } from 'next/navigation';
8
7
 
9
8
  export const useLocalization = () => {
10
9
  const {
@@ -18,8 +17,6 @@ export const useLocalization = () => {
18
17
  localeUrlStrategy
19
18
  } = useContext(LocalizationContext);
20
19
 
21
- const router = useRouter();
22
-
23
20
  /**
24
21
  * Sets the locale in the URL.
25
22
  * @param locale Locale value defined in the settings.
@@ -30,6 +27,8 @@ export const useLocalization = () => {
30
27
 
31
28
  let targetUrl;
32
29
 
30
+ setCookie('pz-locale', locale);
31
+
33
32
  if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
34
33
  const hostParts = hostname.split('.');
35
34
  const subDomain = hostParts[0];
package/jest.config.js CHANGED
@@ -1,13 +1,19 @@
1
1
  const path = require('path');
2
+ const findBaseDir = require('./utils/find-base-dir');
3
+
4
+ const baseDir = findBaseDir();
2
5
 
3
6
  module.exports = {
4
7
  preset: 'ts-jest',
5
8
  testEnvironment: 'node',
6
9
  rootDir: path.resolve(__dirname),
7
- roots: [],
8
10
  testMatch: ['**/*.test.ts'],
9
11
  testPathIgnorePatterns: [],
12
+ roots: [path.resolve(__dirname)],
10
13
  transformIgnorePatterns: [],
14
+ moduleNameMapper: {
15
+ '^settings$': path.resolve(baseDir, 'src/settings.js')
16
+ },
11
17
  transform: {
12
18
  '^.+\\.(tsx?|jsx?|mjs?)$': [
13
19
  'ts-jest',