main repo

This commit is contained in:
Basilosaurusrex
2025-11-24 18:09:40 +01:00
parent b636ee5e70
commit f027651f9b
34146 changed files with 4436636 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
import { HTTP_METHODS } from "../../../../web/http";
import { handleMethodNotAllowedResponse } from "../../helpers/response-handlers";
const AUTOMATIC_ROUTE_METHODS = [
"HEAD",
"OPTIONS"
];
export function autoImplementMethods(handlers) {
// Loop through all the HTTP methods to create the initial methods object.
// Each of the methods will be set to the the 405 response handler.
const methods = HTTP_METHODS.reduce((acc, method)=>({
...acc,
// If the userland module implements the method, then use it. Otherwise,
// use the 405 response handler.
[method]: handlers[method] ?? handleMethodNotAllowedResponse
}), {});
// Get all the methods that could be automatically implemented that were not
// implemented by the userland module.
const implemented = new Set(HTTP_METHODS.filter((method)=>handlers[method]));
const missing = AUTOMATIC_ROUTE_METHODS.filter((method)=>!implemented.has(method));
// Loop over the missing methods to automatically implement them if we can.
for (const method of missing){
// If the userland module doesn't implement the HEAD method, then
// we'll automatically implement it by calling the GET method (if it
// exists).
if (method === "HEAD") {
// If the userland module doesn't implement the GET method, then
// we're done.
if (!handlers.GET) break;
// Implement the HEAD method by calling the GET method.
methods.HEAD = handlers.GET;
// Mark it as implemented.
implemented.add("HEAD");
continue;
}
// If OPTIONS is not provided then implement it.
if (method === "OPTIONS") {
// TODO: check if HEAD is implemented, if so, use it to add more headers
// Get all the methods that were implemented by the userland module.
const allow = [
"OPTIONS",
...implemented
];
// If the list of methods doesn't include HEAD, but it includes GET, then
// add HEAD as it's automatically implemented.
if (!implemented.has("HEAD") && implemented.has("GET")) {
allow.push("HEAD");
}
// Sort and join the list with commas to create the `Allow` header. See:
// https://httpwg.org/specs/rfc9110.html#field.allow
const headers = {
Allow: allow.sort().join(", ")
};
// Implement the OPTIONS method by returning a 204 response with the
// `Allow` header.
methods.OPTIONS = ()=>new Response(null, {
status: 204,
headers
});
// Mark this method as implemented.
implemented.add("OPTIONS");
continue;
}
throw new Error(`Invariant: should handle all automatic implementable methods, got method: ${method}`);
}
return methods;
}
//# sourceMappingURL=auto-implement-methods.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/future/route-modules/app-route/helpers/auto-implement-methods.ts"],"names":["HTTP_METHODS","handleMethodNotAllowedResponse","AUTOMATIC_ROUTE_METHODS","autoImplementMethods","handlers","methods","reduce","acc","method","implemented","Set","filter","missing","has","GET","HEAD","add","allow","push","headers","Allow","sort","join","OPTIONS","Response","status","Error"],"mappings":"AAEA,SAASA,YAAY,QAA0B,uBAAsB;AACrE,SAASC,8BAA8B,QAAQ,kCAAiC;AAEhF,MAAMC,0BAA0B;IAAC;IAAQ;CAAU;AAEnD,OAAO,SAASC,qBACdC,QAA0B;IAE1B,0EAA0E;IAC1E,mEAAmE;IACnE,MAAMC,UAAkDL,aAAaM,MAAM,CACzE,CAACC,KAAKC,SAAY,CAAA;YAChB,GAAGD,GAAG;YACN,wEAAwE;YACxE,gCAAgC;YAChC,CAACC,OAAO,EAAEJ,QAAQ,CAACI,OAAO,IAAIP;QAChC,CAAA,GACA,CAAC;IAGH,4EAA4E;IAC5E,sCAAsC;IACtC,MAAMQ,cAAc,IAAIC,IAAIV,aAAaW,MAAM,CAAC,CAACH,SAAWJ,QAAQ,CAACI,OAAO;IAC5E,MAAMI,UAAUV,wBAAwBS,MAAM,CAC5C,CAACH,SAAW,CAACC,YAAYI,GAAG,CAACL;IAG/B,2EAA2E;IAC3E,KAAK,MAAMA,UAAUI,QAAS;QAC5B,iEAAiE;QACjE,oEAAoE;QACpE,WAAW;QACX,IAAIJ,WAAW,QAAQ;YACrB,gEAAgE;YAChE,cAAc;YACd,IAAI,CAACJ,SAASU,GAAG,EAAE;YAEnB,uDAAuD;YACvDT,QAAQU,IAAI,GAAGX,SAASU,GAAG;YAE3B,0BAA0B;YAC1BL,YAAYO,GAAG,CAAC;YAEhB;QACF;QAEA,gDAAgD;QAChD,IAAIR,WAAW,WAAW;YACxB,wEAAwE;YAExE,oEAAoE;YACpE,MAAMS,QAAuB;gBAAC;mBAAcR;aAAY;YAExD,yEAAyE;YACzE,8CAA8C;YAC9C,IAAI,CAACA,YAAYI,GAAG,CAAC,WAAWJ,YAAYI,GAAG,CAAC,QAAQ;gBACtDI,MAAMC,IAAI,CAAC;YACb;YAEA,wEAAwE;YACxE,oDAAoD;YACpD,MAAMC,UAAU;gBAAEC,OAAOH,MAAMI,IAAI,GAAGC,IAAI,CAAC;YAAM;YAEjD,oEAAoE;YACpE,kBAAkB;YAClBjB,QAAQkB,OAAO,GAAG,IAAM,IAAIC,SAAS,MAAM;oBAAEC,QAAQ;oBAAKN;gBAAQ;YAElE,mCAAmC;YACnCV,YAAYO,GAAG,CAAC;YAEhB;QACF;QAEA,MAAM,IAAIU,MACR,CAAC,0EAA0E,EAAElB,OAAO,CAAC;IAEzF;IAEA,OAAOH;AACT"}

View File

@@ -0,0 +1,14 @@
/**
* Cleans a URL by stripping the protocol, host, and search params.
*
* @param urlString the url to clean
* @returns the cleaned url
*/ export function cleanURL(urlString) {
const url = new URL(urlString);
url.host = "localhost:3000";
url.search = "";
url.protocol = "http";
return url.toString();
}
//# sourceMappingURL=clean-url.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/future/route-modules/app-route/helpers/clean-url.ts"],"names":["cleanURL","urlString","url","URL","host","search","protocol","toString"],"mappings":"AAAA;;;;;CAKC,GACD,OAAO,SAASA,SAASC,SAAiB;IACxC,MAAMC,MAAM,IAAIC,IAAIF;IACpBC,IAAIE,IAAI,GAAG;IACXF,IAAIG,MAAM,GAAG;IACbH,IAAII,QAAQ,GAAG;IACf,OAAOJ,IAAIK,QAAQ;AACrB"}

View File

@@ -0,0 +1,22 @@
const NON_STATIC_METHODS = [
"OPTIONS",
"POST",
"PUT",
"DELETE",
"PATCH"
];
/**
* Gets all the method names for handlers that are not considered static.
*
* @param handlers the handlers from the userland module
* @returns the method names that are not considered static or false if all
* methods are static
*/ export function getNonStaticMethods(handlers) {
// We can currently only statically optimize if only GET/HEAD are used as
// prerender can't be used conditionally based on the method currently.
const methods = NON_STATIC_METHODS.filter((method)=>handlers[method]);
if (methods.length === 0) return false;
return methods;
}
//# sourceMappingURL=get-non-static-methods.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/future/route-modules/app-route/helpers/get-non-static-methods.ts"],"names":["NON_STATIC_METHODS","getNonStaticMethods","handlers","methods","filter","method","length"],"mappings":"AAGA,MAAMA,qBAAqB;IACzB;IACA;IACA;IACA;IACA;CACD;AAED;;;;;;CAMC,GACD,OAAO,SAASC,oBACdC,QAA0B;IAE1B,yEAAyE;IACzE,uEAAuE;IACvE,MAAMC,UAAUH,mBAAmBI,MAAM,CAAC,CAACC,SAAWH,QAAQ,CAACG,OAAO;IACtE,IAAIF,QAAQG,MAAM,KAAK,GAAG,OAAO;IAEjC,OAAOH;AACT"}

View File

@@ -0,0 +1,19 @@
/**
* Get pathname from absolute path.
*
* @param absolutePath the absolute path
* @returns the pathname
*/ export function getPathnameFromAbsolutePath(absolutePath) {
// Remove prefix including app dir
let appDir = "/app/";
if (!absolutePath.includes(appDir)) {
appDir = "\\app\\";
}
const [, ...parts] = absolutePath.split(appDir);
const relativePath = appDir[0] + parts.join(appDir);
// remove extension
const pathname = relativePath.split(".").slice(0, -1).join(".");
return pathname;
}
//# sourceMappingURL=get-pathname-from-absolute-path.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/future/route-modules/app-route/helpers/get-pathname-from-absolute-path.ts"],"names":["getPathnameFromAbsolutePath","absolutePath","appDir","includes","parts","split","relativePath","join","pathname","slice"],"mappings":"AAAA;;;;;CAKC,GACD,OAAO,SAASA,4BAA4BC,YAAoB;IAC9D,kCAAkC;IAClC,IAAIC,SAAS;IACb,IAAI,CAACD,aAAaE,QAAQ,CAACD,SAAS;QAClCA,SAAS;IACX;IACA,MAAM,GAAG,GAAGE,MAAM,GAAGH,aAAaI,KAAK,CAACH;IACxC,MAAMI,eAAeJ,MAAM,CAAC,EAAE,GAAGE,MAAMG,IAAI,CAACL;IAE5C,mBAAmB;IACnB,MAAMM,WAAWF,aAAaD,KAAK,CAAC,KAAKI,KAAK,CAAC,GAAG,CAAC,GAAGF,IAAI,CAAC;IAC3D,OAAOC;AACT"}

View File

@@ -0,0 +1,15 @@
/**
* Converts the query into params.
*
* @param query the query to convert to params
* @returns the params
*/ export function parsedUrlQueryToParams(query) {
const params = {};
for (const [key, value] of Object.entries(query)){
if (typeof value === "undefined") continue;
params[key] = value;
}
return params;
}
//# sourceMappingURL=parsed-url-query-to-params.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/future/route-modules/app-route/helpers/parsed-url-query-to-params.ts"],"names":["parsedUrlQueryToParams","query","params","key","value","Object","entries"],"mappings":"AAEA;;;;;CAKC,GACD,OAAO,SAASA,uBACdC,KAAqB;IAErB,MAAMC,SAA4C,CAAC;IAEnD,KAAK,MAAM,CAACC,KAAKC,MAAM,IAAIC,OAAOC,OAAO,CAACL,OAAQ;QAChD,IAAI,OAAOG,UAAU,aAAa;QAClCF,MAAM,CAACC,IAAI,GAAGC;IAChB;IAEA,OAAOF;AACT"}

View File

@@ -0,0 +1,113 @@
import { RequestCookies } from "next/dist/compiled/@edge-runtime/cookies";
import { NextURL } from "../../../../web/next-url";
import { cleanURL } from "./clean-url";
export function proxyRequest(request, { dynamic }, hooks) {
function handleNextUrlBailout(prop) {
switch(prop){
case "search":
case "searchParams":
case "toString":
case "href":
case "origin":
hooks.staticGenerationBailout(`nextUrl.${prop}`);
return;
default:
return;
}
}
const cache = {};
const handleForceStatic = (url, prop)=>{
switch(prop){
case "search":
return "";
case "searchParams":
if (!cache.searchParams) cache.searchParams = new URLSearchParams();
return cache.searchParams;
case "url":
case "href":
if (!cache.url) cache.url = cleanURL(url);
return cache.url;
case "toJSON":
case "toString":
if (!cache.url) cache.url = cleanURL(url);
if (!cache.toString) cache.toString = ()=>cache.url;
return cache.toString;
case "headers":
if (!cache.headers) cache.headers = new Headers();
return cache.headers;
case "cookies":
if (!cache.headers) cache.headers = new Headers();
if (!cache.cookies) cache.cookies = new RequestCookies(cache.headers);
return cache.cookies;
case "clone":
if (!cache.url) cache.url = cleanURL(url);
return ()=>new NextURL(cache.url);
default:
break;
}
};
const wrappedNextUrl = new Proxy(request.nextUrl, {
get (target, prop) {
handleNextUrlBailout(prop);
if (dynamic === "force-static" && typeof prop === "string") {
const result = handleForceStatic(target.href, prop);
if (result !== undefined) return result;
}
const value = target[prop];
if (typeof value === "function") {
return value.bind(target);
}
return value;
},
set (target, prop, value) {
handleNextUrlBailout(prop);
target[prop] = value;
return true;
}
});
const handleReqBailout = (prop)=>{
switch(prop){
case "headers":
hooks.headerHooks.headers();
return;
// if request.url is accessed directly instead of
// request.nextUrl we bail since it includes query
// values that can be relied on dynamically
case "url":
case "body":
case "blob":
case "json":
case "text":
case "arrayBuffer":
case "formData":
hooks.staticGenerationBailout(`request.${prop}`);
return;
default:
return;
}
};
return new Proxy(request, {
get (target, prop) {
handleReqBailout(prop);
if (prop === "nextUrl") {
return wrappedNextUrl;
}
if (dynamic === "force-static" && typeof prop === "string") {
const result = handleForceStatic(target.url, prop);
if (result !== undefined) return result;
}
const value = target[prop];
if (typeof value === "function") {
return value.bind(target);
}
return value;
},
set (target, prop, value) {
handleReqBailout(prop);
target[prop] = value;
return true;
}
});
}
//# sourceMappingURL=proxy-request.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/future/route-modules/app-route/helpers/proxy-request.ts"],"names":["RequestCookies","NextURL","cleanURL","proxyRequest","request","dynamic","hooks","handleNextUrlBailout","prop","staticGenerationBailout","cache","handleForceStatic","url","searchParams","URLSearchParams","toString","headers","Headers","cookies","wrappedNextUrl","Proxy","nextUrl","get","target","result","href","undefined","value","bind","set","handleReqBailout","headerHooks"],"mappings":"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"}

View File

@@ -0,0 +1,21 @@
import { isNotFoundError } from "../../../../../client/components/not-found";
import { getURLFromRedirectError, isRedirectError } from "../../../../../client/components/redirect";
import { handleNotFoundResponse, handleTemporaryRedirectResponse } from "../../helpers/response-handlers";
export function resolveHandlerError(err) {
if (isRedirectError(err)) {
const redirect = getURLFromRedirectError(err);
if (!redirect) {
throw new Error("Invariant: Unexpected redirect url format");
}
// This is a redirect error! Send the redirect response.
return handleTemporaryRedirectResponse(redirect, err.mutableCookies);
}
if (isNotFoundError(err)) {
// This is a not found error! Send the not found response.
return handleNotFoundResponse();
}
// Return false to indicate that this is not a handled error.
return false;
}
//# sourceMappingURL=resolve-handler-error.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../../src/server/future/route-modules/app-route/helpers/resolve-handler-error.ts"],"names":["isNotFoundError","getURLFromRedirectError","isRedirectError","handleNotFoundResponse","handleTemporaryRedirectResponse","resolveHandlerError","err","redirect","Error","mutableCookies"],"mappings":"AAAA,SAASA,eAAe,QAAQ,6CAA4C;AAC5E,SACEC,uBAAuB,EACvBC,eAAe,QACV,4CAA2C;AAClD,SACEC,sBAAsB,EACtBC,+BAA+B,QAC1B,kCAAiC;AAExC,OAAO,SAASC,oBAAoBC,GAAQ;IAC1C,IAAIJ,gBAAgBI,MAAM;QACxB,MAAMC,WAAWN,wBAAwBK;QACzC,IAAI,CAACC,UAAU;YACb,MAAM,IAAIC,MAAM;QAClB;QAEA,wDAAwD;QACxD,OAAOJ,gCAAgCG,UAAUD,IAAIG,cAAc;IACrE;IAEA,IAAIT,gBAAgBM,MAAM;QACxB,0DAA0D;QAC1D,OAAOH;IACT;IAEA,6DAA6D;IAC7D,OAAO;AACT"}

View File

@@ -0,0 +1,13 @@
if (process.env.NEXT_RUNTIME === "edge") {
module.exports = require("next/dist/server/future/route-modules/app-route/module.js");
} else {
if (process.env.NODE_ENV === "development") {
module.exports = require("next/dist/compiled/next-server/app-route.runtime.dev.js");
} else if (process.env.TURBOPACK) {
module.exports = require("next/dist/compiled/next-server/app-route-turbo.runtime.prod.js");
} else {
module.exports = require("next/dist/compiled/next-server/app-route.runtime.prod.js");
}
}
//# sourceMappingURL=module.compiled.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/future/route-modules/app-route/module.compiled.ts"],"names":["process","env","NEXT_RUNTIME","module","exports","require","NODE_ENV","TURBOPACK"],"mappings":"AAAA,IAAIA,QAAQC,GAAG,CAACC,YAAY,KAAK,QAAQ;IACvCC,OAAOC,OAAO,GAAGC,QAAQ;AAC3B,OAAO;IACL,IAAIL,QAAQC,GAAG,CAACK,QAAQ,KAAK,eAAe;QAC1CH,OAAOC,OAAO,GAAGC,QAAQ;IAC3B,OAAO,IAAIL,QAAQC,GAAG,CAACM,SAAS,EAAE;QAChCJ,OAAOC,OAAO,GAAGC,QAAQ;IAC3B,OAAO;QACLF,OAAOC,OAAO,GAAGC,QAAQ;IAC3B;AACF"}

View File

@@ -0,0 +1,272 @@
import { RouteModule } from "../route-module";
import { RequestAsyncStorageWrapper } from "../../../async-storage/request-async-storage-wrapper";
import { StaticGenerationAsyncStorageWrapper } from "../../../async-storage/static-generation-async-storage-wrapper";
import { handleBadRequestResponse, handleInternalServerErrorResponse } from "../helpers/response-handlers";
import { HTTP_METHODS, isHTTPMethod } from "../../../web/http";
import { addImplicitTags, patchFetch } from "../../../lib/patch-fetch";
import { getTracer } from "../../../lib/trace/tracer";
import { AppRouteRouteHandlersSpan } from "../../../lib/trace/constants";
import { getPathnameFromAbsolutePath } from "./helpers/get-pathname-from-absolute-path";
import { proxyRequest } from "./helpers/proxy-request";
import { resolveHandlerError } from "./helpers/resolve-handler-error";
import * as Log from "../../../../build/output/log";
import { autoImplementMethods } from "./helpers/auto-implement-methods";
import { getNonStaticMethods } from "./helpers/get-non-static-methods";
import { appendMutableCookies } from "../../../web/spec-extension/adapters/request-cookies";
import { RouteKind } from "../../route-kind";
import { parsedUrlQueryToParams } from "./helpers/parsed-url-query-to-params";
import * as serverHooks from "../../../../client/components/hooks-server-context";
import * as headerHooks from "../../../../client/components/headers";
import { staticGenerationBailout } from "../../../../client/components/static-generation-bailout";
import { requestAsyncStorage } from "../../../../client/components/request-async-storage.external";
import { staticGenerationAsyncStorage } from "../../../../client/components/static-generation-async-storage.external";
import { actionAsyncStorage } from "../../../../client/components/action-async-storage.external";
import * as sharedModules from "./shared-modules";
/**
* AppRouteRouteHandler is the handler for app routes.
*/ export class AppRouteRouteModule extends RouteModule {
static #_ = this.sharedModules = sharedModules;
static is(route) {
return route.definition.kind === RouteKind.APP_ROUTE;
}
constructor({ userland, definition, resolvedPagePath, nextConfigOutput }){
super({
userland,
definition
});
/**
* A reference to the request async storage.
*/ this.requestAsyncStorage = requestAsyncStorage;
/**
* A reference to the static generation async storage.
*/ this.staticGenerationAsyncStorage = staticGenerationAsyncStorage;
/**
* An interface to call server hooks which interact with the underlying
* storage.
*/ this.serverHooks = serverHooks;
/**
* An interface to call header hooks which interact with the underlying
* request storage.
*/ this.headerHooks = headerHooks;
/**
* An interface to call static generation bailout hooks which interact with
* the underlying static generation storage.
*/ this.staticGenerationBailout = staticGenerationBailout;
/**
* A reference to the mutation related async storage, such as mutations of
* cookies.
*/ this.actionAsyncStorage = actionAsyncStorage;
this.resolvedPagePath = resolvedPagePath;
this.nextConfigOutput = nextConfigOutput;
// Automatically implement some methods if they aren't implemented by the
// userland module.
this.methods = autoImplementMethods(userland);
// Get the non-static methods for this route.
this.nonStaticMethods = getNonStaticMethods(userland);
// Get the dynamic property from the userland module.
this.dynamic = this.userland.dynamic;
if (this.nextConfigOutput === "export") {
if (!this.dynamic || this.dynamic === "auto") {
this.dynamic = "error";
} else if (this.dynamic === "force-dynamic") {
throw new Error(`export const dynamic = "force-dynamic" on page "${definition.pathname}" cannot be used with "output: export". See more info here: https://nextjs.org/docs/advanced-features/static-html-export`);
}
}
// We only warn in development after here, so return if we're not in
// development.
if (process.env.NODE_ENV === "development") {
// Print error in development if the exported handlers are in lowercase, only
// uppercase handlers are supported.
const lowercased = HTTP_METHODS.map((method)=>method.toLowerCase());
for (const method of lowercased){
if (method in this.userland) {
Log.error(`Detected lowercase method '${method}' in '${this.resolvedPagePath}'. Export the uppercase '${method.toUpperCase()}' method name to fix this error.`);
}
}
// Print error if the module exports a default handler, they must use named
// exports for each HTTP method.
if ("default" in this.userland) {
Log.error(`Detected default export in '${this.resolvedPagePath}'. Export a named export for each HTTP method instead.`);
}
// If there is no methods exported by this module, then return a not found
// response.
if (!HTTP_METHODS.some((method)=>method in this.userland)) {
Log.error(`No HTTP methods exported in '${this.resolvedPagePath}'. Export a named export for each HTTP method.`);
}
}
}
/**
* Resolves the handler function for the given method.
*
* @param method the requested method
* @returns the handler function for the given method
*/ resolve(method) {
// Ensure that the requested method is a valid method (to prevent RCE's).
if (!isHTTPMethod(method)) return handleBadRequestResponse;
// Return the handler.
return this.methods[method];
}
/**
* Executes the route handler.
*/ async execute(request, context) {
// Get the handler function for the given method.
const handler = this.resolve(request.method);
// Get the context for the request.
const requestContext = {
req: request
};
requestContext.renderOpts = {
previewProps: context.prerenderManifest.preview
};
// Get the context for the static generation.
const staticGenerationContext = {
urlPathname: request.nextUrl.pathname,
renderOpts: // If the staticGenerationContext is not provided then we default to
// the default values.
context.staticGenerationContext ?? {
supportsDynamicHTML: false,
originalPathname: this.definition.pathname
}
};
// Add the fetchCache option to the renderOpts.
staticGenerationContext.renderOpts.fetchCache = this.userland.fetchCache;
// Run the handler with the request AsyncLocalStorage to inject the helper
// support. We set this to `unknown` because the type is not known until
// runtime when we do a instanceof check below.
const response = await this.actionAsyncStorage.run({
isAppRoute: true
}, ()=>RequestAsyncStorageWrapper.wrap(this.requestAsyncStorage, requestContext, ()=>StaticGenerationAsyncStorageWrapper.wrap(this.staticGenerationAsyncStorage, staticGenerationContext, (staticGenerationStore)=>{
var _getTracer_getRootSpanAttributes;
// Check to see if we should bail out of static generation based on
// having non-static methods.
if (this.nonStaticMethods) {
this.staticGenerationBailout(`non-static methods used ${this.nonStaticMethods.join(", ")}`);
}
// Update the static generation store based on the dynamic property.
switch(this.dynamic){
case "force-dynamic":
// The dynamic property is set to force-dynamic, so we should
// force the page to be dynamic.
staticGenerationStore.forceDynamic = true;
this.staticGenerationBailout(`force-dynamic`, {
dynamic: this.dynamic
});
break;
case "force-static":
// The dynamic property is set to force-static, so we should
// force the page to be static.
staticGenerationStore.forceStatic = true;
break;
case "error":
// The dynamic property is set to error, so we should throw an
// error if the page is being statically generated.
staticGenerationStore.dynamicShouldError = true;
break;
default:
break;
}
// If the static generation store does not have a revalidate value
// set, then we should set it the revalidate value from the userland
// module or default to false.
staticGenerationStore.revalidate ??= this.userland.revalidate ?? false;
// Wrap the request so we can add additional functionality to cases
// that might change it's output or affect the rendering.
const wrappedRequest = proxyRequest(request, {
dynamic: this.dynamic
}, {
headerHooks: this.headerHooks,
serverHooks: this.serverHooks,
staticGenerationBailout: this.staticGenerationBailout
});
// TODO: propagate this pathname from route matcher
const route = getPathnameFromAbsolutePath(this.resolvedPagePath);
(_getTracer_getRootSpanAttributes = getTracer().getRootSpanAttributes()) == null ? void 0 : _getTracer_getRootSpanAttributes.set("next.route", route);
return getTracer().trace(AppRouteRouteHandlersSpan.runHandler, {
spanName: `executing api route (app) ${route}`,
attributes: {
"next.route": route
}
}, async ()=>{
var _staticGenerationStore_tags;
// Patch the global fetch.
patchFetch({
serverHooks: this.serverHooks,
staticGenerationAsyncStorage: this.staticGenerationAsyncStorage
});
const res = await handler(wrappedRequest, {
params: context.params ? parsedUrlQueryToParams(context.params) : undefined
});
if (!(res instanceof Response)) {
throw new Error(`No response is returned from route handler '${this.resolvedPagePath}'. Ensure you return a \`Response\` or a \`NextResponse\` in all branches of your handler.`);
}
context.staticGenerationContext.fetchMetrics = staticGenerationStore.fetchMetrics;
await Promise.all(staticGenerationStore.pendingRevalidates || []);
addImplicitTags(staticGenerationStore);
context.staticGenerationContext.fetchTags = (_staticGenerationStore_tags = staticGenerationStore.tags) == null ? void 0 : _staticGenerationStore_tags.join(",");
// It's possible cookies were set in the handler, so we need
// to merge the modified cookies and the returned response
// here.
const requestStore = this.requestAsyncStorage.getStore();
if (requestStore && requestStore.mutableCookies) {
const headers = new Headers(res.headers);
if (appendMutableCookies(headers, requestStore.mutableCookies)) {
return new Response(res.body, {
status: res.status,
statusText: res.statusText,
headers
});
}
}
return res;
});
})));
// If the handler did't return a valid response, then return the internal
// error response.
if (!(response instanceof Response)) {
// TODO: validate the correct handling behavior, maybe log something?
return handleInternalServerErrorResponse();
}
if (response.headers.has("x-middleware-rewrite")) {
// TODO: move this error into the `NextResponse.rewrite()` function.
// TODO-APP: re-enable support below when we can proxy these type of requests
throw new Error("NextResponse.rewrite() was used in a app route handler, this is not currently supported. Please remove the invocation to continue.");
// // This is a rewrite created via `NextResponse.rewrite()`. We need to send
// // the response up so it can be handled by the backing server.
// // If the server is running in minimal mode, we just want to forward the
// // response (including the rewrite headers) upstream so it can perform the
// // redirect for us, otherwise return with the special condition so this
// // server can perform a rewrite.
// if (!minimalMode) {
// return { response, condition: 'rewrite' }
// }
// // Relativize the url so it's relative to the base url. This is so the
// // outgoing headers upstream can be relative.
// const rewritePath = response.headers.get('x-middleware-rewrite')!
// const initUrl = getRequestMeta(req, '__NEXT_INIT_URL')!
// const { pathname } = parseUrl(relativizeURL(rewritePath, initUrl))
// response.headers.set('x-middleware-rewrite', pathname)
}
if (response.headers.get("x-middleware-next") === "1") {
// TODO: move this error into the `NextResponse.next()` function.
throw new Error("NextResponse.next() was used in a app route handler, this is not supported. See here for more info: https://nextjs.org/docs/messages/next-response-next-in-app-route-handler");
}
return response;
}
async handle(request, context) {
try {
// Execute the route to get the response.
const response = await this.execute(request, context);
// The response was handled, return it.
return response;
} catch (err) {
// Try to resolve the error to a response, else throw it again.
const response = resolveHandlerError(err);
if (!response) throw err;
// The response was resolved, return it.
return response;
}
}
}
export default AppRouteRouteModule;
//# sourceMappingURL=module.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
// the name of the export has to be the camelCase version of the file name (without the extension)
// TODO: remove this. We need it because using notFound from next/navigation imports this file :(
export * as appRouterContext from "../../../../shared/lib/app-router-context.shared-runtime";
//# sourceMappingURL=shared-modules.js.map

View File

@@ -0,0 +1 @@
{"version":3,"sources":["../../../../../src/server/future/route-modules/app-route/shared-modules.ts"],"names":["appRouterContext"],"mappings":"AAAA,kGAAkG;AAClG,iGAAiG;AACjG,OAAO,KAAKA,gBAAgB,MAAM,2DAA0D"}