@akinon/next 2.0.59-rc.0 → 2.0.59

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,34 +1,10 @@
1
1
  # @akinon/next
2
2
 
3
- ## 2.0.59-rc.0
3
+ ## 2.0.59
4
4
 
5
5
  ### Patch Changes
6
6
 
7
- - 0cf9ea239: BRDG-16491: Prevent redirect when iframe payment is active
8
- - 324f97d55: ZERO-4219: replace masterpass-rest-complete with masterpass-rest-callback
9
- - 51ea06888: ZERO-4377: Fix checkout card type state being cleared after valid bin number responses.
10
- - ZERO-4160: Enhance oauth-login middleware with improved request handling and logging
11
- - 2fea4143: ZERO-4620: Add runtime access to `NEXT_PUBLIC_*` environment variables from Client Components
12
- - b55acb768: ZERO-2577: Fix pagination bug and update usePagination hook and ensure pagination controls rendering correctly
13
- - 760258c1c: ZERO-4160: Enhance oauth-login middleware to handle fetch errors and improve response handling
14
- - 143be2b9d: ZERO-3457: Crop styles are customizable and logic improved for rendering similar products modal
15
- - 7889b08fe: ZERO-4276: Enhance route generation by adding .env loading and custom skip segments support
16
- - 9f8cd3bc5: ZERO-3449: AI Search Active Filters & Crop Style changes have been implemented
17
- - bfafa3f49: ZERO-4160: Refactor oauth-login middleware to use fetchCommerce for API calls and improve cookie handling
18
- - 57d7eb305: ZERO-4276: Refactor route generation logic by removing environment loading and simplifying skip segments handling
19
- - d99a6a7d5: ZERO-3457_1: Fixed the settings prop and made sure everything is customizable.
20
- - 9db81a714: ZERO-4365: Remove brand `@theme/*` alias imports from library packages
21
- - 591e345e1: ZERO-3855: Enhance credit card payment handling in checkout middlewares
22
- - 4de5303c5: ZERO-2504: add cookie filter to api client request
23
- - 95b139dc1: ZERO-3795: Remove duplicate entry for SavedCard in PluginComponents map
24
- - 1d00f2d06: BRDG-16664: Set secure flag for CSRF token cookies in useCaptcha and default middleware
25
- - 4ac7b2a1e: ZERO-4219: fix masterpass-rest callback route format and double-encoded error cookie
26
- - e9598c71: ZERO-4622: Images remotePatterns for improved readability
27
- - 4998a9631: ZERO-4168: Add server-side payload optimization
28
- - 804d2bd6: ZERO-4536: Add akinon.net domain to CSP frame-ancestors directive
29
- - 3909d3224: Edit the duplicate Plugin.SimilarProducts in the plugin-module.
30
- - 6a3d8a63: ZERO-4541: Fix URL query string formatting in getOrders and getOldOrders functions
31
- - e18836b20: ZERO-4160: Restore scope in Sentry addon configuration in akinon.json
7
+ - 9a1a2df: ZERO-4804: Port the deterministic checkout fix to v2 by removing the shared checkout abort controller and the 250 ms delayed validation, and guarding pre-order auto-select middlewares with pre_order preconditions
32
8
 
33
9
  ## 2.0.58
34
10
 
@@ -6,7 +6,6 @@ const findBaseDir = require('../utils/find-base-dir');
6
6
 
7
7
  const generateRoutes = () => {
8
8
  const baseDir = findBaseDir();
9
-
10
9
  const srcDir = path.join(baseDir, 'src');
11
10
  const appDir = path.join(srcDir, 'app');
12
11
 
@@ -35,10 +34,8 @@ const generateRoutes = () => {
35
34
  '[segment]',
36
35
  '[url]',
37
36
  '[theme]',
38
- '[member_type]',
39
- '[clienttype]'
37
+ '[member_type]'
40
38
  ];
41
-
42
39
  const skipCatchAllRoutes = ['[...prettyurl]', '[...not_found]'];
43
40
 
44
41
  const walkDirectory = (dir, basePath = '') => {
@@ -116,6 +116,7 @@ const PluginComponents = new Map([
116
116
  ]
117
117
  ],
118
118
  [Plugin.SavedCard, [Component.SavedCard, Component.IyzicoSavedCard]],
119
+ [Plugin.SavedCard, [Component.SavedCard]],
119
120
  [Plugin.FlowPayment, [Component.FlowPayment]],
120
121
  [
121
122
  Plugin.VirtualTryOn,
@@ -33,6 +33,15 @@ const customBaseQuery: BaseQueryFn<
33
33
  ).length > 1
34
34
  ) {
35
35
  api.abort('Mutation already in progress.');
36
+
37
+ // Short-circuit explicitly instead of relying on the abort to settle
38
+ // before the request reaches the network (ZERO-4638).
39
+ return {
40
+ error: {
41
+ status: 'CUSTOM_ERROR',
42
+ error: 'Mutation already in progress.'
43
+ } as FetchBaseQueryError
44
+ };
36
45
  }
37
46
 
38
47
  const baseQuery = fetchBaseQuery({
@@ -36,12 +36,6 @@ import {
36
36
  import { devLogger } from '@akinon/next/hooks/use-logger-context';
37
37
  import { LogLevel } from '@akinon/next/hooks/use-logger';
38
38
 
39
- const getLatestState = async (getState: () => any): Promise<any> => {
40
- await new Promise((resolve) => setTimeout(resolve, 250));
41
-
42
- return getState();
43
- };
44
-
45
39
  const recentLogMessages = new Map<string, number>();
46
40
 
47
41
  const getStore = async (): Promise<AppStore> => {
@@ -59,6 +53,10 @@ interface CheckoutResponse {
59
53
  redirect_url?: string;
60
54
  }
61
55
 
56
+ // Log-only diagnostics: checkout mutations are never aborted based on these
57
+ // checks anymore. Invalid auto-dispatches are prevented at their source in
58
+ // redux/middlewares/pre-order, and Commerce stays the authority for manual
59
+ // dispatches (ZERO-4638).
62
60
  const validateCheckoutState = (
63
61
  state: any,
64
62
  validations: Array<{
@@ -66,17 +64,10 @@ const validateCheckoutState = (
66
64
  errorMessage: string;
67
65
  severity?: LogLevel;
68
66
  data?: any;
69
- action?: () => void;
70
67
  }>
71
68
  ) => {
72
69
  validations.forEach(
73
- ({
74
- condition,
75
- errorMessage,
76
- severity = 'error',
77
- data: logData,
78
- action
79
- }) => {
70
+ ({ condition, errorMessage, severity = 'error', data: logData }) => {
80
71
  if (condition(state)) {
81
72
  const now = Date.now();
82
73
  const lastLogged = recentLogMessages.get(errorMessage) || 0;
@@ -90,15 +81,12 @@ const validateCheckoutState = (
90
81
  errorMessage,
91
82
  logData || state.checkout?.preOrder
92
83
  );
93
- action?.();
94
84
  break;
95
85
  case 'warn':
96
86
  devLogger.warn(errorMessage, logData || state.checkout?.preOrder);
97
- action?.();
98
87
  break;
99
88
  default:
100
89
  devLogger.info(errorMessage, logData || state.checkout?.preOrder);
101
- action?.();
102
90
  }
103
91
  }
104
92
  }
@@ -254,6 +242,12 @@ const completeMasterpassPayment = async (
254
242
 
255
243
  let checkoutAbortController = new AbortController();
256
244
 
245
+ /**
246
+ * @deprecated Checkout mutations no longer share an abort signal; nothing in
247
+ * the core aborts this controller anymore. Premature auto-dispatches are
248
+ * prevented in redux/middlewares/pre-order instead (ZERO-4638). Kept only so
249
+ * existing external imports keep compiling.
250
+ */
257
251
  export const getCheckoutAbortSignal = () => {
258
252
  if (checkoutAbortController.signal.aborted) {
259
253
  checkoutAbortController = new AbortController();
@@ -261,11 +255,6 @@ export const getCheckoutAbortSignal = () => {
261
255
  return checkoutAbortController.signal;
262
256
  };
263
257
 
264
- const abortCheckout = () => {
265
- checkoutAbortController.abort();
266
- checkoutAbortController = new AbortController();
267
- };
268
-
269
258
  export const checkoutApi = api.injectEndpoints({
270
259
  endpoints: (build) => ({
271
260
  fetchCheckout: build.query<CheckoutResponse, void>({
@@ -452,13 +441,12 @@ export const checkoutApi = api.injectEndpoints({
452
441
  method: 'POST',
453
442
  body: {
454
443
  delivery_option: String(pk)
455
- },
456
- signal: getCheckoutAbortSignal()
444
+ }
457
445
  }),
458
446
  async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
459
447
  dispatch(setShippingStepBusy(true));
460
448
 
461
- const state = await getLatestState(getState);
449
+ const state = getState();
462
450
 
463
451
  validateCheckoutState(state, [
464
452
  {
@@ -468,8 +456,7 @@ export const checkoutApi = api.injectEndpoints({
468
456
  return preOrder?.basket?.basketitem_set?.length === 0;
469
457
  },
470
458
  errorMessage:
471
- 'Your shopping basket is empty. Please add items to your basket before selecting a delivery option.',
472
- action: () => abortCheckout()
459
+ 'Your shopping basket is empty. Please add items to your basket before selecting a delivery option.'
473
460
  }
474
461
  ]);
475
462
 
@@ -490,13 +477,12 @@ export const checkoutApi = api.injectEndpoints({
490
477
  body: {
491
478
  shipping_address: String(shippingAddressPk),
492
479
  billing_address: String(billingAddressPk)
493
- },
494
- signal: getCheckoutAbortSignal()
480
+ }
495
481
  }),
496
482
  async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
497
483
  dispatch(setShippingStepBusy(true));
498
484
 
499
- const state = await getLatestState(getState);
485
+ const state = getState();
500
486
 
501
487
  validateCheckoutState(state, [
502
488
  {
@@ -509,8 +495,7 @@ export const checkoutApi = api.injectEndpoints({
509
495
  : false;
510
496
  },
511
497
  errorMessage:
512
- 'You need to select a delivery option before setting your addresses. Dispatch setAddresses action after delivery option selection.',
513
- action: () => abortCheckout()
498
+ 'You need to select a delivery option before setting your addresses. Dispatch setAddresses action after delivery option selection.'
514
499
  }
515
500
  ]);
516
501
 
@@ -530,13 +515,12 @@ export const checkoutApi = api.injectEndpoints({
530
515
  method: 'POST',
531
516
  body: {
532
517
  shipping_option: String(pk)
533
- },
534
- signal: getCheckoutAbortSignal()
518
+ }
535
519
  }),
536
520
  async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
537
521
  dispatch(setShippingStepBusy(true));
538
522
 
539
- const state = await getLatestState(getState);
523
+ const state = getState();
540
524
 
541
525
  validateCheckoutState(state, [
542
526
  {
@@ -546,8 +530,7 @@ export const checkoutApi = api.injectEndpoints({
546
530
  return !preOrder?.billing_address;
547
531
  },
548
532
  errorMessage:
549
- 'You need to provide a billing address before selecting a shipping option. Dispatch setShippingOption action after billing address selection.',
550
- action: () => abortCheckout()
533
+ 'You need to provide a billing address before selecting a shipping option. Dispatch setShippingOption action after billing address selection.'
551
534
  },
552
535
  {
553
536
  condition: (state) => {
@@ -556,8 +539,7 @@ export const checkoutApi = api.injectEndpoints({
556
539
  return !preOrder?.shipping_address;
557
540
  },
558
541
  errorMessage:
559
- 'You need to provide a shipping address before selecting a shipping option. Dispatch setShippingOption action after shipping address selection.',
560
- action: () => abortCheckout()
542
+ 'You need to provide a shipping address before selecting a shipping option. Dispatch setShippingOption action after shipping address selection.'
561
543
  }
562
544
  ]);
563
545
 
@@ -614,8 +596,7 @@ export const checkoutApi = api.injectEndpoints({
614
596
  method: 'POST',
615
597
  body: {
616
598
  payment_option: String(pk)
617
- },
618
- signal: getCheckoutAbortSignal()
599
+ }
619
600
  }),
620
601
  async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
621
602
  dispatch(setPaymentStepBusy(true));
@@ -624,7 +605,7 @@ export const checkoutApi = api.injectEndpoints({
624
605
  dispatch(setSelectedBankAccountPk(null));
625
606
  dispatch(setCardType(null));
626
607
 
627
- const state = await getLatestState(getState);
608
+ const state = getState();
628
609
 
629
610
  validateCheckoutState(state, [
630
611
  {
@@ -634,8 +615,7 @@ export const checkoutApi = api.injectEndpoints({
634
615
  return !preOrder?.shipping_option?.pk;
635
616
  },
636
617
  errorMessage:
637
- 'You need to select a shipping option before choosing a payment method. Dispatch setPaymentOption action after shipping option selection.',
638
- action: () => abortCheckout()
618
+ 'You need to select a shipping option before choosing a payment method. Dispatch setPaymentOption action after shipping option selection.'
639
619
  }
640
620
  ]);
641
621
 
@@ -738,6 +718,7 @@ export const checkoutApi = api.injectEndpoints({
738
718
  },
739
719
  async onQueryStarted(arg, { dispatch, queryFulfilled }) {
740
720
  dispatch(setPaymentStepBusy(true));
721
+ dispatch(setCardType(arg));
741
722
  await queryFulfilled;
742
723
  dispatch(setPaymentStepBusy(false));
743
724
  }
@@ -8,8 +8,6 @@ import { parse } from 'lossless-json';
8
8
  import logger from '../../utils/log';
9
9
  import { headers as nHeaders } from 'next/headers';
10
10
  import { ServerVariables } from '../../utils/server-variables';
11
- import { optimizeCategoryResponse } from '../../utils/payload-optimizer';
12
- import settings from 'settings';
13
11
 
14
12
  function getCategoryDataHandler(
15
13
  pk: number,
@@ -83,7 +81,7 @@ function getCategoryDataHandler(
83
81
  };
84
82
  }
85
83
 
86
- export const getCategoryData = async ({
84
+ export const getCategoryData = ({
87
85
  pk,
88
86
  searchParams,
89
87
  headers,
@@ -107,16 +105,6 @@ export const getCategoryData = async ({
107
105
  compressed: true
108
106
  }
109
107
  );
110
-
111
- if (settings.payloadOptimization?.enabled && result?.data) {
112
- try {
113
- return { ...result, data: optimizeCategoryResponse(result.data, settings.payloadOptimization) };
114
- } catch (e) {
115
- logger.error('Payload optimization failed for category', { pk, error: (e as Error).message });
116
- }
117
- }
118
-
119
- return result;
120
108
  };
121
109
 
122
110
  function getCategoryBySlugDataHandler(
@@ -7,8 +7,6 @@ import appFetch, { FetchResponseType } from '../../utils/app-fetch';
7
7
  import { parse } from 'lossless-json';
8
8
  import logger from '../../utils/log';
9
9
  import { ServerVariables } from '../../utils/server-variables';
10
- import { optimizeCategoryResponse } from '../../utils/payload-optimizer';
11
- import settings from 'settings';
12
10
 
13
11
  const getListDataHandler = (
14
12
  locale,
@@ -80,14 +78,4 @@ export const getListData = async ({
80
78
  compressed: true
81
79
  }
82
80
  );
83
-
84
- if (settings.payloadOptimization?.enabled && result) {
85
- try {
86
- return optimizeCategoryResponse(result, settings.payloadOptimization);
87
- } catch (e) {
88
- logger.error('Payload optimization failed for list', { error: (e as Error).message });
89
- }
90
- }
91
-
92
- return result;
93
81
  };
@@ -5,8 +5,6 @@ import appFetch from '../../utils/app-fetch';
5
5
  import { normalizeSearchParams } from '../../utils/normalize-search-params';
6
6
  import { ServerVariables } from '../../utils/server-variables';
7
7
  import logger from '../../utils/log';
8
- import { optimizeProductResponse } from '../../utils/payload-optimizer';
9
- import settings from 'settings';
10
8
 
11
9
  type GetProduct = {
12
10
  pk: number | string;
@@ -168,13 +166,5 @@ export const getProductData = async ({
168
166
  throw error;
169
167
  }
170
168
 
171
- if (settings.payloadOptimization?.enabled && result?.data) {
172
- try {
173
- return { ...result, data: optimizeProductResponse(result.data, settings.payloadOptimization) };
174
- } catch (e) {
175
- logger.error('Payload optimization failed for product', { pk, error: (e as Error).message });
176
- }
177
- }
178
-
179
169
  return result;
180
170
  };
@@ -5,9 +5,6 @@ import { generateCommerceSearchParams } from '../../utils';
5
5
  import { normalizeSearchParams } from '../../utils/normalize-search-params';
6
6
  import appFetch from '../../utils/app-fetch';
7
7
  import { ServerVariables } from '../../utils/server-variables';
8
- import { optimizeCategoryResponse } from '../../utils/payload-optimizer';
9
- import logger from '../../utils/log';
10
- import settings from 'settings';
11
8
 
12
9
  const getSpecialPageDataHandler = (
13
10
  pk: number,
@@ -60,14 +57,4 @@ export const getSpecialPageData = async ({
60
57
  compressed: true
61
58
  }
62
59
  );
63
-
64
- if (settings.payloadOptimization?.enabled && result) {
65
- try {
66
- return optimizeCategoryResponse(result, settings.payloadOptimization);
67
- } catch (e) {
68
- logger.error('Payload optimization failed for special-page', { pk, error: (e as Error).message });
69
- }
70
- }
71
-
72
- return result;
73
60
  };
@@ -4,9 +4,6 @@ import { CacheOptions, WidgetResultType, WidgetSchemaType } from '../../types';
4
4
  import appFetch from '../../utils/app-fetch';
5
5
  import { widgets } from '../urls';
6
6
  import { ServerVariables } from '../../utils/server-variables';
7
- import { optimizeWidgetResponse } from '../../utils/payload-optimizer';
8
- import logger from '../../utils/log';
9
- import settings from 'settings';
10
7
 
11
8
  const getWidgetDataHandler =
12
9
  (
@@ -56,7 +53,7 @@ export const getWidgetData = async <T>({
56
53
  cacheOptions?: CacheOptions;
57
54
  headers?: Record<string, string>;
58
55
  }): Promise<WidgetResultType<T>> => {
59
- const result = await Cache.wrap(
56
+ return Cache.wrap(
60
57
  CacheKey.Widget(slug),
61
58
  locale,
62
59
  getWidgetDataHandler(slug, locale, currency, headers),
@@ -65,16 +62,6 @@ export const getWidgetData = async <T>({
65
62
  ...cacheOptions
66
63
  }
67
64
  );
68
-
69
- if (settings.payloadOptimization?.enabled && result) {
70
- try {
71
- return optimizeWidgetResponse(result, settings.payloadOptimization) as WidgetResultType<T>;
72
- } catch (e) {
73
- logger.error('Payload optimization failed for widget', { slug, error: (e as Error).message });
74
- }
75
- }
76
-
77
- return result as WidgetResultType<T>;
78
65
  };
79
66
 
80
67
  const getCollectionWidgetDataHandler =
package/data/urls.ts CHANGED
@@ -40,11 +40,10 @@ export const account = {
40
40
  shipping_option_slug
41
41
  ? `&shipping_option_slug${shipping_option_operator}${shipping_option_slug}`
42
42
  : ''
43
- }${currency ? `&currency=${currency}` : ''}${
44
- filterType && filterValue ? `&${filterType}=${filterValue}` : ''
45
- }`,
43
+ }${currency ? `&currency=${currency}` : ''}
44
+ ${filterType && filterValue ? `&${filterType}=${filterValue}` : ''}`,
46
45
  getOldOrders: ({ page, limit }: { page?: number; limit?: number }) =>
47
- `/users/old-orders/?page=${page || 1}&limit=${limit || 12}`,
46
+ `/users/old-orders/?page=${page || 1}&limit=${limit || 12}}`,
48
47
  getQuotations: (page?: number, status?: string, limit?: number) =>
49
48
  `/b2b/my-quotations/?page=${page || 1}` +
50
49
  (status ? `&status=${status}` : '') +
@@ -184,11 +183,7 @@ export const product = {
184
183
  breadcrumbUrl: (menuitemmodel: string) =>
185
184
  `/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`,
186
185
  bundleProduct: (productPk: string, queryString: string) =>
187
- `/bundle-product/${productPk}/?${queryString}`,
188
- similarProducts: (params?: string) =>
189
- `/similar-products${params ? `?${params}` : ''}`,
190
- similarProductsList: (params?: string) =>
191
- `/similar-product-list${params ? `?${params}` : ''}`
186
+ `/bundle-product/${productPk}/?${queryString}`
192
187
  };
193
188
 
194
189
  export const wishlist = {
package/hooks/index.ts CHANGED
@@ -15,4 +15,3 @@ export * from './use-logger-context';
15
15
  export * from './use-sentry-uncaught-errors';
16
16
  export * from './use-pz-params';
17
17
  export * from './use-toast';
18
- export * from './use-client-env';
@@ -39,7 +39,7 @@ export const useCaptcha = () => {
39
39
  };
40
40
 
41
41
  if (csrfToken) {
42
- setCookie('csrftoken', csrfToken, { secure: true });
42
+ setCookie('csrftoken', csrfToken);
43
43
  }
44
44
 
45
45
  const onCaptchaChange = useCallback(async (response) => {