This commit is contained in:
Kismet Hasanaj
2026-05-02 20:07:02 +02:00
parent ce8672e283
commit 34dc9aec52
9428 changed files with 1733330 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
import type { IncomingHttpHeaders } from 'http';
export type ReadonlyHeaders = Headers & {
/** @deprecated Method unavailable on `ReadonlyHeaders`. Read more: https://nextjs.org/docs/app/api-reference/functions/headers */
append(...args: any[]): void;
/** @deprecated Method unavailable on `ReadonlyHeaders`. Read more: https://nextjs.org/docs/app/api-reference/functions/headers */
set(...args: any[]): void;
/** @deprecated Method unavailable on `ReadonlyHeaders`. Read more: https://nextjs.org/docs/app/api-reference/functions/headers */
delete(...args: any[]): void;
};
export declare class HeadersAdapter extends Headers {
private readonly headers;
constructor(headers: IncomingHttpHeaders);
/**
* Seals a Headers instance to prevent modification by throwing an error when
* any mutating method is called.
*/
static seal(headers: Headers): ReadonlyHeaders;
/**
* 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)
*/
private merge;
/**
* 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: IncomingHttpHeaders | Headers): Headers;
append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
has(name: string): boolean;
set(name: string, value: string): void;
forEach(callbackfn: (value: string, name: string, parent: Headers) => void, thisArg?: any): void;
entries(): HeadersIterator<[string, string]>;
keys(): HeadersIterator<string>;
values(): HeadersIterator<string>;
[Symbol.iterator](): HeadersIterator<[string, string]>;
}
+192
View File
@@ -0,0 +1,192 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
HeadersAdapter: null,
ReadonlyHeadersError: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
HeadersAdapter: function() {
return HeadersAdapter;
},
ReadonlyHeadersError: function() {
return ReadonlyHeadersError;
}
});
const _reflect = require("./reflect");
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();
}
}
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 _reflect.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 _reflect.ReflectAdapter.get(target, original, receiver);
},
set (target, prop, value, receiver) {
if (typeof prop === 'symbol') {
return _reflect.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 _reflect.ReflectAdapter.set(target, original ?? prop, value, receiver);
},
has (target, prop) {
if (typeof prop === 'symbol') return _reflect.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 _reflect.ReflectAdapter.has(target, original);
},
deleteProperty (target, prop) {
if (typeof prop === 'symbol') return _reflect.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 _reflect.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 _reflect.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
@@ -0,0 +1,31 @@
import type { BaseNextRequest } from '../../../base-http';
import type { NodeNextRequest } from '../../../base-http/node';
import type { WebNextRequest } from '../../../base-http/web';
import type { Writable } from 'node:stream';
import { NextRequest } from '../request';
export declare const ResponseAbortedName = "ResponseAborted";
export declare class ResponseAborted extends Error {
readonly name = "ResponseAborted";
}
/**
* 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 declare function createAbortController(response: Writable): AbortController;
/**
* 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 declare function signalFromNodeResponse(response: Writable): AbortSignal;
export declare class NextRequestAdapter {
static fromBaseNextRequest(request: BaseNextRequest, signal: AbortSignal): NextRequest;
static fromNodeNextRequest(request: NodeNextRequest, signal: AbortSignal): NextRequest;
static fromWebNextRequest(request: WebNextRequest): NextRequest;
}
@@ -0,0 +1,142 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
NextRequestAdapter: null,
ResponseAborted: null,
ResponseAbortedName: null,
createAbortController: null,
signalFromNodeResponse: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
NextRequestAdapter: function() {
return NextRequestAdapter;
},
ResponseAborted: function() {
return ResponseAborted;
},
ResponseAbortedName: function() {
return ResponseAbortedName;
},
createAbortController: function() {
return createAbortController;
},
signalFromNodeResponse: function() {
return signalFromNodeResponse;
}
});
const _requestmeta = require("../../../request-meta");
const _utils = require("../../utils");
const _request = require("../request");
const _helpers = require("../../../base-http/helpers");
const ResponseAbortedName = 'ResponseAborted';
class ResponseAborted extends Error {
constructor(...args){
super(...args), this.name = ResponseAbortedName;
}
}
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;
}
function signalFromNodeResponse(response) {
const { errored, destroyed } = response;
if (errored || destroyed) {
return AbortSignal.abort(errored ?? new ResponseAborted());
}
const { signal } = createAbortController(response);
return signal;
}
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' && (0, _helpers.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' && (0, _helpers.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 = (0, _requestmeta.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 _request.NextRequest(url, {
method: request.method,
headers: (0, _utils.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 _request.NextRequest(request.url, {
method: request.method,
headers: (0, _utils.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
@@ -0,0 +1,6 @@
export declare class ReflectAdapter {
static get<T extends object>(target: T, prop: string | symbol, receiver: unknown): any;
static set<T extends object>(target: T, prop: string | symbol, value: any, receiver: any): boolean;
static has<T extends object>(target: T, prop: string | symbol): boolean;
static deleteProperty<T extends object>(target: T, prop: string | symbol): boolean;
}
+30
View File
@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "ReflectAdapter", {
enumerable: true,
get: function() {
return ReflectAdapter;
}
});
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
@@ -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":";;;;+BAAaA;;;eAAAA;;;AAAN,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]}
@@ -0,0 +1,17 @@
import { RequestCookies } from '../cookies';
import { ResponseCookies } from '../cookies';
import type { RequestStore } from '../../../app-render/work-unit-async-storage.external';
export type { ResponseCookies };
export type ReadonlyRequestCookies = Omit<RequestCookies, 'set' | 'clear' | 'delete'> & Pick<ResponseCookies, 'set' | 'delete'>;
export declare class RequestCookiesAdapter {
static seal(cookies: RequestCookies): ReadonlyRequestCookies;
}
export declare function getModifiedCookieValues(cookies: ResponseCookies): ResponseCookie[];
export declare function appendMutableCookies(headers: Headers, mutableCookies: ResponseCookies): boolean;
type ResponseCookie = NonNullable<ReturnType<InstanceType<typeof ResponseCookies>['get']>>;
export declare class MutableRequestCookiesAdapter {
static wrap(cookies: RequestCookies, onUpdateCookies?: (cookies: string[]) => void): ResponseCookies;
}
export declare function createCookiesWithMutableAccessCheck(requestStore: RequestStore): ResponseCookies;
export declare function areCookiesMutableInCurrentPhase(requestStore: RequestStore): boolean;
export declare function responseCookiesToRequestCookies(responseCookies: ResponseCookies): RequestCookies;
@@ -0,0 +1,211 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
MutableRequestCookiesAdapter: null,
ReadonlyRequestCookiesError: null,
RequestCookiesAdapter: null,
appendMutableCookies: null,
areCookiesMutableInCurrentPhase: null,
createCookiesWithMutableAccessCheck: null,
getModifiedCookieValues: null,
responseCookiesToRequestCookies: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
MutableRequestCookiesAdapter: function() {
return MutableRequestCookiesAdapter;
},
ReadonlyRequestCookiesError: function() {
return ReadonlyRequestCookiesError;
},
RequestCookiesAdapter: function() {
return RequestCookiesAdapter;
},
appendMutableCookies: function() {
return appendMutableCookies;
},
areCookiesMutableInCurrentPhase: function() {
return areCookiesMutableInCurrentPhase;
},
createCookiesWithMutableAccessCheck: function() {
return createCookiesWithMutableAccessCheck;
},
getModifiedCookieValues: function() {
return getModifiedCookieValues;
},
responseCookiesToRequestCookies: function() {
return responseCookiesToRequestCookies;
}
});
const _cookies = require("../cookies");
const _reflect = require("./reflect");
const _workasyncstorageexternal = require("../../../app-render/work-async-storage.external");
const _actionrevalidationkind = require("../../../../shared/lib/action-revalidation-kind");
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();
}
}
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 _reflect.ReflectAdapter.get(target, prop, receiver);
}
}
});
}
}
const SYMBOL_MODIFY_COOKIE_VALUES = Symbol.for('next.mutated.cookies');
function getModifiedCookieValues(cookies) {
const modified = cookies[SYMBOL_MODIFY_COOKIE_VALUES];
if (!modified || !Array.isArray(modified) || modified.length === 0) {
return [];
}
return modified;
}
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 _cookies.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;
}
class MutableRequestCookiesAdapter {
static wrap(cookies, onUpdateCookies) {
const responseCookies = new _cookies.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 = _workasyncstorageexternal.workAsyncStorage.getStore();
if (workStore) {
workStore.pathWasRevalidated = _actionrevalidationkind.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 _cookies.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 _reflect.ReflectAdapter.get(target, prop, receiver);
}
}
});
return wrappedCookies;
}
}
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 _reflect.ReflectAdapter.get(target, prop, receiver);
}
}
});
return wrappedCookies;
}
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();
}
}
function responseCookiesToRequestCookies(responseCookies) {
const requestCookies = new _cookies.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