.
This commit is contained in:
+172
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+121
@@ -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
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
+20
@@ -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
|
||||
+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":"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]}
|
||||
+168
@@ -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
|
||||
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
+3
@@ -0,0 +1,3 @@
|
||||
export { RequestCookies, ResponseCookies, stringifyCookie } from 'next/dist/compiled/@edge-runtime/cookies';
|
||||
|
||||
//# sourceMappingURL=cookies.js.map
|
||||
+1
@@ -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]}
|
||||
+76
@@ -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
|
||||
+1
@@ -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]}
|
||||
+12
@@ -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
|
||||
+1
@@ -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]}
|
||||
+81
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+130
@@ -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
|
||||
+1
File diff suppressed because one or more lines are too long
+201
@@ -0,0 +1,201 @@
|
||||
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';
|
||||
import { removeTrailingSlash } from '../../../shared/lib/router/utils/remove-trailing-slash';
|
||||
/**
|
||||
* 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}${removeTrailingSlash(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 'generate-static-params':
|
||||
throw Object.defineProperty(new Error(`Route ${store.route} used "${expression}" inside \`generateStaticParams\` 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: "E1127",
|
||||
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':
|
||||
case 'validation-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
|
||||
+1
File diff suppressed because one or more lines are too long
+283
@@ -0,0 +1,283 @@
|
||||
import { CACHE_ONE_YEAR_SECONDS } 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_SECONDS : 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),
|
||||
rootParams: undefined
|
||||
};
|
||||
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 'validation-client':
|
||||
case 'request':
|
||||
case 'generate-static-params':
|
||||
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 'validation-client':
|
||||
case 'prerender-runtime':
|
||||
case 'prerender-ppr':
|
||||
case 'prerender-legacy':
|
||||
case 'cache':
|
||||
case 'private-cache':
|
||||
case 'unstable-cache':
|
||||
case 'generate-static-params':
|
||||
return workStore.route;
|
||||
default:
|
||||
return workUnitStore;
|
||||
}
|
||||
}
|
||||
|
||||
//# sourceMappingURL=unstable-cache.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+55
@@ -0,0 +1,55 @@
|
||||
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 'validation-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':
|
||||
case 'generate-static-params':
|
||||
break;
|
||||
default:
|
||||
workUnitStore;
|
||||
}
|
||||
}
|
||||
markCurrentScopeAsDynamic(store, workUnitStore, callingExpression);
|
||||
}
|
||||
}
|
||||
|
||||
//# sourceMappingURL=unstable-no-store.js.map
|
||||
+1
@@ -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 'validation-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 case 'generate-static-params':\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;gBACL,KAAK;oBACH,+CAA+C;oBAC/C;gBACF,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;gBACL,KAAK;oBACH;gBACF;oBACEH;YACJ;QACF;QACAL,0BAA0BG,OAAOE,eAAeH;IAClD;AACF","ignoreList":[0]}
|
||||
+5
@@ -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
|
||||
+1
@@ -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]}
|
||||
+15
@@ -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|GPTBot/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
|
||||
+1
@@ -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|GPTBot/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,iXAAiXC,IAAI,CAC1XD;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]}
|
||||
Reference in New Issue
Block a user