feat(blog): add file-based blog with dynamic slugs, MDX content and layout shell

- Introduced blog routing using Next.js App Router
- Implemented dynamic [slug] pages for blog posts
- Added MDX-based content loading via lib/posts
- Integrated shared TopBar layout with navigation
- Established clear content, lib and component separation
This commit is contained in:
PascalSchattenburg
2026-01-22 14:14:15 +01:00
parent b717952234
commit d147843c76
10412 changed files with 2475583 additions and 0 deletions

View File

@@ -0,0 +1,367 @@
import { PageSignatureError } from './error';
import { fromNodeOutgoingHttpHeaders, normalizeNextQueryParam } from './utils';
import { NextFetchEvent, getWaitUntilPromiseFromEvent } from './spec-extension/fetch-event';
import { NextRequest } from './spec-extension/request';
import { NextResponse } from './spec-extension/response';
import { parseRelativeURL, getRelativeURL } from '../../shared/lib/router/utils/relativize-url';
import { NextURL } from './next-url';
import { stripInternalSearchParams } from '../internal-utils';
import { normalizeRscURL } from '../../shared/lib/router/utils/app-paths';
import { FLIGHT_HEADERS, NEXT_REWRITTEN_PATH_HEADER, NEXT_REWRITTEN_QUERY_HEADER, NEXT_RSC_UNION_QUERY, RSC_HEADER } from '../../client/components/app-router-headers';
import { ensureInstrumentationRegistered } from './globals';
import { createRequestStoreForAPI } from '../async-storage/request-store';
import { workUnitAsyncStorage } from '../app-render/work-unit-async-storage.external';
import { createWorkStore } from '../async-storage/work-store';
import { workAsyncStorage } from '../app-render/work-async-storage.external';
import { NEXT_ROUTER_PREFETCH_HEADER } from '../../client/components/app-router-headers';
import { getTracer } from '../lib/trace/tracer';
import { MiddlewareSpan } from '../lib/trace/constants';
import { CloseController } from './web-on-close';
import { getEdgePreviewProps } from './get-edge-preview-props';
import { getBuiltinRequestContext } from '../after/builtin-request-context';
import { getImplicitTags } from '../lib/implicit-tags';
export class NextRequestHint extends NextRequest {
constructor(params){
super(params.input, params.init);
this.sourcePage = params.page;
}
get request() {
throw Object.defineProperty(new PageSignatureError({
page: this.sourcePage
}), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
respondWith() {
throw Object.defineProperty(new PageSignatureError({
page: this.sourcePage
}), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
waitUntil() {
throw Object.defineProperty(new PageSignatureError({
page: this.sourcePage
}), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
}
const headersGetter = {
keys: (headers)=>Array.from(headers.keys()),
get: (headers, key)=>headers.get(key) ?? undefined
};
let propagator = (request, fn)=>{
const tracer = getTracer();
return tracer.withPropagatedContext(request.headers, fn, headersGetter);
};
let testApisIntercepted = false;
function ensureTestApisIntercepted() {
if (!testApisIntercepted) {
testApisIntercepted = true;
if (process.env.NEXT_PRIVATE_TEST_PROXY === 'true') {
const { interceptTestApis, wrapRequestHandler } = // eslint-disable-next-line @next/internal/typechecked-require -- experimental/testmode is not built ins next/dist/esm
require('next/dist/experimental/testmode/server-edge');
interceptTestApis();
propagator = wrapRequestHandler(propagator);
}
}
}
export async function adapter(params) {
var _getBuiltinRequestContext;
ensureTestApisIntercepted();
await ensureInstrumentationRegistered();
// TODO-APP: use explicit marker for this
const isEdgeRendering = typeof globalThis.__BUILD_MANIFEST !== 'undefined';
params.request.url = normalizeRscURL(params.request.url);
const requestURL = params.bypassNextUrl ? new URL(params.request.url) : new NextURL(params.request.url, {
headers: params.request.headers,
nextConfig: params.request.nextConfig
});
// Iterator uses an index to keep track of the current iteration. Because of deleting and appending below we can't just use the iterator.
// Instead we use the keys before iteration.
const keys = [
...requestURL.searchParams.keys()
];
for (const key of keys){
const value = requestURL.searchParams.getAll(key);
const normalizedKey = normalizeNextQueryParam(key);
if (normalizedKey) {
requestURL.searchParams.delete(normalizedKey);
for (const val of value){
requestURL.searchParams.append(normalizedKey, val);
}
requestURL.searchParams.delete(key);
}
}
// Ensure users only see page requests, never data requests.
let buildId = process.env.__NEXT_BUILD_ID || '';
if ('buildId' in requestURL) {
buildId = requestURL.buildId || '';
requestURL.buildId = '';
}
const requestHeaders = fromNodeOutgoingHttpHeaders(params.request.headers);
const isNextDataRequest = requestHeaders.has('x-nextjs-data');
const isRSCRequest = requestHeaders.get(RSC_HEADER) === '1';
if (isNextDataRequest && requestURL.pathname === '/index') {
requestURL.pathname = '/';
}
const flightHeaders = new Map();
// Headers should only be stripped for middleware
if (!isEdgeRendering) {
for (const header of FLIGHT_HEADERS){
const value = requestHeaders.get(header);
if (value !== null) {
flightHeaders.set(header, value);
requestHeaders.delete(header);
}
}
}
const normalizeURL = process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE ? new URL(params.request.url) : requestURL;
const rscHash = normalizeURL.searchParams.get(NEXT_RSC_UNION_QUERY);
const request = new NextRequestHint({
page: params.page,
// Strip internal query parameters off the request.
input: stripInternalSearchParams(normalizeURL).toString(),
init: {
body: params.request.body,
headers: requestHeaders,
method: params.request.method,
nextConfig: params.request.nextConfig,
signal: params.request.signal
}
});
/**
* This allows to identify the request as a data request. The user doesn't
* need to know about this property neither use it. We add it for testing
* purposes.
*/ if (isNextDataRequest) {
Object.defineProperty(request, '__isData', {
enumerable: false,
value: true
});
}
if (// If we are inside of the next start sandbox
// leverage the shared instance if not we need
// to create a fresh cache instance each time
!globalThis.__incrementalCacheShared && params.IncrementalCache) {
;
globalThis.__incrementalCache = new params.IncrementalCache({
CurCacheHandler: params.incrementalCacheHandler,
minimalMode: process.env.NODE_ENV !== 'development',
fetchCacheKeyPrefix: process.env.__NEXT_FETCH_CACHE_KEY_PREFIX,
dev: process.env.NODE_ENV === 'development',
requestHeaders: params.request.headers,
getPrerenderManifest: ()=>{
return {
version: -1,
routes: {},
dynamicRoutes: {},
notFoundRoutes: [],
preview: getEdgePreviewProps()
};
}
});
}
// if we're in an edge runtime sandbox, we should use the waitUntil
// that we receive from the enclosing NextServer
const outerWaitUntil = params.request.waitUntil ?? ((_getBuiltinRequestContext = getBuiltinRequestContext()) == null ? void 0 : _getBuiltinRequestContext.waitUntil);
const event = new NextFetchEvent({
request,
page: params.page,
context: outerWaitUntil ? {
waitUntil: outerWaitUntil
} : undefined
});
let response;
let cookiesFromResponse;
response = await propagator(request, ()=>{
// we only care to make async storage available for middleware
const isMiddleware = params.page === '/middleware' || params.page === '/src/middleware' || params.page === '/proxy' || params.page === '/src/proxy';
if (isMiddleware) {
// if we're in an edge function, we only get a subset of `nextConfig` (no `experimental`),
// so we have to inject it via DefinePlugin.
// in `next start` this will be passed normally (see `NextNodeServer.runMiddleware`).
const waitUntil = event.waitUntil.bind(event);
const closeController = new CloseController();
return getTracer().trace(MiddlewareSpan.execute, {
spanName: `middleware ${request.method}`,
attributes: {
'http.target': request.nextUrl.pathname,
'http.method': request.method
}
}, async ()=>{
try {
var _params_request_nextConfig_experimental, _params_request_nextConfig, _params_request_nextConfig_experimental1, _params_request_nextConfig1;
const onUpdateCookies = (cookies)=>{
cookiesFromResponse = cookies;
};
const previewProps = getEdgePreviewProps();
const page = '/' // Fake Work
;
const fallbackRouteParams = null;
const implicitTags = await getImplicitTags(page, request.nextUrl, fallbackRouteParams);
const requestStore = createRequestStoreForAPI(request, request.nextUrl, implicitTags, onUpdateCookies, previewProps);
const workStore = createWorkStore({
page,
renderOpts: {
cacheLifeProfiles: (_params_request_nextConfig = params.request.nextConfig) == null ? void 0 : (_params_request_nextConfig_experimental = _params_request_nextConfig.experimental) == null ? void 0 : _params_request_nextConfig_experimental.cacheLife,
cacheComponents: false,
experimental: {
isRoutePPREnabled: false,
authInterrupts: !!((_params_request_nextConfig1 = params.request.nextConfig) == null ? void 0 : (_params_request_nextConfig_experimental1 = _params_request_nextConfig1.experimental) == null ? void 0 : _params_request_nextConfig_experimental1.authInterrupts)
},
supportsDynamicResponse: true,
waitUntil,
onClose: closeController.onClose.bind(closeController),
onAfterTaskError: undefined
},
isPrefetchRequest: request.headers.get(NEXT_ROUTER_PREFETCH_HEADER) === '1',
buildId: buildId ?? '',
previouslyRevalidatedTags: []
});
return await workAsyncStorage.run(workStore, ()=>workUnitAsyncStorage.run(requestStore, params.handler, request, event));
} finally{
// middleware cannot stream, so we can consider the response closed
// as soon as the handler returns.
// we can delay running it until a bit later --
// if it's needed, we'll have a `waitUntil` lock anyway.
setTimeout(()=>{
closeController.dispatchClose();
}, 0);
}
});
}
return params.handler(request, event);
});
// check if response is a Response object
if (response && !(response instanceof Response)) {
throw Object.defineProperty(new TypeError('Expected an instance of Response to be returned'), "__NEXT_ERROR_CODE", {
value: "E567",
enumerable: false,
configurable: true
});
}
if (response && cookiesFromResponse) {
response.headers.set('set-cookie', cookiesFromResponse);
}
/**
* For rewrites we must always include the locale in the final pathname
* so we re-create the NextURL forcing it to include it when the it is
* an internal rewrite. Also we make sure the outgoing rewrite URL is
* a data URL if the request was a data request.
*/ const rewrite = response == null ? void 0 : response.headers.get('x-middleware-rewrite');
if (response && rewrite && (isRSCRequest || !isEdgeRendering)) {
var _params_request_nextConfig_experimental_clientParamParsingOrigins, _params_request_nextConfig_experimental, _params_request_nextConfig;
const destination = new NextURL(rewrite, {
forceLocale: true,
headers: params.request.headers,
nextConfig: params.request.nextConfig
});
if (!process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE && !isEdgeRendering) {
if (destination.host === request.nextUrl.host) {
destination.buildId = buildId || destination.buildId;
response.headers.set('x-middleware-rewrite', String(destination));
}
}
/**
* When the request is a data request we must show if there was a rewrite
* with an internal header so the client knows which component to load
* from the data request.
*/ const { url: relativeDestination, isRelative } = parseRelativeURL(destination.toString(), requestURL.toString());
if (!isEdgeRendering && isNextDataRequest && // if the rewrite is external and external rewrite
// resolving config is enabled don't add this header
// so the upstream app can set it instead
!(process.env.__NEXT_EXTERNAL_MIDDLEWARE_REWRITE_RESOLVE && relativeDestination.match(/http(s)?:\/\//))) {
response.headers.set('x-nextjs-rewrite', relativeDestination);
}
// Check to see if this is a non-relative rewrite. If it is, we need
// to check to see if it's an allowed origin to receive the rewritten
// headers.
const isAllowedOrigin = !isRelative ? (_params_request_nextConfig = params.request.nextConfig) == null ? void 0 : (_params_request_nextConfig_experimental = _params_request_nextConfig.experimental) == null ? void 0 : (_params_request_nextConfig_experimental_clientParamParsingOrigins = _params_request_nextConfig_experimental.clientParamParsingOrigins) == null ? void 0 : _params_request_nextConfig_experimental_clientParamParsingOrigins.some((origin)=>new RegExp(origin).test(destination.origin)) : false;
// If this is an RSC request, and the pathname or search has changed, and
// this isn't an external rewrite, we need to set the rewritten pathname and
// query headers.
if (isRSCRequest && (isRelative || isAllowedOrigin)) {
if (requestURL.pathname !== destination.pathname) {
response.headers.set(NEXT_REWRITTEN_PATH_HEADER, destination.pathname);
}
if (requestURL.search !== destination.search) {
response.headers.set(NEXT_REWRITTEN_QUERY_HEADER, // remove the leading ? from the search string
destination.search.slice(1));
}
}
}
/**
* Always forward the `_rsc` search parameter to the rewritten URL for RSC requests,
* unless it's already present. This is necessary to ensure that RSC hash validation
* works correctly after a rewrite. For internal rewrites, the server can validate the
* RSC hash using the original URL, so forwarding the `_rsc` parameter is less critical.
* However, for external rewrites (where the request is proxied to another Next.js server),
* the external server does not have access to the original URL or its search parameters.
* In these cases, forwarding the `_rsc` parameter is essential so that the external server
* can perform the correct RSC hash validation.
*/ if (response && rewrite && isRSCRequest && rscHash) {
const rewriteURL = new URL(rewrite);
if (!rewriteURL.searchParams.has(NEXT_RSC_UNION_QUERY)) {
rewriteURL.searchParams.set(NEXT_RSC_UNION_QUERY, rscHash);
response.headers.set('x-middleware-rewrite', rewriteURL.toString());
}
}
/**
* For redirects we will not include the locale in case when it is the
* default and we must also make sure the outgoing URL is a data one if
* the incoming request was a data request.
*/ const redirect = response == null ? void 0 : response.headers.get('Location');
if (response && redirect && !isEdgeRendering) {
const redirectURL = new NextURL(redirect, {
forceLocale: false,
headers: params.request.headers,
nextConfig: params.request.nextConfig
});
/**
* Responses created from redirects have immutable headers so we have
* to clone the response to be able to modify it.
*/ response = new Response(response.body, response);
if (!process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE) {
if (redirectURL.host === requestURL.host) {
redirectURL.buildId = buildId || redirectURL.buildId;
response.headers.set('Location', getRelativeURL(redirectURL, requestURL));
}
}
/**
* When the request is a data request we can't use the location header as
* it may end up with CORS error. Instead we map to an internal header so
* the client knows the destination.
*/ if (isNextDataRequest) {
response.headers.delete('Location');
response.headers.set('x-nextjs-redirect', getRelativeURL(redirectURL.toString(), requestURL.toString()));
}
}
const finalResponse = response ? response : NextResponse.next();
// Flight headers are not overridable / removable so they are applied at the end.
const middlewareOverrideHeaders = finalResponse.headers.get('x-middleware-override-headers');
const overwrittenHeaders = [];
if (middlewareOverrideHeaders) {
for (const [key, value] of flightHeaders){
finalResponse.headers.set(`x-middleware-request-${key}`, value);
overwrittenHeaders.push(key);
}
if (overwrittenHeaders.length > 0) {
finalResponse.headers.set('x-middleware-override-headers', middlewareOverrideHeaders + ',' + overwrittenHeaders.join(','));
}
}
return {
response: finalResponse,
waitUntil: getWaitUntilPromiseFromEvent(event) ?? Promise.resolve(),
fetchMetrics: request.fetchMetrics
};
}
//# sourceMappingURL=adapter.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,116 @@
import './globals';
import { adapter } from './adapter';
import { IncrementalCache } from '../lib/incremental-cache';
import { RouteMatcher } from '../route-matchers/route-matcher';
import { internal_getCurrentFunctionWaitUntil } from './internal-edge-wait-until';
import { getServerUtils } from '../server-utils';
import { searchParamsToUrlQuery } from '../../shared/lib/router/utils/querystring';
import { CloseController, trackStreamConsumed } from './web-on-close';
import { getEdgePreviewProps } from './get-edge-preview-props';
import { WebNextRequest } from '../../server/base-http/web';
/**
* EdgeRouteModuleWrapper is a wrapper around a route module.
*
* Note that this class should only be used in the edge runtime.
*/ export class EdgeRouteModuleWrapper {
/**
* The constructor is wrapped with private to ensure that it can only be
* constructed by the static wrap method.
*
* @param routeModule the route module to wrap
*/ constructor(routeModule){
this.routeModule = routeModule;
// TODO: (wyattjoh) possibly allow the module to define it's own matcher
this.matcher = new RouteMatcher(routeModule.definition);
}
/**
* This will wrap a module with the EdgeModuleWrapper and return a function
* that can be used as a handler for the edge runtime.
*
* @param module the module to wrap
* @param options any options that should be passed to the adapter and
* override the ones passed from the runtime
* @returns a function that can be used as a handler for the edge runtime
*/ static wrap(routeModule, options) {
// Create the module wrapper.
const wrapper = new EdgeRouteModuleWrapper(routeModule);
// Return the wrapping function.
return (opts)=>{
return adapter({
...opts,
IncrementalCache,
// Bind the handler method to the wrapper so it still has context.
handler: wrapper.handler.bind(wrapper),
page: options.page
});
};
}
async handler(request, evt) {
const utils = getServerUtils({
pageIsDynamic: this.matcher.isDynamic,
page: this.matcher.definition.pathname,
basePath: request.nextUrl.basePath,
// We don't need the `handleRewrite` util, so can just pass an empty object
rewrites: {},
// only used for rewrites, so setting an arbitrary default value here
caseSensitive: false
});
const { nextConfig } = this.routeModule.getNextConfigEdge(new WebNextRequest(request));
const { params } = utils.normalizeDynamicRouteParams(searchParamsToUrlQuery(request.nextUrl.searchParams), false);
const waitUntil = evt.waitUntil.bind(evt);
const closeController = new CloseController();
const previewProps = getEdgePreviewProps();
// Create the context for the handler. This contains the params from the
// match (if any).
const context = {
params,
prerenderManifest: {
version: 4,
routes: {},
dynamicRoutes: {},
preview: previewProps,
notFoundRoutes: []
},
renderOpts: {
supportsDynamicResponse: true,
waitUntil,
onClose: closeController.onClose.bind(closeController),
onAfterTaskError: undefined,
cacheComponents: !!process.env.__NEXT_CACHE_COMPONENTS,
experimental: {
authInterrupts: !!process.env.__NEXT_EXPERIMENTAL_AUTH_INTERRUPTS
},
cacheLifeProfiles: nextConfig.cacheLife
},
sharedContext: {
buildId: ''
}
};
// Get the response from the handler.
let res = await this.routeModule.handle(request, context);
const waitUntilPromises = [
internal_getCurrentFunctionWaitUntil()
];
if (context.renderOpts.pendingWaitUntil) {
waitUntilPromises.push(context.renderOpts.pendingWaitUntil);
}
evt.waitUntil(Promise.all(waitUntilPromises));
if (!res.body) {
// we can delay running it until a bit later --
// if it's needed, we'll have a `waitUntil` lock anyway.
setTimeout(()=>closeController.dispatchClose(), 0);
} else {
// NOTE: if this is a streaming response, onClose may be called later,
// so we can't rely on `closeController.listeners` -- it might be 0 at this point.
const trackedBody = trackStreamConsumed(res.body, ()=>closeController.dispatchClose());
res = new Response(trackedBody, {
status: res.status,
statusText: res.statusText,
headers: res.headers
});
}
return res;
}
}
//# sourceMappingURL=edge-route-module-wrapper.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,28 @@
export class PageSignatureError extends Error {
constructor({ page }){
super(`The middleware "${page}" accepts an async API directly with the form:
export function middleware(request, event) {
return NextResponse.redirect('/new-location')
}
Read more: https://nextjs.org/docs/messages/middleware-new-signature
`);
}
}
export class RemovedPageError extends Error {
constructor(){
super(`The request.page has been deprecated in favour of \`URLPattern\`.
Read more: https://nextjs.org/docs/messages/middleware-request-page
`);
}
}
export class RemovedUAError extends Error {
constructor(){
super(`The request.ua has been removed in favour of \`userAgent\` function.
Read more: https://nextjs.org/docs/messages/middleware-parse-user-agent
`);
}
}
//# sourceMappingURL=error.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/error.ts"],"sourcesContent":["export class PageSignatureError extends Error {\n constructor({ page }: { page: string }) {\n super(`The middleware \"${page}\" accepts an async API directly with the form:\n \n export function middleware(request, event) {\n return NextResponse.redirect('/new-location')\n }\n \n Read more: https://nextjs.org/docs/messages/middleware-new-signature\n `)\n }\n}\n\nexport class RemovedPageError extends Error {\n constructor() {\n super(`The request.page has been deprecated in favour of \\`URLPattern\\`.\n Read more: https://nextjs.org/docs/messages/middleware-request-page\n `)\n }\n}\n\nexport class RemovedUAError extends Error {\n constructor() {\n super(`The request.ua has been removed in favour of \\`userAgent\\` function.\n Read more: https://nextjs.org/docs/messages/middleware-parse-user-agent\n `)\n }\n}\n"],"names":["PageSignatureError","Error","constructor","page","RemovedPageError","RemovedUAError"],"mappings":"AAAA,OAAO,MAAMA,2BAA2BC;IACtCC,YAAY,EAAEC,IAAI,EAAoB,CAAE;QACtC,KAAK,CAAC,CAAC,gBAAgB,EAAEA,KAAK;;;;;;;EAOhC,CAAC;IACD;AACF;AAEA,OAAO,MAAMC,yBAAyBH;IACpCC,aAAc;QACZ,KAAK,CAAC,CAAC;;EAET,CAAC;IACD;AACF;AAEA,OAAO,MAAMG,uBAAuBJ;IAClCC,aAAc;QACZ,KAAK,CAAC,CAAC;;EAET,CAAC;IACD;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,10 @@
// Alias index file of next/server for edge runtime for tree-shaking purpose
export { ImageResponse } from '../spec-extension/image-response';
export { NextRequest } from '../spec-extension/request';
export { NextResponse } from '../spec-extension/response';
export { userAgent, userAgentFromString } from '../spec-extension/user-agent';
export { URLPattern } from '../spec-extension/url-pattern';
export { after } from '../../after';
export { connection } from '../../request/connection';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/exports/index.ts"],"sourcesContent":["// Alias index file of next/server for edge runtime for tree-shaking purpose\n\nexport { ImageResponse } from '../spec-extension/image-response'\nexport { NextRequest } from '../spec-extension/request'\nexport { NextResponse } from '../spec-extension/response'\nexport { userAgent, userAgentFromString } from '../spec-extension/user-agent'\nexport { URLPattern } from '../spec-extension/url-pattern'\nexport { after } from '../../after'\nexport { connection } from '../../request/connection'\n"],"names":["ImageResponse","NextRequest","NextResponse","userAgent","userAgentFromString","URLPattern","after","connection"],"mappings":"AAAA,4EAA4E;AAE5E,SAASA,aAAa,QAAQ,mCAAkC;AAChE,SAASC,WAAW,QAAQ,4BAA2B;AACvD,SAASC,YAAY,QAAQ,6BAA4B;AACzD,SAASC,SAAS,EAAEC,mBAAmB,QAAQ,+BAA8B;AAC7E,SAASC,UAAU,QAAQ,gCAA+B;AAC1D,SAASC,KAAK,QAAQ,cAAa;AACnC,SAASC,UAAU,QAAQ,2BAA0B","ignoreList":[0]}

View File

@@ -0,0 +1,13 @@
/**
* In edge runtime, these props directly accessed from environment variables.
* - local: env vars will be injected through edge-runtime as runtime env vars
* - deployment: env vars will be replaced by edge build pipeline
*/ export function getEdgePreviewProps() {
return {
previewModeId: process.env.__NEXT_PREVIEW_MODE_ID || '',
previewModeSigningKey: process.env.__NEXT_PREVIEW_MODE_SIGNING_KEY || '',
previewModeEncryptionKey: process.env.__NEXT_PREVIEW_MODE_ENCRYPTION_KEY || ''
};
}
//# sourceMappingURL=get-edge-preview-props.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/get-edge-preview-props.ts"],"sourcesContent":["/**\n * In edge runtime, these props directly accessed from environment variables.\n * - local: env vars will be injected through edge-runtime as runtime env vars\n * - deployment: env vars will be replaced by edge build pipeline\n */\nexport function getEdgePreviewProps() {\n return {\n previewModeId: process.env.__NEXT_PREVIEW_MODE_ID || '',\n previewModeSigningKey: process.env.__NEXT_PREVIEW_MODE_SIGNING_KEY || '',\n previewModeEncryptionKey:\n process.env.__NEXT_PREVIEW_MODE_ENCRYPTION_KEY || '',\n }\n}\n"],"names":["getEdgePreviewProps","previewModeId","process","env","__NEXT_PREVIEW_MODE_ID","previewModeSigningKey","__NEXT_PREVIEW_MODE_SIGNING_KEY","previewModeEncryptionKey","__NEXT_PREVIEW_MODE_ENCRYPTION_KEY"],"mappings":"AAAA;;;;CAIC,GACD,OAAO,SAASA;IACd,OAAO;QACLC,eAAeC,QAAQC,GAAG,CAACC,sBAAsB,IAAI;QACrDC,uBAAuBH,QAAQC,GAAG,CAACG,+BAA+B,IAAI;QACtEC,0BACEL,QAAQC,GAAG,CAACK,kCAAkC,IAAI;IACtD;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,102 @@
export async function getEdgeInstrumentationModule() {
const instrumentation = '_ENTRIES' in globalThis && _ENTRIES.middleware_instrumentation && await _ENTRIES.middleware_instrumentation;
return instrumentation;
}
let instrumentationModulePromise = null;
async function registerInstrumentation() {
// Ensure registerInstrumentation is not called in production build
if (process.env.NEXT_PHASE === 'phase-production-build') return;
if (!instrumentationModulePromise) {
instrumentationModulePromise = getEdgeInstrumentationModule();
}
const instrumentation = await instrumentationModulePromise;
if (instrumentation == null ? void 0 : instrumentation.register) {
try {
await instrumentation.register();
} catch (err) {
err.message = `An error occurred while loading instrumentation hook: ${err.message}`;
throw err;
}
}
}
export async function edgeInstrumentationOnRequestError(...args) {
const instrumentation = await getEdgeInstrumentationModule();
try {
var _instrumentation_onRequestError;
await (instrumentation == null ? void 0 : (_instrumentation_onRequestError = instrumentation.onRequestError) == null ? void 0 : _instrumentation_onRequestError.call(instrumentation, ...args));
} catch (err) {
// Log the soft error and continue, since the original error has already been thrown
console.error('Error in instrumentation.onRequestError:', err);
}
}
let registerInstrumentationPromise = null;
export function ensureInstrumentationRegistered() {
if (!registerInstrumentationPromise) {
registerInstrumentationPromise = registerInstrumentation();
}
return registerInstrumentationPromise;
}
function getUnsupportedModuleErrorMessage(module) {
// warning: if you change these messages, you must adjust how dev-overlay's middleware detects modules not found
return `The edge runtime does not support Node.js '${module}' module.
Learn More: https://nextjs.org/docs/messages/node-module-in-edge-runtime`;
}
function __import_unsupported(moduleName) {
const proxy = new Proxy(function() {}, {
get (_obj, prop) {
if (prop === 'then') {
return {};
}
throw Object.defineProperty(new Error(getUnsupportedModuleErrorMessage(moduleName)), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
},
construct () {
throw Object.defineProperty(new Error(getUnsupportedModuleErrorMessage(moduleName)), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
},
apply (_target, _this, args) {
if (typeof args[0] === 'function') {
return args[0](proxy);
}
throw Object.defineProperty(new Error(getUnsupportedModuleErrorMessage(moduleName)), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
});
return new Proxy({}, {
get: ()=>proxy
});
}
function enhanceGlobals() {
if (process.env.NEXT_RUNTIME !== 'edge') {
return;
}
// The condition is true when the "process" module is provided
if (process !== global.process) {
// prefer local process but global.process has correct "env"
process.env = global.process.env;
global.process = process;
}
// to allow building code that import but does not use node.js modules,
// webpack will expect this function to exist in global scope
try {
Object.defineProperty(globalThis, '__import_unsupported', {
value: __import_unsupported,
enumerable: false,
configurable: false
});
} catch {}
// Eagerly fire instrumentation hook to make the startup faster.
void ensureInstrumentationRegistered();
}
enhanceGlobals();
//# sourceMappingURL=globals.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
/**
* List of valid HTTP methods that can be implemented by Next.js's Custom App
* Routes.
*/ export const HTTP_METHODS = [
'GET',
'HEAD',
'OPTIONS',
'POST',
'PUT',
'DELETE',
'PATCH'
];
/**
* Checks to see if the passed string is an HTTP method. Note that this is case
* sensitive.
*
* @param maybeMethod the string that may be an HTTP method
* @returns true if the string is an HTTP method
*/ export function isHTTPMethod(maybeMethod) {
return HTTP_METHODS.includes(maybeMethod);
}
//# sourceMappingURL=http.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/http.ts"],"sourcesContent":["/**\n * List of valid HTTP methods that can be implemented by Next.js's Custom App\n * Routes.\n */\nexport const HTTP_METHODS = [\n 'GET',\n 'HEAD',\n 'OPTIONS',\n 'POST',\n 'PUT',\n 'DELETE',\n 'PATCH',\n] as const\n\n/**\n * A type representing the valid HTTP methods that can be implemented by\n * Next.js's Custom App Routes.\n */\nexport type HTTP_METHOD = (typeof HTTP_METHODS)[number]\n\n/**\n * Checks to see if the passed string is an HTTP method. Note that this is case\n * sensitive.\n *\n * @param maybeMethod the string that may be an HTTP method\n * @returns true if the string is an HTTP method\n */\nexport function isHTTPMethod(maybeMethod: string): maybeMethod is HTTP_METHOD {\n return HTTP_METHODS.includes(maybeMethod as HTTP_METHOD)\n}\n"],"names":["HTTP_METHODS","isHTTPMethod","maybeMethod","includes"],"mappings":"AAAA;;;CAGC,GACD,OAAO,MAAMA,eAAe;IAC1B;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAAS;AAQV;;;;;;CAMC,GACD,OAAO,SAASC,aAAaC,WAAmB;IAC9C,OAAOF,aAAaG,QAAQ,CAACD;AAC/B","ignoreList":[0]}

View File

@@ -0,0 +1,42 @@
// An internal module to expose the "waitUntil" API to Edge SSR and Edge Route Handler functions.
// This is highly experimental and subject to change.
// We still need a global key to bypass Webpack's layering of modules.
const GLOBAL_KEY = Symbol.for('__next_internal_waitUntil__');
const state = // @ts-ignore
globalThis[GLOBAL_KEY] || // @ts-ignore
(globalThis[GLOBAL_KEY] = {
waitUntilCounter: 0,
waitUntilResolve: undefined,
waitUntilPromise: null
});
// No matter how many concurrent requests are being handled, we want to make sure
// that the final promise is only resolved once all of the waitUntil promises have
// settled.
function resolveOnePromise() {
state.waitUntilCounter--;
if (state.waitUntilCounter === 0) {
state.waitUntilResolve();
state.waitUntilPromise = null;
}
}
export function internal_getCurrentFunctionWaitUntil() {
return state.waitUntilPromise;
}
export function internal_runWithWaitUntil(fn) {
const result = fn();
if (result && typeof result === 'object' && 'then' in result && 'finally' in result && typeof result.then === 'function' && typeof result.finally === 'function') {
if (!state.waitUntilCounter) {
// Create the promise for the next batch of waitUntil calls.
state.waitUntilPromise = new Promise((resolve)=>{
state.waitUntilResolve = resolve;
});
}
state.waitUntilCounter++;
return result.finally(()=>{
resolveOnePromise();
});
}
return result;
}
//# sourceMappingURL=internal-edge-wait-until.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/internal-edge-wait-until.ts"],"sourcesContent":["// An internal module to expose the \"waitUntil\" API to Edge SSR and Edge Route Handler functions.\n// This is highly experimental and subject to change.\n\n// We still need a global key to bypass Webpack's layering of modules.\nconst GLOBAL_KEY = Symbol.for('__next_internal_waitUntil__')\n\nconst state: {\n waitUntilCounter: number\n waitUntilResolve: () => void\n waitUntilPromise: Promise<void> | null\n} =\n // @ts-ignore\n globalThis[GLOBAL_KEY] ||\n // @ts-ignore\n (globalThis[GLOBAL_KEY] = {\n waitUntilCounter: 0,\n waitUntilResolve: undefined,\n waitUntilPromise: null,\n })\n\n// No matter how many concurrent requests are being handled, we want to make sure\n// that the final promise is only resolved once all of the waitUntil promises have\n// settled.\nfunction resolveOnePromise() {\n state.waitUntilCounter--\n if (state.waitUntilCounter === 0) {\n state.waitUntilResolve()\n state.waitUntilPromise = null\n }\n}\n\nexport function internal_getCurrentFunctionWaitUntil() {\n return state.waitUntilPromise\n}\n\nexport function internal_runWithWaitUntil<T>(fn: () => T): T {\n const result = fn()\n if (\n result &&\n typeof result === 'object' &&\n 'then' in result &&\n 'finally' in result &&\n typeof result.then === 'function' &&\n typeof result.finally === 'function'\n ) {\n if (!state.waitUntilCounter) {\n // Create the promise for the next batch of waitUntil calls.\n state.waitUntilPromise = new Promise<void>((resolve) => {\n state.waitUntilResolve = resolve\n })\n }\n state.waitUntilCounter++\n return result.finally(() => {\n resolveOnePromise()\n })\n }\n\n return result\n}\n"],"names":["GLOBAL_KEY","Symbol","for","state","globalThis","waitUntilCounter","waitUntilResolve","undefined","waitUntilPromise","resolveOnePromise","internal_getCurrentFunctionWaitUntil","internal_runWithWaitUntil","fn","result","then","finally","Promise","resolve"],"mappings":"AAAA,iGAAiG;AACjG,qDAAqD;AAErD,sEAAsE;AACtE,MAAMA,aAAaC,OAAOC,GAAG,CAAC;AAE9B,MAAMC,QAKJ,aAAa;AACbC,UAAU,CAACJ,WAAW,IACtB,aAAa;AACZI,CAAAA,UAAU,CAACJ,WAAW,GAAG;IACxBK,kBAAkB;IAClBC,kBAAkBC;IAClBC,kBAAkB;AACpB,CAAA;AAEF,iFAAiF;AACjF,kFAAkF;AAClF,WAAW;AACX,SAASC;IACPN,MAAME,gBAAgB;IACtB,IAAIF,MAAME,gBAAgB,KAAK,GAAG;QAChCF,MAAMG,gBAAgB;QACtBH,MAAMK,gBAAgB,GAAG;IAC3B;AACF;AAEA,OAAO,SAASE;IACd,OAAOP,MAAMK,gBAAgB;AAC/B;AAEA,OAAO,SAASG,0BAA6BC,EAAW;IACtD,MAAMC,SAASD;IACf,IACEC,UACA,OAAOA,WAAW,YAClB,UAAUA,UACV,aAAaA,UACb,OAAOA,OAAOC,IAAI,KAAK,cACvB,OAAOD,OAAOE,OAAO,KAAK,YAC1B;QACA,IAAI,CAACZ,MAAME,gBAAgB,EAAE;YAC3B,4DAA4D;YAC5DF,MAAMK,gBAAgB,GAAG,IAAIQ,QAAc,CAACC;gBAC1Cd,MAAMG,gBAAgB,GAAGW;YAC3B;QACF;QACAd,MAAME,gBAAgB;QACtB,OAAOQ,OAAOE,OAAO,CAAC;YACpBN;QACF;IACF;IAEA,OAAOI;AACT","ignoreList":[0]}

View File

@@ -0,0 +1,185 @@
import { detectDomainLocale } from '../../shared/lib/i18n/detect-domain-locale';
import { formatNextPathnameInfo } from '../../shared/lib/router/utils/format-next-pathname-info';
import { getHostname } from '../../shared/lib/get-hostname';
import { getNextPathnameInfo } from '../../shared/lib/router/utils/get-next-pathname-info';
const REGEX_LOCALHOST_HOSTNAME = /(?!^https?:\/\/)(127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}|\[::1\]|localhost)/;
function parseURL(url, base) {
return new URL(String(url).replace(REGEX_LOCALHOST_HOSTNAME, 'localhost'), base && String(base).replace(REGEX_LOCALHOST_HOSTNAME, 'localhost'));
}
const Internal = Symbol('NextURLInternal');
export class NextURL {
constructor(input, baseOrOpts, opts){
let base;
let options;
if (typeof baseOrOpts === 'object' && 'pathname' in baseOrOpts || typeof baseOrOpts === 'string') {
base = baseOrOpts;
options = opts || {};
} else {
options = opts || baseOrOpts || {};
}
this[Internal] = {
url: parseURL(input, base ?? options.base),
options: options,
basePath: ''
};
this.analyze();
}
analyze() {
var _this_Internal_options_nextConfig_i18n, _this_Internal_options_nextConfig, _this_Internal_domainLocale, _this_Internal_options_nextConfig_i18n1, _this_Internal_options_nextConfig1;
const info = getNextPathnameInfo(this[Internal].url.pathname, {
nextConfig: this[Internal].options.nextConfig,
parseData: !process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE,
i18nProvider: this[Internal].options.i18nProvider
});
const hostname = getHostname(this[Internal].url, this[Internal].options.headers);
this[Internal].domainLocale = this[Internal].options.i18nProvider ? this[Internal].options.i18nProvider.detectDomainLocale(hostname) : detectDomainLocale((_this_Internal_options_nextConfig = this[Internal].options.nextConfig) == null ? void 0 : (_this_Internal_options_nextConfig_i18n = _this_Internal_options_nextConfig.i18n) == null ? void 0 : _this_Internal_options_nextConfig_i18n.domains, hostname);
const defaultLocale = ((_this_Internal_domainLocale = this[Internal].domainLocale) == null ? void 0 : _this_Internal_domainLocale.defaultLocale) || ((_this_Internal_options_nextConfig1 = this[Internal].options.nextConfig) == null ? void 0 : (_this_Internal_options_nextConfig_i18n1 = _this_Internal_options_nextConfig1.i18n) == null ? void 0 : _this_Internal_options_nextConfig_i18n1.defaultLocale);
this[Internal].url.pathname = info.pathname;
this[Internal].defaultLocale = defaultLocale;
this[Internal].basePath = info.basePath ?? '';
this[Internal].buildId = info.buildId;
this[Internal].locale = info.locale ?? defaultLocale;
this[Internal].trailingSlash = info.trailingSlash;
}
formatPathname() {
return formatNextPathnameInfo({
basePath: this[Internal].basePath,
buildId: this[Internal].buildId,
defaultLocale: !this[Internal].options.forceLocale ? this[Internal].defaultLocale : undefined,
locale: this[Internal].locale,
pathname: this[Internal].url.pathname,
trailingSlash: this[Internal].trailingSlash
});
}
formatSearch() {
return this[Internal].url.search;
}
get buildId() {
return this[Internal].buildId;
}
set buildId(buildId) {
this[Internal].buildId = buildId;
}
get locale() {
return this[Internal].locale ?? '';
}
set locale(locale) {
var _this_Internal_options_nextConfig_i18n, _this_Internal_options_nextConfig;
if (!this[Internal].locale || !((_this_Internal_options_nextConfig = this[Internal].options.nextConfig) == null ? void 0 : (_this_Internal_options_nextConfig_i18n = _this_Internal_options_nextConfig.i18n) == null ? void 0 : _this_Internal_options_nextConfig_i18n.locales.includes(locale))) {
throw Object.defineProperty(new TypeError(`The NextURL configuration includes no locale "${locale}"`), "__NEXT_ERROR_CODE", {
value: "E597",
enumerable: false,
configurable: true
});
}
this[Internal].locale = locale;
}
get defaultLocale() {
return this[Internal].defaultLocale;
}
get domainLocale() {
return this[Internal].domainLocale;
}
get searchParams() {
return this[Internal].url.searchParams;
}
get host() {
return this[Internal].url.host;
}
set host(value) {
this[Internal].url.host = value;
}
get hostname() {
return this[Internal].url.hostname;
}
set hostname(value) {
this[Internal].url.hostname = value;
}
get port() {
return this[Internal].url.port;
}
set port(value) {
this[Internal].url.port = value;
}
get protocol() {
return this[Internal].url.protocol;
}
set protocol(value) {
this[Internal].url.protocol = value;
}
get href() {
const pathname = this.formatPathname();
const search = this.formatSearch();
return `${this.protocol}//${this.host}${pathname}${search}${this.hash}`;
}
set href(url) {
this[Internal].url = parseURL(url);
this.analyze();
}
get origin() {
return this[Internal].url.origin;
}
get pathname() {
return this[Internal].url.pathname;
}
set pathname(value) {
this[Internal].url.pathname = value;
}
get hash() {
return this[Internal].url.hash;
}
set hash(value) {
this[Internal].url.hash = value;
}
get search() {
return this[Internal].url.search;
}
set search(value) {
this[Internal].url.search = value;
}
get password() {
return this[Internal].url.password;
}
set password(value) {
this[Internal].url.password = value;
}
get username() {
return this[Internal].url.username;
}
set username(value) {
this[Internal].url.username = value;
}
get basePath() {
return this[Internal].basePath;
}
set basePath(value) {
this[Internal].basePath = value.startsWith('/') ? value : `/${value}`;
}
toString() {
return this.href;
}
toJSON() {
return this.href;
}
[Symbol.for('edge-runtime.inspect.custom')]() {
return {
href: this.href,
origin: this.origin,
protocol: this.protocol,
username: this.username,
password: this.password,
host: this.host,
hostname: this.hostname,
port: this.port,
pathname: this.pathname,
search: this.search,
searchParams: this.searchParams,
hash: this.hash
};
}
clone() {
return new NextURL(String(this), this[Internal].options);
}
}
//# sourceMappingURL=next-url.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,437 @@
import { AsyncLocalStorage } from 'async_hooks';
import { COMPILER_NAMES, EDGE_UNSUPPORTED_NODE_APIS } from '../../../shared/lib/constants';
import { EdgeRuntime } from 'next/dist/compiled/edge-runtime';
import { readFileSync, promises as fs } from 'fs';
import { validateURL } from '../utils';
import { pick } from '../../../lib/pick';
import { fetchInlineAsset } from './fetch-inline-assets';
import { runInContext } from 'vm';
import BufferImplementation from 'node:buffer';
import EventsImplementation from 'node:events';
import AssertImplementation from 'node:assert';
import UtilImplementation from 'node:util';
import AsyncHooksImplementation from 'node:async_hooks';
import { intervalsManager, timeoutsManager } from './resource-managers';
import { createLocalRequestContext } from '../../after/builtin-request-context';
import { patchErrorInspectEdgeLite, patchErrorInspectNodeJS } from '../../patch-error-inspect';
let getServerError;
let decorateServerError;
if (process.env.NODE_ENV === 'development') {
getServerError = require('../../dev/node-stack-frames').getServerError;
decorateServerError = require('../../../shared/lib/error-source').decorateServerError;
} else {
getServerError = (error)=>error;
decorateServerError = ()=>{};
}
/**
* A Map of cached module contexts indexed by the module name. It allows
* to have a different cache scoped per module name or depending on the
* provided module key on creation.
*/ const moduleContexts = new Map();
const pendingModuleCaches = new Map();
/**
* Same as clearModuleContext but for all module contexts.
*/ export async function clearAllModuleContexts() {
intervalsManager.removeAll();
timeoutsManager.removeAll();
moduleContexts.clear();
pendingModuleCaches.clear();
}
/**
* For a given path a context, this function checks if there is any module
* context that contains the path with an older content and, if that's the
* case, removes the context from the cache.
*
* This function also clears all intervals and timeouts created by the
* module context.
*/ export async function clearModuleContext(path) {
intervalsManager.removeAll();
timeoutsManager.removeAll();
const handleContext = (key, cache, context)=>{
if (cache == null ? void 0 : cache.paths.has(path)) {
context.delete(key);
}
};
for (const [key, cache] of moduleContexts){
handleContext(key, cache, moduleContexts);
}
for (const [key, cache] of pendingModuleCaches){
handleContext(key, await cache, pendingModuleCaches);
}
}
async function loadWasm(wasm) {
const modules = {};
await Promise.all(wasm.map(async (binding)=>{
const module = await WebAssembly.compile(// @ts-expect-error - Argument of type 'Buffer<ArrayBufferLike>' is not assignable to parameter of type 'BufferSource'.
await fs.readFile(binding.filePath));
modules[binding.name] = module;
}));
return modules;
}
function buildEnvironmentVariablesFrom(injectedEnvironments) {
let env = Object.fromEntries([
...Object.entries(process.env),
...Object.entries(injectedEnvironments),
[
'NEXT_RUNTIME',
'edge'
]
]);
return env;
}
function throwUnsupportedAPIError(name) {
const error = Object.defineProperty(new Error(`A Node.js API is used (${name}) which is not supported in the Edge Runtime.
Learn more: https://nextjs.org/docs/api-reference/edge-runtime`), "__NEXT_ERROR_CODE", {
value: "E97",
enumerable: false,
configurable: true
});
decorateServerError(error, COMPILER_NAMES.edgeServer);
throw error;
}
function createProcessPolyfill(env) {
const processPolyfill = {
env: buildEnvironmentVariablesFrom(env)
};
const overriddenValue = {};
for (const key of Object.keys(process)){
if (key === 'env') continue;
Object.defineProperty(processPolyfill, key, {
get () {
if (overriddenValue[key] !== undefined) {
return overriddenValue[key];
}
if (typeof process[key] === 'function') {
return ()=>throwUnsupportedAPIError(`process.${key}`);
}
return undefined;
},
set (value) {
overriddenValue[key] = value;
},
enumerable: false
});
}
return processPolyfill;
}
function addStub(context, name) {
Object.defineProperty(context, name, {
get () {
return function() {
throwUnsupportedAPIError(name);
};
},
enumerable: false
});
}
function getDecorateUnhandledError(runtime) {
const EdgeRuntimeError = runtime.evaluate(`Error`);
return (error)=>{
if (error instanceof EdgeRuntimeError) {
decorateServerError(error, COMPILER_NAMES.edgeServer);
}
};
}
function getDecorateUnhandledRejection(runtime) {
const EdgeRuntimeError = runtime.evaluate(`Error`);
return (rejected)=>{
if (rejected.reason instanceof EdgeRuntimeError) {
decorateServerError(rejected.reason, COMPILER_NAMES.edgeServer);
}
};
}
const NativeModuleMap = (()=>{
const mods = {
'node:buffer': pick(BufferImplementation, [
'constants',
'kMaxLength',
'kStringMaxLength',
'Buffer',
'SlowBuffer'
]),
'node:events': pick(EventsImplementation, [
'EventEmitter',
'captureRejectionSymbol',
'defaultMaxListeners',
'errorMonitor',
'listenerCount',
'on',
'once'
]),
'node:async_hooks': pick(AsyncHooksImplementation, [
'AsyncLocalStorage',
'AsyncResource'
]),
'node:assert': pick(AssertImplementation, [
'AssertionError',
'deepEqual',
'deepStrictEqual',
'doesNotMatch',
'doesNotReject',
'doesNotThrow',
'equal',
'fail',
'ifError',
'match',
'notDeepEqual',
'notDeepStrictEqual',
'notEqual',
'notStrictEqual',
'ok',
'rejects',
'strict',
'strictEqual',
'throws'
]),
'node:util': pick(UtilImplementation, [
'_extend',
'callbackify',
'format',
'inherits',
'promisify',
'types'
])
};
return new Map(Object.entries(mods));
})();
export const requestStore = new AsyncLocalStorage();
export const edgeSandboxNextRequestContext = createLocalRequestContext();
/**
* Create a module cache specific for the provided parameters. It includes
* a runtime context, require cache and paths cache.
*/ async function createModuleContext(options) {
const warnedEvals = new Set();
const warnedWasmCodegens = new Set();
const { edgeFunctionEntry } = options;
const wasm = await loadWasm(edgeFunctionEntry.wasm ?? []);
const runtime = new EdgeRuntime({
codeGeneration: process.env.NODE_ENV !== 'production' ? {
strings: true,
wasm: true
} : undefined,
extend: (context)=>{
context.process = createProcessPolyfill(edgeFunctionEntry.env);
Object.defineProperty(context, 'require', {
enumerable: false,
value: (id)=>{
const value = NativeModuleMap.get(id);
if (!value) {
throw Object.defineProperty(new TypeError('Native module not found: ' + id), "__NEXT_ERROR_CODE", {
value: "E546",
enumerable: false,
configurable: true
});
}
return value;
}
});
if (process.env.NODE_ENV !== 'production') {
context.__next_log_error__ = function(err) {
options.onError(err);
};
}
context.__next_eval__ = function __next_eval__(fn) {
const key = fn.toString();
if (!warnedEvals.has(key)) {
const warning = getServerError(Object.defineProperty(new Error(`Dynamic Code Evaluation (e. g. 'eval', 'new Function') not allowed in Edge Runtime
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), "__NEXT_ERROR_CODE", {
value: "E149",
enumerable: false,
configurable: true
}), COMPILER_NAMES.edgeServer);
warning.name = 'DynamicCodeEvaluationWarning';
Error.captureStackTrace(warning, __next_eval__);
warnedEvals.add(key);
options.onWarning(warning);
}
return fn();
};
context.__next_webassembly_compile__ = function __next_webassembly_compile__(fn) {
const key = fn.toString();
if (!warnedWasmCodegens.has(key)) {
const warning = getServerError(Object.defineProperty(new Error(`Dynamic WASM code generation (e. g. 'WebAssembly.compile') not allowed in Edge Runtime.
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), "__NEXT_ERROR_CODE", {
value: "E184",
enumerable: false,
configurable: true
}), COMPILER_NAMES.edgeServer);
warning.name = 'DynamicWasmCodeGenerationWarning';
Error.captureStackTrace(warning, __next_webassembly_compile__);
warnedWasmCodegens.add(key);
options.onWarning(warning);
}
return fn();
};
context.__next_webassembly_instantiate__ = async function __next_webassembly_instantiate__(fn) {
const result = await fn();
// If a buffer is given, WebAssembly.instantiate returns an object
// containing both a module and an instance while it returns only an
// instance if a WASM module is given. Utilize the fact to determine
// if the WASM code generation happens.
//
// https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/instantiate#primary_overload_%E2%80%94_taking_wasm_binary_code
const instantiatedFromBuffer = result.hasOwnProperty('module');
const key = fn.toString();
if (instantiatedFromBuffer && !warnedWasmCodegens.has(key)) {
const warning = getServerError(Object.defineProperty(new Error(`Dynamic WASM code generation ('WebAssembly.instantiate' with a buffer parameter) not allowed in Edge Runtime.
Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`), "__NEXT_ERROR_CODE", {
value: "E40",
enumerable: false,
configurable: true
}), COMPILER_NAMES.edgeServer);
warning.name = 'DynamicWasmCodeGenerationWarning';
Error.captureStackTrace(warning, __next_webassembly_instantiate__);
warnedWasmCodegens.add(key);
options.onWarning(warning);
}
return result;
};
const __fetch = context.fetch;
context.fetch = async (input, init = {})=>{
const callingError = Object.defineProperty(new Error('[internal]'), "__NEXT_ERROR_CODE", {
value: "E5",
enumerable: false,
configurable: true
});
const assetResponse = await fetchInlineAsset({
input,
assets: options.edgeFunctionEntry.assets,
distDir: options.distDir,
context
});
if (assetResponse) {
return assetResponse;
}
init.headers = new Headers(init.headers ?? {});
if (!init.headers.has('user-agent')) {
init.headers.set(`user-agent`, `Next.js Middleware`);
}
const response = typeof input === 'object' && 'url' in input ? __fetch(input.url, {
...pick(input, [
'method',
'body',
'cache',
'credentials',
'integrity',
'keepalive',
'mode',
'redirect',
'referrer',
'referrerPolicy',
'signal'
]),
...init,
headers: {
...Object.fromEntries(input.headers),
...Object.fromEntries(init.headers)
}
}) : __fetch(String(input), init);
return await response.catch((err)=>{
callingError.message = err.message;
err.stack = callingError.stack;
throw err;
});
};
const __Request = context.Request;
context.Request = class extends __Request {
constructor(input, init){
const url = typeof input !== 'string' && 'url' in input ? input.url : String(input);
if (typeof input === 'string') {
validateURL(url);
super(input, init);
} else {
super(input, init);
validateURL(url);
}
this.next = init == null ? void 0 : init.next;
}
};
const __redirect = context.Response.redirect.bind(context.Response);
context.Response.redirect = (...args)=>{
validateURL(args[0]);
return __redirect(...args);
};
for (const name of EDGE_UNSUPPORTED_NODE_APIS){
addStub(context, name);
}
Object.assign(context, wasm);
context.performance = performance;
context.AsyncLocalStorage = AsyncLocalStorage;
// @ts-ignore the timeouts have weird types in the edge runtime
context.setInterval = (...args)=>intervalsManager.add(args);
// @ts-ignore the timeouts have weird types in the edge runtime
context.clearInterval = (interval)=>intervalsManager.remove(interval);
// @ts-ignore the timeouts have weird types in the edge runtime
context.setTimeout = (...args)=>timeoutsManager.add(args);
// @ts-ignore the timeouts have weird types in the edge runtime
context.clearTimeout = (timeout)=>timeoutsManager.remove(timeout);
// Duplicated from packages/next/src/server/after/builtin-request-context.ts
// because we need to use the sandboxed `Symbol.for`, not the one from the outside
const NEXT_REQUEST_CONTEXT_SYMBOL = context.Symbol.for('@next/request-context');
Object.defineProperty(context, NEXT_REQUEST_CONTEXT_SYMBOL, {
enumerable: false,
value: edgeSandboxNextRequestContext
});
return context;
}
});
const decorateUnhandledError = getDecorateUnhandledError(runtime);
runtime.context.addEventListener('error', decorateUnhandledError);
const decorateUnhandledRejection = getDecorateUnhandledRejection(runtime);
runtime.context.addEventListener('unhandledrejection', decorateUnhandledRejection);
patchErrorInspectEdgeLite(runtime.context.Error);
// An Error from within the Edge Runtime could also bubble up into the Node.js process.
// For example, uncaught errors are handled in the Node.js runtime.
patchErrorInspectNodeJS(runtime.context.Error);
return {
runtime,
paths: new Map(),
warnedEvals: new Set()
};
}
function getModuleContextShared(options) {
let deferredModuleContext = pendingModuleCaches.get(options.moduleName);
if (!deferredModuleContext) {
deferredModuleContext = createModuleContext(options);
pendingModuleCaches.set(options.moduleName, deferredModuleContext);
}
return deferredModuleContext;
}
/**
* For a given module name this function will get a cached module
* context or create it. It will return the module context along
* with a function that allows to run some code from a given
* filepath within the context.
*/ export async function getModuleContext(options) {
let lazyModuleContext;
if (options.useCache) {
lazyModuleContext = moduleContexts.get(options.moduleName) || await getModuleContextShared(options);
}
if (!lazyModuleContext) {
lazyModuleContext = await createModuleContext(options);
moduleContexts.set(options.moduleName, lazyModuleContext);
}
const moduleContext = lazyModuleContext;
const evaluateInContext = (filepath)=>{
if (!moduleContext.paths.has(filepath)) {
const content = readFileSync(filepath, 'utf-8');
try {
runInContext(content, moduleContext.runtime.context, {
filename: filepath
});
moduleContext.paths.set(filepath, content);
} catch (error) {
if (options.useCache) {
moduleContext == null ? void 0 : moduleContext.paths.delete(filepath);
}
throw error;
}
}
};
return {
...moduleContext,
evaluateInContext
};
}
//# sourceMappingURL=context.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,29 @@
import { createReadStream, promises as fs } from 'fs';
import { requestToBodyStream } from '../../body-streams';
import { resolve } from 'path';
/**
* Short-circuits the `fetch` function
* to return a stream for a given asset, if a user used `new URL("file", import.meta.url)`.
* This allows to embed assets in Edge Runtime.
*/ export async function fetchInlineAsset(options) {
const inputString = String(options.input);
if (!inputString.startsWith('blob:')) {
return;
}
const name = inputString.replace('blob:', '');
const asset = options.assets ? options.assets.find((x)=>x.name === name) : {
name,
filePath: name
};
if (!asset) {
return;
}
const filePath = resolve(options.distDir, asset.filePath);
const fileIsReadable = await fs.access(filePath).then(()=>true, ()=>false);
if (fileIsReadable) {
const readStream = createReadStream(filePath);
return new options.context.Response(requestToBodyStream(options.context, Uint8Array, readStream));
}
}
//# sourceMappingURL=fetch-inline-assets.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/sandbox/fetch-inline-assets.ts"],"sourcesContent":["import type { EdgeFunctionDefinition } from '../../../build/webpack/plugins/middleware-plugin'\nimport { createReadStream, promises as fs } from 'fs'\nimport { requestToBodyStream } from '../../body-streams'\nimport { resolve } from 'path'\n\n/**\n * Short-circuits the `fetch` function\n * to return a stream for a given asset, if a user used `new URL(\"file\", import.meta.url)`.\n * This allows to embed assets in Edge Runtime.\n */\nexport async function fetchInlineAsset(options: {\n input: RequestInfo | URL\n distDir: string\n assets: EdgeFunctionDefinition['assets']\n context: { Response: typeof Response; ReadableStream: typeof ReadableStream }\n}): Promise<Response | undefined> {\n const inputString = String(options.input)\n if (!inputString.startsWith('blob:')) {\n return\n }\n\n const name = inputString.replace('blob:', '')\n const asset = options.assets\n ? options.assets.find((x) => x.name === name)\n : {\n name,\n filePath: name,\n }\n if (!asset) {\n return\n }\n\n const filePath = resolve(options.distDir, asset.filePath)\n const fileIsReadable = await fs.access(filePath).then(\n () => true,\n () => false\n )\n\n if (fileIsReadable) {\n const readStream = createReadStream(filePath)\n return new options.context.Response(\n requestToBodyStream(options.context, Uint8Array, readStream)\n )\n }\n}\n"],"names":["createReadStream","promises","fs","requestToBodyStream","resolve","fetchInlineAsset","options","inputString","String","input","startsWith","name","replace","asset","assets","find","x","filePath","distDir","fileIsReadable","access","then","readStream","context","Response","Uint8Array"],"mappings":"AACA,SAASA,gBAAgB,EAAEC,YAAYC,EAAE,QAAQ,KAAI;AACrD,SAASC,mBAAmB,QAAQ,qBAAoB;AACxD,SAASC,OAAO,QAAQ,OAAM;AAE9B;;;;CAIC,GACD,OAAO,eAAeC,iBAAiBC,OAKtC;IACC,MAAMC,cAAcC,OAAOF,QAAQG,KAAK;IACxC,IAAI,CAACF,YAAYG,UAAU,CAAC,UAAU;QACpC;IACF;IAEA,MAAMC,OAAOJ,YAAYK,OAAO,CAAC,SAAS;IAC1C,MAAMC,QAAQP,QAAQQ,MAAM,GACxBR,QAAQQ,MAAM,CAACC,IAAI,CAAC,CAACC,IAAMA,EAAEL,IAAI,KAAKA,QACtC;QACEA;QACAM,UAAUN;IACZ;IACJ,IAAI,CAACE,OAAO;QACV;IACF;IAEA,MAAMI,WAAWb,QAAQE,QAAQY,OAAO,EAAEL,MAAMI,QAAQ;IACxD,MAAME,iBAAiB,MAAMjB,GAAGkB,MAAM,CAACH,UAAUI,IAAI,CACnD,IAAM,MACN,IAAM;IAGR,IAAIF,gBAAgB;QAClB,MAAMG,aAAatB,iBAAiBiB;QACpC,OAAO,IAAIX,QAAQiB,OAAO,CAACC,QAAQ,CACjCrB,oBAAoBG,QAAQiB,OAAO,EAAEE,YAAYH;IAErD;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,4 @@
export * from './sandbox';
export { clearModuleContext } from './context';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/sandbox/index.ts"],"sourcesContent":["export * from './sandbox'\nexport { clearModuleContext } from './context'\n"],"names":["clearModuleContext"],"mappings":"AAAA,cAAc,YAAW;AACzB,SAASA,kBAAkB,QAAQ,YAAW","ignoreList":[0]}

View File

@@ -0,0 +1,66 @@
class ResourceManager {
add(resourceArgs) {
const resource = this.create(resourceArgs);
this.resources.push(resource);
return resource;
}
remove(resource) {
this.resources = this.resources.filter((r)=>r !== resource);
this.destroy(resource);
}
removeAll() {
this.resources.forEach(this.destroy);
this.resources = [];
}
constructor(){
this.resources = [];
}
}
class IntervalsManager extends ResourceManager {
create(args) {
// TODO: use the edge runtime provided `setInterval` instead
return webSetIntervalPolyfill(...args);
}
destroy(interval) {
clearInterval(interval);
}
}
class TimeoutsManager extends ResourceManager {
create(args) {
// TODO: use the edge runtime provided `setTimeout` instead
return webSetTimeoutPolyfill(...args);
}
destroy(timeout) {
clearTimeout(timeout);
}
}
function webSetIntervalPolyfill(callback, ms, ...args) {
return setInterval(()=>{
// node's `setInterval` sets `this` to the `Timeout` instance it returned,
// but web `setInterval` always sets `this` to `window`
// see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval#the_this_problem
return callback.apply(globalThis, args);
}, ms)[Symbol.toPrimitive]();
}
function webSetTimeoutPolyfill(callback, ms, ...args) {
const wrappedCallback = ()=>{
try {
// node's `setTimeout` sets `this` to the `Timeout` instance it returned,
// but web `setTimeout` always sets `this` to `window`
// see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#the_this_problem
return callback.apply(globalThis, args);
} finally{
// On certain older node versions (<20.16.0, <22.4.0),
// a `setTimeout` whose Timeout was converted to a primitive will leak.
// See: https://github.com/nodejs/node/issues/53335
// We can work around this by explicitly calling `clearTimeout` after the callback runs.
clearTimeout(timeout);
}
};
const timeout = setTimeout(wrappedCallback, ms);
return timeout[Symbol.toPrimitive]();
}
export const intervalsManager = new IntervalsManager();
export const timeoutsManager = new TimeoutsManager();
//# sourceMappingURL=resource-managers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/sandbox/resource-managers.ts"],"sourcesContent":["abstract class ResourceManager<T, Args> {\n private resources: T[] = []\n\n abstract create(resourceArgs: Args): T\n abstract destroy(resource: T): void\n\n add(resourceArgs: Args) {\n const resource = this.create(resourceArgs)\n this.resources.push(resource)\n return resource\n }\n\n remove(resource: T) {\n this.resources = this.resources.filter((r) => r !== resource)\n this.destroy(resource)\n }\n\n removeAll() {\n this.resources.forEach(this.destroy)\n this.resources = []\n }\n}\n\nclass IntervalsManager extends ResourceManager<\n number,\n Parameters<typeof setInterval>\n> {\n create(args: Parameters<typeof setInterval>) {\n // TODO: use the edge runtime provided `setInterval` instead\n return webSetIntervalPolyfill(...args)\n }\n\n destroy(interval: number) {\n clearInterval(interval)\n }\n}\n\nclass TimeoutsManager extends ResourceManager<\n number,\n Parameters<typeof setTimeout>\n> {\n create(args: Parameters<typeof setTimeout>) {\n // TODO: use the edge runtime provided `setTimeout` instead\n return webSetTimeoutPolyfill(...args)\n }\n\n destroy(timeout: number) {\n clearTimeout(timeout)\n }\n}\n\nfunction webSetIntervalPolyfill<TArgs extends any[]>(\n callback: (...args: TArgs) => void,\n ms?: number,\n ...args: TArgs\n): number {\n return setInterval(() => {\n // node's `setInterval` sets `this` to the `Timeout` instance it returned,\n // but web `setInterval` always sets `this` to `window`\n // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setInterval#the_this_problem\n return callback.apply(globalThis, args)\n }, ms)[Symbol.toPrimitive]()\n}\n\nfunction webSetTimeoutPolyfill<TArgs extends any[]>(\n callback: (...args: TArgs) => void,\n ms?: number,\n ...args: TArgs\n): number {\n const wrappedCallback = () => {\n try {\n // node's `setTimeout` sets `this` to the `Timeout` instance it returned,\n // but web `setTimeout` always sets `this` to `window`\n // see: https://developer.mozilla.org/en-US/docs/Web/API/Window/setTimeout#the_this_problem\n return callback.apply(globalThis, args)\n } finally {\n // On certain older node versions (<20.16.0, <22.4.0),\n // a `setTimeout` whose Timeout was converted to a primitive will leak.\n // See: https://github.com/nodejs/node/issues/53335\n // We can work around this by explicitly calling `clearTimeout` after the callback runs.\n clearTimeout(timeout)\n }\n }\n const timeout = setTimeout(wrappedCallback, ms)\n return timeout[Symbol.toPrimitive]()\n}\n\nexport const intervalsManager = new IntervalsManager()\nexport const timeoutsManager = new TimeoutsManager()\n"],"names":["ResourceManager","add","resourceArgs","resource","create","resources","push","remove","filter","r","destroy","removeAll","forEach","IntervalsManager","args","webSetIntervalPolyfill","interval","clearInterval","TimeoutsManager","webSetTimeoutPolyfill","timeout","clearTimeout","callback","ms","setInterval","apply","globalThis","Symbol","toPrimitive","wrappedCallback","setTimeout","intervalsManager","timeoutsManager"],"mappings":"AAAA,MAAeA;IAMbC,IAAIC,YAAkB,EAAE;QACtB,MAAMC,WAAW,IAAI,CAACC,MAAM,CAACF;QAC7B,IAAI,CAACG,SAAS,CAACC,IAAI,CAACH;QACpB,OAAOA;IACT;IAEAI,OAAOJ,QAAW,EAAE;QAClB,IAAI,CAACE,SAAS,GAAG,IAAI,CAACA,SAAS,CAACG,MAAM,CAAC,CAACC,IAAMA,MAAMN;QACpD,IAAI,CAACO,OAAO,CAACP;IACf;IAEAQ,YAAY;QACV,IAAI,CAACN,SAAS,CAACO,OAAO,CAAC,IAAI,CAACF,OAAO;QACnC,IAAI,CAACL,SAAS,GAAG,EAAE;IACrB;;aAnBQA,YAAiB,EAAE;;AAoB7B;AAEA,MAAMQ,yBAAyBb;IAI7BI,OAAOU,IAAoC,EAAE;QAC3C,4DAA4D;QAC5D,OAAOC,0BAA0BD;IACnC;IAEAJ,QAAQM,QAAgB,EAAE;QACxBC,cAAcD;IAChB;AACF;AAEA,MAAME,wBAAwBlB;IAI5BI,OAAOU,IAAmC,EAAE;QAC1C,2DAA2D;QAC3D,OAAOK,yBAAyBL;IAClC;IAEAJ,QAAQU,OAAe,EAAE;QACvBC,aAAaD;IACf;AACF;AAEA,SAASL,uBACPO,QAAkC,EAClCC,EAAW,EACX,GAAGT,IAAW;IAEd,OAAOU,YAAY;QACjB,0EAA0E;QAC1E,uDAAuD;QACvD,4FAA4F;QAC5F,OAAOF,SAASG,KAAK,CAACC,YAAYZ;IACpC,GAAGS,GAAG,CAACI,OAAOC,WAAW,CAAC;AAC5B;AAEA,SAAST,sBACPG,QAAkC,EAClCC,EAAW,EACX,GAAGT,IAAW;IAEd,MAAMe,kBAAkB;QACtB,IAAI;YACF,yEAAyE;YACzE,sDAAsD;YACtD,2FAA2F;YAC3F,OAAOP,SAASG,KAAK,CAACC,YAAYZ;QACpC,SAAU;YACR,sDAAsD;YACtD,uEAAuE;YACvE,mDAAmD;YACnD,wFAAwF;YACxFO,aAAaD;QACf;IACF;IACA,MAAMA,UAAUU,WAAWD,iBAAiBN;IAC5C,OAAOH,OAAO,CAACO,OAAOC,WAAW,CAAC;AACpC;AAEA,OAAO,MAAMG,mBAAmB,IAAIlB,mBAAkB;AACtD,OAAO,MAAMmB,kBAAkB,IAAId,kBAAiB","ignoreList":[0]}

View File

@@ -0,0 +1,108 @@
import { getModuleContext, requestStore, edgeSandboxNextRequestContext } from './context';
import { requestToBodyStream } from '../../body-streams';
import { getBuiltinRequestContext } from '../../after/builtin-request-context';
import { RouterServerContextSymbol, routerServerGlobal } from '../../lib/router-utils/router-server-context';
export const ErrorSource = Symbol('SandboxError');
const FORBIDDEN_HEADERS = [
'content-length',
'content-encoding',
'transfer-encoding'
];
/**
* Decorates the runner function making sure all errors it can produce are
* tagged with `edge-server` so they can properly be rendered in dev.
*/ function withTaggedErrors(fn) {
if (process.env.NODE_ENV === 'development') {
const { getServerError } = require('../../dev/node-stack-frames');
return (params)=>fn(params).then((result)=>{
var _result_waitUntil;
return {
...result,
waitUntil: result == null ? void 0 : (_result_waitUntil = result.waitUntil) == null ? void 0 : _result_waitUntil.catch((error)=>{
// TODO: used COMPILER_NAMES.edgeServer instead. Verify that it does not increase the runtime size.
throw getServerError(error, 'edge-server');
})
};
}).catch((error)=>{
// TODO: used COMPILER_NAMES.edgeServer instead
throw getServerError(error, 'edge-server');
});
}
return fn;
}
export async function getRuntimeContext(params) {
const { runtime, evaluateInContext } = await getModuleContext({
moduleName: params.name,
onWarning: params.onWarning ?? (()=>{}),
onError: params.onError ?? (()=>{}),
useCache: params.useCache !== false,
edgeFunctionEntry: params.edgeFunctionEntry,
distDir: params.distDir
});
if (params.incrementalCache) {
runtime.context.globalThis.__incrementalCacheShared = true;
runtime.context.globalThis.__incrementalCache = params.incrementalCache;
}
// expose router server context for access to dev handlers like
// logErrorWithOriginalStack
;
runtime.context.globalThis[RouterServerContextSymbol] = routerServerGlobal[RouterServerContextSymbol];
if (params.serverComponentsHmrCache) {
runtime.context.globalThis.__serverComponentsHmrCache = params.serverComponentsHmrCache;
}
for (const paramPath of params.paths){
evaluateInContext(paramPath);
}
return runtime;
}
export const run = withTaggedErrors(async function runWithTaggedErrors(params) {
var _params_request_body;
const runtime = await getRuntimeContext(params);
const edgeFunction = (await runtime.context._ENTRIES[`middleware_${params.name}`]).default;
const cloned = ![
'HEAD',
'GET'
].includes(params.request.method) ? (_params_request_body = params.request.body) == null ? void 0 : _params_request_body.cloneBodyStream() : undefined;
const KUint8Array = runtime.evaluate('Uint8Array');
const urlInstance = new URL(params.request.url);
params.request.url = urlInstance.toString();
const headers = new Headers();
for (const [key, value] of Object.entries(params.request.headers)){
headers.set(key, (value == null ? void 0 : value.toString()) ?? '');
}
try {
let result = undefined;
const builtinRequestCtx = {
...getBuiltinRequestContext(),
// FIXME(after):
// arguably, this is an abuse of "@next/request-context" --
// it'd make more sense to simply forward its existing value into the sandbox (in `createModuleContext`)
// but here we're using it to just pass in `waitUntil` regardless if we were running in this context or not.
waitUntil: params.request.waitUntil
};
await edgeSandboxNextRequestContext.run(builtinRequestCtx, ()=>requestStore.run({
headers
}, async ()=>{
result = await edgeFunction({
request: {
...params.request,
body: cloned && requestToBodyStream(runtime.context, KUint8Array, cloned)
}
});
for (const headerName of FORBIDDEN_HEADERS){
result.response.headers.delete(headerName);
}
}));
if (!result) throw Object.defineProperty(new Error('Edge function did not return a response'), "__NEXT_ERROR_CODE", {
value: "E332",
enumerable: false,
configurable: true
});
return result;
} finally{
var _params_request_body1;
await ((_params_request_body1 = params.request.body) == null ? void 0 : _params_request_body1.finalize());
}
});
//# sourceMappingURL=sandbox.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,172 @@
import { ReflectAdapter } from './reflect';
/**
* @internal
*/ export class ReadonlyHeadersError extends Error {
constructor(){
super('Headers cannot be modified. Read more: https://nextjs.org/docs/app/api-reference/functions/headers');
}
static callable() {
throw new ReadonlyHeadersError();
}
}
export class HeadersAdapter extends Headers {
constructor(headers){
// We've already overridden the methods that would be called, so we're just
// calling the super constructor to ensure that the instanceof check works.
super();
this.headers = new Proxy(headers, {
get (target, prop, receiver) {
// Because this is just an object, we expect that all "get" operations
// are for properties. If it's a "get" for a symbol, we'll just return
// the symbol.
if (typeof prop === 'symbol') {
return ReflectAdapter.get(target, prop, receiver);
}
const lowercased = prop.toLowerCase();
// Let's find the original casing of the key. This assumes that there is
// no mixed case keys (e.g. "Content-Type" and "content-type") in the
// headers object.
const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased);
// If the original casing doesn't exist, return undefined.
if (typeof original === 'undefined') return;
// If the original casing exists, return the value.
return ReflectAdapter.get(target, original, receiver);
},
set (target, prop, value, receiver) {
if (typeof prop === 'symbol') {
return ReflectAdapter.set(target, prop, value, receiver);
}
const lowercased = prop.toLowerCase();
// Let's find the original casing of the key. This assumes that there is
// no mixed case keys (e.g. "Content-Type" and "content-type") in the
// headers object.
const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased);
// If the original casing doesn't exist, use the prop as the key.
return ReflectAdapter.set(target, original ?? prop, value, receiver);
},
has (target, prop) {
if (typeof prop === 'symbol') return ReflectAdapter.has(target, prop);
const lowercased = prop.toLowerCase();
// Let's find the original casing of the key. This assumes that there is
// no mixed case keys (e.g. "Content-Type" and "content-type") in the
// headers object.
const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased);
// If the original casing doesn't exist, return false.
if (typeof original === 'undefined') return false;
// If the original casing exists, return true.
return ReflectAdapter.has(target, original);
},
deleteProperty (target, prop) {
if (typeof prop === 'symbol') return ReflectAdapter.deleteProperty(target, prop);
const lowercased = prop.toLowerCase();
// Let's find the original casing of the key. This assumes that there is
// no mixed case keys (e.g. "Content-Type" and "content-type") in the
// headers object.
const original = Object.keys(headers).find((o)=>o.toLowerCase() === lowercased);
// If the original casing doesn't exist, return true.
if (typeof original === 'undefined') return true;
// If the original casing exists, delete the property.
return ReflectAdapter.deleteProperty(target, original);
}
});
}
/**
* Seals a Headers instance to prevent modification by throwing an error when
* any mutating method is called.
*/ static seal(headers) {
return new Proxy(headers, {
get (target, prop, receiver) {
switch(prop){
case 'append':
case 'delete':
case 'set':
return ReadonlyHeadersError.callable;
default:
return ReflectAdapter.get(target, prop, receiver);
}
}
});
}
/**
* Merges a header value into a string. This stores multiple values as an
* array, so we need to merge them into a string.
*
* @param value a header value
* @returns a merged header value (a string)
*/ merge(value) {
if (Array.isArray(value)) return value.join(', ');
return value;
}
/**
* Creates a Headers instance from a plain object or a Headers instance.
*
* @param headers a plain object or a Headers instance
* @returns a headers instance
*/ static from(headers) {
if (headers instanceof Headers) return headers;
return new HeadersAdapter(headers);
}
append(name, value) {
const existing = this.headers[name];
if (typeof existing === 'string') {
this.headers[name] = [
existing,
value
];
} else if (Array.isArray(existing)) {
existing.push(value);
} else {
this.headers[name] = value;
}
}
delete(name) {
delete this.headers[name];
}
get(name) {
const value = this.headers[name];
if (typeof value !== 'undefined') return this.merge(value);
return null;
}
has(name) {
return typeof this.headers[name] !== 'undefined';
}
set(name, value) {
this.headers[name] = value;
}
forEach(callbackfn, thisArg) {
for (const [name, value] of this.entries()){
callbackfn.call(thisArg, value, name, this);
}
}
*entries() {
for (const key of Object.keys(this.headers)){
const name = key.toLowerCase();
// We assert here that this is a string because we got it from the
// Object.keys() call above.
const value = this.get(name);
yield [
name,
value
];
}
}
*keys() {
for (const key of Object.keys(this.headers)){
const name = key.toLowerCase();
yield name;
}
}
*values() {
for (const key of Object.keys(this.headers)){
// We assert here that this is a string because we got it from the
// Object.keys() call above.
const value = this.get(key);
yield value;
}
}
[Symbol.iterator]() {
return this.entries();
}
}
//# sourceMappingURL=headers.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,121 @@
import { getRequestMeta } from '../../../request-meta';
import { fromNodeOutgoingHttpHeaders } from '../../utils';
import { NextRequest } from '../request';
import { isNodeNextRequest, isWebNextRequest } from '../../../base-http/helpers';
export const ResponseAbortedName = 'ResponseAborted';
export class ResponseAborted extends Error {
constructor(...args){
super(...args), this.name = ResponseAbortedName;
}
}
/**
* Creates an AbortController tied to the closing of a ServerResponse (or other
* appropriate Writable).
*
* If the `close` event is fired before the `finish` event, then we'll send the
* `abort` signal.
*/ export function createAbortController(response) {
const controller = new AbortController();
// If `finish` fires first, then `res.end()` has been called and the close is
// just us finishing the stream on our side. If `close` fires first, then we
// know the client disconnected before we finished.
response.once('close', ()=>{
if (response.writableFinished) return;
controller.abort(new ResponseAborted());
});
return controller;
}
/**
* Creates an AbortSignal tied to the closing of a ServerResponse (or other
* appropriate Writable).
*
* This cannot be done with the request (IncomingMessage or Readable) because
* the `abort` event will not fire if to data has been fully read (because that
* will "close" the readable stream and nothing fires after that).
*/ export function signalFromNodeResponse(response) {
const { errored, destroyed } = response;
if (errored || destroyed) {
return AbortSignal.abort(errored ?? new ResponseAborted());
}
const { signal } = createAbortController(response);
return signal;
}
export class NextRequestAdapter {
static fromBaseNextRequest(request, signal) {
if (// The type check here ensures that `req` is correctly typed, and the
// environment variable check provides dead code elimination.
process.env.NEXT_RUNTIME === 'edge' && isWebNextRequest(request)) {
return NextRequestAdapter.fromWebNextRequest(request);
} else if (// The type check here ensures that `req` is correctly typed, and the
// environment variable check provides dead code elimination.
process.env.NEXT_RUNTIME !== 'edge' && isNodeNextRequest(request)) {
return NextRequestAdapter.fromNodeNextRequest(request, signal);
} else {
throw Object.defineProperty(new Error('Invariant: Unsupported NextRequest type'), "__NEXT_ERROR_CODE", {
value: "E345",
enumerable: false,
configurable: true
});
}
}
static fromNodeNextRequest(request, signal) {
// HEAD and GET requests can not have a body.
let body = null;
if (request.method !== 'GET' && request.method !== 'HEAD' && request.body) {
// @ts-expect-error - this is handled by undici, when streams/web land use it instead
body = request.body;
}
let url;
if (request.url.startsWith('http')) {
url = new URL(request.url);
} else {
// Grab the full URL from the request metadata.
const base = getRequestMeta(request, 'initURL');
if (!base || !base.startsWith('http')) {
// Because the URL construction relies on the fact that the URL provided
// is absolute, we need to provide a base URL. We can't use the request
// URL because it's relative, so we use a dummy URL instead.
url = new URL(request.url, 'http://n');
} else {
url = new URL(request.url, base);
}
}
return new NextRequest(url, {
method: request.method,
headers: fromNodeOutgoingHttpHeaders(request.headers),
duplex: 'half',
signal,
// geo
// ip
// nextConfig
// body can not be passed if request was aborted
// or we get a Request body was disturbed error
...signal.aborted ? {} : {
body
}
});
}
static fromWebNextRequest(request) {
// HEAD and GET requests can not have a body.
let body = null;
if (request.method !== 'GET' && request.method !== 'HEAD') {
body = request.body;
}
return new NextRequest(request.url, {
method: request.method,
headers: fromNodeOutgoingHttpHeaders(request.headers),
duplex: 'half',
signal: request.request.signal,
// geo
// ip
// nextConfig
// body can not be passed if request was aborted
// or we get a Request body was disturbed error
...request.request.signal.aborted ? {} : {
body
}
});
}
}
//# sourceMappingURL=next-request.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,20 @@
export class ReflectAdapter {
static get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver);
if (typeof value === 'function') {
return value.bind(target);
}
return value;
}
static set(target, prop, value, receiver) {
return Reflect.set(target, prop, value, receiver);
}
static has(target, prop) {
return Reflect.has(target, prop);
}
static deleteProperty(target, prop) {
return Reflect.deleteProperty(target, prop);
}
}
//# sourceMappingURL=reflect.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/web/spec-extension/adapters/reflect.ts"],"sourcesContent":["export class ReflectAdapter {\n static get<T extends object>(\n target: T,\n prop: string | symbol,\n receiver: unknown\n ): any {\n const value = Reflect.get(target, prop, receiver)\n if (typeof value === 'function') {\n return value.bind(target)\n }\n\n return value\n }\n\n static set<T extends object>(\n target: T,\n prop: string | symbol,\n value: any,\n receiver: any\n ): boolean {\n return Reflect.set(target, prop, value, receiver)\n }\n\n static has<T extends object>(target: T, prop: string | symbol): boolean {\n return Reflect.has(target, prop)\n }\n\n static deleteProperty<T extends object>(\n target: T,\n prop: string | symbol\n ): boolean {\n return Reflect.deleteProperty(target, prop)\n }\n}\n"],"names":["ReflectAdapter","get","target","prop","receiver","value","Reflect","bind","set","has","deleteProperty"],"mappings":"AAAA,OAAO,MAAMA;IACX,OAAOC,IACLC,MAAS,EACTC,IAAqB,EACrBC,QAAiB,EACZ;QACL,MAAMC,QAAQC,QAAQL,GAAG,CAACC,QAAQC,MAAMC;QACxC,IAAI,OAAOC,UAAU,YAAY;YAC/B,OAAOA,MAAME,IAAI,CAACL;QACpB;QAEA,OAAOG;IACT;IAEA,OAAOG,IACLN,MAAS,EACTC,IAAqB,EACrBE,KAAU,EACVD,QAAa,EACJ;QACT,OAAOE,QAAQE,GAAG,CAACN,QAAQC,MAAME,OAAOD;IAC1C;IAEA,OAAOK,IAAsBP,MAAS,EAAEC,IAAqB,EAAW;QACtE,OAAOG,QAAQG,GAAG,CAACP,QAAQC;IAC7B;IAEA,OAAOO,eACLR,MAAS,EACTC,IAAqB,EACZ;QACT,OAAOG,QAAQI,cAAc,CAACR,QAAQC;IACxC;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,168 @@
import { RequestCookies } from '../cookies';
import { ResponseCookies } from '../cookies';
import { ReflectAdapter } from './reflect';
import { workAsyncStorage } from '../../../app-render/work-async-storage.external';
import { ActionDidRevalidateStaticAndDynamic } from '../../../../shared/lib/action-revalidation-kind';
/**
* @internal
*/ export class ReadonlyRequestCookiesError extends Error {
constructor(){
super('Cookies can only be modified in a Server Action or Route Handler. Read more: https://nextjs.org/docs/app/api-reference/functions/cookies#options');
}
static callable() {
throw new ReadonlyRequestCookiesError();
}
}
export class RequestCookiesAdapter {
static seal(cookies) {
return new Proxy(cookies, {
get (target, prop, receiver) {
switch(prop){
case 'clear':
case 'delete':
case 'set':
return ReadonlyRequestCookiesError.callable;
default:
return ReflectAdapter.get(target, prop, receiver);
}
}
});
}
}
const SYMBOL_MODIFY_COOKIE_VALUES = Symbol.for('next.mutated.cookies');
export function getModifiedCookieValues(cookies) {
const modified = cookies[SYMBOL_MODIFY_COOKIE_VALUES];
if (!modified || !Array.isArray(modified) || modified.length === 0) {
return [];
}
return modified;
}
export function appendMutableCookies(headers, mutableCookies) {
const modifiedCookieValues = getModifiedCookieValues(mutableCookies);
if (modifiedCookieValues.length === 0) {
return false;
}
// Return a new response that extends the response with
// the modified cookies as fallbacks. `res` cookies
// will still take precedence.
const resCookies = new ResponseCookies(headers);
const returnedCookies = resCookies.getAll();
// Set the modified cookies as fallbacks.
for (const cookie of modifiedCookieValues){
resCookies.set(cookie);
}
// Set the original cookies as the final values.
for (const cookie of returnedCookies){
resCookies.set(cookie);
}
return true;
}
export class MutableRequestCookiesAdapter {
static wrap(cookies, onUpdateCookies) {
const responseCookies = new ResponseCookies(new Headers());
for (const cookie of cookies.getAll()){
responseCookies.set(cookie);
}
let modifiedValues = [];
const modifiedCookies = new Set();
const updateResponseCookies = ()=>{
// TODO-APP: change method of getting workStore
const workStore = workAsyncStorage.getStore();
if (workStore) {
workStore.pathWasRevalidated = ActionDidRevalidateStaticAndDynamic;
}
const allCookies = responseCookies.getAll();
modifiedValues = allCookies.filter((c)=>modifiedCookies.has(c.name));
if (onUpdateCookies) {
const serializedCookies = [];
for (const cookie of modifiedValues){
const tempCookies = new ResponseCookies(new Headers());
tempCookies.set(cookie);
serializedCookies.push(tempCookies.toString());
}
onUpdateCookies(serializedCookies);
}
};
const wrappedCookies = new Proxy(responseCookies, {
get (target, prop, receiver) {
switch(prop){
// A special symbol to get the modified cookie values
case SYMBOL_MODIFY_COOKIE_VALUES:
return modifiedValues;
// TODO: Throw error if trying to set a cookie after the response
// headers have been set.
case 'delete':
return function(...args) {
modifiedCookies.add(typeof args[0] === 'string' ? args[0] : args[0].name);
try {
target.delete(...args);
return wrappedCookies;
} finally{
updateResponseCookies();
}
};
case 'set':
return function(...args) {
modifiedCookies.add(typeof args[0] === 'string' ? args[0] : args[0].name);
try {
target.set(...args);
return wrappedCookies;
} finally{
updateResponseCookies();
}
};
default:
return ReflectAdapter.get(target, prop, receiver);
}
}
});
return wrappedCookies;
}
}
export function createCookiesWithMutableAccessCheck(requestStore) {
const wrappedCookies = new Proxy(requestStore.mutableCookies, {
get (target, prop, receiver) {
switch(prop){
case 'delete':
return function(...args) {
ensureCookiesAreStillMutable(requestStore, 'cookies().delete');
target.delete(...args);
return wrappedCookies;
};
case 'set':
return function(...args) {
ensureCookiesAreStillMutable(requestStore, 'cookies().set');
target.set(...args);
return wrappedCookies;
};
default:
return ReflectAdapter.get(target, prop, receiver);
}
}
});
return wrappedCookies;
}
export function areCookiesMutableInCurrentPhase(requestStore) {
return requestStore.phase === 'action';
}
/** Ensure that cookies() starts throwing on mutation
* if we changed phases and can no longer mutate.
*
* This can happen when going:
* 'render' -> 'after'
* 'action' -> 'render'
* */ function ensureCookiesAreStillMutable(requestStore, _callingExpression) {
if (!areCookiesMutableInCurrentPhase(requestStore)) {
// TODO: maybe we can give a more precise error message based on callingExpression?
throw new ReadonlyRequestCookiesError();
}
}
export function responseCookiesToRequestCookies(responseCookies) {
const requestCookies = new RequestCookies(new Headers());
for (const cookie of responseCookies.getAll()){
requestCookies.set(cookie);
}
return requestCookies;
}
//# sourceMappingURL=request-cookies.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export { RequestCookies, ResponseCookies, stringifyCookie } from 'next/dist/compiled/@edge-runtime/cookies';
//# sourceMappingURL=cookies.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/spec-extension/cookies.ts"],"sourcesContent":["export {\n RequestCookies,\n ResponseCookies,\n stringifyCookie,\n} from 'next/dist/compiled/@edge-runtime/cookies'\n"],"names":["RequestCookies","ResponseCookies","stringifyCookie"],"mappings":"AAAA,SACEA,cAAc,EACdC,eAAe,EACfC,eAAe,QACV,2CAA0C","ignoreList":[0]}

View File

@@ -0,0 +1,76 @@
import { PageSignatureError } from '../error';
const responseSymbol = Symbol('response');
const passThroughSymbol = Symbol('passThrough');
const waitUntilSymbol = Symbol('waitUntil');
class FetchEvent {
constructor(_request, waitUntil){
this[passThroughSymbol] = false;
this[waitUntilSymbol] = waitUntil ? {
kind: 'external',
function: waitUntil
} : {
kind: 'internal',
promises: []
};
}
// TODO: is this dead code? NextFetchEvent never lets this get called
respondWith(response) {
if (!this[responseSymbol]) {
this[responseSymbol] = Promise.resolve(response);
}
}
// TODO: is this dead code? passThroughSymbol is unused
passThroughOnException() {
this[passThroughSymbol] = true;
}
waitUntil(promise) {
if (this[waitUntilSymbol].kind === 'external') {
// if we received an external waitUntil, we delegate to it
// TODO(after): this will make us not go through `getServerError(error, 'edge-server')` in `sandbox`
const waitUntil = this[waitUntilSymbol].function;
return waitUntil(promise);
} else {
// if we didn't receive an external waitUntil, we make it work on our own
// (and expect the caller to do something with the promises)
this[waitUntilSymbol].promises.push(promise);
}
}
}
export function getWaitUntilPromiseFromEvent(event) {
return event[waitUntilSymbol].kind === 'internal' ? Promise.all(event[waitUntilSymbol].promises).then(()=>{}) : undefined;
}
export class NextFetchEvent extends FetchEvent {
constructor(params){
var _params_context;
super(params.request, (_params_context = params.context) == null ? void 0 : _params_context.waitUntil);
this.sourcePage = params.page;
}
/**
* @deprecated The `request` is now the first parameter and the API is now async.
*
* Read more: https://nextjs.org/docs/messages/middleware-new-signature
*/ get request() {
throw Object.defineProperty(new PageSignatureError({
page: this.sourcePage
}), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
/**
* @deprecated Using `respondWith` is no longer needed.
*
* Read more: https://nextjs.org/docs/messages/middleware-new-signature
*/ respondWith() {
throw Object.defineProperty(new PageSignatureError({
page: this.sourcePage
}), "__NEXT_ERROR_CODE", {
value: "E394",
enumerable: false,
configurable: true
});
}
}
//# sourceMappingURL=fetch-event.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/spec-extension/fetch-event.ts"],"sourcesContent":["import type { WaitUntil } from '../../after/builtin-request-context'\nimport { PageSignatureError } from '../error'\nimport type { NextRequest } from './request'\n\nconst responseSymbol = Symbol('response')\nconst passThroughSymbol = Symbol('passThrough')\nconst waitUntilSymbol = Symbol('waitUntil')\n\nclass FetchEvent {\n // TODO(after): get rid of the 'internal' variant and always use an external waitUntil\n // (this means removing `FetchEventResult.waitUntil` which also requires a builder change)\n readonly [waitUntilSymbol]:\n | { kind: 'internal'; promises: Promise<any>[] }\n | { kind: 'external'; function: WaitUntil };\n\n [responseSymbol]?: Promise<Response>;\n [passThroughSymbol] = false\n\n constructor(_request: Request, waitUntil?: WaitUntil) {\n this[waitUntilSymbol] = waitUntil\n ? { kind: 'external', function: waitUntil }\n : { kind: 'internal', promises: [] }\n }\n\n // TODO: is this dead code? NextFetchEvent never lets this get called\n respondWith(response: Response | Promise<Response>): void {\n if (!this[responseSymbol]) {\n this[responseSymbol] = Promise.resolve(response)\n }\n }\n\n // TODO: is this dead code? passThroughSymbol is unused\n passThroughOnException(): void {\n this[passThroughSymbol] = true\n }\n\n waitUntil(promise: Promise<any>): void {\n if (this[waitUntilSymbol].kind === 'external') {\n // if we received an external waitUntil, we delegate to it\n // TODO(after): this will make us not go through `getServerError(error, 'edge-server')` in `sandbox`\n const waitUntil = this[waitUntilSymbol].function\n return waitUntil(promise)\n } else {\n // if we didn't receive an external waitUntil, we make it work on our own\n // (and expect the caller to do something with the promises)\n this[waitUntilSymbol].promises.push(promise)\n }\n }\n}\n\nexport function getWaitUntilPromiseFromEvent(\n event: FetchEvent\n): Promise<void> | undefined {\n return event[waitUntilSymbol].kind === 'internal'\n ? Promise.all(event[waitUntilSymbol].promises).then(() => {})\n : undefined\n}\n\nexport class NextFetchEvent extends FetchEvent {\n sourcePage: string\n\n constructor(params: {\n request: NextRequest\n page: string\n context: { waitUntil: WaitUntil } | undefined\n }) {\n super(params.request, params.context?.waitUntil)\n this.sourcePage = params.page\n }\n\n /**\n * @deprecated The `request` is now the first parameter and the API is now async.\n *\n * Read more: https://nextjs.org/docs/messages/middleware-new-signature\n */\n get request() {\n throw new PageSignatureError({\n page: this.sourcePage,\n })\n }\n\n /**\n * @deprecated Using `respondWith` is no longer needed.\n *\n * Read more: https://nextjs.org/docs/messages/middleware-new-signature\n */\n respondWith() {\n throw new PageSignatureError({\n page: this.sourcePage,\n })\n }\n}\n"],"names":["PageSignatureError","responseSymbol","Symbol","passThroughSymbol","waitUntilSymbol","FetchEvent","constructor","_request","waitUntil","kind","function","promises","respondWith","response","Promise","resolve","passThroughOnException","promise","push","getWaitUntilPromiseFromEvent","event","all","then","undefined","NextFetchEvent","params","request","context","sourcePage","page"],"mappings":"AACA,SAASA,kBAAkB,QAAQ,WAAU;AAG7C,MAAMC,iBAAiBC,OAAO;AAC9B,MAAMC,oBAAoBD,OAAO;AACjC,MAAME,kBAAkBF,OAAO;AAE/B,MAAMG;IAUJC,YAAYC,QAAiB,EAAEC,SAAqB,CAAE;YAFtD,CAACL,kBAAkB,GAAG;QAGpB,IAAI,CAACC,gBAAgB,GAAGI,YACpB;YAAEC,MAAM;YAAYC,UAAUF;QAAU,IACxC;YAAEC,MAAM;YAAYE,UAAU,EAAE;QAAC;IACvC;IAEA,qEAAqE;IACrEC,YAAYC,QAAsC,EAAQ;QACxD,IAAI,CAAC,IAAI,CAACZ,eAAe,EAAE;YACzB,IAAI,CAACA,eAAe,GAAGa,QAAQC,OAAO,CAACF;QACzC;IACF;IAEA,uDAAuD;IACvDG,yBAA+B;QAC7B,IAAI,CAACb,kBAAkB,GAAG;IAC5B;IAEAK,UAAUS,OAAqB,EAAQ;QACrC,IAAI,IAAI,CAACb,gBAAgB,CAACK,IAAI,KAAK,YAAY;YAC7C,0DAA0D;YAC1D,oGAAoG;YACpG,MAAMD,YAAY,IAAI,CAACJ,gBAAgB,CAACM,QAAQ;YAChD,OAAOF,UAAUS;QACnB,OAAO;YACL,yEAAyE;YACzE,4DAA4D;YAC5D,IAAI,CAACb,gBAAgB,CAACO,QAAQ,CAACO,IAAI,CAACD;QACtC;IACF;AACF;AAEA,OAAO,SAASE,6BACdC,KAAiB;IAEjB,OAAOA,KAAK,CAAChB,gBAAgB,CAACK,IAAI,KAAK,aACnCK,QAAQO,GAAG,CAACD,KAAK,CAAChB,gBAAgB,CAACO,QAAQ,EAAEW,IAAI,CAAC,KAAO,KACzDC;AACN;AAEA,OAAO,MAAMC,uBAAuBnB;IAGlCC,YAAYmB,MAIX,CAAE;YACqBA;QAAtB,KAAK,CAACA,OAAOC,OAAO,GAAED,kBAAAA,OAAOE,OAAO,qBAAdF,gBAAgBjB,SAAS;QAC/C,IAAI,CAACoB,UAAU,GAAGH,OAAOI,IAAI;IAC/B;IAEA;;;;GAIC,GACD,IAAIH,UAAU;QACZ,MAAM,qBAEJ,CAFI,IAAI1B,mBAAmB;YAC3B6B,MAAM,IAAI,CAACD,UAAU;QACvB,IAFM,qBAAA;mBAAA;wBAAA;0BAAA;QAEL;IACH;IAEA;;;;GAIC,GACDhB,cAAc;QACZ,MAAM,qBAEJ,CAFI,IAAIZ,mBAAmB;YAC3B6B,MAAM,IAAI,CAACD,UAAU;QACvB,IAFM,qBAAA;mBAAA;wBAAA;0BAAA;QAEL;IACH;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,12 @@
/**
* @deprecated ImageResponse moved from "next/server" to "next/og" since Next.js 14, please import from "next/og" instead.
* Migration with codemods: https://nextjs.org/docs/app/building-your-application/upgrading/codemods#next-og-import
*/ export function ImageResponse() {
throw Object.defineProperty(new Error('ImageResponse moved from "next/server" to "next/og" since Next.js 14, please import from "next/og" instead'), "__NEXT_ERROR_CODE", {
value: "E183",
enumerable: false,
configurable: true
});
}
//# sourceMappingURL=image-response.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/spec-extension/image-response.ts"],"sourcesContent":["/**\n * @deprecated ImageResponse moved from \"next/server\" to \"next/og\" since Next.js 14, please import from \"next/og\" instead.\n * Migration with codemods: https://nextjs.org/docs/app/building-your-application/upgrading/codemods#next-og-import\n */\nexport function ImageResponse(): never {\n throw new Error(\n 'ImageResponse moved from \"next/server\" to \"next/og\" since Next.js 14, please import from \"next/og\" instead'\n )\n}\n"],"names":["ImageResponse","Error"],"mappings":"AAAA;;;CAGC,GACD,OAAO,SAASA;IACd,MAAM,qBAEL,CAFK,IAAIC,MACR,+GADI,qBAAA;eAAA;oBAAA;sBAAA;IAEN;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,81 @@
import { NextURL } from '../next-url';
import { toNodeOutgoingHttpHeaders, validateURL } from '../utils';
import { RemovedUAError, RemovedPageError } from '../error';
import { RequestCookies } from './cookies';
export const INTERNALS = Symbol('internal request');
/**
* This class extends the [Web `Request` API](https://developer.mozilla.org/docs/Web/API/Request) with additional convenience methods.
*
* Read more: [Next.js Docs: `NextRequest`](https://nextjs.org/docs/app/api-reference/functions/next-request)
*/ export class NextRequest extends Request {
constructor(input, init = {}){
const url = typeof input !== 'string' && 'url' in input ? input.url : String(input);
validateURL(url);
// node Request instance requires duplex option when a body
// is present or it errors, we don't handle this for
// Request being passed in since it would have already
// errored if this wasn't configured
if (process.env.NEXT_RUNTIME !== 'edge') {
if (init.body && init.duplex !== 'half') {
init.duplex = 'half';
}
}
if (input instanceof Request) super(input, init);
else super(url, init);
const nextUrl = new NextURL(url, {
headers: toNodeOutgoingHttpHeaders(this.headers),
nextConfig: init.nextConfig
});
this[INTERNALS] = {
cookies: new RequestCookies(this.headers),
nextUrl,
url: process.env.__NEXT_NO_MIDDLEWARE_URL_NORMALIZE ? url : nextUrl.toString()
};
}
[Symbol.for('edge-runtime.inspect.custom')]() {
return {
cookies: this.cookies,
nextUrl: this.nextUrl,
url: this.url,
// rest of props come from Request
bodyUsed: this.bodyUsed,
cache: this.cache,
credentials: this.credentials,
destination: this.destination,
headers: Object.fromEntries(this.headers),
integrity: this.integrity,
keepalive: this.keepalive,
method: this.method,
mode: this.mode,
redirect: this.redirect,
referrer: this.referrer,
referrerPolicy: this.referrerPolicy,
signal: this.signal
};
}
get cookies() {
return this[INTERNALS].cookies;
}
get nextUrl() {
return this[INTERNALS].nextUrl;
}
/**
* @deprecated
* `page` has been deprecated in favour of `URLPattern`.
* Read more: https://nextjs.org/docs/messages/middleware-request-page
*/ get page() {
throw new RemovedPageError();
}
/**
* @deprecated
* `ua` has been removed in favour of \`userAgent\` function.
* Read more: https://nextjs.org/docs/messages/middleware-parse-user-agent
*/ get ua() {
throw new RemovedUAError();
}
get url() {
return this[INTERNALS].url;
}
}
//# sourceMappingURL=request.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,130 @@
import { stringifyCookie } from '../../web/spec-extension/cookies';
import { NextURL } from '../next-url';
import { toNodeOutgoingHttpHeaders, validateURL } from '../utils';
import { ReflectAdapter } from './adapters/reflect';
import { ResponseCookies } from './cookies';
const INTERNALS = Symbol('internal response');
const REDIRECTS = new Set([
301,
302,
303,
307,
308
]);
function handleMiddlewareField(init, headers) {
var _init_request;
if (init == null ? void 0 : (_init_request = init.request) == null ? void 0 : _init_request.headers) {
if (!(init.request.headers instanceof Headers)) {
throw Object.defineProperty(new Error('request.headers must be an instance of Headers'), "__NEXT_ERROR_CODE", {
value: "E119",
enumerable: false,
configurable: true
});
}
const keys = [];
for (const [key, value] of init.request.headers){
headers.set('x-middleware-request-' + key, value);
keys.push(key);
}
headers.set('x-middleware-override-headers', keys.join(','));
}
}
/**
* This class extends the [Web `Response` API](https://developer.mozilla.org/docs/Web/API/Response) with additional convenience methods.
*
* Read more: [Next.js Docs: `NextResponse`](https://nextjs.org/docs/app/api-reference/functions/next-response)
*/ export class NextResponse extends Response {
constructor(body, init = {}){
super(body, init);
const headers = this.headers;
const cookies = new ResponseCookies(headers);
const cookiesProxy = new Proxy(cookies, {
get (target, prop, receiver) {
switch(prop){
case 'delete':
case 'set':
{
return (...args)=>{
const result = Reflect.apply(target[prop], target, args);
const newHeaders = new Headers(headers);
if (result instanceof ResponseCookies) {
headers.set('x-middleware-set-cookie', result.getAll().map((cookie)=>stringifyCookie(cookie)).join(','));
}
handleMiddlewareField(init, newHeaders);
return result;
};
}
default:
return ReflectAdapter.get(target, prop, receiver);
}
}
});
this[INTERNALS] = {
cookies: cookiesProxy,
url: init.url ? new NextURL(init.url, {
headers: toNodeOutgoingHttpHeaders(headers),
nextConfig: init.nextConfig
}) : undefined
};
}
[Symbol.for('edge-runtime.inspect.custom')]() {
return {
cookies: this.cookies,
url: this.url,
// rest of props come from Response
body: this.body,
bodyUsed: this.bodyUsed,
headers: Object.fromEntries(this.headers),
ok: this.ok,
redirected: this.redirected,
status: this.status,
statusText: this.statusText,
type: this.type
};
}
get cookies() {
return this[INTERNALS].cookies;
}
static json(body, init) {
const response = Response.json(body, init);
return new NextResponse(response.body, response);
}
static redirect(url, init) {
const status = typeof init === 'number' ? init : (init == null ? void 0 : init.status) ?? 307;
if (!REDIRECTS.has(status)) {
throw Object.defineProperty(new RangeError('Failed to execute "redirect" on "response": Invalid status code'), "__NEXT_ERROR_CODE", {
value: "E529",
enumerable: false,
configurable: true
});
}
const initObj = typeof init === 'object' ? init : {};
const headers = new Headers(initObj == null ? void 0 : initObj.headers);
headers.set('Location', validateURL(url));
return new NextResponse(null, {
...initObj,
headers,
status
});
}
static rewrite(destination, init) {
const headers = new Headers(init == null ? void 0 : init.headers);
headers.set('x-middleware-rewrite', validateURL(destination));
handleMiddlewareField(init, headers);
return new NextResponse(null, {
...init,
headers
});
}
static next(init) {
const headers = new Headers(init == null ? void 0 : init.headers);
headers.set('x-middleware-next', '1');
handleMiddlewareField(init, headers);
return new NextResponse(null, {
...init,
headers
});
}
}
//# sourceMappingURL=response.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,193 @@
import { abortAndThrowOnSynchronousRequestDataAccess, postponeWithTracking } from '../../app-render/dynamic-rendering';
import { isDynamicRoute } from '../../../shared/lib/router/utils';
import { NEXT_CACHE_IMPLICIT_TAG_ID, NEXT_CACHE_SOFT_TAG_MAX_LENGTH } from '../../../lib/constants';
import { workAsyncStorage } from '../../app-render/work-async-storage.external';
import { workUnitAsyncStorage } from '../../app-render/work-unit-async-storage.external';
import { DynamicServerError } from '../../../client/components/hooks-server-context';
import { InvariantError } from '../../../shared/lib/invariant-error';
import { ActionDidRevalidateDynamicOnly, ActionDidRevalidateStaticAndDynamic as ActionDidRevalidate } from '../../../shared/lib/action-revalidation-kind';
/**
* This function allows you to purge [cached data](https://nextjs.org/docs/app/building-your-application/caching) on-demand for a specific cache tag.
*
* Read more: [Next.js Docs: `revalidateTag`](https://nextjs.org/docs/app/api-reference/functions/revalidateTag)
*/ export function revalidateTag(tag, profile) {
if (!profile) {
console.warn('"revalidateTag" without the second argument is now deprecated, add second argument of "max" or use "updateTag". See more info here: https://nextjs.org/docs/messages/revalidate-tag-single-arg');
}
return revalidate([
tag
], `revalidateTag ${tag}`, profile);
}
/**
* This function allows you to update [cached data](https://nextjs.org/docs/app/building-your-application/caching) on-demand for a specific cache tag.
* This can only be called from within a Server Action to enable read-your-own-writes semantics.
*
* Read more: [Next.js Docs: `updateTag`](https://nextjs.org/docs/app/api-reference/functions/updateTag)
*/ export function updateTag(tag) {
const workStore = workAsyncStorage.getStore();
// TODO: change this after investigating why phase: 'action' is
// set for route handlers
if (!workStore || workStore.page.endsWith('/route')) {
throw Object.defineProperty(new Error('updateTag can only be called from within a Server Action. ' + 'To invalidate cache tags in Route Handlers or other contexts, use revalidateTag instead. ' + 'See more info here: https://nextjs.org/docs/app/api-reference/functions/updateTag'), "__NEXT_ERROR_CODE", {
value: "E872",
enumerable: false,
configurable: true
});
}
// updateTag uses immediate expiration (no profile) without deprecation warning
return revalidate([
tag
], `updateTag ${tag}`, undefined);
}
/**
* This function allows you to refresh client cache from server actions.
* It's useful as dynamic data can be cached on the client which won't
* be refreshed by updateTag
*/ export function refresh() {
const workStore = workAsyncStorage.getStore();
const workUnitStore = workUnitAsyncStorage.getStore();
if (!workStore || workStore.page.endsWith('/route') || (workUnitStore == null ? void 0 : workUnitStore.phase) !== 'action') {
throw Object.defineProperty(new Error('refresh can only be called from within a Server Action. ' + 'See more info here: https://nextjs.org/docs/app/api-reference/functions/refresh'), "__NEXT_ERROR_CODE", {
value: "E870",
enumerable: false,
configurable: true
});
}
if (workStore) {
// The Server Action version of refresh() only revalidates the dynamic data
// on the client. It doesn't affect cached data.
workStore.pathWasRevalidated = ActionDidRevalidateDynamicOnly;
}
}
/**
* This function allows you to purge [cached data](https://nextjs.org/docs/app/building-your-application/caching) on-demand for a specific path.
*
* Read more: [Next.js Docs: `revalidatePath`](https://nextjs.org/docs/app/api-reference/functions/revalidatePath)
*/ export function revalidatePath(originalPath, type) {
if (originalPath.length > NEXT_CACHE_SOFT_TAG_MAX_LENGTH) {
console.warn(`Warning: revalidatePath received "${originalPath}" which exceeded max length of ${NEXT_CACHE_SOFT_TAG_MAX_LENGTH}. See more info here https://nextjs.org/docs/app/api-reference/functions/revalidatePath`);
return;
}
let normalizedPath = `${NEXT_CACHE_IMPLICIT_TAG_ID}${originalPath || '/'}`;
if (type) {
normalizedPath += `${normalizedPath.endsWith('/') ? '' : '/'}${type}`;
} else if (isDynamicRoute(originalPath)) {
console.warn(`Warning: a dynamic page path "${originalPath}" was passed to "revalidatePath", but the "type" parameter is missing. This has no effect by default, see more info here https://nextjs.org/docs/app/api-reference/functions/revalidatePath`);
}
const tags = [
normalizedPath
];
if (normalizedPath === `${NEXT_CACHE_IMPLICIT_TAG_ID}/`) {
tags.push(`${NEXT_CACHE_IMPLICIT_TAG_ID}/index`);
} else if (normalizedPath === `${NEXT_CACHE_IMPLICIT_TAG_ID}/index`) {
tags.push(`${NEXT_CACHE_IMPLICIT_TAG_ID}/`);
}
return revalidate(tags, `revalidatePath ${originalPath}`);
}
function revalidate(tags, expression, profile) {
var _store_cacheLifeProfiles;
const store = workAsyncStorage.getStore();
if (!store || !store.incrementalCache) {
throw Object.defineProperty(new Error(`Invariant: static generation store missing in ${expression}`), "__NEXT_ERROR_CODE", {
value: "E263",
enumerable: false,
configurable: true
});
}
const workUnitStore = workUnitAsyncStorage.getStore();
if (workUnitStore) {
if (workUnitStore.phase === 'render') {
throw Object.defineProperty(new Error(`Route ${store.route} used "${expression}" during render which is unsupported. To ensure revalidation is performed consistently it must always happen outside of renders and cached functions. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`), "__NEXT_ERROR_CODE", {
value: "E7",
enumerable: false,
configurable: true
});
}
switch(workUnitStore.type){
case 'cache':
case 'private-cache':
throw Object.defineProperty(new Error(`Route ${store.route} used "${expression}" inside a "use cache" which is unsupported. To ensure revalidation is performed consistently it must always happen outside of renders and cached functions. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`), "__NEXT_ERROR_CODE", {
value: "E181",
enumerable: false,
configurable: true
});
case 'unstable-cache':
throw Object.defineProperty(new Error(`Route ${store.route} used "${expression}" inside a function cached with "unstable_cache(...)" which is unsupported. To ensure revalidation is performed consistently it must always happen outside of renders and cached functions. See more info here: https://nextjs.org/docs/app/building-your-application/rendering/static-and-dynamic#dynamic-rendering`), "__NEXT_ERROR_CODE", {
value: "E306",
enumerable: false,
configurable: true
});
case 'prerender':
case 'prerender-runtime':
// cacheComponents Prerender
const error = Object.defineProperty(new Error(`Route ${store.route} used ${expression} without first calling \`await connection()\`.`), "__NEXT_ERROR_CODE", {
value: "E406",
enumerable: false,
configurable: true
});
return abortAndThrowOnSynchronousRequestDataAccess(store.route, expression, error, workUnitStore);
case 'prerender-client':
throw Object.defineProperty(new InvariantError(`${expression} must not be used within a client component. Next.js should be preventing ${expression} from being included in client components statically, but did not in this case.`), "__NEXT_ERROR_CODE", {
value: "E693",
enumerable: false,
configurable: true
});
case 'prerender-ppr':
return postponeWithTracking(store.route, expression, workUnitStore.dynamicTracking);
case 'prerender-legacy':
workUnitStore.revalidate = 0;
const err = Object.defineProperty(new DynamicServerError(`Route ${store.route} couldn't be rendered statically because it used \`${expression}\`. See more info here: https://nextjs.org/docs/messages/dynamic-server-error`), "__NEXT_ERROR_CODE", {
value: "E558",
enumerable: false,
configurable: true
});
store.dynamicUsageDescription = expression;
store.dynamicUsageStack = err.stack;
throw err;
case 'request':
if (process.env.NODE_ENV !== 'production') {
// TODO: This is most likely incorrect. It would lead to the ISR
// status being flipped when revalidating a static page with a server
// action.
workUnitStore.usedDynamic = true;
// TODO(restart-on-cache-miss): we should do a sync IO error here in dev
// to match prerender behavior
}
break;
default:
workUnitStore;
}
}
if (!store.pendingRevalidatedTags) {
store.pendingRevalidatedTags = [];
}
for (const tag of tags){
const existingIndex = store.pendingRevalidatedTags.findIndex((item)=>{
if (item.tag !== tag) return false;
// Compare profiles: both strings, both objects, or both undefined
if (typeof item.profile === 'string' && typeof profile === 'string') {
return item.profile === profile;
}
if (typeof item.profile === 'object' && typeof profile === 'object') {
return JSON.stringify(item.profile) === JSON.stringify(profile);
}
return item.profile === profile;
});
if (existingIndex === -1) {
store.pendingRevalidatedTags.push({
tag,
profile
});
}
}
// if profile is provided and this is a stale-while-revalidate
// update we do not mark the path as revalidated so that server
// actions don't pull their own writes
const cacheLife = profile && typeof profile === 'object' ? profile : profile && typeof profile === 'string' && (store == null ? void 0 : (_store_cacheLifeProfiles = store.cacheLifeProfiles) == null ? void 0 : _store_cacheLifeProfiles[profile]) ? store.cacheLifeProfiles[profile] : undefined;
if (!profile || (cacheLife == null ? void 0 : cacheLife.expire) === 0) {
// TODO: only revalidate if the path matches
store.pathWasRevalidated = ActionDidRevalidate;
}
}
//# sourceMappingURL=revalidate.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,278 @@
import { CACHE_ONE_YEAR } from '../../../lib/constants';
import { validateRevalidate, validateTags } from '../../lib/patch-fetch';
import { workAsyncStorage } from '../../app-render/work-async-storage.external';
import { getCacheSignal, getDraftModeProviderForCacheScope, workUnitAsyncStorage } from '../../app-render/work-unit-async-storage.external';
import { CachedRouteKind, IncrementalCacheKind } from '../../response-cache';
let noStoreFetchIdx = 0;
async function cacheNewResult(result, incrementalCache, cacheKey, tags, revalidate, fetchIdx, fetchUrl) {
await incrementalCache.set(cacheKey, {
kind: CachedRouteKind.FETCH,
data: {
headers: {},
// TODO: handle non-JSON values?
body: JSON.stringify(result),
status: 200,
url: ''
},
revalidate: typeof revalidate !== 'number' ? CACHE_ONE_YEAR : revalidate
}, {
fetchCache: true,
tags,
fetchIdx,
fetchUrl
});
return;
}
/**
* This function allows you to cache the results of expensive operations, like database queries, and reuse them across multiple requests.
*
* Read more: [Next.js Docs: `unstable_cache`](https://nextjs.org/docs/app/api-reference/functions/unstable_cache)
*/ export function unstable_cache(cb, keyParts, options = {}) {
if (options.revalidate === 0) {
throw Object.defineProperty(new Error(`Invariant revalidate: 0 can not be passed to unstable_cache(), must be "false" or "> 0" ${cb.toString()}`), "__NEXT_ERROR_CODE", {
value: "E57",
enumerable: false,
configurable: true
});
}
// Validate the tags provided are valid
const tags = options.tags ? validateTags(options.tags, `unstable_cache ${cb.toString()}`) : [];
// Validate the revalidate options
validateRevalidate(options.revalidate, `unstable_cache ${cb.name || cb.toString()}`);
// Stash the fixed part of the key at construction time. The invocation key will combine
// the fixed key with the arguments when actually called
// @TODO if cb.toString() is long we should hash it
// @TODO come up with a collision-free way to combine keyParts
// @TODO consider validating the keyParts are all strings. TS can't provide runtime guarantees
// and the error produced by accidentally using something that cannot be safely coerced is likely
// hard to debug
const fixedKey = `${cb.toString()}-${Array.isArray(keyParts) && keyParts.join(',')}`;
const cachedCb = async (...args)=>{
const workStore = workAsyncStorage.getStore();
const workUnitStore = workUnitAsyncStorage.getStore();
// We must be able to find the incremental cache otherwise we throw
const maybeIncrementalCache = (workStore == null ? void 0 : workStore.incrementalCache) || globalThis.__incrementalCache;
if (!maybeIncrementalCache) {
throw Object.defineProperty(new Error(`Invariant: incrementalCache missing in unstable_cache ${cb.toString()}`), "__NEXT_ERROR_CODE", {
value: "E469",
enumerable: false,
configurable: true
});
}
const incrementalCache = maybeIncrementalCache;
const cacheSignal = workUnitStore ? getCacheSignal(workUnitStore) : null;
if (cacheSignal) {
cacheSignal.beginRead();
}
try {
// If there's no request store, we aren't in a request (or we're not in
// app router) and if there's no static generation store, we aren't in app
// router. Default to an empty pathname and search params when there's no
// request store or static generation store available.
const fetchUrlPrefix = workStore && workUnitStore ? getFetchUrlPrefix(workStore, workUnitStore) : '';
// Construct the complete cache key for this function invocation
// @TODO stringify is likely not safe here. We will coerce undefined to null which will make
// the keyspace smaller than the execution space
const invocationKey = `${fixedKey}-${JSON.stringify(args)}`;
const cacheKey = await incrementalCache.generateCacheKey(invocationKey);
// $urlWithPath,$sortedQueryStringKeys,$hashOfEveryThingElse
const fetchUrl = `unstable_cache ${fetchUrlPrefix} ${cb.name ? ` ${cb.name}` : cacheKey}`;
const fetchIdx = (workStore ? workStore.nextFetchId : noStoreFetchIdx) ?? 1;
const implicitTags = workUnitStore == null ? void 0 : workUnitStore.implicitTags;
const innerCacheStore = {
type: 'unstable-cache',
phase: 'render',
implicitTags,
draftMode: workUnitStore && workStore && getDraftModeProviderForCacheScope(workStore, workUnitStore)
};
if (workStore) {
workStore.nextFetchId = fetchIdx + 1;
// We are in an App Router context. We try to return the cached entry if it exists and is valid
// If the entry is fresh we return it. If the entry is stale we return it but revalidate the entry in
// the background. If the entry is missing or invalid we generate a new entry and return it.
let isNestedUnstableCache = false;
if (workUnitStore) {
switch(workUnitStore.type){
case 'cache':
case 'private-cache':
case 'prerender':
case 'prerender-runtime':
case 'prerender-ppr':
case 'prerender-legacy':
// We update the store's revalidate property if the option.revalidate is a higher precedence
// options.revalidate === undefined doesn't affect timing.
// options.revalidate === false doesn't shrink timing. it stays at the maximum.
if (typeof options.revalidate === 'number') {
if (workUnitStore.revalidate < options.revalidate) {
// The store is already revalidating on a shorter time interval, leave it alone
} else {
workUnitStore.revalidate = options.revalidate;
}
}
// We need to accumulate the tags for this invocation within the store
const collectedTags = workUnitStore.tags;
if (collectedTags === null) {
workUnitStore.tags = tags.slice();
} else {
for (const tag of tags){
// @TODO refactor tags to be a set to avoid this O(n) lookup
if (!collectedTags.includes(tag)) {
collectedTags.push(tag);
}
}
}
break;
case 'unstable-cache':
isNestedUnstableCache = true;
break;
case 'prerender-client':
case 'request':
break;
default:
workUnitStore;
}
}
if (// when we are nested inside of other unstable_cache's
// we should bypass cache similar to fetches
!isNestedUnstableCache && workStore.fetchCache !== 'force-no-store' && !workStore.isOnDemandRevalidate && !incrementalCache.isOnDemandRevalidate && !workStore.isDraftMode) {
// We attempt to get the current cache entry from the incremental cache.
const cacheEntry = await incrementalCache.get(cacheKey, {
kind: IncrementalCacheKind.FETCH,
revalidate: options.revalidate,
tags,
softTags: implicitTags == null ? void 0 : implicitTags.tags,
fetchIdx,
fetchUrl
});
if (cacheEntry && cacheEntry.value) {
// The entry exists and has a value
if (cacheEntry.value.kind !== CachedRouteKind.FETCH) {
// The entry is invalid and we need a special warning
// @TODO why do we warn this way? Should this just be an error? How are these errors surfaced
// so bugs can be reported
// @TODO the invocation key can have sensitive data in it. we should not log this entire object
console.error(`Invariant invalid cacheEntry returned for ${invocationKey}`);
// will fall through to generating a new cache entry below
} else {
// We have a valid cache entry so we will be returning it. We also check to see if we need
// to background revalidate it by checking if it is stale.
const cachedResponse = cacheEntry.value.data.body !== undefined ? JSON.parse(cacheEntry.value.data.body) : undefined;
if (cacheEntry.isStale) {
if (!workStore.pendingRevalidates) {
workStore.pendingRevalidates = {};
}
// Check if there's already a pending revalidation to avoid duplicate work
if (!workStore.pendingRevalidates[invocationKey]) {
// Create the revalidation promise
const revalidationPromise = workUnitAsyncStorage.run(innerCacheStore, cb, ...args).then(async (result)=>{
await cacheNewResult(result, incrementalCache, cacheKey, tags, options.revalidate, fetchIdx, fetchUrl);
return result;
}).catch((err)=>{
// @TODO This error handling seems wrong. We swallow the error?
console.error(`revalidating cache with key: ${invocationKey}`, err);
// Return the stale value on error for foreground revalidation
return cachedResponse;
});
// Attach the empty catch here so we don't get a "unhandled promise
// rejection" warning. (Behavior is matched with patch-fetch)
if (workStore.isStaticGeneration) {
revalidationPromise.catch(()=>{});
}
workStore.pendingRevalidates[invocationKey] = revalidationPromise;
}
// Check if we need to do foreground revalidation
if (workStore.isStaticGeneration) {
// When the page is revalidating and the cache entry is stale,
// we need to wait for fresh data (blocking revalidate)
return workStore.pendingRevalidates[invocationKey];
}
// Otherwise, we're doing background revalidation - return stale immediately
}
// We had a valid cache entry so we return it here
return cachedResponse;
}
}
}
// If we got this far then we had an invalid cache entry and need to generate a new one
const result = await workUnitAsyncStorage.run(innerCacheStore, cb, ...args);
if (!workStore.isDraftMode) {
if (!workStore.pendingRevalidates) {
workStore.pendingRevalidates = {};
}
// We need to push the cache result promise to pending
// revalidates otherwise it won't be awaited and is just
// dangling
workStore.pendingRevalidates[invocationKey] = cacheNewResult(result, incrementalCache, cacheKey, tags, options.revalidate, fetchIdx, fetchUrl);
}
return result;
} else {
noStoreFetchIdx += 1;
// We are in Pages Router or were called outside of a render. We don't have a store
// so we just call the callback directly when it needs to run.
// If the entry is fresh we return it. If the entry is stale we return it but revalidate the entry in
// the background. If the entry is missing or invalid we generate a new entry and return it.
if (!incrementalCache.isOnDemandRevalidate) {
// We aren't doing an on demand revalidation so we check use the cache if valid
const cacheEntry = await incrementalCache.get(cacheKey, {
kind: IncrementalCacheKind.FETCH,
revalidate: options.revalidate,
tags,
fetchIdx,
fetchUrl,
softTags: implicitTags == null ? void 0 : implicitTags.tags
});
if (cacheEntry && cacheEntry.value) {
// The entry exists and has a value
if (cacheEntry.value.kind !== CachedRouteKind.FETCH) {
// The entry is invalid and we need a special warning
// @TODO why do we warn this way? Should this just be an error? How are these errors surfaced
// so bugs can be reported
console.error(`Invariant invalid cacheEntry returned for ${invocationKey}`);
// will fall through to generating a new cache entry below
} else if (!cacheEntry.isStale) {
// We have a valid cache entry and it is fresh so we return it
return cacheEntry.value.data.body !== undefined ? JSON.parse(cacheEntry.value.data.body) : undefined;
}
}
}
// If we got this far then we had an invalid cache entry and need to generate a new one
const result = await workUnitAsyncStorage.run(innerCacheStore, cb, ...args);
// we need to wait setting the new cache result here as
// we don't have pending revalidates on workStore to
// push to and we can't have a dangling promise
await cacheNewResult(result, incrementalCache, cacheKey, tags, options.revalidate, fetchIdx, fetchUrl);
return result;
}
} finally{
if (cacheSignal) {
cacheSignal.endRead();
}
}
};
// TODO: once AsyncLocalStorage.run() returns the correct types this override will no longer be necessary
return cachedCb;
}
function getFetchUrlPrefix(workStore, workUnitStore) {
switch(workUnitStore.type){
case 'request':
const pathname = workUnitStore.url.pathname;
const searchParams = new URLSearchParams(workUnitStore.url.search);
const sortedSearch = [
...searchParams.keys()
].sort((a, b)=>a.localeCompare(b)).map((key)=>`${key}=${searchParams.get(key)}`).join('&');
return `${pathname}${sortedSearch.length ? '?' : ''}${sortedSearch}`;
case 'prerender':
case 'prerender-client':
case 'prerender-runtime':
case 'prerender-ppr':
case 'prerender-legacy':
case 'cache':
case 'private-cache':
case 'unstable-cache':
return workStore.route;
default:
return workUnitStore;
}
}
//# sourceMappingURL=unstable-cache.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
import { workAsyncStorage } from '../../app-render/work-async-storage.external';
import { workUnitAsyncStorage } from '../../app-render/work-unit-async-storage.external';
import { markCurrentScopeAsDynamic } from '../../app-render/dynamic-rendering';
/**
* This function can be used to declaratively opt out of static rendering and indicate a particular component should not be cached.
*
* It marks the current scope as dynamic.
*
* - In [non-PPR](https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering) cases this will make a static render
* halt and mark the page as dynamic.
* - In PPR cases this will postpone the render at this location.
*
* If we are inside a cache scope then this function does nothing.
*
* @note It expects to be called within App Router and will error otherwise.
*
* Read more: [Next.js Docs: `unstable_noStore`](https://nextjs.org/docs/app/api-reference/functions/unstable_noStore)
*/ export function unstable_noStore() {
const callingExpression = 'unstable_noStore()';
const store = workAsyncStorage.getStore();
const workUnitStore = workUnitAsyncStorage.getStore();
if (!store) {
// This generally implies we are being called in Pages router. We should probably not support
// unstable_noStore in contexts outside of `react-server` condition but since we historically
// have not errored here previously, we maintain that behavior for now.
return;
} else if (store.forceStatic) {
return;
} else {
store.isUnstableNoStore = true;
if (workUnitStore) {
switch(workUnitStore.type){
case 'prerender':
case 'prerender-client':
case 'prerender-runtime':
// unstable_noStore() is a noop in Dynamic I/O.
return;
case 'prerender-ppr':
case 'prerender-legacy':
case 'request':
case 'cache':
case 'private-cache':
case 'unstable-cache':
break;
default:
workUnitStore;
}
}
markCurrentScopeAsDynamic(store, workUnitStore, callingExpression);
}
}
//# sourceMappingURL=unstable-no-store.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/spec-extension/unstable-no-store.ts"],"sourcesContent":["import { workAsyncStorage } from '../../app-render/work-async-storage.external'\nimport { workUnitAsyncStorage } from '../../app-render/work-unit-async-storage.external'\nimport { markCurrentScopeAsDynamic } from '../../app-render/dynamic-rendering'\n\n/**\n * This function can be used to declaratively opt out of static rendering and indicate a particular component should not be cached.\n *\n * It marks the current scope as dynamic.\n *\n * - In [non-PPR](https://nextjs.org/docs/app/api-reference/next-config-js/partial-prerendering) cases this will make a static render\n * halt and mark the page as dynamic.\n * - In PPR cases this will postpone the render at this location.\n *\n * If we are inside a cache scope then this function does nothing.\n *\n * @note It expects to be called within App Router and will error otherwise.\n *\n * Read more: [Next.js Docs: `unstable_noStore`](https://nextjs.org/docs/app/api-reference/functions/unstable_noStore)\n */\nexport function unstable_noStore() {\n const callingExpression = 'unstable_noStore()'\n const store = workAsyncStorage.getStore()\n const workUnitStore = workUnitAsyncStorage.getStore()\n if (!store) {\n // This generally implies we are being called in Pages router. We should probably not support\n // unstable_noStore in contexts outside of `react-server` condition but since we historically\n // have not errored here previously, we maintain that behavior for now.\n return\n } else if (store.forceStatic) {\n return\n } else {\n store.isUnstableNoStore = true\n if (workUnitStore) {\n switch (workUnitStore.type) {\n case 'prerender':\n case 'prerender-client':\n case 'prerender-runtime':\n // unstable_noStore() is a noop in Dynamic I/O.\n return\n case 'prerender-ppr':\n case 'prerender-legacy':\n case 'request':\n case 'cache':\n case 'private-cache':\n case 'unstable-cache':\n break\n default:\n workUnitStore satisfies never\n }\n }\n markCurrentScopeAsDynamic(store, workUnitStore, callingExpression)\n }\n}\n"],"names":["workAsyncStorage","workUnitAsyncStorage","markCurrentScopeAsDynamic","unstable_noStore","callingExpression","store","getStore","workUnitStore","forceStatic","isUnstableNoStore","type"],"mappings":"AAAA,SAASA,gBAAgB,QAAQ,+CAA8C;AAC/E,SAASC,oBAAoB,QAAQ,oDAAmD;AACxF,SAASC,yBAAyB,QAAQ,qCAAoC;AAE9E;;;;;;;;;;;;;;CAcC,GACD,OAAO,SAASC;IACd,MAAMC,oBAAoB;IAC1B,MAAMC,QAAQL,iBAAiBM,QAAQ;IACvC,MAAMC,gBAAgBN,qBAAqBK,QAAQ;IACnD,IAAI,CAACD,OAAO;QACV,6FAA6F;QAC7F,6FAA6F;QAC7F,uEAAuE;QACvE;IACF,OAAO,IAAIA,MAAMG,WAAW,EAAE;QAC5B;IACF,OAAO;QACLH,MAAMI,iBAAiB,GAAG;QAC1B,IAAIF,eAAe;YACjB,OAAQA,cAAcG,IAAI;gBACxB,KAAK;gBACL,KAAK;gBACL,KAAK;oBACH,+CAA+C;oBAC/C;gBACF,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;oBACH;gBACF;oBACEH;YACJ;QACF;QACAL,0BAA0BG,OAAOE,eAAeH;IAClD;AACF","ignoreList":[0]}

View File

@@ -0,0 +1,5 @@
const GlobalURLPattern = // @ts-expect-error: URLPattern is not available in Node.js
typeof URLPattern === 'undefined' ? undefined : URLPattern;
export { GlobalURLPattern as URLPattern };
//# sourceMappingURL=url-pattern.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/spec-extension/url-pattern.ts"],"sourcesContent":["const GlobalURLPattern =\n // @ts-expect-error: URLPattern is not available in Node.js\n typeof URLPattern === 'undefined' ? undefined : URLPattern\n\nexport { GlobalURLPattern as URLPattern }\n"],"names":["GlobalURLPattern","URLPattern","undefined"],"mappings":"AAAA,MAAMA,mBACJ,2DAA2D;AAC3D,OAAOC,eAAe,cAAcC,YAAYD;AAElD,SAASD,oBAAoBC,UAAU,GAAE","ignoreList":[0]}

View File

@@ -0,0 +1,15 @@
import parseua from 'next/dist/compiled/ua-parser-js';
export function isBot(input) {
return /Googlebot|Mediapartners-Google|AdsBot-Google|googleweblight|Storebot-Google|Google-PageRenderer|Google-InspectionTool|Bingbot|BingPreview|Slurp|DuckDuckBot|baiduspider|yandex|sogou|LinkedInBot|bitlybot|tumblr|vkShare|quora link preview|facebookexternalhit|facebookcatalog|Twitterbot|applebot|redditbot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|ia_archiver/i.test(input);
}
export function userAgentFromString(input) {
return {
...parseua(input),
isBot: input === undefined ? false : isBot(input)
};
}
export function userAgent({ headers }) {
return userAgentFromString(headers.get('user-agent') || undefined);
}
//# sourceMappingURL=user-agent.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/web/spec-extension/user-agent.ts"],"sourcesContent":["import parseua from 'next/dist/compiled/ua-parser-js'\n\ninterface UserAgent {\n isBot: boolean\n ua: string\n browser: {\n name?: string\n version?: string\n major?: string\n }\n device: {\n model?: string\n type?: string\n vendor?: string\n }\n engine: {\n name?: string\n version?: string\n }\n os: {\n name?: string\n version?: string\n }\n cpu: {\n architecture?: string\n }\n}\n\nexport function isBot(input: string): boolean {\n return /Googlebot|Mediapartners-Google|AdsBot-Google|googleweblight|Storebot-Google|Google-PageRenderer|Google-InspectionTool|Bingbot|BingPreview|Slurp|DuckDuckBot|baiduspider|yandex|sogou|LinkedInBot|bitlybot|tumblr|vkShare|quora link preview|facebookexternalhit|facebookcatalog|Twitterbot|applebot|redditbot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|ia_archiver/i.test(\n input\n )\n}\n\nexport function userAgentFromString(input: string | undefined): UserAgent {\n return {\n ...parseua(input),\n isBot: input === undefined ? false : isBot(input),\n }\n}\n\nexport function userAgent({ headers }: { headers: Headers }): UserAgent {\n return userAgentFromString(headers.get('user-agent') || undefined)\n}\n"],"names":["parseua","isBot","input","test","userAgentFromString","undefined","userAgent","headers","get"],"mappings":"AAAA,OAAOA,aAAa,kCAAiC;AA4BrD,OAAO,SAASC,MAAMC,KAAa;IACjC,OAAO,0WAA0WC,IAAI,CACnXD;AAEJ;AAEA,OAAO,SAASE,oBAAoBF,KAAyB;IAC3D,OAAO;QACL,GAAGF,QAAQE,MAAM;QACjBD,OAAOC,UAAUG,YAAY,QAAQJ,MAAMC;IAC7C;AACF;AAEA,OAAO,SAASI,UAAU,EAAEC,OAAO,EAAwB;IACzD,OAAOH,oBAAoBG,QAAQC,GAAG,CAAC,iBAAiBH;AAC1D","ignoreList":[0]}

View File

@@ -0,0 +1,10 @@
/**
* Proxy allows you to run code before a request is completed.
* Then, based on the incoming request, you can modify the response
* by rewriting, redirecting, modifying the request or response headers,
* or responding directly.
*
* Read more: [Next.js Docs: Proxy](https://nextjs.org/docs/app/building-your-application/routing/middleware)
*/ export { };
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/types.ts"],"sourcesContent":["import type { ExperimentalConfig, I18NConfig } from '../config-shared'\nimport type { NextRequest } from './spec-extension/request'\nimport type { NextFetchEvent } from './spec-extension/fetch-event'\nimport type { NextResponse } from './spec-extension/response'\nimport type { CloneableBody } from '../body-streams'\nimport type { OutgoingHttpHeaders } from 'http'\nimport type { FetchMetrics } from '../base-http'\n\n/**\n * @deprecated Use `ProxyConfig` instead. Middleware has been renamed to Proxy.\n */\nexport type { MiddlewareConfigInput as MiddlewareConfig } from '../../build/segment-config/middleware/middleware-config'\n\nexport type { MiddlewareConfigInput as ProxyConfig } from '../../build/segment-config/middleware/middleware-config'\n\nexport interface RequestData {\n headers: OutgoingHttpHeaders\n method: string\n nextConfig?: {\n basePath?: string\n i18n?: I18NConfig | null\n trailingSlash?: boolean\n experimental?: Pick<\n ExperimentalConfig,\n 'cacheLife' | 'authInterrupts' | 'clientParamParsingOrigins'\n >\n }\n page?: {\n name?: string\n params?: { [key: string]: string | string[] | undefined }\n }\n url: string\n body?: ReadableStream<Uint8Array>\n signal: AbortSignal\n /** passed in when running in edge runtime sandbox */\n waitUntil?: (promise: Promise<any>) => void\n}\n\nexport type NodejsRequestData = Omit<RequestData, 'body'> & {\n body?: CloneableBody\n}\n\nexport interface FetchEventResult {\n response: Response\n waitUntil: Promise<any>\n fetchMetrics?: FetchMetrics\n}\n\nexport type NextMiddlewareResult =\n | NextResponse\n | Response\n | null\n | undefined\n | void\n\n/**\n * Middleware allows you to run code before a request is completed.\n * Then, based on the incoming request, you can modify the response\n * by rewriting, redirecting, modifying the request or response headers,\n * or responding directly.\n *\n * @deprecated Use `NextProxy` instead. Middleware has been renamed to Proxy.\n * Read more: [Next.js Docs: Middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)\n */\nexport type NextMiddleware = (\n request: NextRequest,\n event: NextFetchEvent\n) => NextMiddlewareResult | Promise<NextMiddlewareResult>\n\n/**\n * Proxy allows you to run code before a request is completed.\n * Then, based on the incoming request, you can modify the response\n * by rewriting, redirecting, modifying the request or response headers,\n * or responding directly.\n *\n * Read more: [Next.js Docs: Proxy](https://nextjs.org/docs/app/building-your-application/routing/middleware)\n */\nexport type NextProxy = NextMiddleware\n"],"names":[],"mappings":"AAqEA;;;;;;;CAOC,GACD,WAAsC","ignoreList":[0]}

View File

@@ -0,0 +1,145 @@
import { NEXT_INTERCEPTION_MARKER_PREFIX, NEXT_QUERY_PARAM_PREFIX } from '../../lib/constants';
/**
* Converts a Node.js IncomingHttpHeaders object to a Headers object. Any
* headers with multiple values will be joined with a comma and space. Any
* headers that have an undefined value will be ignored and others will be
* coerced to strings.
*
* @param nodeHeaders the headers object to convert
* @returns the converted headers object
*/ export function fromNodeOutgoingHttpHeaders(nodeHeaders) {
const headers = new Headers();
for (let [key, value] of Object.entries(nodeHeaders)){
const values = Array.isArray(value) ? value : [
value
];
for (let v of values){
if (typeof v === 'undefined') continue;
if (typeof v === 'number') {
v = v.toString();
}
headers.append(key, v);
}
}
return headers;
}
/*
Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas
that are within a single set-cookie field-value, such as in the Expires portion.
This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2
Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128
React Native's fetch does this for *every* header, including set-cookie.
Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25
Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
*/ export function splitCookiesString(cookiesString) {
var cookiesStrings = [];
var pos = 0;
var start;
var ch;
var lastComma;
var nextStart;
var cookiesSeparatorFound;
function skipWhitespace() {
while(pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))){
pos += 1;
}
return pos < cookiesString.length;
}
function notSpecialChar() {
ch = cookiesString.charAt(pos);
return ch !== '=' && ch !== ';' && ch !== ',';
}
while(pos < cookiesString.length){
start = pos;
cookiesSeparatorFound = false;
while(skipWhitespace()){
ch = cookiesString.charAt(pos);
if (ch === ',') {
// ',' is a cookie separator if we have later first '=', not ';' or ','
lastComma = pos;
pos += 1;
skipWhitespace();
nextStart = pos;
while(pos < cookiesString.length && notSpecialChar()){
pos += 1;
}
// currently special character
if (pos < cookiesString.length && cookiesString.charAt(pos) === '=') {
// we found cookies separator
cookiesSeparatorFound = true;
// pos is inside the next cookie, so back up and return it.
pos = nextStart;
cookiesStrings.push(cookiesString.substring(start, lastComma));
start = pos;
} else {
// in param ',' or param separator ';',
// we continue from that comma
pos = lastComma + 1;
}
} else {
pos += 1;
}
}
if (!cookiesSeparatorFound || pos >= cookiesString.length) {
cookiesStrings.push(cookiesString.substring(start, cookiesString.length));
}
}
return cookiesStrings;
}
/**
* Converts a Headers object to a Node.js OutgoingHttpHeaders object. This is
* required to support the set-cookie header, which may have multiple values.
*
* @param headers the headers object to convert
* @returns the converted headers object
*/ export function toNodeOutgoingHttpHeaders(headers) {
const nodeHeaders = {};
const cookies = [];
if (headers) {
for (const [key, value] of headers.entries()){
if (key.toLowerCase() === 'set-cookie') {
// We may have gotten a comma joined string of cookies, or multiple
// set-cookie headers. We need to merge them into one header array
// to represent all the cookies.
cookies.push(...splitCookiesString(value));
nodeHeaders[key] = cookies.length === 1 ? cookies[0] : cookies;
} else {
nodeHeaders[key] = value;
}
}
}
return nodeHeaders;
}
/**
* Validate the correctness of a user-provided URL.
*/ export function validateURL(url) {
try {
return String(new URL(String(url)));
} catch (error) {
throw Object.defineProperty(new Error(`URL is malformed "${String(url)}". Please use only absolute URLs - https://nextjs.org/docs/messages/middleware-relative-urls`, {
cause: error
}), "__NEXT_ERROR_CODE", {
value: "E61",
enumerable: false,
configurable: true
});
}
}
/**
* Normalizes `nxtP` and `nxtI` query param values to remove the prefix.
* This function does not mutate the input key.
*/ export function normalizeNextQueryParam(key) {
const prefixes = [
NEXT_QUERY_PARAM_PREFIX,
NEXT_INTERCEPTION_MARKER_PREFIX
];
for (const prefix of prefixes){
if (key !== prefix && key.startsWith(prefix)) {
return key.substring(prefix.length);
}
}
return null;
}
//# sourceMappingURL=utils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
/** Monitor when the consumer finishes reading the response body.
that's as close as we can get to `res.on('close')` using web APIs.
*/ export function trackBodyConsumed(body, onEnd) {
if (typeof body === 'string') {
const generator = async function* generate() {
const encoder = new TextEncoder();
yield encoder.encode(body);
onEnd();
};
// @ts-expect-error BodyInit typings doesn't seem to include AsyncIterables even though it's supported in practice
return generator();
} else {
return trackStreamConsumed(body, onEnd);
}
}
export function trackStreamConsumed(stream, onEnd) {
// NOTE: This function must handle `stream` being aborted or cancelled,
// so it can't just be this:
//
// return stream.pipeThrough(new TransformStream({ flush() { onEnd() } }))
//
// because that doesn't handle cancellations.
// (and cancellation handling via `Transformer.cancel` is only available in node >20)
const dest = new TransformStream();
const runOnEnd = ()=>onEnd();
stream.pipeTo(dest.writable).then(runOnEnd, runOnEnd);
return dest.readable;
}
export class CloseController {
onClose(callback) {
if (this.isClosed) {
throw Object.defineProperty(new Error('Cannot subscribe to a closed CloseController'), "__NEXT_ERROR_CODE", {
value: "E365",
enumerable: false,
configurable: true
});
}
this.target.addEventListener('close', callback);
this.listeners++;
}
dispatchClose() {
if (this.isClosed) {
throw Object.defineProperty(new Error('Cannot close a CloseController multiple times'), "__NEXT_ERROR_CODE", {
value: "E229",
enumerable: false,
configurable: true
});
}
if (this.listeners > 0) {
this.target.dispatchEvent(new Event('close'));
}
this.isClosed = true;
}
constructor(){
this.target = new EventTarget();
this.listeners = 0;
this.isClosed = false;
}
}
//# sourceMappingURL=web-on-close.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/web/web-on-close.ts"],"sourcesContent":["/** Monitor when the consumer finishes reading the response body.\nthat's as close as we can get to `res.on('close')` using web APIs.\n*/\nexport function trackBodyConsumed(\n body: string | ReadableStream,\n onEnd: () => void\n): BodyInit {\n if (typeof body === 'string') {\n const generator = async function* generate() {\n const encoder = new TextEncoder()\n yield encoder.encode(body)\n onEnd()\n }\n // @ts-expect-error BodyInit typings doesn't seem to include AsyncIterables even though it's supported in practice\n return generator()\n } else {\n return trackStreamConsumed(body, onEnd)\n }\n}\n\nexport function trackStreamConsumed<TChunk>(\n stream: ReadableStream<TChunk>,\n onEnd: () => void\n): ReadableStream<TChunk> {\n // NOTE: This function must handle `stream` being aborted or cancelled,\n // so it can't just be this:\n //\n // return stream.pipeThrough(new TransformStream({ flush() { onEnd() } }))\n //\n // because that doesn't handle cancellations.\n // (and cancellation handling via `Transformer.cancel` is only available in node >20)\n const dest = new TransformStream()\n const runOnEnd = () => onEnd()\n stream.pipeTo(dest.writable).then(runOnEnd, runOnEnd)\n return dest.readable\n}\n\nexport class CloseController {\n private target = new EventTarget()\n listeners = 0\n isClosed = false\n\n onClose(callback: () => void) {\n if (this.isClosed) {\n throw new Error('Cannot subscribe to a closed CloseController')\n }\n\n this.target.addEventListener('close', callback)\n this.listeners++\n }\n\n dispatchClose() {\n if (this.isClosed) {\n throw new Error('Cannot close a CloseController multiple times')\n }\n if (this.listeners > 0) {\n this.target.dispatchEvent(new Event('close'))\n }\n this.isClosed = true\n }\n}\n"],"names":["trackBodyConsumed","body","onEnd","generator","generate","encoder","TextEncoder","encode","trackStreamConsumed","stream","dest","TransformStream","runOnEnd","pipeTo","writable","then","readable","CloseController","onClose","callback","isClosed","Error","target","addEventListener","listeners","dispatchClose","dispatchEvent","Event","EventTarget"],"mappings":"AAAA;;AAEA,GACA,OAAO,SAASA,kBACdC,IAA6B,EAC7BC,KAAiB;IAEjB,IAAI,OAAOD,SAAS,UAAU;QAC5B,MAAME,YAAY,gBAAgBC;YAChC,MAAMC,UAAU,IAAIC;YACpB,MAAMD,QAAQE,MAAM,CAACN;YACrBC;QACF;QACA,kHAAkH;QAClH,OAAOC;IACT,OAAO;QACL,OAAOK,oBAAoBP,MAAMC;IACnC;AACF;AAEA,OAAO,SAASM,oBACdC,MAA8B,EAC9BP,KAAiB;IAEjB,uEAAuE;IACvE,4BAA4B;IAC5B,EAAE;IACF,4EAA4E;IAC5E,EAAE;IACF,6CAA6C;IAC7C,qFAAqF;IACrF,MAAMQ,OAAO,IAAIC;IACjB,MAAMC,WAAW,IAAMV;IACvBO,OAAOI,MAAM,CAACH,KAAKI,QAAQ,EAAEC,IAAI,CAACH,UAAUA;IAC5C,OAAOF,KAAKM,QAAQ;AACtB;AAEA,OAAO,MAAMC;IAKXC,QAAQC,QAAoB,EAAE;QAC5B,IAAI,IAAI,CAACC,QAAQ,EAAE;YACjB,MAAM,qBAAyD,CAAzD,IAAIC,MAAM,iDAAV,qBAAA;uBAAA;4BAAA;8BAAA;YAAwD;QAChE;QAEA,IAAI,CAACC,MAAM,CAACC,gBAAgB,CAAC,SAASJ;QACtC,IAAI,CAACK,SAAS;IAChB;IAEAC,gBAAgB;QACd,IAAI,IAAI,CAACL,QAAQ,EAAE;YACjB,MAAM,qBAA0D,CAA1D,IAAIC,MAAM,kDAAV,qBAAA;uBAAA;4BAAA;8BAAA;YAAyD;QACjE;QACA,IAAI,IAAI,CAACG,SAAS,GAAG,GAAG;YACtB,IAAI,CAACF,MAAM,CAACI,aAAa,CAAC,IAAIC,MAAM;QACtC;QACA,IAAI,CAACP,QAAQ,GAAG;IAClB;;aArBQE,SAAS,IAAIM;aACrBJ,YAAY;aACZJ,WAAW;;AAoBb","ignoreList":[0]}