@akinon/next 1.3.0 → 1.5.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,21 @@
1
1
  # @akinon/next
2
2
 
3
+ ## 1.5.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Support GPay plugin
8
+ - Extend Next Metadata type
9
+
10
+ ## 1.4.0
11
+
12
+ ### Minor Changes
13
+
14
+ - Fix basket reset on mobile app checkout page
15
+ - Upgrade typescript to v5 and remove ts-expect-error lines
16
+ - Fix 3D payment with different currencies
17
+ - Support tailwind theming
18
+
3
19
  ## 1.3.0
4
20
 
5
21
  ### Minor Changes
package/api/auth.ts CHANGED
@@ -8,13 +8,16 @@ import { urlLocaleMatcherRegex } from '../utils';
8
8
  import logger from '@akinon/next/utils/log';
9
9
  import { AuthError } from '../types';
10
10
 
11
- async function getCurrentUser(sessionId: string) {
11
+ async function getCurrentUser(sessionId: string, currency = '') {
12
+ const headers = {
13
+ 'Content-Type': 'application/json',
14
+ Cookie: `osessionid=${sessionId}`,
15
+ 'x-currency': currency
16
+ };
17
+
12
18
  const currentUser = await (
13
19
  await fetch(URLS.user.currentUser, {
14
- headers: {
15
- 'Content-Type': 'application/json',
16
- Cookie: `osessionid=${sessionId}`
17
- }
20
+ headers
18
21
  })
19
22
  ).json();
20
23
 
@@ -51,7 +54,10 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
51
54
  return null;
52
55
  }
53
56
 
54
- const currentUser = await getCurrentUser(sessionId);
57
+ const currentUser = await getCurrentUser(
58
+ sessionId,
59
+ req.cookies['pz-currency'] ?? ''
60
+ );
55
61
  return currentUser;
56
62
  }
57
63
  }),
@@ -78,6 +84,7 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
78
84
  headers.set('Content-Type', 'application/json');
79
85
  headers.set('cookie', `${req.headers.cookie}`);
80
86
  headers.set('Accept-Language', `${language}`);
87
+ headers.set('x-currency', req.cookies['pz-currency'] ?? '');
81
88
 
82
89
  logger.debug('Trying to login/register', {
83
90
  formType: credentials.formType
@@ -160,7 +167,10 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
160
167
  }
161
168
  }
162
169
 
163
- const currentUser = await getCurrentUser(sessionId);
170
+ const currentUser = await getCurrentUser(
171
+ sessionId,
172
+ req.cookies['pz-currency'] ?? ''
173
+ );
164
174
  return currentUser;
165
175
  }
166
176
  })
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ const BASE_DIR =
7
+ path.resolve(__dirname, '../../../apps/projectzeropwa') || process.cwd();
8
+ const getFullPath = (relativePath) => path.join(BASE_DIR, relativePath);
9
+
10
+ const theme = require(getFullPath('src/theme.js'));
11
+
12
+ try {
13
+ const spawn = require('cross-spawn');
14
+ const tsConfigPath = getFullPath('tsconfig.json');
15
+
16
+ if (!fs.existsSync(tsConfigPath)) {
17
+ throw new Error(`tsconfig.json not found at ${tsConfigPath}`);
18
+ }
19
+
20
+ const tsConfigContent = fs.readFileSync(tsConfigPath, 'utf8');
21
+ const themePaths = ['"./*"'];
22
+
23
+ if (theme !== 'default') {
24
+ themePaths.splice(0, 0, `"themes/${theme}/*"`);
25
+ }
26
+
27
+ const newContent = tsConfigContent.replace(
28
+ /"@theme\/\*":\s\[.+\]/,
29
+ `"@theme/*": [${themePaths.join(', ')}]`
30
+ );
31
+
32
+ fs.writeFileSync(tsConfigPath, newContent);
33
+
34
+ if (fs.existsSync(getFullPath('../../turbo.json'))) {
35
+ spawn.sync('turbo', ['clean'], {
36
+ stdio: 'inherit'
37
+ });
38
+ }
39
+ } catch (error) {
40
+ console.error('Error:', error.message);
41
+ }
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ const spawn = require('cross-spawn');
4
+
5
+ spawn.sync('pz-install-plugins', [], { stdio: 'inherit' });
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ const spawn = require('cross-spawn');
4
+
5
+ spawn.sync('pz-install-theme', [], { stdio: 'inherit' });
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ const spawn = require('cross-spawn');
4
+
5
+ spawn.sync('pz-install-theme', [], { stdio: 'inherit' });
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -1,13 +1,15 @@
1
1
  import dynamic from 'next/dynamic';
2
2
  import plugins from 'plugins';
3
3
  import logger from '../utils/log';
4
+ import { useMemo } from 'react';
4
5
 
5
6
  enum Plugin {
6
7
  BasketGiftPack = 'pz-basket-gift-pack',
7
8
  ClickCollect = 'pz-click-collect',
8
9
  OneClickCheckout = 'pz-one-click-checkout',
9
10
  PayOnDelivery = 'pz-pay-on-delivery',
10
- CheckoutGiftPack = 'pz-checkout-gift-pack'
11
+ CheckoutGiftPack = 'pz-checkout-gift-pack',
12
+ GPay = 'pz-gpay'
11
13
  }
12
14
 
13
15
  export enum Component {
@@ -15,7 +17,8 @@ export enum Component {
15
17
  ClickCollect = 'ClickCollect',
16
18
  OneClickCheckoutButtons = 'OneClickCheckoutButtons',
17
19
  PayOnDelivery = 'PayOnDelivery',
18
- CheckoutGiftPack = 'CheckoutGiftPack'
20
+ CheckoutGiftPack = 'CheckoutGiftPack',
21
+ GPay = 'GPayOption'
19
22
  }
20
23
 
21
24
  const PluginComponents = new Map([
@@ -23,7 +26,8 @@ const PluginComponents = new Map([
23
26
  [Plugin.ClickCollect, [Component.ClickCollect]],
24
27
  [Plugin.OneClickCheckout, [Component.OneClickCheckoutButtons]],
25
28
  [Plugin.PayOnDelivery, [Component.PayOnDelivery]],
26
- [Plugin.CheckoutGiftPack, [Component.CheckoutGiftPack]]
29
+ [Plugin.CheckoutGiftPack, [Component.CheckoutGiftPack]],
30
+ [Plugin.GPay, [Component.GPay]]
27
31
  ]);
28
32
 
29
33
  const getPlugin = (component: Component) => {
@@ -47,31 +51,37 @@ export default function PluginModule({
47
51
  return null;
48
52
  }
49
53
 
50
- const Component = dynamic(
51
- () => {
52
- let promise: any;
54
+ const Component = useMemo(
55
+ () =>
56
+ dynamic(
57
+ () => {
58
+ let promise: any;
53
59
 
54
- try {
55
- if (plugin === Plugin.BasketGiftPack) {
56
- promise = import(`${'pz-basket-gift-pack'}`);
57
- } else if (plugin === Plugin.ClickCollect) {
58
- promise = import(`${'pz-click-collect'}`);
59
- } else if (plugin === Plugin.OneClickCheckout) {
60
- promise = import(`${'pz-one-click-checkout'}`);
61
- } else if (plugin === Plugin.PayOnDelivery) {
62
- promise = import(`${'pz-pay-on-delivery'}`);
63
- } else if (plugin === Plugin.CheckoutGiftPack) {
64
- promise = import(`${'pz-checkout-gift-pack'}`);
65
- }
66
- } catch (error) {
67
- logger.error(error);
68
- }
60
+ try {
61
+ if (plugin === Plugin.BasketGiftPack) {
62
+ promise = import(`${'pz-basket-gift-pack'}`);
63
+ } else if (plugin === Plugin.ClickCollect) {
64
+ promise = import(`${'pz-click-collect'}`);
65
+ } else if (plugin === Plugin.OneClickCheckout) {
66
+ promise = import(`${'pz-one-click-checkout'}`);
67
+ } else if (plugin === Plugin.PayOnDelivery) {
68
+ promise = import(`${'pz-pay-on-delivery'}`);
69
+ } else if (plugin === Plugin.CheckoutGiftPack) {
70
+ promise = import(`${'pz-checkout-gift-pack'}`);
71
+ } else if (plugin === Plugin.GPay) {
72
+ promise = import(`${'pz-gpay'}`);
73
+ }
74
+ } catch (error) {
75
+ logger.error(error);
76
+ }
69
77
 
70
- return promise
71
- ? promise.then((mod) => mod[component]).catch(() => () => null)
72
- : new Promise<any>((resolve) => resolve(() => null));
73
- },
74
- { ssr: false }
78
+ return promise
79
+ ? promise.then((mod) => mod[component]).catch(() => () => null)
80
+ : new Promise<any>((resolve) => resolve(() => null));
81
+ },
82
+ { ssr: false }
83
+ ),
84
+ [plugin]
75
85
  );
76
86
 
77
87
  return <Component {...props} />;
@@ -70,13 +70,21 @@ const userApi = api.injectEndpoints({
70
70
  method: 'POST',
71
71
  body
72
72
  })
73
- })
73
+ }),
74
+ changeEmailVerification: build.query<void, string>({
75
+ query: (token) => ({
76
+ url: buildClientRequestUrl(user.changeEmailVerification(token), {
77
+ contentType: 'application/json'
78
+ })
79
+ })
80
+ }),
74
81
  }),
75
82
  overrideExisting: false
76
83
  });
77
84
 
78
85
  export const {
79
86
  useGetCaptchaQuery,
87
+ useChangeEmailVerificationQuery,
80
88
  useValidateCaptchaMutation,
81
89
  useLogoutMutation,
82
90
  useForgotPasswordMutation
package/data/urls.ts CHANGED
@@ -142,6 +142,7 @@ export const user = {
142
142
  captcha: '/users/pz-captcha',
143
143
  profiles: '/users/profile',
144
144
  forgotPassword: '/users/password/reset',
145
+ changeEmailVerification: (token: string) => `/users/email-set-primary/${token}`,
145
146
  csrfToken: '/csrf_token'
146
147
  };
147
148
 
@@ -15,11 +15,10 @@ export const withSegmentDefaults =
15
15
  options: SegmentDefaultsOptions
16
16
  ) =>
17
17
  (props: T) => {
18
- let componentProps = { ...props };
18
+ const componentProps = { ...props };
19
19
 
20
20
  return (
21
21
  <>
22
- {/* @ts-expect-error Server Component */}
23
22
  <Component {...componentProps} />
24
23
  </>
25
24
  );
@@ -35,7 +35,6 @@ export const withSegmentDefaults =
35
35
 
36
36
  return await (
37
37
  <>
38
- {/* @ts-expect-error Server Component */}
39
38
  <Component {...componentProps} />
40
39
  </>
41
40
  );
@@ -3,6 +3,7 @@ import settings from 'settings';
3
3
  import { PzNextRequest } from '.';
4
4
  import logger from '../utils/log';
5
5
  import { basket } from '../data/urls';
6
+ import { urlLocaleMatcherRegex } from '../utils';
6
7
 
7
8
  const resetBasket = async (req: PzNextRequest) => {
8
9
  await fetch(`${settings.commerceUrl}${basket.getBasket}`, {
@@ -23,6 +24,7 @@ const withCurrency =
23
24
  const { currencies, defaultCurrencyCode, defaultLocaleValue } =
24
25
  settings.localization;
25
26
  const locale = req.middlewareParams.rewrites.locale ?? defaultLocaleValue;
27
+ const url = req.nextUrl.clone();
26
28
 
27
29
  if (!defaultCurrencyCode) {
28
30
  logger.error('Default currency code is not defined in settings.');
@@ -60,7 +62,19 @@ const withCurrency =
60
62
  activeCurrency = defaultCurrencyCode;
61
63
  }
62
64
 
63
- if (req.cookies.get('pz-currency')?.value !== activeCurrency) {
65
+ if (
66
+ req.cookies.get('pz-currency') &&
67
+ req.cookies.get('pz-currency')?.value !== activeCurrency &&
68
+ url.pathname.match(urlLocaleMatcherRegex) &&
69
+ !url.search.includes('mobile_app=true') &&
70
+ !url.search.includes('page=CreditCardThreeDSecurePage')
71
+ ) {
72
+ logger.info('Currency changed. Resetting basket...', {
73
+ sessionid: req.cookies.get('osessionid')?.value ?? '',
74
+ oldCurrency: req.cookies.get('pz-currency')?.value,
75
+ newCurrency: activeCurrency
76
+ });
77
+
64
78
  resetBasket(req);
65
79
  }
66
80
 
@@ -18,7 +18,8 @@ const withOauthLogin =
18
18
 
19
19
  const headers = {
20
20
  'x-forwarded-host':
21
- req.headers.get('x-forwarded-host') || req.headers.get('host') || ''
21
+ req.headers.get('x-forwarded-host') || req.headers.get('host') || '',
22
+ 'x-currency': req.cookies.get('pz-currency')?.value ?? ''
22
23
  };
23
24
 
24
25
  if (loginUrlMatcherRegex.test(url.pathname)) {
@@ -55,7 +55,8 @@ const withRedirectionPayment =
55
55
  headers: {
56
56
  'X-Requested-With': 'XMLHttpRequest',
57
57
  'Content-Type': 'application/x-www-form-urlencoded',
58
- Cookie: req.headers.get('cookie') ?? ''
58
+ Cookie: req.headers.get('cookie') ?? '',
59
+ 'x-currency': req.cookies.get('pz-currency')?.value ?? ''
59
60
  },
60
61
  body
61
62
  });
@@ -37,67 +37,95 @@ const withThreeDRedirection =
37
37
  return middleware(req, event);
38
38
  }
39
39
 
40
- const body = await streamToString(req.body);
41
40
  const requestUrl = `${Settings.commerceUrl}/orders/checkout/${url.search}`;
41
+ const requestHeaders = {
42
+ 'X-Requested-With': 'XMLHttpRequest',
43
+ 'Content-Type': 'application/x-www-form-urlencoded',
44
+ Cookie: `osessionid=${req.cookies.get('osessionid')?.value ?? ''}`,
45
+ 'x-currency': req.cookies.get('pz-currency')?.value ?? ''
46
+ };
42
47
 
43
- const request = await fetch(requestUrl, {
44
- method: 'POST',
45
- headers: {
46
- 'X-Requested-With': 'XMLHttpRequest',
47
- 'Content-Type': 'application/x-www-form-urlencoded',
48
- Cookie: `osessionid=${req.cookies.get('osessionid')?.value ?? ''}`
49
- },
50
- body
51
- });
52
-
53
- logger.info('Complete 3D payment request', {
54
- requestUrl,
55
- status: request.status
56
- });
57
-
58
- const response = await request.json();
59
-
60
- const { context_list: contextList } = response;
61
- const redirectionContext = contextList?.find(
62
- (context) => context.page_context?.redirect_url
63
- );
64
- const redirectUrl = redirectionContext?.page_context?.redirect_url;
65
-
66
- logger.info('Order success page context list', {
67
- middleware: 'three-d-redirection',
68
- contextList
69
- });
70
-
71
- if (!redirectUrl) {
72
- logger.warn(
73
- 'No redirection url for order success page found in page_context',
74
- {
75
- middleware: 'three-d-redirection'
76
- }
48
+ try {
49
+ const body = await streamToString(req.body);
50
+
51
+ const request = await fetch(requestUrl, {
52
+ method: 'POST',
53
+ headers: requestHeaders,
54
+ body
55
+ });
56
+
57
+ logger.info('Complete 3D payment request', {
58
+ requestUrl,
59
+ status: request.status,
60
+ requestHeaders
61
+ });
62
+
63
+ const response = await request.json();
64
+
65
+ const { context_list: contextList } = response;
66
+ const redirectionContext = contextList?.find(
67
+ (context) => context.page_context?.redirect_url
77
68
  );
78
- return middleware(req, event);
79
- }
69
+ const redirectUrl = redirectionContext?.page_context?.redirect_url;
80
70
 
81
- const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
82
- redirectUrl,
83
- req.cookies.get('pz-locale')?.value
84
- )}`;
71
+ logger.info('Order success page context list', {
72
+ middleware: 'three-d-redirection',
73
+ contextList
74
+ });
85
75
 
86
- logger.info('Redirecting to order success page', {
87
- middleware: 'three-d-redirection',
88
- redirectUrlWithLocale
89
- });
76
+ if (!redirectUrl) {
77
+ logger.warn(
78
+ 'No redirection url for order success page found in page_context. Redirecting to checkout page.',
79
+ {
80
+ middleware: 'three-d-redirection',
81
+ requestHeaders,
82
+ response: JSON.stringify(response)
83
+ }
84
+ );
85
+
86
+ const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
87
+ '/orders/checkout/',
88
+ req.cookies.get('pz-locale')?.value
89
+ )}`;
90
+
91
+ return NextResponse.redirect(redirectUrlWithLocale, 303);
92
+ }
90
93
 
91
- // Using POST method while redirecting causes an error,
92
- // So we use 303 status code to change the method to GET
93
- const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
94
+ const redirectUrlWithLocale = `${url.origin}${getUrlPathWithLocale(
95
+ redirectUrl,
96
+ req.cookies.get('pz-locale')?.value
97
+ )}`;
94
98
 
95
- nextResponse.headers.set(
96
- 'Set-Cookie',
97
- request.headers.get('set-cookie') ?? ''
98
- );
99
+ logger.info('Redirecting to order success page', {
100
+ middleware: 'three-d-redirection',
101
+ redirectUrlWithLocale
102
+ });
103
+
104
+ // Using POST method while redirecting causes an error,
105
+ // So we use 303 status code to change the method to GET
106
+ const nextResponse = NextResponse.redirect(redirectUrlWithLocale, 303);
99
107
 
100
- return nextResponse;
108
+ nextResponse.headers.set(
109
+ 'Set-Cookie',
110
+ request.headers.get('set-cookie') ?? ''
111
+ );
112
+
113
+ return nextResponse;
114
+ } catch (error) {
115
+ logger.error('Error while completing 3D payment', {
116
+ middleware: 'three-d-redirection',
117
+ error,
118
+ requestHeaders
119
+ });
120
+
121
+ return NextResponse.redirect(
122
+ `${url.origin}${getUrlPathWithLocale(
123
+ '/orders/checkout/',
124
+ req.cookies.get('pz-locale')?.value
125
+ )}`,
126
+ 303
127
+ );
128
+ }
101
129
  };
102
130
 
103
131
  export default withThreeDRedirection;
package/package.json CHANGED
@@ -1,11 +1,17 @@
1
1
  {
2
2
  "name": "@akinon/next",
3
3
  "description": "Core package for Project Zero Next",
4
- "version": "1.3.0",
4
+ "version": "1.5.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
8
- "pz-install-plugins": "bin/pz-install-plugins.js"
8
+ "pz-postbuild": "bin/pz-postbuild.js",
9
+ "pz-prebuild": "bin/pz-prebuild.js",
10
+ "pz-postinstall": "bin/pz-postinstall.js",
11
+ "pz-prestart": "bin/pz-prestart.js",
12
+ "pz-poststart": "bin/pz-poststart.js",
13
+ "pz-predev": "bin/pz-predev.js",
14
+ "pz-postdev": "bin/pz-postdev.js"
9
15
  },
10
16
  "dependencies": {
11
17
  "@reduxjs/toolkit": "1.9.2",
package/types/index.ts CHANGED
@@ -9,6 +9,7 @@ declare global {
9
9
 
10
10
  export * from './commerce';
11
11
  export * from './gtm';
12
+ export * from './metadata';
12
13
 
13
14
  export interface Locale {
14
15
  label: string;
@@ -0,0 +1,7 @@
1
+ import { Metadata as NextMetaData } from 'next';
2
+
3
+ export interface Metadata extends NextMetaData {
4
+ alternates?: {
5
+ languages: Record<string, string>;
6
+ };
7
+ }