.
This commit is contained in:
+43
@@ -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
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+31
@@ -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;
|
||||
}
|
||||
+142
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+6
@@ -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
@@ -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
|
||||
+1
@@ -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]}
|
||||
+17
@@ -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;
|
||||
+211
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user