import { JSX } from 'react'; import settings from 'settings'; import { ResolvedPageProps, ResolvedLayoutProps, ResolvedRootLayoutProps } from '../../types'; import { redirect } from 'next/navigation'; import { ServerVariables } from '../../utils/server-variables'; import { ROUTES } from 'routes'; import logger from '../../utils/log'; import { decodePzValue, getPzSegmentsConfig, getBuiltInSegments, isLegacyMode } from '../../utils/pz-segments'; type SegmentType = 'root-layout' | 'layout' | 'page'; interface SegmentDefaultsOptions { segmentType: SegmentType; } export const withSegmentDefaults = ( Component: ( props?: T ) => null | JSX.Element | Promise | Promise, options: SegmentDefaultsOptions ) => async (props: any) => { const resolvedParams = await props.params; const resolvedSearchParams = props.searchParams ? await props.searchParams : undefined; // Normalize Next 16's raw Record shape into a // URLSearchParams instance so v1 brands (which expect URLSearchParams) can // call .get/.has/etc without runtime breakage. const normalizedSearchParams = resolvedSearchParams instanceof URLSearchParams ? resolvedSearchParams : new URLSearchParams( Object.entries(resolvedSearchParams ?? {}).flatMap(([k, v]) => Array.isArray(v) ? v.map((item): [string, string] => [k, String(item)]) : v != null ? ([[k, String(v)]] as [string, string][]) : [] ) ); let componentProps = { ...props, params: resolvedParams, searchParams: normalizedSearchParams, ...('children' in props ? { children: (props as any).children } : {}) } as T; let localeValue: string; let currencyValue: string; if (isLegacyMode(settings)) { localeValue = resolvedParams.locale; currencyValue = resolvedParams.currency; } else { const pzConfig = getPzSegmentsConfig(settings); const parsed = decodePzValue(resolvedParams.pz, pzConfig); const builtIn = getBuiltInSegments(parsed, settings); localeValue = builtIn.locale; currencyValue = builtIn.currency; } if (options.segmentType === 'root-layout') { componentProps = (await addRootLayoutProps( componentProps as ResolvedRootLayoutProps, localeValue )) as T; checkRedisVariables(); } ServerVariables.locale = localeValue; ServerVariables.currency = currencyValue; return await ( <> ); }; const addRootLayoutProps = async ( componentProps: ResolvedRootLayoutProps, localeValue: string ) => { if (isLegacyMode(settings)) { const params = componentProps.params; if ( params.commerce !== encodeURIComponent(decodeURI(settings.commerceUrl)) || !settings.localization.locales.find((l) => l.value === localeValue) ) { return redirect(ROUTES.HOME); } } else if ( !settings.localization.locales.find((l) => l.value === localeValue) ) { return redirect(ROUTES.HOME); } const { getTranslations } = settings.useOptimizedTranslations ? require('translations') : require('../../utils/server-translation'); const translations = await getTranslations(localeValue); componentProps.translations = translations; const locale = settings.localization.locales.find( (l) => l.value === localeValue ); const [isoCode] = locale.value.split('-'); componentProps.locale = { ...locale, isoCode }; return componentProps; }; const checkRedisVariables = () => { const requiredVariableValues = [ process.env.CACHE_HOST, process.env.CACHE_PORT ]; if (!settings.usePrettyUrlRoute) { requiredVariableValues.push(process.env.CACHE_SECRET); } if ( !requiredVariableValues.every((v) => v) && process.env.NODE_ENV === 'production' ) { logger.warn('Redis cache variables are not set', { requiredVariables: ['CACHE_HOST', 'CACHE_PORT', 'CACHE_SECRET'], values: requiredVariableValues }); } };