@akinon/next 2.0.0-beta.10 → 2.0.0-beta.11

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,11 @@
1
1
  # @akinon/next
2
2
 
3
+ ## 2.0.0-beta.11
4
+
5
+ ### Minor Changes
6
+
7
+ - ac783d6: ZERO-3482: Update tailwindcss to version 4.1.11 and enhance button cursor styles
8
+
3
9
  ## 2.0.0-beta.10
4
10
 
5
11
  ### Minor Changes
@@ -0,0 +1,83 @@
1
+ import { resolve } from 'path';
2
+ import type { NextConfig } from 'next';
3
+
4
+ function findBaseDir() {
5
+ const insideNodeModules = __dirname.includes('node_modules');
6
+
7
+ if (insideNodeModules) {
8
+ return resolve(__dirname, '../../../../');
9
+ } else {
10
+ return resolve(__dirname, '../../../apps/projectzeronext');
11
+ }
12
+ }
13
+
14
+ const baseDir = findBaseDir();
15
+
16
+ jest.mock('next-pwa', () => {
17
+ return () => (config: NextConfig) => config;
18
+ });
19
+
20
+ jest.mock('@sentry/nextjs', () => ({
21
+ withSentryConfig: (config: NextConfig) => config
22
+ }));
23
+
24
+ jest.mock('../with-pz-config.js', () => {
25
+ return (config: NextConfig) => {
26
+ const originalHeaders = config.headers;
27
+
28
+ config.headers = async () => {
29
+ const originalHeadersResult = (await originalHeaders?.()) ?? [];
30
+
31
+ return [
32
+ {
33
+ source: '/(.*)',
34
+ headers: [
35
+ {
36
+ key: 'Content-Security-Policy',
37
+ value: 'https://*.akifast.com akifast.com'
38
+ }
39
+ ]
40
+ },
41
+ ...originalHeadersResult
42
+ ];
43
+ };
44
+ return config;
45
+ };
46
+ });
47
+
48
+ interface Header {
49
+ key: string;
50
+ value: string;
51
+ }
52
+
53
+ interface HeaderGroup {
54
+ source: string;
55
+ headers: Header[];
56
+ }
57
+
58
+ const nextConfigPath = resolve(baseDir, 'next.config.mjs');
59
+ let nextConfig: any;
60
+
61
+ beforeAll(async () => {
62
+ nextConfig = await import(nextConfigPath);
63
+ });
64
+
65
+ describe('Next.js Configuration', () => {
66
+ it('should contain Content-Security-Policy header with akifast domain values', async () => {
67
+ const headers = nextConfig.default.headers;
68
+ expect(headers).toBeDefined();
69
+
70
+ const headersResult = await headers();
71
+
72
+ const cspHeaders = headersResult
73
+ .flatMap((headerGroup: HeaderGroup) => headerGroup.headers)
74
+ .filter((header: Header) => header.key === 'Content-Security-Policy');
75
+
76
+ expect(cspHeaders.length).toBeGreaterThan(0);
77
+
78
+ const lastCspHeader = cspHeaders[cspHeaders.length - 1];
79
+
80
+ expect(lastCspHeader.value).toContain('akifast.com');
81
+ expect(lastCspHeader.value).toContain('https://*.akifast.com');
82
+ });
83
+ });
@@ -0,0 +1,23 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "display": "Default",
4
+ "compilerOptions": {
5
+ "composite": false,
6
+ "declaration": true,
7
+ "declarationMap": true,
8
+ "esModuleInterop": true,
9
+ "forceConsistentCasingInFileNames": true,
10
+ "inlineSources": false,
11
+ "isolatedModules": true,
12
+ "moduleResolution": "node",
13
+ "noUnusedLocals": false,
14
+ "noUnusedParameters": false,
15
+ "preserveWatchOutput": true,
16
+ "skipLibCheck": true,
17
+ "strict": true,
18
+ "jsx": "react-jsx",
19
+ "allowJs": true
20
+ },
21
+ "exclude": ["node_modules"],
22
+ "include": [".", "../."]
23
+ }
package/api/auth.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { NextApiRequest, NextApiResponse } from 'next';
2
- import NextAuth, { Session } from 'next-auth';
2
+ import NextAuth, { Session, NextAuthOptions } from 'next-auth';
3
3
  import CredentialProvider from 'next-auth/providers/credentials';
4
4
  import { ROUTES } from 'routes';
5
5
  import { URLS, user } from '../data/urls';
@@ -7,6 +7,8 @@ import Settings from 'settings';
7
7
  import { urlLocaleMatcherRegex } from '../utils';
8
8
  import logger from '@akinon/next/utils/log';
9
9
  import { AuthError } from '../types';
10
+ import getRootHostname from '../utils/get-root-hostname';
11
+ import { LocaleUrlStrategy } from '../localization';
10
12
 
11
13
  async function getCurrentUser(sessionId: string, currency = '') {
12
14
  const headers = {
@@ -40,7 +42,15 @@ async function getCurrentUser(sessionId: string, currency = '') {
40
42
  };
41
43
  }
42
44
 
43
- const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
45
+ type CustomNextAuthOptions = (
46
+ req: NextApiRequest,
47
+ res: NextApiResponse
48
+ ) => Partial<NextAuthOptions>;
49
+
50
+ const defaultNextAuthOptions = (
51
+ req: NextApiRequest,
52
+ res: NextApiResponse
53
+ ): NextAuthOptions => {
44
54
  return {
45
55
  providers: [
46
56
  CredentialProvider({
@@ -150,7 +160,19 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
150
160
 
151
161
  if (sessionId) {
152
162
  const maxAge = 30 * 24 * 60 * 60; // 30 days in seconds
153
- const cookieOptions = `Path=/; HttpOnly; Secure; Max-Age=${maxAge}`;
163
+ const { localeUrlStrategy } = Settings.localization;
164
+
165
+ const fallbackHost =
166
+ req.headers['x-forwarded-host']?.toString() ||
167
+ req.headers.host?.toString();
168
+ const hostname =
169
+ process.env.NEXT_PUBLIC_URL || `https://${fallbackHost}`;
170
+ const rootHostname =
171
+ localeUrlStrategy === LocaleUrlStrategy.Subdomain
172
+ ? getRootHostname(hostname)
173
+ : null;
174
+ const domainOption = rootHostname ? `Domain=${rootHostname};` : '';
175
+ const cookieOptions = `Path=/; HttpOnly; Secure; Max-Age=${maxAge}; ${domainOption}`;
154
176
 
155
177
  res.setHeader('Set-Cookie', [
156
178
  `osessionid=${sessionId}; ${cookieOptions}`,
@@ -253,8 +275,36 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
253
275
  };
254
276
  };
255
277
 
256
- const Auth = (req, res) => {
257
- return NextAuth(req, res, nextAuthOptions(req, res));
278
+ const Auth = (
279
+ req: NextApiRequest,
280
+ res: NextApiResponse,
281
+ customOptions?: CustomNextAuthOptions
282
+ ) => {
283
+ const baseOptions = defaultNextAuthOptions(req, res);
284
+ const customOptionsResult = customOptions ? customOptions(req, res) : {};
285
+
286
+ const mergedOptions = {
287
+ ...baseOptions,
288
+ ...customOptionsResult,
289
+ providers: [
290
+ ...baseOptions.providers,
291
+ ...(customOptionsResult.providers || [])
292
+ ],
293
+ callbacks: {
294
+ ...baseOptions.callbacks,
295
+ ...customOptionsResult.callbacks
296
+ },
297
+ events: {
298
+ ...baseOptions.events,
299
+ ...customOptionsResult.events
300
+ },
301
+ pages: {
302
+ ...baseOptions.pages,
303
+ ...customOptionsResult.pages
304
+ }
305
+ };
306
+
307
+ return NextAuth(req, res, mergedOptions);
258
308
  };
259
309
 
260
310
  export default Auth;
package/api/client.ts CHANGED
@@ -5,6 +5,8 @@ import logger from '../utils/log';
5
5
  import formatCookieString from '../utils/format-cookie-string';
6
6
  import cookieParser from 'set-cookie-parser';
7
7
  import { cookies } from 'next/headers';
8
+ import getRootHostname from '../utils/get-root-hostname';
9
+ import { LocaleUrlStrategy } from '../localization';
8
10
 
9
11
  interface RouteParams {
10
12
  params: {
@@ -191,8 +193,23 @@ async function proxyRequest(...args) {
191
193
  const responseHeaders: any = {};
192
194
 
193
195
  if (filteredCookies.length > 0) {
196
+ const { localeUrlStrategy } = settings.localization;
197
+
198
+ const fallbackHost =
199
+ req.headers.get('x-forwarded-host') || req.headers.get('host');
200
+ const hostname = process.env.NEXT_PUBLIC_URL || `https://${fallbackHost}`;
201
+ const rootHostname =
202
+ localeUrlStrategy === LocaleUrlStrategy.Subdomain
203
+ ? getRootHostname(hostname)
204
+ : null;
205
+
194
206
  responseHeaders['set-cookie'] = filteredCookies
195
- .map(formatCookieString)
207
+ .map((cookie) => {
208
+ if (!cookie.domain && rootHostname) {
209
+ cookie.domain = rootHostname;
210
+ }
211
+ return formatCookieString(cookie);
212
+ })
196
213
  .join(', ');
197
214
  }
198
215
 
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ presets: [
3
+ ['@babel/preset-env', { targets: { node: 'current' } }],
4
+ '@babel/preset-typescript'
5
+ ]
6
+ };
@@ -2,6 +2,7 @@
2
2
 
3
3
  const runScript = require('./run-script');
4
4
 
5
+ runScript('pz-run-tests.js');
5
6
  runScript('pz-install-theme.js');
6
7
  runScript('pz-pre-check-dist.js');
7
8
  runScript('pz-generate-translations.js');
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+ const glob = require('glob');
7
+ const findBaseDir = require('../utils/find-base-dir');
8
+ const checkMonorepo = require('../utils/check-monorepo');
9
+
10
+ const IS_MONOREPO = checkMonorepo() !== null;
11
+ const BASE_DIR = findBaseDir();
12
+ const PLUGINS = require(path.join(BASE_DIR, 'src', 'plugins.js'));
13
+
14
+ function findPluginTestFiles(akinonNextPackagePath) {
15
+ const pluginsRootPath = path.join(
16
+ akinonNextPackagePath,
17
+ '..',
18
+ '..',
19
+ IS_MONOREPO ? 'packages' : '@akinon'
20
+ );
21
+
22
+ if (!fs.existsSync(pluginsRootPath)) {
23
+ console.log('Plugins directory not found:', pluginsRootPath);
24
+ return [];
25
+ }
26
+
27
+ return PLUGINS.reduce((testFiles, pluginName) => {
28
+ const pluginDirectoryPath = path.join(pluginsRootPath, pluginName);
29
+ if (fs.existsSync(pluginDirectoryPath)) {
30
+ const pluginTestFiles = glob.sync('**/*.test.ts', {
31
+ cwd: pluginDirectoryPath,
32
+ absolute: true
33
+ });
34
+
35
+ return testFiles.concat(pluginTestFiles);
36
+ } else {
37
+ console.log(`Plugin directory not found: ${pluginName}`);
38
+ }
39
+ return testFiles;
40
+ }, []);
41
+ }
42
+
43
+ function isJestInstalled() {
44
+ try {
45
+ const jestExecutablePath = path.join(
46
+ BASE_DIR,
47
+ 'node_modules',
48
+ '.bin',
49
+ 'jest'
50
+ );
51
+ return fs.existsSync(jestExecutablePath);
52
+ } catch (error) {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ function getAkinonNextPackagePath() {
58
+ if (!IS_MONOREPO) {
59
+ return path.resolve(__dirname, '..');
60
+ }
61
+
62
+ return path.resolve(__dirname, '../../../packages/akinon-next');
63
+ }
64
+
65
+ if (!isJestInstalled()) {
66
+ console.error('\x1b[31mError: Jest is not installed in the project!\x1b[0m');
67
+ console.error(
68
+ 'Please install all dependencies by running one of the following commands:'
69
+ );
70
+ console.error(' npm install');
71
+ console.error(' yarn install');
72
+ process.exit(1);
73
+ }
74
+
75
+ const jestExecutablePath = path.join(BASE_DIR, 'node_modules', '.bin', 'jest');
76
+ const akinonNextPackagePath = getAkinonNextPackagePath();
77
+
78
+ const testFiles = [
79
+ ...glob.sync('__tests__/**/*.test.ts', {
80
+ cwd: akinonNextPackagePath,
81
+ absolute: true
82
+ }),
83
+ ...findPluginTestFiles(akinonNextPackagePath)
84
+ ];
85
+
86
+ console.log(`Found ${testFiles.length} test files to run`);
87
+
88
+ const jestArguments = [
89
+ ...testFiles,
90
+ `--config ${path.join(akinonNextPackagePath, 'jest.config.js')}`,
91
+ '--runTestsByPath',
92
+ '--passWithNoTests'
93
+ ];
94
+
95
+ spawn(jestExecutablePath, jestArguments, {
96
+ cwd: akinonNextPackagePath,
97
+ stdio: 'inherit',
98
+ shell: true
99
+ });
@@ -35,7 +35,7 @@ export const Button = (props: ButtonProps) => {
35
35
 
36
36
  const buttonClasses = twMerge(
37
37
  clsx(
38
- 'px-4 text-xs transition-all duration-200',
38
+ 'px-4 text-xs transition-all duration-200 cursor-pointer',
39
39
  'inline-flex gap-2 justify-center items-center',
40
40
  variants[appearance],
41
41
  sizes[size],
@@ -55,8 +55,8 @@ export const Price = (props: NumericFormatProps & PriceProps) => {
55
55
  : formattedValue;
56
56
 
57
57
  const currentCurrencyDecimalScale = Settings.localization.currencies.find(
58
- (currency) => currency.code === currencyCode_
59
- ).decimalScale;
58
+ (currency) => currency?.code === currencyCode_
59
+ )?.decimalScale;
60
60
 
61
61
  return (
62
62
  <NumericFormat
@@ -25,19 +25,33 @@ export const useLocalization = () => {
25
25
  * @param locale Locale value defined in the settings.
26
26
  */
27
27
  const setLocale = (locale: string) => {
28
- const localePath =
29
- locale === defaultLocaleValue &&
30
- localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales
31
- ? ''
32
- : `/${locale}`;
28
+ const { protocol, hostname, port, search, pathname } = location;
29
+ const pathnameWithoutLocale = pathname.replace(urlLocaleMatcherRegex, '');
33
30
 
34
- const pathnameWithoutLocale = location.pathname.replace(
35
- urlLocaleMatcherRegex,
36
- ''
37
- );
31
+ let targetUrl;
32
+
33
+ if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
34
+ const hostParts = hostname.split('.');
35
+ const subDomain = hostParts[0];
36
+ const isSubdomainLocale = locales.some((loc) => loc.value === subDomain);
37
+ const baseDomain = isSubdomainLocale
38
+ ? hostParts.slice(1).join('.')
39
+ : hostname;
40
+
41
+ const formattedPort = port ? `:${port}` : '';
42
+
43
+ targetUrl = `${protocol}//${locale}.${baseDomain}${formattedPort}${pathnameWithoutLocale}${search}`;
44
+ } else {
45
+ const shouldHideLocale =
46
+ locale === defaultLocaleValue &&
47
+ localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales;
48
+ const localePath = shouldHideLocale ? '' : `/${locale}`;
49
+
50
+ targetUrl = `${localePath}${pathnameWithoutLocale}${search}`;
51
+ }
38
52
 
39
53
  // router.push is not used because reloading the page also clears the client side cache.
40
- location.href = `${localePath}${pathnameWithoutLocale}${location.search}`;
54
+ location.href = targetUrl;
41
55
  };
42
56
 
43
57
  /**
package/jest.config.js ADDED
@@ -0,0 +1,19 @@
1
+ const path = require('path');
2
+
3
+ module.exports = {
4
+ preset: 'ts-jest',
5
+ testEnvironment: 'node',
6
+ rootDir: path.resolve(__dirname),
7
+ roots: [],
8
+ testMatch: ['**/*.test.ts'],
9
+ testPathIgnorePatterns: [],
10
+ transformIgnorePatterns: [],
11
+ transform: {
12
+ '^.+\\.(tsx?|jsx?|mjs?)$': [
13
+ 'ts-jest',
14
+ {
15
+ tsconfig: path.resolve(__dirname, '__tests__/tsconfig.json')
16
+ }
17
+ ]
18
+ }
19
+ };
@@ -19,6 +19,8 @@ import withLocale from './locale';
19
19
  import logger from '../utils/log';
20
20
  import { user } from '../data/urls';
21
21
  import { getUrlPathWithLocale } from '../utils/localization';
22
+ import getRootHostname from '../utils/get-root-hostname';
23
+ import { LocaleUrlStrategy } from '../localization';
22
24
 
23
25
  const withPzDefault =
24
26
  (middleware: NextMiddleware) =>
@@ -292,6 +294,19 @@ const withPzDefault =
292
294
  )}`;
293
295
  }
294
296
 
297
+ if (
298
+ !req.middlewareParams.found &&
299
+ Settings.customNotFoundEnabled
300
+ ) {
301
+ const pathname = url.pathname
302
+ .replace(/\/+$/, '')
303
+ .split('/');
304
+ url.pathname = url.pathname.replace(
305
+ pathname.pop(),
306
+ 'pz-not-found'
307
+ );
308
+ }
309
+
295
310
  Settings.rewrites.forEach((rewrite) => {
296
311
  url.pathname = url.pathname.replace(
297
312
  rewrite.source,
@@ -328,6 +343,21 @@ const withPzDefault =
328
343
  middlewareResult = NextResponse.rewrite(url);
329
344
  }
330
345
 
346
+ const { localeUrlStrategy } =
347
+ Settings.localization;
348
+
349
+ const fallbackHost =
350
+ req.headers.get('x-forwarded-host') ||
351
+ req.headers.get('host');
352
+ const hostname =
353
+ process.env.NEXT_PUBLIC_URL ||
354
+ `https://${fallbackHost}`;
355
+ const rootHostname =
356
+ localeUrlStrategy ===
357
+ LocaleUrlStrategy.Subdomain
358
+ ? getRootHostname(hostname)
359
+ : null;
360
+
331
361
  if (
332
362
  !url.pathname.startsWith(`/${currency}/orders`)
333
363
  ) {
@@ -337,6 +367,7 @@ const withPzDefault =
337
367
  ? locale
338
368
  : defaultLocaleValue,
339
369
  {
370
+ domain: rootHostname,
340
371
  sameSite: 'none',
341
372
  secure: true,
342
373
  expires: new Date(
@@ -345,10 +376,12 @@ const withPzDefault =
345
376
  }
346
377
  );
347
378
  }
379
+
348
380
  middlewareResult.cookies.set(
349
381
  'pz-currency',
350
382
  currency,
351
383
  {
384
+ domain: rootHostname,
352
385
  sameSite: 'none',
353
386
  secure: true,
354
387
  expires: new Date(
@@ -397,7 +430,10 @@ const withPzDefault =
397
430
  ).json();
398
431
  middlewareResult.cookies.set(
399
432
  'csrftoken',
400
- csrf_token
433
+ csrf_token,
434
+ {
435
+ domain: rootHostname
436
+ }
401
437
  );
402
438
  }
403
439
  } catch (error) {
@@ -13,7 +13,8 @@ const getMatchedLocale = (pathname: string, req: PzNextRequest) => {
13
13
  const { localeUrlStrategy, defaultLocaleValue } = settings.localization;
14
14
 
15
15
  if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
16
- const host = req.headers.get('x-forwarded-host');
16
+ const host =
17
+ req.headers.get('x-forwarded-host') || req.headers.get('host') || '';
17
18
 
18
19
  if (host) {
19
20
  const subDomain = host.split('.')[0] || '';
@@ -44,7 +45,9 @@ const withLocale =
44
45
  try {
45
46
  const url = req.nextUrl.clone();
46
47
  const matchedLocale = getMatchedLocale(url.pathname, req);
47
- let { localeUrlStrategy, defaultLocaleValue, redirectToDefaultLocale } =
48
+ let { localeUrlStrategy } = settings.localization;
49
+
50
+ const { defaultLocaleValue, redirectToDefaultLocale } =
48
51
  settings.localization;
49
52
 
50
53
  localeUrlStrategy =
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": "2.0.0-beta.10",
4
+ "version": "2.0.0-beta.11",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -13,14 +13,17 @@
13
13
  "pz-predev": "bin/pz-predev.js",
14
14
  "pz-postdev": "bin/pz-postdev.js"
15
15
  },
16
+ "scripts": {
17
+ "test": "jest"
18
+ },
16
19
  "dependencies": {
20
+ "@neshca/cache-handler": "1.5.1",
17
21
  "@opentelemetry/exporter-trace-otlp-http": "0.46.0",
18
22
  "@opentelemetry/resources": "1.19.0",
19
23
  "@opentelemetry/sdk-node": "0.46.0",
20
24
  "@opentelemetry/sdk-trace-node": "1.19.0",
21
25
  "@opentelemetry/semantic-conventions": "1.19.0",
22
26
  "@reduxjs/toolkit": "1.9.7",
23
- "@neshca/cache-handler": "1.5.1",
24
27
  "@sentry/nextjs": "9.5.0",
25
28
  "cross-spawn": "7.0.3",
26
29
  "generic-pool": "3.9.0",
@@ -31,7 +34,7 @@
31
34
  "set-cookie-parser": "2.6.0"
32
35
  },
33
36
  "devDependencies": {
34
- "@akinon/eslint-plugin-projectzero": "2.0.0-beta.10",
37
+ "@akinon/eslint-plugin-projectzero": "2.0.0-beta.11",
35
38
  "@types/react-redux": "7.1.30",
36
39
  "@types/set-cookie-parser": "2.4.7",
37
40
  "@typescript-eslint/eslint-plugin": "8.18.2",
@@ -36,6 +36,7 @@ interface CheckoutResult {
36
36
  errors?: Record<string, string[]>;
37
37
  pre_order?: PreOrder;
38
38
  context_list?: CheckoutContext[];
39
+ redirect_url?: string;
39
40
  };
40
41
  }
41
42
 
@@ -57,6 +58,33 @@ export const errorMiddleware: Middleware = ({ dispatch }: MiddlewareParams) => {
57
58
  };
58
59
  };
59
60
 
61
+ export const redirectUrlMiddleware: Middleware = () => {
62
+ return (next) => (action) => {
63
+ const result: CheckoutResult = next(action);
64
+ const redirectUrl = result?.payload?.redirect_url;
65
+
66
+ if (redirectUrl) {
67
+ const currentLocale = getCookie('pz-locale');
68
+
69
+ let url = redirectUrl;
70
+
71
+ if (currentLocale && !redirectUrl.includes('/orders/redirection')) {
72
+ const { defaultLocaleValue, localeUrlStrategy } = settings.localization;
73
+
74
+ url =
75
+ currentLocale === defaultLocaleValue &&
76
+ localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales
77
+ ? redirectUrl
78
+ : `/${currentLocale}${redirectUrl}`;
79
+ }
80
+
81
+ window.location.href = url;
82
+ }
83
+
84
+ return result;
85
+ };
86
+ };
87
+
60
88
  export const contextListMiddleware: Middleware = ({
61
89
  dispatch,
62
90
  getState
@@ -5,6 +5,7 @@ import { ROUTES } from 'routes';
5
5
  import type { Middleware } from '@reduxjs/toolkit';
6
6
  import {
7
7
  errorMiddleware,
8
+ redirectUrlMiddleware,
8
9
  contextListMiddleware,
9
10
  hepsiPayMiddleware,
10
11
  walletPaymentMiddleware
@@ -50,6 +51,7 @@ const middlewares = [
50
51
  rtkQueryResponseHandler,
51
52
  rtkQueryErrorHandler,
52
53
  errorMiddleware,
54
+ redirectUrlMiddleware,
53
55
  ...preOrderMiddlewares,
54
56
  contextListMiddleware,
55
57
  hepsiPayMiddleware,
@@ -0,0 +1,16 @@
1
+ const defaultPlugins = require('../plugins');
2
+
3
+ const getAkinonNextContent = (plugins = defaultPlugins) => {
4
+ return [
5
+ `./node_modules/@akinon/next/components/**/*.{js,ts,jsx,tsx}`,
6
+ `../../node_modules/@akinon/next/components/**/*.{js,ts,jsx,tsx}`,
7
+ ...plugins
8
+ .map((plugin) => [
9
+ `./node_modules/@akinon/${plugin}/**/*.{js,ts,jsx,tsx}`,
10
+ `../../node_modules/@akinon/${plugin}/**/*.{js,ts,jsx,tsx}`
11
+ ])
12
+ .flat()
13
+ ];
14
+ };
15
+
16
+ module.exports = getAkinonNextContent;
package/types/index.ts CHANGED
@@ -209,6 +209,7 @@ export interface Settings {
209
209
  */
210
210
  resetBasketOnCurrencyChange?: boolean;
211
211
  frontendIds?: Record<string, number>;
212
+ customNotFoundEnabled: boolean;
212
213
  }
213
214
 
214
215
  export interface CacheOptions {
@@ -0,0 +1,28 @@
1
+ import Settings from 'settings';
2
+
3
+ export default function getRootHostname(
4
+ url: string | undefined
5
+ ): string | null {
6
+ if (!url) return null;
7
+
8
+ try {
9
+ const urlObj = new URL(url);
10
+ const hostname = urlObj.hostname;
11
+ const parts = hostname.split('.');
12
+
13
+ const firstPart = parts[0];
14
+
15
+ const isLocale = Settings.localization.locales.some(
16
+ (locale) => locale.value === firstPart
17
+ );
18
+
19
+ if (isLocale) {
20
+ const hostnameAfterLocale = parts.slice(1).join('.');
21
+ return `.${hostnameAfterLocale}`;
22
+ }
23
+
24
+ return `.${hostname}`;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }
package/utils/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import settings from 'settings';
2
2
  import { LocaleUrlStrategy } from '../localization';
3
- import { CDNOptions, ClientRequestOptions, Locale } from '../types';
3
+ import { CDNOptions, ClientRequestOptions } from '../types';
4
4
 
5
5
  export * from './get-currency';
6
6
  export * from './menu-generator';
@@ -155,14 +155,15 @@ export function buildCDNUrl(url: string, config?: CDNOptions) {
155
155
  const { locales, localeUrlStrategy, defaultLocaleValue } =
156
156
  settings.localization;
157
157
 
158
- const isLocaleExcluded = (locale: Locale) =>
159
- ![LocaleUrlStrategy.ShowAllLocales, LocaleUrlStrategy.Subdomain].includes(
160
- localeUrlStrategy
161
- ) && locale.value !== defaultLocaleValue;
162
-
163
158
  export const urlLocaleMatcherRegex = new RegExp(
164
- `^/(${locales
165
- .filter((l) => !isLocaleExcluded(l))
159
+ `^/(${settings.localization.locales
160
+ .filter((l) =>
161
+ ![LocaleUrlStrategy.ShowAllLocales, LocaleUrlStrategy.Subdomain].includes(
162
+ settings.localization.localeUrlStrategy
163
+ )
164
+ ? l.value !== settings.localization.defaultLocaleValue
165
+ : l
166
+ )
166
167
  .map((l) => l.value)
167
168
  .join('|')})(?=/|$)`
168
169
  );