@akinon/next 2.0.0-beta.16 → 2.0.0-beta.18
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 +22 -0
- package/api/barcode-search.ts +59 -0
- package/bin/pz-generate-routes.js +11 -1
- package/components/client-root.tsx +12 -2
- package/components/index.ts +1 -0
- package/components/logger-popup.tsx +213 -0
- package/components/plugin-module.tsx +2 -1
- package/data/client/account.ts +5 -1
- package/data/client/basket.ts +39 -0
- package/data/client/checkout.ts +207 -26
- package/hooks/index.ts +2 -0
- package/hooks/use-logger-context.tsx +114 -0
- package/hooks/use-logger.ts +92 -0
- package/middlewares/bfcache-headers.ts +18 -0
- package/middlewares/default.ts +8 -6
- package/middlewares/index.ts +3 -1
- package/middlewares/oauth-login.ts +200 -57
- package/package.json +2 -2
- package/redux/middlewares/checkout.ts +9 -0
- package/redux/reducers/checkout.ts +6 -0
- package/types/commerce/checkout.ts +15 -0
package/data/client/checkout.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '../../redux/reducers/checkout';
|
|
11
11
|
import {
|
|
12
12
|
CheckoutContext,
|
|
13
|
+
AccountUsage,
|
|
13
14
|
ExtraField,
|
|
14
15
|
GuestLoginFormParams,
|
|
15
16
|
Order,
|
|
@@ -32,6 +33,16 @@ import {
|
|
|
32
33
|
buildDirectPurchaseForm,
|
|
33
34
|
buildPurchaseForm
|
|
34
35
|
} from '@akinon/pz-masterpass/src/utils';
|
|
36
|
+
import { devLogger } from '@akinon/next/hooks/use-logger-context';
|
|
37
|
+
import { LogLevel } from '@akinon/next/hooks/use-logger';
|
|
38
|
+
|
|
39
|
+
const getLatestState = async (getState: () => any): Promise<any> => {
|
|
40
|
+
await new Promise((resolve) => setTimeout(resolve, 250));
|
|
41
|
+
|
|
42
|
+
return getState();
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const recentLogMessages = new Map<string, number>();
|
|
35
46
|
|
|
36
47
|
const getStore = async (): Promise<AppStore> => {
|
|
37
48
|
const { store } = await import('redux/store');
|
|
@@ -48,6 +59,53 @@ interface CheckoutResponse {
|
|
|
48
59
|
redirect_url?: string;
|
|
49
60
|
}
|
|
50
61
|
|
|
62
|
+
const validateCheckoutState = (
|
|
63
|
+
state: any,
|
|
64
|
+
validations: Array<{
|
|
65
|
+
condition: (state: any) => boolean;
|
|
66
|
+
errorMessage: string;
|
|
67
|
+
severity?: LogLevel;
|
|
68
|
+
data?: any;
|
|
69
|
+
action?: () => void;
|
|
70
|
+
}>
|
|
71
|
+
) => {
|
|
72
|
+
validations.forEach(
|
|
73
|
+
({
|
|
74
|
+
condition,
|
|
75
|
+
errorMessage,
|
|
76
|
+
severity = 'error',
|
|
77
|
+
data: logData,
|
|
78
|
+
action
|
|
79
|
+
}) => {
|
|
80
|
+
if (condition(state)) {
|
|
81
|
+
const now = Date.now();
|
|
82
|
+
const lastLogged = recentLogMessages.get(errorMessage) || 0;
|
|
83
|
+
|
|
84
|
+
if (now - lastLogged > 2000) {
|
|
85
|
+
recentLogMessages.set(errorMessage, now);
|
|
86
|
+
|
|
87
|
+
switch (severity) {
|
|
88
|
+
case 'error':
|
|
89
|
+
devLogger.error(
|
|
90
|
+
errorMessage,
|
|
91
|
+
logData || state.checkout?.preOrder
|
|
92
|
+
);
|
|
93
|
+
action?.();
|
|
94
|
+
break;
|
|
95
|
+
case 'warn':
|
|
96
|
+
devLogger.warn(errorMessage, logData || state.checkout?.preOrder);
|
|
97
|
+
action?.();
|
|
98
|
+
break;
|
|
99
|
+
default:
|
|
100
|
+
devLogger.info(errorMessage, logData || state.checkout?.preOrder);
|
|
101
|
+
action?.();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
51
109
|
interface SetAddressesParams {
|
|
52
110
|
shippingAddressPk: number;
|
|
53
111
|
billingAddressPk: number;
|
|
@@ -194,6 +252,20 @@ const completeMasterpassPayment = async (
|
|
|
194
252
|
});
|
|
195
253
|
};
|
|
196
254
|
|
|
255
|
+
let checkoutAbortController = new AbortController();
|
|
256
|
+
|
|
257
|
+
export const getCheckoutAbortSignal = () => {
|
|
258
|
+
if (checkoutAbortController.signal.aborted) {
|
|
259
|
+
checkoutAbortController = new AbortController();
|
|
260
|
+
}
|
|
261
|
+
return checkoutAbortController.signal;
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const abortCheckout = () => {
|
|
265
|
+
checkoutAbortController.abort();
|
|
266
|
+
checkoutAbortController = new AbortController();
|
|
267
|
+
};
|
|
268
|
+
|
|
197
269
|
export const checkoutApi = api.injectEndpoints({
|
|
198
270
|
endpoints: (build) => ({
|
|
199
271
|
fetchCheckout: build.query<CheckoutResponse, void>({
|
|
@@ -380,12 +452,33 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
380
452
|
method: 'POST',
|
|
381
453
|
body: {
|
|
382
454
|
delivery_option: String(pk)
|
|
383
|
-
}
|
|
455
|
+
},
|
|
456
|
+
signal: getCheckoutAbortSignal()
|
|
384
457
|
}),
|
|
385
|
-
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
458
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
|
|
386
459
|
dispatch(setShippingStepBusy(true));
|
|
387
|
-
|
|
388
|
-
|
|
460
|
+
|
|
461
|
+
const state = await getLatestState(getState);
|
|
462
|
+
|
|
463
|
+
validateCheckoutState(state, [
|
|
464
|
+
{
|
|
465
|
+
condition: (state) => {
|
|
466
|
+
const preOrder = state.checkout?.preOrder;
|
|
467
|
+
|
|
468
|
+
return preOrder?.basket?.basketitem_set?.length === 0;
|
|
469
|
+
},
|
|
470
|
+
errorMessage:
|
|
471
|
+
'Your shopping basket is empty. Please add items to your basket before selecting a delivery option.',
|
|
472
|
+
action: () => abortCheckout()
|
|
473
|
+
}
|
|
474
|
+
]);
|
|
475
|
+
|
|
476
|
+
try {
|
|
477
|
+
await queryFulfilled;
|
|
478
|
+
dispatch(setShippingStepBusy(false));
|
|
479
|
+
} catch (error) {
|
|
480
|
+
dispatch(setShippingStepBusy(false));
|
|
481
|
+
}
|
|
389
482
|
}
|
|
390
483
|
}),
|
|
391
484
|
setAddresses: build.mutation<CheckoutResponse, SetAddressesParams>({
|
|
@@ -397,12 +490,36 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
397
490
|
body: {
|
|
398
491
|
shipping_address: String(shippingAddressPk),
|
|
399
492
|
billing_address: String(billingAddressPk)
|
|
400
|
-
}
|
|
493
|
+
},
|
|
494
|
+
signal: getCheckoutAbortSignal()
|
|
401
495
|
}),
|
|
402
|
-
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
496
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
|
|
403
497
|
dispatch(setShippingStepBusy(true));
|
|
404
|
-
|
|
405
|
-
|
|
498
|
+
|
|
499
|
+
const state = await getLatestState(getState);
|
|
500
|
+
|
|
501
|
+
validateCheckoutState(state, [
|
|
502
|
+
{
|
|
503
|
+
condition: (state) => {
|
|
504
|
+
const deliveryOptions = state.checkout?.deliveryOptions;
|
|
505
|
+
const preOrder = state.checkout?.preOrder;
|
|
506
|
+
|
|
507
|
+
return deliveryOptions?.length > 0
|
|
508
|
+
? preOrder && !preOrder.delivery_option?.pk
|
|
509
|
+
: false;
|
|
510
|
+
},
|
|
511
|
+
errorMessage:
|
|
512
|
+
'You need to select a delivery option before setting your addresses. Dispatch setAddresses action after delivery option selection.',
|
|
513
|
+
action: () => abortCheckout()
|
|
514
|
+
}
|
|
515
|
+
]);
|
|
516
|
+
|
|
517
|
+
try {
|
|
518
|
+
await queryFulfilled;
|
|
519
|
+
dispatch(setShippingStepBusy(false));
|
|
520
|
+
} catch (error) {
|
|
521
|
+
dispatch(setShippingStepBusy(false));
|
|
522
|
+
}
|
|
406
523
|
}
|
|
407
524
|
}),
|
|
408
525
|
setShippingOption: build.mutation<CheckoutResponse, number>({
|
|
@@ -413,12 +530,43 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
413
530
|
method: 'POST',
|
|
414
531
|
body: {
|
|
415
532
|
shipping_option: String(pk)
|
|
416
|
-
}
|
|
533
|
+
},
|
|
534
|
+
signal: getCheckoutAbortSignal()
|
|
417
535
|
}),
|
|
418
|
-
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
536
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
|
|
419
537
|
dispatch(setShippingStepBusy(true));
|
|
420
|
-
|
|
421
|
-
|
|
538
|
+
|
|
539
|
+
const state = await getLatestState(getState);
|
|
540
|
+
|
|
541
|
+
validateCheckoutState(state, [
|
|
542
|
+
{
|
|
543
|
+
condition: (state) => {
|
|
544
|
+
const preOrder = state.checkout?.preOrder;
|
|
545
|
+
|
|
546
|
+
return !preOrder?.billing_address;
|
|
547
|
+
},
|
|
548
|
+
errorMessage:
|
|
549
|
+
'You need to provide a billing address before selecting a shipping option. Dispatch setShippingOption action after billing address selection.',
|
|
550
|
+
action: () => abortCheckout()
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
condition: (state) => {
|
|
554
|
+
const preOrder = state.checkout?.preOrder;
|
|
555
|
+
|
|
556
|
+
return !preOrder?.shipping_address;
|
|
557
|
+
},
|
|
558
|
+
errorMessage:
|
|
559
|
+
'You need to provide a shipping address before selecting a shipping option. Dispatch setShippingOption action after shipping address selection.',
|
|
560
|
+
action: () => abortCheckout()
|
|
561
|
+
}
|
|
562
|
+
]);
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
await queryFulfilled;
|
|
566
|
+
dispatch(setShippingStepBusy(false));
|
|
567
|
+
} catch (error) {
|
|
568
|
+
dispatch(setShippingStepBusy(false));
|
|
569
|
+
}
|
|
422
570
|
}
|
|
423
571
|
}),
|
|
424
572
|
setDataSourceShippingOptions: build.mutation<CheckoutResponse, number[]>({
|
|
@@ -466,16 +614,37 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
466
614
|
method: 'POST',
|
|
467
615
|
body: {
|
|
468
616
|
payment_option: String(pk)
|
|
469
|
-
}
|
|
617
|
+
},
|
|
618
|
+
signal: getCheckoutAbortSignal()
|
|
470
619
|
}),
|
|
471
|
-
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
620
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled, getState }) {
|
|
472
621
|
dispatch(setPaymentStepBusy(true));
|
|
473
622
|
dispatch(setInstallmentOptions([]));
|
|
474
623
|
dispatch(setBankAccounts([]));
|
|
475
624
|
dispatch(setSelectedBankAccountPk(null));
|
|
476
625
|
dispatch(setCardType(null));
|
|
477
|
-
|
|
478
|
-
|
|
626
|
+
|
|
627
|
+
const state = await getLatestState(getState);
|
|
628
|
+
|
|
629
|
+
validateCheckoutState(state, [
|
|
630
|
+
{
|
|
631
|
+
condition: (state) => {
|
|
632
|
+
const preOrder = state.checkout?.preOrder;
|
|
633
|
+
|
|
634
|
+
return !preOrder?.shipping_option?.pk;
|
|
635
|
+
},
|
|
636
|
+
errorMessage:
|
|
637
|
+
'You need to select a shipping option before choosing a payment method. Dispatch setPaymentOption action after shipping option selection.',
|
|
638
|
+
action: () => abortCheckout()
|
|
639
|
+
}
|
|
640
|
+
]);
|
|
641
|
+
|
|
642
|
+
try {
|
|
643
|
+
await queryFulfilled;
|
|
644
|
+
dispatch(setPaymentStepBusy(false));
|
|
645
|
+
} catch (error) {
|
|
646
|
+
dispatch(setPaymentStepBusy(false));
|
|
647
|
+
}
|
|
479
648
|
}
|
|
480
649
|
}),
|
|
481
650
|
setWalletSelectionPage: build.mutation<
|
|
@@ -776,16 +945,28 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
776
945
|
url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage)
|
|
777
946
|
})
|
|
778
947
|
}),
|
|
779
|
-
payWithLoyaltyBalance: build.mutation<
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
948
|
+
payWithLoyaltyBalance: build.mutation<
|
|
949
|
+
any,
|
|
950
|
+
string | { account_usages: AccountUsage[] }
|
|
951
|
+
>({
|
|
952
|
+
query: (params) => {
|
|
953
|
+
const isAccountUsages =
|
|
954
|
+
typeof params === 'object' && 'account_usages' in params;
|
|
955
|
+
|
|
956
|
+
return {
|
|
957
|
+
url: buildCheckoutRequestUrl(checkout.loyaltyMoneyUsage, {
|
|
958
|
+
useFormData: true
|
|
959
|
+
}),
|
|
960
|
+
method: 'POST',
|
|
961
|
+
body: isAccountUsages
|
|
962
|
+
? {
|
|
963
|
+
account_usages: JSON.stringify(params.account_usages)
|
|
964
|
+
}
|
|
965
|
+
: {
|
|
966
|
+
loyalty_amount_to_use: params
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
},
|
|
789
970
|
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
790
971
|
dispatch(setPaymentStepBusy(true));
|
|
791
972
|
dispatch(setPaymentOptions([]));
|
package/hooks/index.ts
CHANGED
|
@@ -10,5 +10,7 @@ export * from './use-mobile-iframe-handler';
|
|
|
10
10
|
export * from './use-payment-options';
|
|
11
11
|
export * from './use-pagination';
|
|
12
12
|
export * from './use-message-listener';
|
|
13
|
+
export * from './use-logger';
|
|
14
|
+
export * from './use-logger-context';
|
|
13
15
|
export * from './use-sentry-uncaught-errors';
|
|
14
16
|
export * from './use-pz-params';
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React, {
|
|
4
|
+
createContext,
|
|
5
|
+
useContext,
|
|
6
|
+
ReactNode,
|
|
7
|
+
useMemo,
|
|
8
|
+
useRef,
|
|
9
|
+
useEffect
|
|
10
|
+
} from 'react';
|
|
11
|
+
import { useLogger, LogEntry, LogLevel } from './use-logger';
|
|
12
|
+
|
|
13
|
+
const LOG_LEVELS: LogLevel[] = ['info', 'warn', 'error'];
|
|
14
|
+
|
|
15
|
+
interface LoggerContextType {
|
|
16
|
+
logs: LogEntry[];
|
|
17
|
+
isVisible: boolean;
|
|
18
|
+
toggleVisibility: () => void;
|
|
19
|
+
clearLogs: () => void;
|
|
20
|
+
info: (message: string, payload?: any) => string;
|
|
21
|
+
warn: (message: string, payload?: any) => string;
|
|
22
|
+
error: (message: string, payload?: any) => string;
|
|
23
|
+
isDevelopment: boolean;
|
|
24
|
+
hasError: boolean;
|
|
25
|
+
hasWarning: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const LoggerContext = createContext<LoggerContextType | undefined>(undefined);
|
|
29
|
+
|
|
30
|
+
let globalAddLogFunction:
|
|
31
|
+
| ((level: string, message: string, payload?: any) => string)
|
|
32
|
+
| null = null;
|
|
33
|
+
|
|
34
|
+
// temporary queue for logs generated before the logger is initialized
|
|
35
|
+
const pendingLogs: Array<{ level: string; message: string; payload?: any }> =
|
|
36
|
+
[];
|
|
37
|
+
|
|
38
|
+
const createLogFunction =
|
|
39
|
+
(level: LogLevel) => (message: string, payload?: any) => {
|
|
40
|
+
if (
|
|
41
|
+
typeof window !== 'undefined' &&
|
|
42
|
+
process.env.NODE_ENV === 'development'
|
|
43
|
+
) {
|
|
44
|
+
try {
|
|
45
|
+
if (globalAddLogFunction) {
|
|
46
|
+
globalAddLogFunction(level, message, payload);
|
|
47
|
+
} else {
|
|
48
|
+
pendingLogs.push({ level, message, payload });
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// prevent errors
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return '';
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const stableLogger = LOG_LEVELS.reduce((logger, level) => {
|
|
59
|
+
logger[level] = createLogFunction(level);
|
|
60
|
+
|
|
61
|
+
return logger;
|
|
62
|
+
}, {} as Record<LogLevel, (message: string, payload?: any) => string>);
|
|
63
|
+
|
|
64
|
+
export const LoggerProvider = ({ children }: { children: ReactNode }) => {
|
|
65
|
+
const loggerHook = useLogger();
|
|
66
|
+
|
|
67
|
+
const addLogRef = useRef<
|
|
68
|
+
(level: string, message: string, payload?: any) => string
|
|
69
|
+
>((level, message, payload) => {
|
|
70
|
+
if (LOG_LEVELS.includes(level as LogLevel)) {
|
|
71
|
+
return loggerHook[level as LogLevel](message, payload);
|
|
72
|
+
}
|
|
73
|
+
return '';
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
useEffect(() => {
|
|
77
|
+
globalAddLogFunction = addLogRef.current;
|
|
78
|
+
|
|
79
|
+
if (pendingLogs.length > 0) {
|
|
80
|
+
pendingLogs.forEach((log) => {
|
|
81
|
+
if (globalAddLogFunction) {
|
|
82
|
+
globalAddLogFunction(log.level, log.message, log.payload);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
pendingLogs.length = 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return () => {
|
|
90
|
+
globalAddLogFunction = null;
|
|
91
|
+
};
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const contextValue = useMemo(
|
|
95
|
+
() => loggerHook,
|
|
96
|
+
[loggerHook.logs, loggerHook.isVisible, loggerHook.isDevelopment] // eslint-disable-line react-hooks/exhaustive-deps
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<LoggerContext.Provider value={contextValue}>
|
|
101
|
+
{children}
|
|
102
|
+
</LoggerContext.Provider>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export const useLoggerContext = () => {
|
|
107
|
+
const context = useContext(LoggerContext);
|
|
108
|
+
if (context === undefined) {
|
|
109
|
+
throw new Error('useLoggerContext must be used within a LoggerProvider');
|
|
110
|
+
}
|
|
111
|
+
return context;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export const devLogger = stableLogger;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect, useRef, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
export type LogLevel = 'info' | 'warn' | 'error';
|
|
4
|
+
|
|
5
|
+
export interface LogEntry {
|
|
6
|
+
id: string;
|
|
7
|
+
level: LogLevel;
|
|
8
|
+
message: string;
|
|
9
|
+
timestamp: Date;
|
|
10
|
+
payload?: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const LOG_LEVELS: LogEntry['level'][] = ['info', 'warn', 'error'];
|
|
14
|
+
|
|
15
|
+
export function useLogger() {
|
|
16
|
+
const [logs, setLogs] = useState<LogEntry[]>([]);
|
|
17
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
18
|
+
const logsRef = useRef<LogEntry[]>([]);
|
|
19
|
+
const [isDevelopment, setIsDevelopment] = useState(false);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
setIsDevelopment(
|
|
23
|
+
process.env.NODE_ENV === 'development' ||
|
|
24
|
+
window.location.hostname === 'localhost' ||
|
|
25
|
+
window.location.hostname === '127.0.0.1'
|
|
26
|
+
);
|
|
27
|
+
}, []);
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
logsRef.current = logs;
|
|
31
|
+
}, [logs]);
|
|
32
|
+
|
|
33
|
+
const addLog = useCallback(
|
|
34
|
+
(level: LogEntry['level'], message: string, payload?: any) => {
|
|
35
|
+
const newLog: LogEntry = {
|
|
36
|
+
id: Math.random().toString(36).substring(2, 9),
|
|
37
|
+
level,
|
|
38
|
+
message,
|
|
39
|
+
timestamp: new Date(),
|
|
40
|
+
payload
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
setLogs((prevLogs) => [newLog, ...prevLogs]);
|
|
44
|
+
|
|
45
|
+
return newLog.id;
|
|
46
|
+
},
|
|
47
|
+
[]
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const clearLogs = useCallback(() => {
|
|
51
|
+
setLogs([]);
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
const toggleVisibility = useCallback(() => {
|
|
55
|
+
setIsVisible((prev) => !prev);
|
|
56
|
+
}, []);
|
|
57
|
+
|
|
58
|
+
const createLogMethod = useCallback(
|
|
59
|
+
(level: LogEntry['level']) => (message: string, payload?: any) =>
|
|
60
|
+
addLog(level, message, payload),
|
|
61
|
+
[addLog]
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const logMethods = useMemo(
|
|
65
|
+
() =>
|
|
66
|
+
LOG_LEVELS.reduce((methods, level) => {
|
|
67
|
+
methods[level] = createLogMethod(level);
|
|
68
|
+
return methods;
|
|
69
|
+
}, {} as Record<LogEntry['level'], (message: string, payload?: any) => string>),
|
|
70
|
+
[createLogMethod]
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const hasError = useMemo(
|
|
74
|
+
() => logs.some((log) => log.level === 'error'),
|
|
75
|
+
[logs]
|
|
76
|
+
);
|
|
77
|
+
const hasWarning = useMemo(
|
|
78
|
+
() => logs.some((log) => log.level === 'warn'),
|
|
79
|
+
[logs]
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
logs,
|
|
84
|
+
isVisible,
|
|
85
|
+
toggleVisibility,
|
|
86
|
+
clearLogs,
|
|
87
|
+
...logMethods,
|
|
88
|
+
isDevelopment,
|
|
89
|
+
hasError,
|
|
90
|
+
hasWarning
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NextMiddleware } from 'next/server';
|
|
2
|
+
|
|
3
|
+
const withBfcacheHeaders = (middleware: NextMiddleware): NextMiddleware => {
|
|
4
|
+
return async (req, event) => {
|
|
5
|
+
const response = await middleware(req, event);
|
|
6
|
+
|
|
7
|
+
if (process.env.BF_CACHE === 'true' && response) {
|
|
8
|
+
response.headers.set(
|
|
9
|
+
'Cache-Control',
|
|
10
|
+
'private, no-cache, max-age=0, must-revalidate'
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return response;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default withBfcacheHeaders;
|
package/middlewares/default.ts
CHANGED
|
@@ -14,7 +14,8 @@ import {
|
|
|
14
14
|
withUrlRedirection,
|
|
15
15
|
withCompleteWallet,
|
|
16
16
|
withWalletCompleteRedirection,
|
|
17
|
-
withMasterpassRestCallback
|
|
17
|
+
withMasterpassRestCallback,
|
|
18
|
+
withBfcacheHeaders
|
|
18
19
|
} from '.';
|
|
19
20
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
20
21
|
import { getPzSegmentsConfig, encodePzValue, isLegacyMode } from '../utils/pz-segments';
|
|
@@ -256,10 +257,11 @@ const withPzDefault =
|
|
|
256
257
|
withCompleteWallet(
|
|
257
258
|
withWalletCompleteRedirection(
|
|
258
259
|
withMasterpassRestCallback(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
withBfcacheHeaders(
|
|
261
|
+
async (
|
|
262
|
+
req: PzNextRequest,
|
|
263
|
+
event: NextFetchEvent
|
|
264
|
+
) => {
|
|
263
265
|
let middlewareResult: NextResponse | void =
|
|
264
266
|
NextResponse.next();
|
|
265
267
|
|
|
@@ -563,7 +565,7 @@ const withPzDefault =
|
|
|
563
565
|
}
|
|
564
566
|
|
|
565
567
|
return middlewareResult;
|
|
566
|
-
}
|
|
568
|
+
})
|
|
567
569
|
)
|
|
568
570
|
)
|
|
569
571
|
)
|
package/middlewares/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ import withSavedCardRedirection from './saved-card-redirection';
|
|
|
12
12
|
import withCompleteWallet from './complete-wallet';
|
|
13
13
|
import withWalletCompleteRedirection from './wallet-complete-redirection';
|
|
14
14
|
import withMasterpassRestCallback from './masterpass-rest-callback';
|
|
15
|
+
import withBfcacheHeaders from './bfcache-headers';
|
|
15
16
|
import { NextRequest } from 'next/server';
|
|
16
17
|
|
|
17
18
|
export {
|
|
@@ -28,7 +29,8 @@ export {
|
|
|
28
29
|
withSavedCardRedirection,
|
|
29
30
|
withCompleteWallet,
|
|
30
31
|
withWalletCompleteRedirection,
|
|
31
|
-
withMasterpassRestCallback
|
|
32
|
+
withMasterpassRestCallback,
|
|
33
|
+
withBfcacheHeaders
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
export interface PzNextRequest extends NextRequest {
|