@akinon/next 1.93.0-snapshot-ZERO-3586-20250828143733 → 1.94.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.
Files changed (53) hide show
  1. package/CHANGELOG.md +39 -1337
  2. package/__tests__/next-config.test.ts +10 -1
  3. package/api/cache.ts +5 -41
  4. package/components/accordion.tsx +5 -20
  5. package/components/file-input.tsx +3 -65
  6. package/components/input.tsx +0 -2
  7. package/components/link.tsx +12 -16
  8. package/components/modal.tsx +16 -32
  9. package/components/plugin-module.tsx +3 -30
  10. package/data/client/checkout.ts +4 -5
  11. package/data/server/category.ts +30 -52
  12. package/data/server/flatpage.ts +13 -20
  13. package/data/server/form.ts +1 -4
  14. package/data/server/landingpage.ts +13 -20
  15. package/data/server/list.ts +14 -25
  16. package/data/server/menu.ts +1 -4
  17. package/data/server/product.ts +40 -68
  18. package/data/server/seo.ts +1 -4
  19. package/data/server/special-page.ts +13 -18
  20. package/data/server/widget.ts +1 -4
  21. package/data/urls.ts +1 -5
  22. package/hocs/server/with-segment-defaults.tsx +2 -5
  23. package/hooks/use-localization.ts +3 -2
  24. package/jest.config.js +1 -7
  25. package/lib/cache-handler.mjs +26 -850
  26. package/lib/cache.ts +13 -432
  27. package/middlewares/checkout-provider.ts +1 -1
  28. package/middlewares/complete-gpay.ts +1 -2
  29. package/middlewares/complete-masterpass.ts +1 -2
  30. package/middlewares/default.ts +13 -50
  31. package/middlewares/locale.ts +1 -9
  32. package/middlewares/pretty-url.ts +1 -2
  33. package/middlewares/redirection-payment.ts +1 -2
  34. package/middlewares/saved-card-redirection.ts +1 -2
  35. package/middlewares/three-d-redirection.ts +1 -2
  36. package/middlewares/url-redirection.ts +15 -9
  37. package/package.json +3 -4
  38. package/plugins.d.ts +0 -8
  39. package/plugins.js +1 -3
  40. package/redux/middlewares/checkout.ts +1 -5
  41. package/sentry/index.ts +17 -54
  42. package/types/commerce/order.ts +0 -1
  43. package/types/index.ts +1 -43
  44. package/utils/app-fetch.ts +2 -7
  45. package/utils/index.ts +10 -34
  46. package/utils/redirect.ts +6 -31
  47. package/with-pz-config.js +5 -2
  48. package/__tests__/redirect.test.ts +0 -319
  49. package/api/image-proxy.ts +0 -75
  50. package/api/similar-product-list.ts +0 -84
  51. package/api/similar-products.ts +0 -120
  52. package/data/server/basket.ts +0 -72
  53. package/utils/redirect-ignore.ts +0 -35
@@ -149,8 +149,7 @@ const withThreeDRedirection =
149
149
  logger.info('Redirecting to order success page', {
150
150
  middleware: 'three-d-redirection',
151
151
  redirectUrlWithLocale,
152
- ip,
153
- setCookie: request.headers.get('set-cookie')
152
+ ip
154
153
  });
155
154
 
156
155
  // Using POST method while redirecting causes an error,
@@ -4,7 +4,6 @@ import { PzNextRequest } from '.';
4
4
  import logger from '../utils/log';
5
5
  import { urlLocaleMatcherRegex } from '../utils';
6
6
  import { getUrlPathWithLocale } from '../utils/localization';
7
- import { shouldIgnoreRedirect } from '../utils/redirect-ignore';
8
7
  import { ROUTES } from 'routes';
9
8
 
10
9
  // This middleware is used to handle url redirections set in Omnitron
@@ -51,7 +50,7 @@ const withUrlRedirection =
51
50
  const location = request.headers.get('location');
52
51
  const redirectUrl = new URL(
53
52
  request.headers.get('location'),
54
- location.startsWith('http') ? '' : url.origin
53
+ location.startsWith('http') ? '' : process.env.NEXT_PUBLIC_URL
55
54
  );
56
55
 
57
56
  redirectUrl.pathname = getUrlPathWithLocale(
@@ -61,13 +60,20 @@ const withUrlRedirection =
61
60
 
62
61
  const setCookies = request.headers.getSetCookie();
63
62
 
64
- if (
65
- shouldIgnoreRedirect(
66
- url.pathname,
67
- req.middlewareParams.rewrites.locale
68
- )
69
- ) {
70
- return middleware(req, event);
63
+ if (settings.commerceRedirectionIgnoreList) {
64
+ const shouldIgnoreRedirect =
65
+ settings.commerceRedirectionIgnoreList.some((ignorePath) =>
66
+ redirectUrl.pathname.startsWith(
67
+ getUrlPathWithLocale(
68
+ ignorePath,
69
+ req.middlewareParams.rewrites.locale
70
+ )
71
+ )
72
+ );
73
+
74
+ if (shouldIgnoreRedirect) {
75
+ return middleware(req, event);
76
+ }
71
77
  }
72
78
 
73
79
  const response = NextResponse.redirect(redirectUrl.toString(), {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@akinon/next",
3
3
  "description": "Core package for Project Zero Next",
4
- "version": "1.93.0-snapshot-ZERO-3586-20250828143733",
4
+ "version": "1.94.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -17,8 +17,7 @@
17
17
  "test": "jest"
18
18
  },
19
19
  "dependencies": {
20
- "@bokuweb/zstd-wasm": "^0.0.27",
21
- "@neshca/cache-handler": "1.9.0",
20
+ "@neshca/cache-handler": "1.5.1",
22
21
  "@opentelemetry/exporter-trace-otlp-http": "0.46.0",
23
22
  "@opentelemetry/resources": "1.19.0",
24
23
  "@opentelemetry/sdk-node": "0.46.0",
@@ -35,7 +34,7 @@
35
34
  "set-cookie-parser": "2.6.0"
36
35
  },
37
36
  "devDependencies": {
38
- "@akinon/eslint-plugin-projectzero": "1.93.0-snapshot-ZERO-3586-20250828143733",
37
+ "@akinon/eslint-plugin-projectzero": "1.94.0",
39
38
  "@babel/core": "7.26.10",
40
39
  "@babel/preset-env": "7.26.9",
41
40
  "@babel/preset-typescript": "7.27.0",
package/plugins.d.ts CHANGED
@@ -38,11 +38,3 @@ declare module '@akinon/pz-iyzico-saved-card' {
38
38
  declare module '@akinon/pz-apple-pay' {}
39
39
 
40
40
  declare module '@akinon/pz-flow-payment' {}
41
-
42
- declare module '@akinon/pz-similar-products' {
43
- export const SimilarProductsModal: any;
44
- export const SimilarProductsFilterSidebar: any;
45
- export const SimilarProductsResultsGrid: any;
46
- export const SimilarProductsPlugin: any;
47
- export const SimilarProductsButtonPlugin: any;
48
- }
package/plugins.js CHANGED
@@ -16,7 +16,5 @@ module.exports = [
16
16
  'pz-tabby-extension',
17
17
  'pz-apple-pay',
18
18
  'pz-tamara-extension',
19
- 'pz-hepsipay',
20
- 'pz-flow-payment',
21
- 'pz-similar-products'
19
+ 'pz-flow-payment'
22
20
  ];
@@ -51,11 +51,7 @@ export const errorMiddleware: Middleware = ({ dispatch }: MiddlewareParams) => {
51
51
  const result: CheckoutResult = next(action);
52
52
  const errors = result?.payload?.errors;
53
53
 
54
- if (
55
- !!errors &&
56
- ((typeof errors === 'object' && Object.keys(errors).length > 0) ||
57
- (Array.isArray(errors) && errors.length > 0))
58
- ) {
54
+ if (errors) {
59
55
  dispatch(setErrors(errors));
60
56
  }
61
57
 
package/sentry/index.ts CHANGED
@@ -13,73 +13,36 @@ const ALLOWED_CLIENT_LOG_TYPES: ClientLogType[] = [
13
13
  ClientLogType.CHECKOUT
14
14
  ];
15
15
 
16
- const isNetworkError = (exception: unknown): boolean => {
17
- if (!(exception instanceof Error)) return false;
18
-
19
- const networkErrorPatterns = [
20
- 'networkerror',
21
- 'failed to fetch',
22
- 'network request failed',
23
- 'network error',
24
- 'loading chunk',
25
- 'chunk load failed'
26
- ];
27
-
28
- if (exception.name === 'NetworkError') return true;
29
-
30
- if (exception.name === 'TypeError') {
31
- return networkErrorPatterns.some((pattern) =>
32
- exception.message.toLowerCase().includes(pattern)
33
- );
34
- }
35
-
36
- return networkErrorPatterns.some((pattern) =>
37
- exception.message.toLowerCase().includes(pattern)
38
- );
39
- };
40
-
41
16
  export const initSentry = (
42
17
  type: 'Server' | 'Client' | 'Edge',
43
18
  options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {}
44
19
  ) => {
45
- // TODO: Remove Zero Project DSN
20
+ // TODO: Handle options with ESLint rules
46
21
 
47
- const baseConfig = {
22
+ Sentry.init({
48
23
  dsn:
49
- SENTRY_DSN ||
50
24
  options.dsn ||
25
+ SENTRY_DSN ||
51
26
  'https://d8558ef8997543deacf376c7d8d7cf4b@o64293.ingest.sentry.io/4504338423742464',
52
27
  initialScope: {
53
28
  tags: {
54
29
  APP_TYPE: 'ProjectZeroNext',
55
- TYPE: type,
56
- ...((options.initialScope as any)?.tags || {})
30
+ TYPE: type
57
31
  }
58
32
  },
59
33
  tracesSampleRate: 0,
60
- integrations: []
61
- };
62
-
63
- if (type === 'Server' || type === 'Edge') {
64
- Sentry.init(baseConfig);
65
- } else if (type === 'Client') {
66
- Sentry.init({
67
- ...baseConfig,
68
- beforeSend: (event, hint) => {
69
- if (
70
- !ALLOWED_CLIENT_LOG_TYPES.includes(
71
- event.tags?.LOG_TYPE as ClientLogType
72
- )
73
- ) {
74
- return null;
75
- }
76
-
77
- if (isNetworkError(hint?.originalException)) {
78
- return null;
79
- }
80
-
81
- return event;
34
+ integrations: [],
35
+ beforeSend: (event, hint) => {
36
+ if (
37
+ type === 'Client' &&
38
+ !ALLOWED_CLIENT_LOG_TYPES.includes(
39
+ event.tags?.LOG_TYPE as ClientLogType
40
+ )
41
+ ) {
42
+ return null;
82
43
  }
83
- });
84
- }
44
+
45
+ return event;
46
+ }
47
+ });
85
48
  };
@@ -114,7 +114,6 @@ export interface Order {
114
114
  pk: number;
115
115
  name: string;
116
116
  slug: string;
117
- logo: string;
118
117
  [key: string]: any;
119
118
  };
120
119
  }
package/types/index.ts CHANGED
@@ -83,12 +83,6 @@ export interface Settings {
83
83
  };
84
84
  usePrettyUrlRoute?: boolean;
85
85
  commerceUrl: string;
86
- /**
87
- * This option allows you to track Sentry events on the client side, in addition to server and edge environments.
88
- *
89
- * It overrides process.env.NEXT_PUBLIC_SENTRY_DSN and process.env.SENTRY_DSN.
90
- */
91
- sentryDsn?: string;
92
86
  redis: {
93
87
  defaultExpirationTime: number;
94
88
  };
@@ -222,15 +216,6 @@ export interface CacheOptions {
222
216
  cache?: boolean;
223
217
  expire?: number;
224
218
  useProxy?: boolean;
225
- compressed?: boolean;
226
- }
227
-
228
- export interface SetCookieOptions {
229
- expires?: number; // days
230
- path?: string;
231
- domain?: string;
232
- secure?: boolean;
233
- sameSite?: 'strict' | 'lax' | 'none';
234
219
  }
235
220
 
236
221
  export interface ClientRequestOptions {
@@ -298,13 +283,7 @@ export interface ButtonProps
298
283
  target?: '_blank' | '_self' | '_parent' | '_top';
299
284
  }
300
285
 
301
- export interface FileInputProps extends React.HTMLProps<HTMLInputElement> {
302
- fileClassName?: string;
303
- fileNameWrapperClassName?: string;
304
- fileInputClassName?: string;
305
- onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
306
- buttonClassName?: string;
307
- }
286
+ export type FileInputProps = React.HTMLProps<HTMLInputElement>;
308
287
 
309
288
  export interface PriceProps {
310
289
  currencyCode?: string;
@@ -325,19 +304,15 @@ export interface InputProps extends React.HTMLProps<HTMLInputElement> {
325
304
 
326
305
  export interface AccordionProps {
327
306
  isCollapse?: boolean;
328
- collapseClassName?: string;
329
307
  title?: string;
330
308
  subTitle?: string;
331
309
  icons?: string[];
332
310
  iconSize?: number;
333
311
  iconColor?: string;
334
312
  children?: ReactNode;
335
- headerClassName?: string;
336
313
  className?: string;
337
314
  titleClassName?: string;
338
- subTitleClassName?: string;
339
315
  dataTestId?: string;
340
- contentClassName?: string;
341
316
  }
342
317
 
343
318
  export interface PluginModuleComponentProps {
@@ -362,20 +337,3 @@ export interface PaginationProps {
362
337
  direction?: 'next' | 'prev';
363
338
  isLoading?: boolean;
364
339
  }
365
-
366
- export interface ModalProps {
367
- portalId: string;
368
- children?: React.ReactNode;
369
- open?: boolean;
370
- setOpen?: (open: boolean) => void;
371
- title?: React.ReactNode;
372
- showCloseButton?: React.ReactNode;
373
- className?: string;
374
- overlayClassName?: string;
375
- headerWrapperClassName?: string;
376
- titleClassName?: string;
377
- closeButtonClassName?: string;
378
- iconName?: string;
379
- iconSize?: number;
380
- iconClassName?: string;
381
- }
@@ -43,12 +43,12 @@ const appFetch = async <T>({
43
43
  const requestURL = `${decodeURIComponent(commerceUrl)}${url}`;
44
44
 
45
45
  init.headers = {
46
- cookie: nextCookies.toString(),
47
46
  ...(init.headers ?? {}),
48
47
  ...(ServerVariables.globalHeaders ?? {}),
49
48
  'Accept-Language': currentLocale.apiValue,
50
49
  'x-currency': currency,
51
- 'x-forwarded-for': ip
50
+ 'x-forwarded-for': ip,
51
+ cookie: nextCookies.toString()
52
52
  };
53
53
 
54
54
  init.next = {
@@ -60,11 +60,6 @@ const appFetch = async <T>({
60
60
  status = req.status;
61
61
  logger.debug(`FETCH END ${url}`, { status: req.status, ip });
62
62
 
63
- if (!req.ok) {
64
- const errorMessage = `HTTP ${req.status}: ${req.statusText}`;
65
- throw new Error(errorMessage);
66
- }
67
-
68
63
  if (responseType === FetchResponseType.JSON) {
69
64
  response = (await req.json()) as T;
70
65
  } else {
package/utils/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import settings from 'settings';
2
2
  import { LocaleUrlStrategy } from '../localization';
3
- import { CDNOptions, ClientRequestOptions, SetCookieOptions } from '../types';
4
- import getRootHostname from './get-root-hostname';
3
+ import { CDNOptions, ClientRequestOptions } from '../types';
5
4
 
6
5
  export * from './get-currency';
7
6
  export * from './menu-generator';
@@ -21,40 +20,14 @@ export function getCookie(name: string) {
21
20
  }
22
21
  }
23
22
 
24
- export function setCookie(
25
- name: string,
26
- value: string,
27
- options: SetCookieOptions = {}
28
- ) {
29
- const cookieParts = [`${name}=${value}`];
30
-
31
- if (options.expires) {
32
- const date = new Date();
33
- date.setTime(date.getTime() + options.expires * 24 * 60 * 60 * 1000);
34
- cookieParts.push(`expires=${date.toUTCString()}`);
35
- }
23
+ export function setCookie(name: string, val: string) {
24
+ const date = new Date();
25
+ const value = val;
36
26
 
37
- cookieParts.push(`path=${options.path ?? '/'}`);
27
+ date.setTime(date.getTime() + 7 * 24 * 60 * 60 * 1000);
38
28
 
39
- if (options.secure) {
40
- cookieParts.push('secure');
41
- }
42
-
43
- if (options.sameSite) {
44
- cookieParts.push(`sameSite=${options.sameSite}`);
45
- }
46
-
47
- const domain =
48
- options.domain ??
49
- (settings.localization.localeUrlStrategy === LocaleUrlStrategy.Subdomain
50
- ? getRootHostname(document.location.href)
51
- : null);
52
-
53
- if (domain) {
54
- cookieParts.push(`domain=${domain}`);
55
- }
56
-
57
- document.cookie = cookieParts.join('; ');
29
+ document.cookie =
30
+ name + '=' + value + '; expires=' + date.toUTCString() + '; path=/';
58
31
  }
59
32
 
60
33
  export function removeCookie(name: string) {
@@ -179,6 +152,9 @@ export function buildCDNUrl(url: string, config?: CDNOptions) {
179
152
  return `${rootWithoutOptions}${options}${fileExtension}`;
180
153
  }
181
154
 
155
+ const { locales, localeUrlStrategy, defaultLocaleValue } =
156
+ settings.localization;
157
+
182
158
  export const urlLocaleMatcherRegex = new RegExp(
183
159
  `^/(${settings.localization.locales
184
160
  .filter((l) =>
package/utils/redirect.ts CHANGED
@@ -1,48 +1,23 @@
1
1
  import { redirect as nextRedirect, RedirectType } from 'next/navigation';
2
2
  import Settings from 'settings';
3
- import { headers, cookies } from 'next/headers';
3
+ import { headers } from 'next/headers';
4
+ import { ServerVariables } from '@akinon/next/utils/server-variables';
4
5
  import { getUrlPathWithLocale } from '@akinon/next/utils/localization';
5
- import { urlLocaleMatcherRegex } from '@akinon/next/utils';
6
6
 
7
7
  export const redirect = (path: string, type?: RedirectType) => {
8
8
  const nextHeaders = headers();
9
- const nextCookies = cookies();
10
9
  const pageUrl = new URL(
11
- nextHeaders.get('pz-url') ?? process.env.NEXT_PUBLIC_URL ?? ''
10
+ nextHeaders.get('pz-url') ?? process.env.NEXT_PUBLIC_URL
12
11
  );
13
12
 
14
- let currentLocaleValue = Settings.localization.defaultLocaleValue;
15
- const urlLocaleMatch = pageUrl.pathname.match(urlLocaleMatcherRegex);
16
-
17
- if (urlLocaleMatch && urlLocaleMatch[0]) {
18
- currentLocaleValue = urlLocaleMatch[0].replace('/', '');
19
- } else {
20
- const cookieLocale = nextCookies.get('pz-locale')?.value;
21
- if (
22
- cookieLocale &&
23
- Settings.localization.locales.find((l) => l.value === cookieLocale)
24
- ) {
25
- currentLocaleValue = cookieLocale;
26
- }
27
- }
28
-
29
13
  const currentLocale = Settings.localization.locales.find(
30
- (locale) => locale.value === currentLocaleValue
14
+ (locale) => locale.value === ServerVariables.locale
31
15
  );
32
16
 
33
- if (!currentLocale) {
34
- currentLocaleValue = Settings.localization.defaultLocaleValue;
35
- }
36
-
37
- const searchParams = new URLSearchParams(pageUrl.search);
38
-
39
- const callbackUrl =
40
- pageUrl.pathname.replace(urlLocaleMatcherRegex, '') +
41
- (searchParams.toString() ? `?${searchParams.toString()}` : '');
42
-
17
+ const callbackUrl = pageUrl.pathname;
43
18
  const redirectUrlWithLocale = getUrlPathWithLocale(
44
19
  path,
45
- currentLocale?.value
20
+ currentLocale.localePath ?? currentLocale.value
46
21
  );
47
22
 
48
23
  const redirectUrl = `${redirectUrlWithLocale}?callbackUrl=${callbackUrl}`;
package/with-pz-config.js CHANGED
@@ -16,7 +16,11 @@ const defaultConfig = {
16
16
  remotePatterns: [
17
17
  {
18
18
  protocol: 'https',
19
- hostname: '**'
19
+ hostname: '**.akinoncloud.com'
20
+ },
21
+ {
22
+ protocol: 'https',
23
+ hostname: '**.akinoncdn.com'
20
24
  }
21
25
  ]
22
26
  },
@@ -57,7 +61,6 @@ const defaultConfig = {
57
61
  }, {}),
58
62
  translations: false
59
63
  };
60
-
61
64
  return config;
62
65
  },
63
66
  sentry: {