@websolutespa/bom-mixer-models 3.0.4 → 4.0.0-next.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,19 @@
1
1
  # @websolutespa/bom-mixer-models
2
2
 
3
+ ## 4.0.0-next.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+ - @websolutespa/bom-core@4.0.0-next.0
9
+ - @websolutespa/bom-mixer-store@4.0.0-next.0
10
+
11
+ ## 3.0.5
12
+
13
+ ### Patch Changes
14
+
15
+ - Modified: RouteInterceptor autoDetection temporary redirect
16
+
3
17
  ## 3.0.4
4
18
 
5
19
  ### Patch Changes
@@ -1,6 +1,6 @@
1
1
  import { getPublicUrl, resolveHref } from "../page-BLbZbnWg.js";
2
2
  import { resolveRoute } from "../route-k0W3AKyo.js";
3
- import { asEquatable, defaultLocale, defaultMarket, getHost, isDevelopment } from "@websolutespa/bom-core";
3
+ import { asEquatable, defaultLocale, defaultMarket, getHost } from "@websolutespa/bom-core";
4
4
  import { StoreStrategy, localApiGet, localApiPost, storeApiGet, storeApiPost, storeStrategy } from "@websolutespa/bom-mixer-store";
5
5
  import { NextResponse } from "next/server";
6
6
 
@@ -90,8 +90,7 @@ async function routeInterceptor(request, next) {
90
90
  if (redirectUrl) {
91
91
  const resolvedUrl = resolveHref(redirectUrl);
92
92
  const search = request.nextUrl.search;
93
- const isTemporaryRedirect = isDevelopment || search;
94
- const response$1 = NextResponse.redirect(resolvedUrl + search, { status: isTemporaryRedirect ? 303 : 301 });
93
+ const response$1 = NextResponse.redirect(resolvedUrl + search, { status: 307 });
95
94
  response$1.cookies.set({
96
95
  expires: new Date(Date.now() + 1440 * 60 * 1e3),
97
96
  httpOnly: true,
@@ -1 +1 @@
1
- {"version":3,"file":"proxy.js","names":["request: NextRequest","defaultLocale?: string","defaultLocale","defaultMarket?: string","defaultMarket","markets: IMarket[]","routes: IRoute[]","next: NextFetchEvent","route: string | undefined","countries: string[]","x","market","route: IRoute | undefined","response","error: any","error","hrefBeforeRedirect: string | null"],"sources":["../../src/route/route.interceptor.ts"],"sourcesContent":["import { IMarket, IRoute, asEquatable, defaultLocale, defaultMarket, getHost, isDevelopment } from '@websolutespa/bom-core';\r\nimport { StoreStrategy, localApiGet, localApiPost, storeApiGet, storeApiPost, storeStrategy } from '@websolutespa/bom-mixer-store';\r\nimport { NextFetchEvent, NextRequest, NextResponse } from 'next/server';\r\nimport { getPublicUrl, resolveHref } from '../page/page';\r\nimport { resolveRoute } from './route';\r\n\r\nexport async function detectLocale(request: NextRequest, defaultLocale?: string) {\r\n const acceptLanguage = request.headers.get('accept-language')?.split(',')[0];\r\n const detectedLocale = request.cookies.get('NEXT_LOCALE')?.value || acceptLanguage || defaultLocale || process.env.DEFAULT_LOCALE;\r\n return detectedLocale as string;\r\n}\r\n\r\nexport async function detectCountry(request: NextRequest, defaultMarket?: string) {\r\n let ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip');\r\n if (ip) {\r\n ip = ip.split(',')[0];\r\n }\r\n const isValidIp = ip?.match(/^(\\d+?)\\.(\\d+?)\\.(\\d+?)\\.(\\d+?)$/);\r\n let detectedCountry = '';\r\n try {\r\n const token = process.env.IPINFO_TOKEN;\r\n if (token) {\r\n const url = isValidIp ?\r\n `https://api.ipinfo.io/lite/${ip}/country_code?token=${token}` :\r\n `https://api.ipinfo.io/lite/me/country_code?token=${token}`;\r\n const response = await fetch(url);\r\n if (response.ok) {\r\n const data = await response.text();\r\n detectedCountry = data.toLowerCase();\r\n }\r\n } else {\r\n const url = `https://geoip.websolute.it/Ip2Location/get_info.aspx?ipaddress=${isValidIp ? ip : ''}`;\r\n const response = await fetch(url);\r\n if (response.ok) {\r\n const xml = await response.text();\r\n detectedCountry = (\r\n ((xml.split('<CountryCode><![CDATA[')[1] || '').split(']]></CountryCode>')[0] || '')\r\n ).replace('-', '').toLowerCase();\r\n // console.log('detectedCountry', detectedCountry);\r\n }\r\n }\r\n // console.log('routeInterceptor.detectCountry', detectedCountry);\r\n return detectedCountry || defaultMarket || process.env.DEFAULT_MARKET;\r\n } catch (error) {\r\n console.log('routeInterceptor.detectCountry.error', error);\r\n }\r\n return (defaultMarket || process.env.DEFAULT_MARKET) as string;\r\n}\r\n\r\nasync function getMarkets() {\r\n const apiGet = storeStrategy === StoreStrategy.DecoratedApi ? localApiGet : storeApiGet;\r\n const markets: IMarket[] = await apiGet('/market?pagination=false');\r\n return markets;\r\n}\r\n\r\nasync function getRootRoutes() {\r\n const apiGet = storeStrategy === StoreStrategy.DecoratedApi ? localApiGet : storeApiGet;\r\n const routes: IRoute[] = await apiGet('/route?where[isRoot][equals]=true');\r\n return routes;\r\n}\r\n\r\nexport async function routeAutoDetection(request: NextRequest, next: NextFetchEvent): Promise<string | undefined> {\r\n let route: string | undefined;\r\n const url = request.nextUrl;\r\n if (url.pathname === '/') {\r\n const cookie = request.cookies.get('detectedRoute');\r\n if (cookie?.value) {\r\n return cookie.value;\r\n }\r\n // console.log('routeInterceptor.routeAutoDetection', url.pathname);\r\n const rootRoutes = await getRootRoutes();\r\n // console.log('routeInterceptor.routeAutoDetection.rootRoutes', rootRoutes);\r\n const defaultRoute = rootRoutes.find(x => x.id === '/');\r\n // console.log('routeInterceptor.routeAutoDetection.defaultRoute', defaultRoute);\r\n if (defaultRoute) {\r\n return;\r\n }\r\n const markets = await getMarkets();\r\n // console.log('routeInterceptor.routeAutoDetection.markets', markets);\r\n const market = markets.find(x => x.isDefault) || markets.find(x => x.id === defaultMarket) || markets[0];\r\n // console.log('routeInterceptor.routeAutoDetection.market', market);\r\n const detectedCountry = await detectCountry(request, market.id);\r\n // console.log('routeInterceptor.routeAutoDetection.detectedCountry', detectedCountry);\r\n const detectedMarket = detectedCountry ? (\r\n markets.filter(x => Array.isArray(x.countries)).find(x => {\r\n const countries: string[] = (x.countries || []).map(x => typeof x === 'string' ? x : x.id as string);\r\n return countries.includes(detectedCountry);\r\n }) ||\r\n markets.find(x => !Array.isArray(x.countries))\r\n ) : undefined;\r\n // console.log('routeInterceptor.routeAutoDetection.detectedMarket', detectedMarket);\r\n let detectedMarketRoutes = rootRoutes.filter(x => x.market === detectedMarket?.id);\r\n if (detectedMarketRoutes.length === 0) {\r\n detectedMarketRoutes = rootRoutes.filter(x => x.market === market.id);\r\n }\r\n // console.log('routeInterceptor.routeAutoDetection.detectedMarketRoutes', detectedMarketRoutes);\r\n const defaultMarketLocale = market.defaultLanguage ? (\r\n typeof market.defaultLanguage === 'string' ? market.defaultLanguage : market.defaultLanguage.id\r\n ) as string : defaultLocale;\r\n // console.log('routeInterceptor.routeAutoDetection.defaultMarketLocale', defaultMarketLocale);\r\n const detectedLocale = await detectLocale(request, defaultMarketLocale);\r\n // console.log('routeInterceptor.routeAutoDetection.detectedLocale', detectedLocale);\r\n const detectedLocaleRoutes = detectedMarketRoutes.filter(x => x.locale === detectedLocale);\r\n const detectedRoute =\r\n detectedLocaleRoutes.length > 0 ?\r\n detectedLocaleRoutes[0]! :\r\n detectedMarketRoutes.find(x => {\r\n const market = markets.find(m => m.id === x.market)!;\r\n const marketLocale = (market.defaultLanguage ? asEquatable(market.defaultLanguage as string) : defaultMarketLocale);\r\n return x.locale === marketLocale;\r\n })!;\r\n // console.log('routeInterceptor.routeAutoDetection.detectedRoute', detectedRoute);\r\n if (detectedRoute && detectedRoute.id !== url.pathname) {\r\n console.log('routeInterceptor.routeAutoDetection', detectedLocale, detectedCountry, detectedRoute.id);\r\n route = detectedRoute.id;\r\n }\r\n // console.log('routeInterceptor.routeAutoDetection', locale, detectedCountry, rootRoutes);\r\n }\r\n return route;\r\n}\r\n\r\nexport async function routeInterceptor(request: NextRequest, next: NextFetchEvent) {\r\n let route: IRoute | undefined;\r\n let url = request.nextUrl;\r\n const hrefBeforeRedirect = getHrefBeforeRedirect(request);\r\n // console.log('routeInterceptor._live', request.nextUrl.searchParams.get('_live'));\r\n try {\r\n const redirectUrl = await routeAutoDetection(request, next);\r\n // console.log('routeInterceptor.routeAutoDetection', redirectUrl);\r\n if (redirectUrl) {\r\n // console.log('routeInterceptor', 'redirectUrl', redirectUrl);\r\n const resolvedUrl = resolveHref(redirectUrl);\r\n // console.log('routeInterceptor', 'resolvedUrl', resolvedUrl);\r\n const search = request.nextUrl.search;\r\n const isTemporaryRedirect = isDevelopment || search;\r\n const response = NextResponse.redirect(resolvedUrl + search, { status: isTemporaryRedirect ? 303 : 301 });\r\n response.cookies.set({\r\n expires: new Date(Date.now() + 1 * 24 * 60 * 60 * 1000),\r\n httpOnly: true,\r\n name: 'detectedRoute',\r\n path: '/',\r\n sameSite: 'lax',\r\n secure: true,\r\n value: redirectUrl,\r\n });\r\n return response;\r\n }\r\n // console.log('routeInterceptor request.cookies', request.cookies);\r\n // console.log('routeInterceptor', url.pathname);\r\n if (storeStrategy === StoreStrategy.DecoratedApi) {\r\n route = await localApiPost('/route', { pathname: url.pathname, href: url.href, hrefBeforeRedirect });\r\n } else {\r\n route = await storeApiPost('/route', { pathname: url.pathname, href: url.href, hrefBeforeRedirect });\r\n }\r\n // console.log('route', route);\r\n if (!route) {\r\n console.log('routeInterceptor.route.notfound', url.pathname);\r\n return;\r\n }\r\n } catch (error: any) {\r\n if (error.status >= 300 && error.status < 400) {\r\n console.log('routeInterceptor.3xx', url.pathname, error.status, error.statusText);\r\n try {\r\n const { redirectUrl } = await error.json();\r\n // console.log('redirectUrl', redirectUrl);\r\n const response = NextResponse.redirect(redirectUrl, error.status);\r\n return response;\r\n } catch (error) {\r\n const publicUrl = getPublicUrl();\r\n console.log('routeInterceptor.3xx.error', `cannot parse response, redirecting to ${publicUrl}`);\r\n const response = NextResponse.redirect(publicUrl, 307);\r\n return response;\r\n }\r\n } else if (error.status === 410) {\r\n console.log('routeInterceptor.410', url.pathname, error.status, error.statusText);\r\n url = request.nextUrl.clone();\r\n url.pathname = `/${error.status}`;\r\n const response = NextResponse.rewrite(url, {\r\n status: error.status,\r\n statusText: error.statusText,\r\n request,\r\n });\r\n return response;\r\n } else if (error.status >= 400 && error.status < 500) {\r\n console.log('routeInterceptor.4xx', url.pathname, error.status, error.statusText);\r\n const response = NextResponse.next({\r\n status: error.status,\r\n statusText: error.statusText,\r\n request,\r\n });\r\n return response;\r\n }\r\n console.log('routeInterceptor.error', url.pathname, error.url, error.status, error.statusText || error);\r\n return;\r\n }\r\n // console.log('routeInterceptor.route.found', url.pathname, '->', route);\r\n url = request.nextUrl.clone();\r\n const resolvedPathname = resolveRoute(route);\r\n // console.log('resolvedPathname', resolvedPathname);\r\n url.pathname = resolvedPathname;\r\n // console.log('routeInterceptor.route', route.schema, route.page, route.market, route.locale);\r\n const response = NextResponse.rewrite(url);\r\n // const response = NextResponse.redirect(url);\r\n return response;\r\n}\r\n\r\nfunction getHrefBeforeRedirect(request: NextRequest): string | null {\r\n let hrefBeforeRedirect: string | null = null;\r\n const urlBeforeRedirect = request.nextUrl.clone();\r\n const host = getHost(request.headers);\r\n if (host) {\r\n urlBeforeRedirect.host = host;\r\n urlBeforeRedirect.port = host.split(':')[1] || '';\r\n hrefBeforeRedirect = urlBeforeRedirect.href;\r\n }\r\n return hrefBeforeRedirect;\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"proxy.js","names":["request: NextRequest","defaultLocale?: string","defaultLocale","defaultMarket?: string","defaultMarket","markets: IMarket[]","routes: IRoute[]","next: NextFetchEvent","route: string | undefined","countries: string[]","x","market","route: IRoute | undefined","response","error: any","error","hrefBeforeRedirect: string | null"],"sources":["../../src/route/route.interceptor.ts"],"sourcesContent":["import { IMarket, IRoute, asEquatable, defaultLocale, defaultMarket, getHost } from '@websolutespa/bom-core';\r\nimport { StoreStrategy, localApiGet, localApiPost, storeApiGet, storeApiPost, storeStrategy } from '@websolutespa/bom-mixer-store';\r\nimport { NextFetchEvent, NextRequest, NextResponse } from 'next/server';\r\nimport { getPublicUrl, resolveHref } from '../page/page';\r\nimport { resolveRoute } from './route';\r\n\r\nexport async function detectLocale(request: NextRequest, defaultLocale?: string) {\r\n const acceptLanguage = request.headers.get('accept-language')?.split(',')[0];\r\n const detectedLocale = request.cookies.get('NEXT_LOCALE')?.value || acceptLanguage || defaultLocale || process.env.DEFAULT_LOCALE;\r\n return detectedLocale as string;\r\n}\r\n\r\nexport async function detectCountry(request: NextRequest, defaultMarket?: string) {\r\n let ip = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip');\r\n if (ip) {\r\n ip = ip.split(',')[0];\r\n }\r\n const isValidIp = ip?.match(/^(\\d+?)\\.(\\d+?)\\.(\\d+?)\\.(\\d+?)$/);\r\n let detectedCountry = '';\r\n try {\r\n const token = process.env.IPINFO_TOKEN;\r\n if (token) {\r\n const url = isValidIp ?\r\n `https://api.ipinfo.io/lite/${ip}/country_code?token=${token}` :\r\n `https://api.ipinfo.io/lite/me/country_code?token=${token}`;\r\n const response = await fetch(url);\r\n if (response.ok) {\r\n const data = await response.text();\r\n detectedCountry = data.toLowerCase();\r\n }\r\n } else {\r\n const url = `https://geoip.websolute.it/Ip2Location/get_info.aspx?ipaddress=${isValidIp ? ip : ''}`;\r\n const response = await fetch(url);\r\n if (response.ok) {\r\n const xml = await response.text();\r\n detectedCountry = (\r\n ((xml.split('<CountryCode><![CDATA[')[1] || '').split(']]></CountryCode>')[0] || '')\r\n ).replace('-', '').toLowerCase();\r\n // console.log('detectedCountry', detectedCountry);\r\n }\r\n }\r\n // console.log('routeInterceptor.detectCountry', detectedCountry);\r\n return detectedCountry || defaultMarket || process.env.DEFAULT_MARKET;\r\n } catch (error) {\r\n console.log('routeInterceptor.detectCountry.error', error);\r\n }\r\n return (defaultMarket || process.env.DEFAULT_MARKET) as string;\r\n}\r\n\r\nasync function getMarkets() {\r\n const apiGet = storeStrategy === StoreStrategy.DecoratedApi ? localApiGet : storeApiGet;\r\n const markets: IMarket[] = await apiGet('/market?pagination=false');\r\n return markets;\r\n}\r\n\r\nasync function getRootRoutes() {\r\n const apiGet = storeStrategy === StoreStrategy.DecoratedApi ? localApiGet : storeApiGet;\r\n const routes: IRoute[] = await apiGet('/route?where[isRoot][equals]=true');\r\n return routes;\r\n}\r\n\r\nexport async function routeAutoDetection(request: NextRequest, next: NextFetchEvent): Promise<string | undefined> {\r\n let route: string | undefined;\r\n const url = request.nextUrl;\r\n if (url.pathname === '/') {\r\n const cookie = request.cookies.get('detectedRoute');\r\n if (cookie?.value) {\r\n return cookie.value;\r\n }\r\n // console.log('routeInterceptor.routeAutoDetection', url.pathname);\r\n const rootRoutes = await getRootRoutes();\r\n // console.log('routeInterceptor.routeAutoDetection.rootRoutes', rootRoutes);\r\n const defaultRoute = rootRoutes.find(x => x.id === '/');\r\n // console.log('routeInterceptor.routeAutoDetection.defaultRoute', defaultRoute);\r\n if (defaultRoute) {\r\n return;\r\n }\r\n const markets = await getMarkets();\r\n // console.log('routeInterceptor.routeAutoDetection.markets', markets);\r\n const market = markets.find(x => x.isDefault) || markets.find(x => x.id === defaultMarket) || markets[0];\r\n // console.log('routeInterceptor.routeAutoDetection.market', market);\r\n const detectedCountry = await detectCountry(request, market.id);\r\n // console.log('routeInterceptor.routeAutoDetection.detectedCountry', detectedCountry);\r\n const detectedMarket = detectedCountry ? (\r\n markets.filter(x => Array.isArray(x.countries)).find(x => {\r\n const countries: string[] = (x.countries || []).map(x => typeof x === 'string' ? x : x.id as string);\r\n return countries.includes(detectedCountry);\r\n }) ||\r\n markets.find(x => !Array.isArray(x.countries))\r\n ) : undefined;\r\n // console.log('routeInterceptor.routeAutoDetection.detectedMarket', detectedMarket);\r\n let detectedMarketRoutes = rootRoutes.filter(x => x.market === detectedMarket?.id);\r\n if (detectedMarketRoutes.length === 0) {\r\n detectedMarketRoutes = rootRoutes.filter(x => x.market === market.id);\r\n }\r\n // console.log('routeInterceptor.routeAutoDetection.detectedMarketRoutes', detectedMarketRoutes);\r\n const defaultMarketLocale = market.defaultLanguage ? (\r\n typeof market.defaultLanguage === 'string' ? market.defaultLanguage : market.defaultLanguage.id\r\n ) as string : defaultLocale;\r\n // console.log('routeInterceptor.routeAutoDetection.defaultMarketLocale', defaultMarketLocale);\r\n const detectedLocale = await detectLocale(request, defaultMarketLocale);\r\n // console.log('routeInterceptor.routeAutoDetection.detectedLocale', detectedLocale);\r\n const detectedLocaleRoutes = detectedMarketRoutes.filter(x => x.locale === detectedLocale);\r\n const detectedRoute =\r\n detectedLocaleRoutes.length > 0 ?\r\n detectedLocaleRoutes[0]! :\r\n detectedMarketRoutes.find(x => {\r\n const market = markets.find(m => m.id === x.market)!;\r\n const marketLocale = (market.defaultLanguage ? asEquatable(market.defaultLanguage as string) : defaultMarketLocale);\r\n return x.locale === marketLocale;\r\n })!;\r\n // console.log('routeInterceptor.routeAutoDetection.detectedRoute', detectedRoute);\r\n if (detectedRoute && detectedRoute.id !== url.pathname) {\r\n console.log('routeInterceptor.routeAutoDetection', detectedLocale, detectedCountry, detectedRoute.id);\r\n route = detectedRoute.id;\r\n }\r\n // console.log('routeInterceptor.routeAutoDetection', locale, detectedCountry, rootRoutes);\r\n }\r\n return route;\r\n}\r\n\r\nexport async function routeInterceptor(request: NextRequest, next: NextFetchEvent) {\r\n let route: IRoute | undefined;\r\n let url = request.nextUrl;\r\n const hrefBeforeRedirect = getHrefBeforeRedirect(request);\r\n // console.log('routeInterceptor._live', request.nextUrl.searchParams.get('_live'));\r\n try {\r\n const redirectUrl = await routeAutoDetection(request, next);\r\n // console.log('routeInterceptor.routeAutoDetection', redirectUrl);\r\n if (redirectUrl) {\r\n // console.log('routeInterceptor', 'redirectUrl', redirectUrl);\r\n const resolvedUrl = resolveHref(redirectUrl);\r\n // console.log('routeInterceptor', 'resolvedUrl', resolvedUrl);\r\n const search = request.nextUrl.search;\r\n /*\r\n // !!! always return a temporary redirect\r\n const isTemporaryRedirect = isDevelopment || search;\r\n const response = NextResponse.redirect(resolvedUrl + search, { status: isTemporaryRedirect ? 307 : 308 });\r\n */\r\n const response = NextResponse.redirect(resolvedUrl + search, { status: 307 });\r\n response.cookies.set({\r\n expires: new Date(Date.now() + 1 * 24 * 60 * 60 * 1000),\r\n httpOnly: true,\r\n name: 'detectedRoute',\r\n path: '/',\r\n sameSite: 'lax',\r\n secure: true,\r\n value: redirectUrl,\r\n });\r\n return response;\r\n }\r\n // console.log('routeInterceptor request.cookies', request.cookies);\r\n // console.log('routeInterceptor', url.pathname);\r\n if (storeStrategy === StoreStrategy.DecoratedApi) {\r\n route = await localApiPost('/route', { pathname: url.pathname, href: url.href, hrefBeforeRedirect });\r\n } else {\r\n route = await storeApiPost('/route', { pathname: url.pathname, href: url.href, hrefBeforeRedirect });\r\n }\r\n // console.log('route', route);\r\n if (!route) {\r\n console.log('routeInterceptor.route.notfound', url.pathname);\r\n return;\r\n }\r\n } catch (error: any) {\r\n if (error.status >= 300 && error.status < 400) {\r\n console.log('routeInterceptor.3xx', url.pathname, error.status, error.statusText);\r\n try {\r\n const { redirectUrl } = await error.json();\r\n // console.log('redirectUrl', redirectUrl);\r\n const response = NextResponse.redirect(redirectUrl, error.status);\r\n return response;\r\n } catch (error) {\r\n const publicUrl = getPublicUrl();\r\n console.log('routeInterceptor.3xx.error', `cannot parse response, redirecting to ${publicUrl}`);\r\n const response = NextResponse.redirect(publicUrl, 307);\r\n return response;\r\n }\r\n } else if (error.status === 410) {\r\n console.log('routeInterceptor.410', url.pathname, error.status, error.statusText);\r\n url = request.nextUrl.clone();\r\n url.pathname = `/${error.status}`;\r\n const response = NextResponse.rewrite(url, {\r\n status: error.status,\r\n statusText: error.statusText,\r\n request,\r\n });\r\n return response;\r\n } else if (error.status >= 400 && error.status < 500) {\r\n console.log('routeInterceptor.4xx', url.pathname, error.status, error.statusText);\r\n const response = NextResponse.next({\r\n status: error.status,\r\n statusText: error.statusText,\r\n request,\r\n });\r\n return response;\r\n }\r\n console.log('routeInterceptor.error', url.pathname, error.url, error.status, error.statusText || error);\r\n return;\r\n }\r\n // console.log('routeInterceptor.route.found', url.pathname, '->', route);\r\n url = request.nextUrl.clone();\r\n const resolvedPathname = resolveRoute(route);\r\n // console.log('resolvedPathname', resolvedPathname);\r\n url.pathname = resolvedPathname;\r\n // console.log('routeInterceptor.route', route.schema, route.page, route.market, route.locale);\r\n const response = NextResponse.rewrite(url);\r\n // const response = NextResponse.redirect(url);\r\n return response;\r\n}\r\n\r\nfunction getHrefBeforeRedirect(request: NextRequest): string | null {\r\n let hrefBeforeRedirect: string | null = null;\r\n const urlBeforeRedirect = request.nextUrl.clone();\r\n const host = getHost(request.headers);\r\n if (host) {\r\n urlBeforeRedirect.host = host;\r\n urlBeforeRedirect.port = host.split(':')[1] || '';\r\n hrefBeforeRedirect = urlBeforeRedirect.href;\r\n }\r\n return hrefBeforeRedirect;\r\n}\r\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-mixer-models",
3
- "version": "3.0.4",
3
+ "version": "4.0.0-next.0",
4
4
  "description": "Mixer Models module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -21,17 +21,17 @@
21
21
  },
22
22
  "dependencies": {},
23
23
  "peerDependencies": {
24
- "@websolutespa/bom-core": "^3.0.3",
25
- "@websolutespa/bom-mixer-store": "^3.0.1",
24
+ "@websolutespa/bom-core": "^4.0.0-next.0",
25
+ "@websolutespa/bom-mixer-store": "^4.0.0-next.0",
26
26
  "iron-session": "8.0.4",
27
27
  "next": "16.1.6"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/react-dom": "19.2.3",
31
31
  "@types/react": "19.2.10",
32
- "@websolutespa/bom-cli": "^3.0.2",
32
+ "@websolutespa/bom-cli": "^4.0.0-next.0",
33
33
  "@websolutespa/eslint-config": "^3.0.0",
34
- "@websolutespa/test": "^3.0.1",
34
+ "@websolutespa/test": "^4.0.0-next.0",
35
35
  "@websolutespa/tsconfig": "^3.0.0",
36
36
  "eslint": "9.31.0",
37
37
  "npm-run-all": "4.1.5",
@@ -1,4 +1,4 @@
1
- import { IMarket, IRoute, asEquatable, defaultLocale, defaultMarket, getHost, isDevelopment } from '@websolutespa/bom-core';
1
+ import { IMarket, IRoute, asEquatable, defaultLocale, defaultMarket, getHost } from '@websolutespa/bom-core';
2
2
  import { StoreStrategy, localApiGet, localApiPost, storeApiGet, storeApiPost, storeStrategy } from '@websolutespa/bom-mixer-store';
3
3
  import { NextFetchEvent, NextRequest, NextResponse } from 'next/server';
4
4
  import { getPublicUrl, resolveHref } from '../page/page';
@@ -132,8 +132,12 @@ export async function routeInterceptor(request: NextRequest, next: NextFetchEven
132
132
  const resolvedUrl = resolveHref(redirectUrl);
133
133
  // console.log('routeInterceptor', 'resolvedUrl', resolvedUrl);
134
134
  const search = request.nextUrl.search;
135
+ /*
136
+ // !!! always return a temporary redirect
135
137
  const isTemporaryRedirect = isDevelopment || search;
136
- const response = NextResponse.redirect(resolvedUrl + search, { status: isTemporaryRedirect ? 303 : 301 });
138
+ const response = NextResponse.redirect(resolvedUrl + search, { status: isTemporaryRedirect ? 307 : 308 });
139
+ */
140
+ const response = NextResponse.redirect(resolvedUrl + search, { status: 307 });
137
141
  response.cookies.set({
138
142
  expires: new Date(Date.now() + 1 * 24 * 60 * 60 * 1000),
139
143
  httpOnly: true,