{"version":3,"file":"intlayerProxy.cjs","names":["internationalization","routing","resolveProxyMode","isProxyStorageLocaleEnabled","formatProxyEnabledMessage","ROUTING_MODE","getRewriteRules","getCanonicalPath","NextResponse","getDomainHostname","getDomainOrigin","getLocaleFromDomain","getInternalPath","getLocaleFromStorageServer","localeDetector","resolveLocalizedPath"],"sources":["../../../src/proxy/intlayerProxy.ts"],"sourcesContent":["import { internationalization, log, routing } from '@intlayer/config/built';\nimport { ROUTING_MODE } from '@intlayer/config/defaultValues';\nimport { getAppLogger } from '@intlayer/config/logger';\nimport {\n  formatProxyEnabledMessage,\n  getCanonicalPath,\n  getDomainHostname,\n  getDomainOrigin,\n  getInternalPath,\n  getLocaleFromDomain,\n  getRewriteRules,\n  isProxyStorageLocaleEnabled,\n  type LocaleDomainMap,\n  resolveLocalizedPath,\n  resolveProxyMode,\n} from '@intlayer/core/localization';\nimport {\n  getLocaleFromStorageServer,\n  setLocaleInStorageServer,\n} from '@intlayer/core/utils';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport {\n  type NextFetchEvent,\n  type NextRequest,\n  NextResponse,\n} from 'next/server';\nimport { localeDetector } from './localeDetector';\n\n/**\n * Controls whether locale detection occurs during Next.js prefetch requests\n * - true: Detect and apply locale during prefetch\n * - false: Use default locale during prefetch (recommended)\n *\n * This setting affects how Next.js handles locale prefetching:\n *\n * Example scenario:\n * - User's browser language is 'fr'\n * - Current page is /fr/about\n * - Link prefetches /about\n *\n * With `detectLocaleOnPrefetchNoPrefix:true`\n * - Prefetch detects 'fr' locale from browser\n * - Redirects prefetch to /fr/about\n *\n * With `detectLocaleOnPrefetchNoPrefix:false` (default)\n * - Prefetch uses default locale\n * - Redirects prefetch to /en/about (assuming 'en' is default)\n *\n * When to use true:\n * - Your app uses non-localized internal links (e.g. <a href=\"/about\">)\n * - You want consistent locale detection behavior between regular and prefetch requests\n *\n * When to use false (default):\n * - Your app uses locale-prefixed links (e.g. <a href=\"/fr/about\">)\n * - You want to optimize prefetching performance\n * - You want to avoid potential redirect loops\n */\nconst DEFAULT_DETECT_LOCALE_ON_PREFETCH_NO_PREFIX = false;\n\nconst { locales, defaultLocale } = internationalization ?? {};\nconst { basePath, mode, rewrite, domains, enableProxy } = routing ?? {};\n\n// Resolved behaviour of the locale-routing proxy. `disabled` turns\n// `intlayerProxy` into a pass-through so apps can handle routing themselves.\n// The env var backing this is injected at build time so bundlers can tree-shake\n// the guarded branches.\nconst proxyMode = resolveProxyMode(enableProxy);\n\n// Next.js inlines NODE_ENV into every bundle, edge middleware included, so this\n// is both reliable and statically eliminable. `next build` is the only command\n// that injects `INTLAYER_ROUTING_ENABLE_PROXY`, meaning a dev server always\n// reaches `resolveProxyMode` through the configuration value.\n//\n// Matched against `development` rather than \"not production\" on purpose: only\n// `next dev` runs a dev server. Any other value (`test`, a custom staging env)\n// has no dev server in play and must keep the full production behaviour.\nconst isDevServer = process.env.NODE_ENV === 'development';\n\n// In auto mode, a dev server keeps locale routing URL-driven: the stored locale\n// is not used as a redirect source, so a stale cookie cannot keep pulling every\n// navigation to another locale while developing.\nconst canUseStorageLocale = isProxyStorageLocaleEnabled(proxyMode, isDevServer);\n\n// Announce the proxy the way the Vite plugin does on `configureServer`. Next\n// has no server-start hook for middleware, so this runs when the middleware\n// module is first evaluated — at the first request `next dev` routes through\n// it. Restricted to the dev server on purpose: in production this module is\n// re-evaluated on every edge cold start, and the line would be pure noise.\nif (isDevServer && proxyMode !== 'disabled') {\n  getAppLogger({ log })(formatProxyEnabledMessage(!canUseStorageLocale), {\n    level: 'info',\n  });\n}\n\n// Note: cookie names are resolved inside LocaleStorage based on configuration\n\n// Derived flags from routing.mode\nconst effectiveMode = mode ?? ROUTING_MODE;\nconst noPrefix =\n  (!(\n    process.env.INTLAYER_ROUTING_MODE &&\n    process.env.INTLAYER_ROUTING_MODE !== 'no-prefix'\n  ) &&\n    effectiveMode === 'no-prefix') ||\n  (!(\n    process.env.INTLAYER_ROUTING_MODE &&\n    process.env.INTLAYER_ROUTING_MODE !== 'search-params'\n  ) &&\n    effectiveMode === 'search-params');\nconst prefixDefault =\n  !(\n    process.env.INTLAYER_ROUTING_MODE &&\n    process.env.INTLAYER_ROUTING_MODE !== 'prefix-all'\n  ) && effectiveMode === 'prefix-all';\n\nconst internalPrefix = !noPrefix;\n\nconst rewriteRules =\n  process.env.INTLAYER_ROUTING_REWRITE_RULES !== 'false'\n    ? getRewriteRules(rewrite, 'url')\n    : undefined;\n\n/**\n * Resolves the locale a localized (\"pretty\") path belongs to.\n *\n * A path such as `/a-propos` only exists because a rewrite rule maps it to the\n * French locale, so it declares its locale just as explicitly as an `/fr`\n * prefix would. Without this, a visitor whose `Accept-Language` (or stored\n * locale) says `en` gets `/a-propos` canonicalized against the English rules\n * only, which never matches — the proxy then treats it as an unlocalized path\n * and redirects to `/en/a-propos`, a URL no locale owns.\n *\n * Only patterns that differ from their canonical form are considered: a rule\n * whose localized path equals the canonical one (`en: '/about'`) carries no\n * locale signal, and matching on it would hijack the default locale's\n * unprefixed URLs.\n *\n * @param pathname - The request pathname, without any locale prefix.\n * @returns The locale declared by the path, or `undefined` when it declares none.\n */\nconst getRewriteLocale = (pathname: string): Locale | undefined =>\n  rewriteRules\n    ? (locales?.find(\n        (candidate) =>\n          getCanonicalPath(pathname, candidate as Locale, rewriteRules) !==\n          pathname\n      ) as Locale | undefined)\n    : undefined;\n\n/**\n * Detects if the request is a prefetch request from Next.js.\n *\n * Next.js prefetch requests are identified by:\n * - purpose: 'prefetch' (standard prefetch header)\n * - next-router-prefetch: '1' (Next.js app-router prefetch)\n *\n * Note: `next-url` and `x-nextjs-data` are intentionally NOT used here.\n * Both are also sent on real client-side navigations (RSC navigation\n * requests and pages-router data requests respectively), so treating them\n * as prefetch would force such navigations to the default locale instead\n * of the user's stored locale.\n *\n * During prefetch, we should ignore cookie-based locale detection\n * to prevent unwanted redirects when users are switching locales.\n *\n * @param request - The incoming Next.js request object.\n * @returns - True if the request is a prefetch request, false otherwise.\n */\nconst isPrefetchRequest = (request: NextRequest): boolean => {\n  const purpose = request.headers.get('purpose');\n  const nextRouterPrefetch = request.headers.get('next-router-prefetch');\n\n  return purpose === 'prefetch' || nextRouterPrefetch === '1';\n};\n\n// Ensure locale is reflected in search params when routing mode is 'search-params'\nconst appendLocaleSearchIfNeeded = (\n  search: string | undefined,\n  locale: Locale\n): string | undefined => {\n  if (\n    (process.env.INTLAYER_ROUTING_MODE &&\n      process.env.INTLAYER_ROUTING_MODE !== 'search-params') ||\n    effectiveMode !== 'search-params'\n  )\n    return search;\n  const params = new URLSearchParams(search ?? '');\n  params.set('locale', locale);\n  return `?${params.toString()}`;\n};\n\n/**\n * Proxy that handles the internationalization layer\n *\n * Usage:\n *\n * ```ts\n * // ./src/proxy.ts\n *\n * export { intlayerProxy as proxy } from '@intlayer/next/proxy';\n *\n * // applies this proxy only to files in the app directory\n * export const config = {\n *   matcher: '/((?!api|static|.*\\\\..*|_next).*)',\n * };\n * ```\n *\n * Main proxy function for handling internationalization.\n *\n * @param request - The incoming Next.js request object.\n * @param event - The Next.js fetch event (optional).\n * @param response - The Next.js response object (optional).\n * @returns - The response to be returned to the client.\n *\n */\nexport const intlayerProxy = (\n  request: NextRequest,\n  _event?: NextFetchEvent,\n  _response?: NextResponse\n): NextResponse => {\n  // When the proxy is disabled, pass the request through untouched.\n  if (proxyMode === 'disabled') {\n    return NextResponse.next();\n  }\n\n  const pathname = request.nextUrl.pathname;\n\n  const localLocale = getLocalLocale(request);\n\n  if (noPrefix) {\n    return handleNoPrefix(request, localLocale, pathname);\n  }\n\n  const pathLocale = getPathLocale(pathname);\n\n  // Domain routing: if the path locale is mapped to a different domain, redirect there.\n  // e.g. intlayer.org/zh/about → https://intlayer.zh/about\n  if (\n    process.env.INTLAYER_ROUTING_DOMAINS !== 'false' &&\n    pathLocale &&\n    domains\n  ) {\n    const localeDomain = domains[pathLocale];\n\n    if (localeDomain) {\n      const domainHost = getDomainHostname(localeDomain);\n\n      if (domainHost !== request.nextUrl.hostname) {\n        const rawPath = pathname.slice(`/${pathLocale}`.length) || '/';\n        const targetOrigin = getDomainOrigin(localeDomain);\n\n        return NextResponse.redirect(\n          new URL(`${rawPath}${request.nextUrl.search}`, targetOrigin)\n        );\n      }\n    }\n  }\n\n  // Domain routing: if the current hostname is exclusively mapped to one locale,\n  // treat it as that locale's domain — no URL prefix needed.\n  // e.g. intlayer.zh/about → internally rewrite to /zh/about\n  if (process.env.INTLAYER_ROUTING_DOMAINS !== 'false' && !pathLocale) {\n    const domainLocale = getLocaleFromDomain(\n      request.nextUrl.hostname,\n      domains as LocaleDomainMap\n    );\n\n    if (domainLocale) {\n      const canonicalPath = getCanonicalPath(\n        pathname,\n        domainLocale,\n        rewriteRules\n      );\n\n      // Never emit a trailing slash (`/zh/`): Next.js trailing-slash\n      // normalisation would redirect it back and forth with this proxy.\n      const internalPath = getInternalPath(canonicalPath, domainLocale);\n\n      return rewriteUrl(\n        request,\n        internalPath + (request.nextUrl.search ?? ''),\n        domainLocale\n      );\n    }\n  }\n\n  return handlePrefix(request, localLocale, pathLocale, pathname);\n};\n\n/**\n * Retrieves the locale from the request cookies if available and valid.\n *\n * Returns `undefined` when the stored locale is not allowed to drive locale\n * resolution (auto mode on a dev server), which makes every caller fall through\n * to `Accept-Language` detection and then the default locale.\n *\n * @param request - The incoming Next.js request object.\n * @returns - The locale found in the cookies, or undefined if not found or invalid.\n */\nconst getLocalLocale = (request: NextRequest): Locale | undefined => {\n  if (!canUseStorageLocale) return undefined;\n\n  return getLocaleFromStorageServer({\n    getCookie: (name: string) => request.cookies.get(name)?.value ?? null,\n    getHeader: (name: string) => request.headers.get(name) ?? null,\n  });\n};\n\n/**\n * Handles the case where URLs do not have locale prefixes.\n */\nconst handleNoPrefix = (\n  request: NextRequest,\n  localLocale: Locale | undefined,\n  pathname: string\n): NextResponse => {\n  const pathLocale = getPathLocale(pathname);\n\n  if (pathLocale) {\n    const pathWithoutLocale = pathname.slice(`/${pathLocale}`.length) || '/';\n\n    const canonicalPath = getCanonicalPath(\n      pathWithoutLocale,\n      pathLocale,\n      rewriteRules\n    );\n\n    const search = appendLocaleSearchIfNeeded(\n      request.nextUrl.search,\n      pathLocale\n    );\n\n    const redirectPath = search\n      ? `${canonicalPath}${search}`\n      : `${canonicalPath}${request.nextUrl.search ?? ''}`;\n\n    // Persist the explicitly-requested locale: stripping the prefix drops the\n    // only locale signal from the URL, so without this the follow-up request\n    // would fall back to cookie / Accept-Language detection and could resolve\n    // a different locale.\n    return redirectUrl(request, redirectPath, pathLocale);\n  }\n\n  if (\n    !(\n      process.env.INTLAYER_ROUTING_MODE &&\n      process.env.INTLAYER_ROUTING_MODE !== 'search-params'\n    ) &&\n    effectiveMode === 'search-params'\n  ) {\n    const existingSearchParams = new URLSearchParams(request.nextUrl.search);\n    const existingLocale = existingSearchParams.get('locale');\n\n    const isExistingValid = locales?.includes(existingLocale as Locale);\n\n    let locale = (localLocale ??\n      (isExistingValid ? (existingLocale as Locale) : undefined) ??\n      localeDetector?.(request) ??\n      defaultLocale) as Locale;\n\n    if (!locales?.includes(locale as Locale)) {\n      locale = defaultLocale as Locale;\n    }\n\n    const canonicalPath = getCanonicalPath(\n      pathname,\n      locale as Locale,\n      rewriteRules\n    );\n\n    if (existingLocale === locale) {\n      const internalPath = internalPrefix\n        ? getInternalPath(canonicalPath, locale as Locale)\n        : canonicalPath;\n      const rewritePath = `${internalPath}${request.nextUrl.search ?? ''}`;\n      return rewriteUrl(request, rewritePath, locale as Locale);\n    }\n\n    const search = appendLocaleSearchIfNeeded(\n      request.nextUrl.search,\n      locale as Locale\n    );\n    // Use original pathname for redirect to preserve user's URL input, just adding params\n    const redirectPath = search\n      ? `${pathname}${search}`\n      : `${pathname}${request.nextUrl.search ?? ''}`;\n\n    return redirectUrl(request, redirectPath);\n  }\n\n  // effectiveMode === 'no-prefix'\n  let locale = (localLocale ??\n    localeDetector?.(request) ??\n    defaultLocale) as Locale;\n\n  if (!locales?.includes(locale as Locale)) {\n    locale = defaultLocale as Locale;\n  }\n\n  // Without a prefix, a localized path is the only locale signal the URL\n  // carries, so it takes precedence over storage / `Accept-Language`.\n  locale = getRewriteLocale(pathname) ?? locale;\n\n  const canonicalPath = getCanonicalPath(\n    pathname,\n    locale as Locale,\n    rewriteRules\n  );\n\n  const internalPath = internalPrefix\n    ? getInternalPath(canonicalPath, locale as Locale)\n    : canonicalPath;\n  const search = appendLocaleSearchIfNeeded(\n    request.nextUrl.search,\n    locale as Locale\n  );\n  const rewritePath = search\n    ? `${internalPath}${search}`\n    : `${internalPath}${request.nextUrl.search ?? ''}`;\n\n  return rewriteUrl(request, rewritePath, locale as Locale);\n};\n\n/**\n * Checks whether a pathname starts with the given locale as a full path\n * segment (`/fr` or `/fr/...`). A bare `startsWith('/fr')` would also match\n * unrelated paths like `/friends`, causing wrong prefix stripping and\n * self-redirect loops.\n *\n * @param pathname - The pathname to test.\n * @param locale - The locale to look for as the first path segment.\n * @returns - True if the first path segment is exactly the locale.\n */\nconst hasLocaleSegmentPrefix = (pathname: string, locale: Locale): boolean =>\n  pathname === `/${locale}` || pathname.startsWith(`/${locale}/`);\n\n/**\n * Extracts the locale from the URL pathname if present.\n *\n * @param pathname - The pathname from the request URL.\n * @returns - The locale found in the pathname, or undefined if not found.\n */\nconst getPathLocale = (pathname: string): Locale | undefined =>\n  (locales as Locale[] | undefined)?.find((locale) =>\n    hasLocaleSegmentPrefix(pathname, locale)\n  );\n\n/**\n * Handles the case where URLs have locale prefixes.\n *\n * @param request - The incoming Next.js request object.\n * @param localLocale - The locale from the cookie.\n * @param pathLocale - The locale extracted from the pathname.\n * @param pathname - The pathname from the request URL.\n * @param basePathTrailingSlash - Indicates if the basePath ends with a slash.\n * @returns - The response to be returned to the client.\n */\nconst handlePrefix = (\n  request: NextRequest,\n  localLocale: Locale | undefined,\n  pathLocale: Locale | undefined,\n  pathname: string\n): NextResponse => {\n  if (!pathLocale) {\n    const isPrefetch = isPrefetchRequest(request);\n    if (isPrefetch && !DEFAULT_DETECT_LOCALE_ON_PREFETCH_NO_PREFIX) {\n      return handleMissingPathLocale(\n        request,\n        defaultLocale as Locale,\n        pathname\n      );\n    }\n    return handleMissingPathLocale(request, localLocale, pathname);\n  }\n\n  return handleExistingPathLocale(request, pathLocale, pathname);\n};\n\n/**\n * Handles requests where the locale is missing from the URL pathname.\n *\n * @param request - The incoming Next.js request object.\n * @param localLocale - The locale from the cookie.\n * @param pathname - The pathname from the request URL.\n * @param basePathTrailingSlash - Indicates if the basePath ends with a slash.\n * @returns - The response to be returned to the client.\n */\nconst handleMissingPathLocale = (\n  request: NextRequest,\n  localLocale: Locale | undefined,\n  pathname: string\n): NextResponse => {\n  let locale = (localLocale ??\n    localeDetector?.(request) ??\n    defaultLocale) as Locale;\n\n  if (!locales?.includes(locale as Locale)) {\n    locale = defaultLocale as Locale;\n  }\n\n  // A localized path names its own locale, and the URL always outranks the\n  // stored / negotiated one — otherwise /a-propos read as 'en' resolves to no\n  // rule and gets redirected to the ownerless /en/a-propos.\n  locale = getRewriteLocale(pathname) ?? locale;\n\n  // Resolve to canonical path.\n  // If user visits /a-propos (implied 'fr'), we resolve to /about\n  const canonicalPath = getCanonicalPath(pathname, locale, rewriteRules);\n\n  // Determine target localized path for redirection\n  // /about + 'fr' -> /a-propos\n  const { path: targetLocalizedPath } = resolveLocalizedPath(\n    canonicalPath,\n    locale,\n    rewriteRules\n  );\n\n  const newPath = constructPath(\n    locale,\n    targetLocalizedPath,\n    basePath as string,\n    appendLocaleSearchIfNeeded(request.nextUrl.search, locale)\n  );\n\n  // Never emit a trailing slash (`/en/` for canonicalPath `/`): Next.js\n  // trailing-slash normalisation would redirect it back and forth with this\n  // proxy. `getInternalPath` collapses the root path to `/${locale}`.\n  return prefixDefault || locale !== defaultLocale\n    ? redirectUrl(request, newPath)\n    : rewriteUrl(\n        request,\n        internalPrefix ? getInternalPath(canonicalPath, locale) : canonicalPath,\n        locale\n      ); // Rewrite must use Canonical\n};\n\n/**\n * Handles requests where the locale exists in the URL pathname.\n *\n * @param request - The incoming Next.js request object.\n * @param localLocale - The locale from the cookie.\n * @param pathLocale - The locale extracted from the pathname.\n * @param pathname - The pathname from the request URL.\n * @returns - The response to be returned to the client.\n */\nconst handleExistingPathLocale = (\n  request: NextRequest,\n  pathLocale: Locale,\n  pathname: string\n): NextResponse => {\n  const rawPath = pathname.slice(`/${pathLocale}`.length) || '/';\n\n  // 1. Identify the Canonical Path (Internal Next.js path)\n  // Ex: /a-propos (from URL) -> /about (Canonical)\n  const canonicalPath = getCanonicalPath(rawPath, pathLocale, rewriteRules);\n\n  // By skipping the forced localLocale check, we allow the explicit pathLocale\n  // to take precedence, which correctly updates the header/cookie when navigating.\n\n  // Rewrite Logic\n  // We must rewrite to the Next.js internal structure: /[locale]/[canonicalPath]\n  // Ex: Rewrite /fr/a-propos -> /fr/about\n\n  // 2. Redirect to localized path if needed (Canonical -> Localized)\n  // Ex: /fr/about -> /fr/a-propos\n  const { path: targetLocalizedPath, isRewritten } = resolveLocalizedPath(\n    canonicalPath,\n    pathLocale,\n    rewriteRules\n  );\n\n  if (isRewritten && targetLocalizedPath !== rawPath) {\n    const newPath = constructPath(\n      pathLocale,\n      targetLocalizedPath,\n      basePath as string,\n      appendLocaleSearchIfNeeded(request.nextUrl.search, pathLocale)\n    );\n    return redirectUrl(request, newPath);\n  }\n\n  // Never emit a trailing slash (`/fr/` for the bare `/fr` URL): rewriting\n  // `/fr` to `/fr/` makes Next.js issue a trailing-slash normalisation\n  // redirect back to `/fr`, which this proxy rewrites again — an infinite\n  // redirect loop. `getInternalPath` collapses the root path to `/${locale}`.\n  const internalUrl = internalPrefix\n    ? getInternalPath(canonicalPath, pathLocale)\n    : canonicalPath;\n\n  // Only handle redirect if we are strictly managing default locale prefixing\n  // Fix: pass `canonicalPath` (the path *without* the locale prefix, e.g. /pricing)\n  // instead of `pathname` (the full path including prefix, e.g. /en/pricing).\n  // Previously this caused an infinite redirect loop in prefix-no-default mode\n  // because handleDefaultLocaleRedirect built the redirect target from its third\n  // argument, which reproduced the same URL on every response.\n  if (!prefixDefault && pathLocale === defaultLocale) {\n    return handleDefaultLocaleRedirect(request, pathLocale, canonicalPath);\n  }\n\n  const search = request.nextUrl.search;\n  return rewriteUrl(request, internalUrl + (search ?? ''), pathLocale);\n};\n\n/**\n * Handles the scenario where the locale in the cookie does not match the locale in the URL pathname.\n *\n * @param request - The incoming Next.js request object.\n * @param pathname - The pathname from the request URL.\n * @param pathLocale - The locale extracted from the pathname.\n * @param localLocale - The locale from the cookie.\n * @param basePath - The base path of the application.\n * @returns - The new URL path with the correct locale.\n */\n// Function handleCookieLocaleMismatch was removed because the URL locale should take precedence over the stored locale.\n\n/**\n * The key fix for 404s without [locale] folders\n */\nconst handleDefaultLocaleRedirect = (\n  request: NextRequest,\n  pathLocale: Locale,\n  canonicalPath: string // Internal path (e.g. /about)\n): NextResponse => {\n  // Always called with !prefixDefault && pathLocale === defaultLocale (pre-validated by caller).\n  // Redirect to strip the default-locale prefix from the URL.\n  const { path: targetLocalizedPath } = resolveLocalizedPath(\n    canonicalPath,\n    pathLocale,\n    rewriteRules\n  );\n\n  const basePathValue = (basePath as string) || '';\n  const basePathTrailingSlash = basePathValue.endsWith('/');\n  let finalPath = targetLocalizedPath;\n  if (finalPath.startsWith('/')) finalPath = finalPath.slice(1);\n\n  const fullPath = `${basePathValue}${basePathTrailingSlash ? '' : '/'}${finalPath}`;\n\n  const searchWithLocale = appendLocaleSearchIfNeeded(\n    request.nextUrl.search,\n    pathLocale\n  );\n\n  // Persist the explicitly-requested default locale. Stripping the prefix\n  // (e.g. /es → /) drops the only locale signal from the URL, so without this\n  // the follow-up request to the canonical path would fall back to\n  // Accept-Language detection and could resolve a different locale (e.g. /en).\n  return redirectUrl(\n    request,\n    fullPath + (searchWithLocale ?? request.nextUrl.search ?? ''),\n    pathLocale\n  );\n};\n\n/**\n * Constructs a new path by combining the locale, path, basePath, and search parameters.\n *\n * @param locale - The locale to include in the path.\n * @param path - The original path from the request.\n * @param basePath - The base path of the application.\n * @param [search] - The query string from the request URL (optional).\n * @returns - The constructed new path.\n */\nconst constructPath = (\n  locale: Locale,\n  path: string,\n  basePath: string,\n  search?: string\n): string => {\n  // Remove existing locale prefix from path if it was passed by mistake,\n  // though we usually pass localized paths here now.\n  const pathWithoutPrefix = hasLocaleSegmentPrefix(path, locale)\n    ? path.slice(`/${locale}`.length) || '/'\n    : path;\n\n  if (\n    (!(\n      process.env.INTLAYER_ROUTING_MODE &&\n      process.env.INTLAYER_ROUTING_MODE !== 'no-prefix'\n    ) &&\n      effectiveMode === 'no-prefix') ||\n    (!(\n      process.env.INTLAYER_ROUTING_MODE &&\n      process.env.INTLAYER_ROUTING_MODE !== 'search-params'\n    ) &&\n      effectiveMode === 'search-params')\n  ) {\n    // `search` is either undefined or already has a leading '?' (from\n    // appendLocaleSearchIfNeeded / request.nextUrl.search), so append as-is.\n    return `${pathWithoutPrefix}${search ?? ''}`;\n  }\n\n  // Prefix handling\n  const pathWithLocalePrefix = hasLocaleSegmentPrefix(path, locale)\n    ? path\n    : `${locale}${path.startsWith('/') ? '' : '/'}${path}`;\n\n  const basePathValue = basePath || '';\n  const basePathTrailingSlash = basePathValue.endsWith('/');\n  const newPath = `${basePathValue}${basePathTrailingSlash ? '' : '/'}${pathWithLocalePrefix}`;\n\n  // Clean double slashes\n  const cleanPath = newPath.replace(/\\/+/g, '/');\n\n  // Never emit a trailing slash (`/fr/` for the root path): the framework's\n  // trailing-slash normalisation would redirect it back and forth with this\n  // proxy, creating an infinite redirect loop.\n  return cleanPath !== '/' && cleanPath.endsWith('/')\n    ? cleanPath.slice(0, -1)\n    : cleanPath;\n};\n\n/**\n * This handles the internal path Next.js sees.\n * To support optional [locale] folders, we need to decide if we\n * keep the locale prefix or strip it.\n */\nconst rewriteUrl = (\n  request: NextRequest,\n  newPath: string,\n  locale: Locale\n): NextResponse => {\n  const search = request.nextUrl.search;\n\n  // Next.js strips `basePath` from `request.nextUrl.pathname` before the\n  // middleware runs, so every path computed from it (e.g. `/en/about`) lacks\n  // the basePath prefix. When we pass that as an absolute path to `new URL`,\n  // it replaces the entire path after the origin, silently discarding the\n  // basePath (e.g. `new URL('/en/', 'http://host/weather/')` →\n  // `http://host/en/`). Prepending the configured basePath restores the\n  // correct mount-point so rewrites resolve under the app root.\n  const basePathValue = (basePath as string) || '';\n  const pathWithBase =\n    basePathValue && !newPath.startsWith(basePathValue)\n      ? `${basePathValue}${newPath}`\n      : newPath;\n\n  const pathWithSearch =\n    search && !pathWithBase.includes('?')\n      ? `${pathWithBase}${search}`\n      : pathWithBase;\n\n  const requestHeaders = new Headers(request.headers);\n  setLocaleInStorageServer(locale, {\n    setHeader: (name: string, value: string) => {\n      requestHeaders.set(name, value);\n    },\n  });\n\n  const targetUrl = new URL(pathWithSearch, request.url);\n\n  // If the target URL is exactly the current request URL,\n  // we just want to `next()` to avoid losing headers on a redundant rewrite.\n  const response =\n    targetUrl.href === request.nextUrl.href\n      ? NextResponse.next({\n          request: {\n            headers: requestHeaders,\n          },\n        })\n      : NextResponse.rewrite(targetUrl, {\n          request: {\n            headers: requestHeaders,\n          },\n        });\n\n  setLocaleInStorageServer(locale, {\n    setHeader: (name: string, value: string) => {\n      response.headers.set(name, value);\n    },\n  });\n  return response;\n};\n\n/**\n * Redirects the request to the new path.\n *\n * @param request - The incoming Next.js request object.\n * @param newPath - The new path to redirect to.\n * @param persistLocale - When provided, the locale is written to storage\n *   (cookie/header, per config) on the redirect response so the follow-up\n *   request resolves the same locale instead of re-running detection.\n * @returns - The redirect response.\n */\nconst redirectUrl = (\n  request: NextRequest,\n  newPath: string,\n  persistLocale?: Locale\n): NextResponse => {\n  const search = request.nextUrl.search;\n  const pathWithSearch =\n    search && !newPath.includes('?') ? `${newPath}${search}` : newPath;\n\n  const target = new URL(pathWithSearch, request.url);\n\n  // Prevent open redirect: if the resolved origin differs from the request\n  // origin, strip it back to a same-origin URL using only the path/search/hash.\n  const safeTarget =\n    target.origin === request.nextUrl.origin\n      ? target\n      : new URL(\n          `${target.pathname}${target.search}${target.hash}`,\n          request.url\n        );\n\n  const response = NextResponse.redirect(safeTarget);\n\n  if (persistLocale) {\n    persistLocaleOnResponse(response, persistLocale);\n  }\n\n  return response;\n};\n\n/**\n * Writes the resolved locale to the outgoing response's storage (cookie and/or\n * header, according to `routing.storage`). Only the cookie survives a client\n * redirect, so this is what carries an explicitly-selected locale across a\n * prefix-stripping redirect. Enabled cookie/header targets are resolved by\n * {@link setLocaleInStorageServer} from the config; disabled ones are no-ops.\n *\n * @param response - The outgoing Next.js response to attach storage to.\n * @param locale - The locale to persist.\n */\nconst persistLocaleOnResponse = (\n  response: NextResponse,\n  locale: Locale\n): void => {\n  setLocaleInStorageServer(locale, {\n    setCookieStore: (name, value, attributes) => {\n      response.cookies.set(name, value, {\n        path: attributes.path,\n        domain: attributes.domain,\n        expires:\n          typeof attributes.expires === 'number'\n            ? new Date(attributes.expires)\n            : attributes.expires,\n        secure: attributes.secure,\n        sameSite: attributes.sameSite,\n        httpOnly: attributes.httpOnly,\n      });\n    },\n    setHeader: (name, value) => {\n      response.headers.set(name, value);\n    },\n  });\n};\n"],"mappings":"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"}