| | import type { BaseNextRequest, BaseNextResponse } from '../base-http' |
| | import type { IncomingHttpHeaders } from 'http' |
| | import type { RequestStore } from '../app-render/work-unit-async-storage.external' |
| | import type { RenderOpts } from '../app-render/types' |
| | import type { NextRequest } from '../web/spec-extension/request' |
| | import type { __ApiPreviewProps } from '../api-utils' |
| |
|
| | import { FLIGHT_HEADERS } from '../../client/components/app-router-headers' |
| | import { |
| | HeadersAdapter, |
| | type ReadonlyHeaders, |
| | } from '../web/spec-extension/adapters/headers' |
| | import { |
| | MutableRequestCookiesAdapter, |
| | RequestCookiesAdapter, |
| | responseCookiesToRequestCookies, |
| | createCookiesWithMutableAccessCheck, |
| | type ReadonlyRequestCookies, |
| | } from '../web/spec-extension/adapters/request-cookies' |
| | import { ResponseCookies, RequestCookies } from '../web/spec-extension/cookies' |
| | import { DraftModeProvider } from './draft-mode-provider' |
| | import { splitCookiesString } from '../web/utils' |
| | import type { ServerComponentsHmrCache } from '../response-cache' |
| | import type { RenderResumeDataCache } from '../resume-data-cache/resume-data-cache' |
| | import type { Params } from '../request/params' |
| | import type { ImplicitTags } from '../lib/implicit-tags' |
| | import type { OpaqueFallbackRouteParams } from '../request/fallback-params' |
| |
|
| | function getHeaders(headers: Headers | IncomingHttpHeaders): ReadonlyHeaders { |
| | const cleaned = HeadersAdapter.from(headers) |
| | for (const header of FLIGHT_HEADERS) { |
| | cleaned.delete(header) |
| | } |
| |
|
| | return HeadersAdapter.seal(cleaned) |
| | } |
| |
|
| | function getMutableCookies( |
| | headers: Headers | IncomingHttpHeaders, |
| | onUpdateCookies?: (cookies: string[]) => void |
| | ): ResponseCookies { |
| | const cookies = new RequestCookies(HeadersAdapter.from(headers)) |
| | return MutableRequestCookiesAdapter.wrap(cookies, onUpdateCookies) |
| | } |
| |
|
| | export type WrapperRenderOpts = Partial<Pick<RenderOpts, 'onUpdateCookies'>> & { |
| | previewProps?: __ApiPreviewProps |
| | } |
| |
|
| | type RequestContext = RequestResponsePair & { |
| | |
| | |
| | |
| | |
| | |
| | url: { |
| | |
| | |
| | |
| | pathname: string |
| |
|
| | |
| | |
| | |
| | |
| | search?: string |
| | } |
| | phase: RequestStore['phase'] |
| | renderOpts?: WrapperRenderOpts |
| | isHmrRefresh?: boolean |
| | serverComponentsHmrCache?: ServerComponentsHmrCache |
| | implicitTags: ImplicitTags |
| | } |
| |
|
| | type RequestResponsePair = |
| | | { req: BaseNextRequest; res: BaseNextResponse } |
| | | { req: NextRequest; res: undefined } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | function mergeMiddlewareCookies( |
| | req: RequestContext['req'], |
| | existingCookies: RequestCookies | ResponseCookies |
| | ) { |
| | if ( |
| | 'x-middleware-set-cookie' in req.headers && |
| | typeof req.headers['x-middleware-set-cookie'] === 'string' |
| | ) { |
| | const setCookieValue = req.headers['x-middleware-set-cookie'] |
| | const responseHeaders = new Headers() |
| |
|
| | for (const cookie of splitCookiesString(setCookieValue)) { |
| | responseHeaders.append('set-cookie', cookie) |
| | } |
| |
|
| | const responseCookies = new ResponseCookies(responseHeaders) |
| |
|
| | |
| | for (const cookie of responseCookies.getAll()) { |
| | existingCookies.set(cookie) |
| | } |
| | } |
| | } |
| |
|
| | export function createRequestStoreForRender( |
| | req: RequestContext['req'], |
| | res: RequestContext['res'], |
| | url: RequestContext['url'], |
| | rootParams: Params, |
| | implicitTags: RequestContext['implicitTags'], |
| | onUpdateCookies: RenderOpts['onUpdateCookies'], |
| | previewProps: WrapperRenderOpts['previewProps'], |
| | isHmrRefresh: RequestContext['isHmrRefresh'], |
| | serverComponentsHmrCache: RequestContext['serverComponentsHmrCache'], |
| | renderResumeDataCache: RenderResumeDataCache | null, |
| | devFallbackParams: OpaqueFallbackRouteParams | null |
| | ): RequestStore { |
| | return createRequestStoreImpl( |
| | |
| | 'render', |
| | req, |
| | res, |
| | url, |
| | rootParams, |
| | implicitTags, |
| | onUpdateCookies, |
| | renderResumeDataCache, |
| | previewProps, |
| | isHmrRefresh, |
| | serverComponentsHmrCache, |
| | devFallbackParams |
| | ) |
| | } |
| |
|
| | export function createRequestStoreForAPI( |
| | req: RequestContext['req'], |
| | url: RequestContext['url'], |
| | implicitTags: RequestContext['implicitTags'], |
| | onUpdateCookies: RenderOpts['onUpdateCookies'], |
| | previewProps: WrapperRenderOpts['previewProps'] |
| | ): RequestStore { |
| | return createRequestStoreImpl( |
| | |
| | 'action', |
| | req, |
| | undefined, |
| | url, |
| | {}, |
| | implicitTags, |
| | onUpdateCookies, |
| | null, |
| | previewProps, |
| | false, |
| | undefined, |
| | null |
| | ) |
| | } |
| |
|
| | function createRequestStoreImpl( |
| | phase: RequestStore['phase'], |
| | req: RequestContext['req'], |
| | res: RequestContext['res'], |
| | url: RequestContext['url'], |
| | rootParams: Params, |
| | implicitTags: RequestContext['implicitTags'], |
| | onUpdateCookies: RenderOpts['onUpdateCookies'], |
| | renderResumeDataCache: RenderResumeDataCache | null, |
| | previewProps: WrapperRenderOpts['previewProps'], |
| | isHmrRefresh: RequestContext['isHmrRefresh'], |
| | serverComponentsHmrCache: RequestContext['serverComponentsHmrCache'], |
| | devFallbackParams: OpaqueFallbackRouteParams | null | undefined |
| | ): RequestStore { |
| | function defaultOnUpdateCookies(cookies: string[]) { |
| | if (res) { |
| | res.setHeader('Set-Cookie', cookies) |
| | } |
| | } |
| |
|
| | const cache: { |
| | headers?: ReadonlyHeaders |
| | cookies?: ReadonlyRequestCookies |
| | mutableCookies?: ResponseCookies |
| | userspaceMutableCookies?: ResponseCookies |
| | draftMode?: DraftModeProvider |
| | } = {} |
| |
|
| | return { |
| | type: 'request', |
| | phase, |
| | implicitTags, |
| | |
| | |
| | |
| | url: { pathname: url.pathname, search: url.search ?? '' }, |
| | rootParams, |
| | get headers() { |
| | if (!cache.headers) { |
| | |
| | |
| | cache.headers = getHeaders(req.headers) |
| | } |
| |
|
| | return cache.headers |
| | }, |
| | get cookies() { |
| | if (!cache.cookies) { |
| | |
| | |
| | const requestCookies = new RequestCookies( |
| | HeadersAdapter.from(req.headers) |
| | ) |
| |
|
| | mergeMiddlewareCookies(req, requestCookies) |
| |
|
| | |
| | |
| | cache.cookies = RequestCookiesAdapter.seal(requestCookies) |
| | } |
| |
|
| | return cache.cookies |
| | }, |
| | set cookies(value: ReadonlyRequestCookies) { |
| | cache.cookies = value |
| | }, |
| | get mutableCookies() { |
| | if (!cache.mutableCookies) { |
| | const mutableCookies = getMutableCookies( |
| | req.headers, |
| | onUpdateCookies || (res ? defaultOnUpdateCookies : undefined) |
| | ) |
| |
|
| | mergeMiddlewareCookies(req, mutableCookies) |
| |
|
| | cache.mutableCookies = mutableCookies |
| | } |
| | return cache.mutableCookies |
| | }, |
| | get userspaceMutableCookies() { |
| | if (!cache.userspaceMutableCookies) { |
| | const userspaceMutableCookies = |
| | createCookiesWithMutableAccessCheck(this) |
| | cache.userspaceMutableCookies = userspaceMutableCookies |
| | } |
| | return cache.userspaceMutableCookies |
| | }, |
| | get draftMode() { |
| | if (!cache.draftMode) { |
| | cache.draftMode = new DraftModeProvider( |
| | previewProps, |
| | req, |
| | this.cookies, |
| | this.mutableCookies |
| | ) |
| | } |
| |
|
| | return cache.draftMode |
| | }, |
| | renderResumeDataCache: renderResumeDataCache ?? null, |
| | isHmrRefresh, |
| | serverComponentsHmrCache: |
| | serverComponentsHmrCache || |
| | (globalThis as any).__serverComponentsHmrCache, |
| | devFallbackParams, |
| | } |
| | } |
| |
|
| | export function synchronizeMutableCookies(store: RequestStore) { |
| | |
| | store.cookies = RequestCookiesAdapter.seal( |
| | responseCookiesToRequestCookies(store.mutableCookies) |
| | ) |
| | } |
| |
|