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

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

View File

@@ -0,0 +1,51 @@
import type { ResponseCacheEntry, ResponseGenerator, ResponseCacheBase, IncrementalResponseCacheEntry, IncrementalResponseCache } from './types';
import type { RouteKind } from '../route-kind';
export * from './types';
export default class ResponseCache implements ResponseCacheBase {
private readonly getBatcher;
private readonly revalidateBatcher;
private previousCacheItem?;
private minimal_mode?;
constructor(minimal_mode: boolean);
/**
* Gets the response cache entry for the given key.
*
* @param key - The key to get the response cache entry for.
* @param responseGenerator - The response generator to use to generate the response cache entry.
* @param context - The context for the get request.
* @returns The response cache entry.
*/
get(key: string | null, responseGenerator: ResponseGenerator, context: {
routeKind: RouteKind;
isOnDemandRevalidate?: boolean;
isPrefetch?: boolean;
incrementalCache: IncrementalResponseCache;
isRoutePPREnabled?: boolean;
isFallback?: boolean;
waitUntil?: (prom: Promise<any>) => void;
}): Promise<ResponseCacheEntry | null>;
/**
* Handles the get request for the response cache.
*
* @param key - The key to get the response cache entry for.
* @param responseGenerator - The response generator to use to generate the response cache entry.
* @param context - The context for the get request.
* @param resolve - The resolve function to use to resolve the response cache entry.
* @returns The response cache entry.
*/
private handleGet;
/**
* Revalidates the cache entry for the given key.
*
* @param key - The key to revalidate the cache entry for.
* @param incrementalCache - The incremental cache to use to revalidate the cache entry.
* @param isRoutePPREnabled - Whether the route is PPR enabled.
* @param isFallback - Whether the route is a fallback.
* @param responseGenerator - The response generator to use to generate the response cache entry.
* @param previousIncrementalCacheEntry - The previous cache entry to use to revalidate the cache entry.
* @param hasResolved - Whether the response has been resolved.
* @returns The revalidated cache entry.
*/
revalidate(key: string, incrementalCache: IncrementalResponseCache, isRoutePPREnabled: boolean, isFallback: boolean, responseGenerator: ResponseGenerator, previousIncrementalCacheEntry: IncrementalResponseCacheEntry | null, hasResolved: boolean, waitUntil?: (prom: Promise<any>) => void): Promise<IncrementalResponseCacheEntry | null>;
private handleRevalidate;
}

View File

@@ -0,0 +1,211 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return ResponseCache;
}
});
0 && __export(require("./types"));
const _batcher = require("../../lib/batcher");
const _scheduler = require("../../lib/scheduler");
const _utils = require("./utils");
_export_star(require("./types"), exports);
function _export_star(from, to) {
Object.keys(from).forEach(function(k) {
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
Object.defineProperty(to, k, {
enumerable: true,
get: function() {
return from[k];
}
});
}
});
return from;
}
class ResponseCache {
constructor(minimal_mode){
this.getBatcher = _batcher.Batcher.create({
// Ensure on-demand revalidate doesn't block normal requests, it should be
// safe to run an on-demand revalidate for the same key as a normal request.
cacheKeyFn: ({ key, isOnDemandRevalidate })=>`${key}-${isOnDemandRevalidate ? '1' : '0'}`,
// We wait to do any async work until after we've added our promise to
// `pendingResponses` to ensure that any any other calls will reuse the
// same promise until we've fully finished our work.
schedulerFn: _scheduler.scheduleOnNextTick
});
this.revalidateBatcher = _batcher.Batcher.create({
// We wait to do any async work until after we've added our promise to
// `pendingResponses` to ensure that any any other calls will reuse the
// same promise until we've fully finished our work.
schedulerFn: _scheduler.scheduleOnNextTick
});
this.minimal_mode = minimal_mode;
}
/**
* Gets the response cache entry for the given key.
*
* @param key - The key to get the response cache entry for.
* @param responseGenerator - The response generator to use to generate the response cache entry.
* @param context - The context for the get request.
* @returns The response cache entry.
*/ async get(key, responseGenerator, context) {
var _this_previousCacheItem;
// If there is no key for the cache, we can't possibly look this up in the
// cache so just return the result of the response generator.
if (!key) {
return responseGenerator({
hasResolved: false,
previousCacheEntry: null
});
}
// Check minimal mode cache before doing any other work
if (this.minimal_mode && ((_this_previousCacheItem = this.previousCacheItem) == null ? void 0 : _this_previousCacheItem.key) === key && this.previousCacheItem.expiresAt > Date.now()) {
return (0, _utils.toResponseCacheEntry)(this.previousCacheItem.entry);
}
const { incrementalCache, isOnDemandRevalidate = false, isFallback = false, isRoutePPREnabled = false, isPrefetch = false, waitUntil, routeKind } = context;
const response = await this.getBatcher.batch({
key,
isOnDemandRevalidate
}, ({ resolve })=>{
const promise = this.handleGet(key, responseGenerator, {
incrementalCache,
isOnDemandRevalidate,
isFallback,
isRoutePPREnabled,
isPrefetch,
routeKind
}, resolve);
// We need to ensure background revalidates are passed to waitUntil.
if (waitUntil) waitUntil(promise);
return promise;
});
return (0, _utils.toResponseCacheEntry)(response);
}
/**
* Handles the get request for the response cache.
*
* @param key - The key to get the response cache entry for.
* @param responseGenerator - The response generator to use to generate the response cache entry.
* @param context - The context for the get request.
* @param resolve - The resolve function to use to resolve the response cache entry.
* @returns The response cache entry.
*/ async handleGet(key, responseGenerator, context, resolve) {
let previousIncrementalCacheEntry = null;
let resolved = false;
try {
// Get the previous cache entry if not in minimal mode
previousIncrementalCacheEntry = !this.minimal_mode ? await context.incrementalCache.get(key, {
kind: (0, _utils.routeKindToIncrementalCacheKind)(context.routeKind),
isRoutePPREnabled: context.isRoutePPREnabled,
isFallback: context.isFallback
}) : null;
if (previousIncrementalCacheEntry && !context.isOnDemandRevalidate) {
resolve(previousIncrementalCacheEntry);
resolved = true;
if (!previousIncrementalCacheEntry.isStale || context.isPrefetch) {
// The cached value is still valid, so we don't need to update it yet.
return previousIncrementalCacheEntry;
}
}
// Revalidate the cache entry
const incrementalResponseCacheEntry = await this.revalidate(key, context.incrementalCache, context.isRoutePPREnabled, context.isFallback, responseGenerator, previousIncrementalCacheEntry, previousIncrementalCacheEntry !== null && !context.isOnDemandRevalidate);
// Handle null response
if (!incrementalResponseCacheEntry) {
// Unset the previous cache item if it was set so we don't use it again.
if (this.minimal_mode) this.previousCacheItem = undefined;
return null;
}
// Resolve for on-demand revalidation or if not already resolved
if (context.isOnDemandRevalidate && !resolved) {
return incrementalResponseCacheEntry;
}
return incrementalResponseCacheEntry;
} catch (err) {
// If we've already resolved the cache entry, we can't reject as we
// already resolved the cache entry so log the error here.
if (resolved) {
console.error(err);
return null;
}
throw err;
}
}
/**
* Revalidates the cache entry for the given key.
*
* @param key - The key to revalidate the cache entry for.
* @param incrementalCache - The incremental cache to use to revalidate the cache entry.
* @param isRoutePPREnabled - Whether the route is PPR enabled.
* @param isFallback - Whether the route is a fallback.
* @param responseGenerator - The response generator to use to generate the response cache entry.
* @param previousIncrementalCacheEntry - The previous cache entry to use to revalidate the cache entry.
* @param hasResolved - Whether the response has been resolved.
* @returns The revalidated cache entry.
*/ async revalidate(key, incrementalCache, isRoutePPREnabled, isFallback, responseGenerator, previousIncrementalCacheEntry, hasResolved, waitUntil) {
return this.revalidateBatcher.batch(key, ()=>{
const promise = this.handleRevalidate(key, incrementalCache, isRoutePPREnabled, isFallback, responseGenerator, previousIncrementalCacheEntry, hasResolved);
// We need to ensure background revalidates are passed to waitUntil.
if (waitUntil) waitUntil(promise);
return promise;
});
}
async handleRevalidate(key, incrementalCache, isRoutePPREnabled, isFallback, responseGenerator, previousIncrementalCacheEntry, hasResolved) {
try {
// Generate the response cache entry using the response generator.
const responseCacheEntry = await responseGenerator({
hasResolved,
previousCacheEntry: previousIncrementalCacheEntry,
isRevalidating: true
});
if (!responseCacheEntry) {
return null;
}
// Convert the response cache entry to an incremental response cache entry.
const incrementalResponseCacheEntry = await (0, _utils.fromResponseCacheEntry)({
...responseCacheEntry,
isMiss: !previousIncrementalCacheEntry
});
// We want to persist the result only if it has a cache control value
// defined.
if (incrementalResponseCacheEntry.cacheControl) {
if (this.minimal_mode) {
this.previousCacheItem = {
key,
entry: incrementalResponseCacheEntry,
expiresAt: Date.now() + 1000
};
} else {
await incrementalCache.set(key, incrementalResponseCacheEntry.value, {
cacheControl: incrementalResponseCacheEntry.cacheControl,
isRoutePPREnabled,
isFallback
});
}
}
return incrementalResponseCacheEntry;
} catch (err) {
// When a path is erroring we automatically re-set the existing cache
// with new revalidate and expire times to prevent non-stop retrying.
if (previousIncrementalCacheEntry == null ? void 0 : previousIncrementalCacheEntry.cacheControl) {
const revalidate = Math.min(Math.max(previousIncrementalCacheEntry.cacheControl.revalidate || 3, 3), 30);
const expire = previousIncrementalCacheEntry.cacheControl.expire === undefined ? undefined : Math.max(revalidate + 3, previousIncrementalCacheEntry.cacheControl.expire);
await incrementalCache.set(key, previousIncrementalCacheEntry.value, {
cacheControl: {
revalidate: revalidate,
expire: expire
},
isRoutePPREnabled,
isFallback
});
}
// We haven't resolved yet, so let's throw to indicate an error.
throw err;
}
}
}
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,205 @@
import type { OutgoingHttpHeaders } from 'http';
import type RenderResult from '../render-result';
import type { CacheControl, Revalidate } from '../lib/cache-control';
import type { RouteKind } from '../route-kind';
export interface ResponseCacheBase {
get(key: string | null, responseGenerator: ResponseGenerator, context: {
isOnDemandRevalidate?: boolean;
isPrefetch?: boolean;
incrementalCache: IncrementalCache;
/**
* This is a hint to the cache to help it determine what kind of route
* this is so it knows where to look up the cache entry from. If not
* provided it will test the filesystem to check.
*/
routeKind: RouteKind;
/**
* True if this is a fallback request.
*/
isFallback?: boolean;
/**
* True if the route is enabled for PPR.
*/
isRoutePPREnabled?: boolean;
}): Promise<ResponseCacheEntry | null>;
}
export interface ServerComponentsHmrCache {
get(key: string): CachedFetchData | undefined;
set(key: string, data: CachedFetchData): void;
}
export type CachedFetchData = {
headers: Record<string, string>;
body: string;
url: string;
status?: number;
};
export declare const enum CachedRouteKind {
APP_PAGE = "APP_PAGE",
APP_ROUTE = "APP_ROUTE",
PAGES = "PAGES",
FETCH = "FETCH",
REDIRECT = "REDIRECT",
IMAGE = "IMAGE"
}
export interface CachedFetchValue {
kind: CachedRouteKind.FETCH;
data: CachedFetchData;
tags?: string[];
revalidate: number;
}
export interface CachedRedirectValue {
kind: CachedRouteKind.REDIRECT;
props: Object;
}
export interface CachedAppPageValue {
kind: CachedRouteKind.APP_PAGE;
html: RenderResult;
rscData: Buffer | undefined;
status: number | undefined;
postponed: string | undefined;
headers: OutgoingHttpHeaders | undefined;
segmentData: Map<string, Buffer> | undefined;
}
export interface CachedPageValue {
kind: CachedRouteKind.PAGES;
html: RenderResult;
pageData: Object;
status: number | undefined;
headers: OutgoingHttpHeaders | undefined;
}
export interface CachedRouteValue {
kind: CachedRouteKind.APP_ROUTE;
body: Buffer;
status: number;
headers: OutgoingHttpHeaders;
}
export interface CachedImageValue {
kind: CachedRouteKind.IMAGE;
etag: string;
upstreamEtag: string;
buffer: Buffer;
extension: string;
isMiss?: boolean;
isStale?: boolean;
}
export interface IncrementalCachedAppPageValue {
kind: CachedRouteKind.APP_PAGE;
html: string;
rscData: Buffer | undefined;
headers: OutgoingHttpHeaders | undefined;
postponed: string | undefined;
status: number | undefined;
segmentData: Map<string, Buffer> | undefined;
}
export interface IncrementalCachedPageValue {
kind: CachedRouteKind.PAGES;
html: string;
pageData: Object;
headers: OutgoingHttpHeaders | undefined;
status: number | undefined;
}
export interface IncrementalResponseCacheEntry {
cacheControl?: CacheControl;
/**
* timestamp in milliseconds to revalidate after
*/
revalidateAfter?: Revalidate;
/**
* `-1` here dictates a blocking revalidate should be used
*/
isStale?: boolean | -1;
isMiss?: boolean;
value: Exclude<IncrementalCacheValue, CachedFetchValue> | null;
}
export interface IncrementalFetchCacheEntry {
/**
* `-1` here dictates a blocking revalidate should be used
*/
isStale?: boolean | -1;
value: CachedFetchValue;
}
export type IncrementalCacheEntry = IncrementalResponseCacheEntry | IncrementalFetchCacheEntry;
export type IncrementalCacheValue = CachedRedirectValue | IncrementalCachedPageValue | IncrementalCachedAppPageValue | CachedImageValue | CachedFetchValue | CachedRouteValue;
export type ResponseCacheValue = CachedRedirectValue | CachedPageValue | CachedAppPageValue | CachedImageValue | CachedRouteValue;
export type ResponseCacheEntry = {
cacheControl?: CacheControl;
value: ResponseCacheValue | null;
isStale?: boolean | -1;
isMiss?: boolean;
};
/**
* @param hasResolved whether the responseGenerator has resolved it's promise
* @param previousCacheEntry the previous cache entry if it exists or the current
*/
export type ResponseGenerator = (state: {
hasResolved: boolean;
previousCacheEntry?: IncrementalResponseCacheEntry | null;
isRevalidating?: boolean;
span?: any;
/**
* When true, this indicates that the response generator is being called in a
* context where the response must be generated statically.
*
* CRITICAL: This should only currently be used when revalidating due to a
* dynamic RSC request.
*/
forceStaticRender?: boolean;
}) => Promise<ResponseCacheEntry | null>;
export declare const enum IncrementalCacheKind {
APP_PAGE = "APP_PAGE",
APP_ROUTE = "APP_ROUTE",
PAGES = "PAGES",
FETCH = "FETCH",
IMAGE = "IMAGE"
}
export interface GetIncrementalFetchCacheContext {
kind: IncrementalCacheKind.FETCH;
revalidate?: Revalidate;
fetchUrl?: string;
fetchIdx?: number;
tags?: string[];
softTags?: string[];
}
export interface GetIncrementalResponseCacheContext {
kind: Exclude<IncrementalCacheKind, IncrementalCacheKind.FETCH>;
/**
* True if the route is enabled for PPR.
*/
isRoutePPREnabled?: boolean;
/**
* True if this is a fallback request.
*/
isFallback: boolean;
}
export interface SetIncrementalFetchCacheContext {
fetchCache: true;
fetchUrl?: string;
fetchIdx?: number;
tags?: string[];
isImplicitBuildTimeCache?: boolean;
}
export interface SetIncrementalResponseCacheContext {
fetchCache?: false;
cacheControl?: CacheControl;
/**
* True if the route is enabled for PPR.
*/
isRoutePPREnabled?: boolean;
/**
* True if this is a fallback request.
*/
isFallback?: boolean;
}
export interface IncrementalResponseCache {
get(cacheKey: string, ctx: GetIncrementalResponseCacheContext): Promise<IncrementalResponseCacheEntry | null>;
set(key: string, data: Exclude<IncrementalCacheValue, CachedFetchValue> | null, ctx: SetIncrementalResponseCacheContext): Promise<void>;
}
export interface IncrementalCache extends IncrementalResponseCache {
get(cacheKey: string, ctx: GetIncrementalFetchCacheContext): Promise<IncrementalFetchCacheEntry | null>;
get(cacheKey: string, ctx: GetIncrementalResponseCacheContext): Promise<IncrementalResponseCacheEntry | null>;
set(key: string, data: CachedFetchValue | null, ctx: SetIncrementalFetchCacheContext): Promise<void>;
set(key: string, data: Exclude<IncrementalCacheValue, CachedFetchValue> | null, ctx: SetIncrementalResponseCacheContext): Promise<void>;
revalidateTag(tags: string | string[], durations?: {
expire?: number;
}): Promise<void>;
}

View File

@@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
CachedRouteKind: null,
IncrementalCacheKind: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
CachedRouteKind: function() {
return CachedRouteKind;
},
IncrementalCacheKind: function() {
return IncrementalCacheKind;
}
});
var CachedRouteKind = /*#__PURE__*/ function(CachedRouteKind) {
CachedRouteKind["APP_PAGE"] = "APP_PAGE";
CachedRouteKind["APP_ROUTE"] = "APP_ROUTE";
CachedRouteKind["PAGES"] = "PAGES";
CachedRouteKind["FETCH"] = "FETCH";
CachedRouteKind["REDIRECT"] = "REDIRECT";
CachedRouteKind["IMAGE"] = "IMAGE";
return CachedRouteKind;
}({});
var IncrementalCacheKind = /*#__PURE__*/ function(IncrementalCacheKind) {
IncrementalCacheKind["APP_PAGE"] = "APP_PAGE";
IncrementalCacheKind["APP_ROUTE"] = "APP_ROUTE";
IncrementalCacheKind["PAGES"] = "PAGES";
IncrementalCacheKind["FETCH"] = "FETCH";
IncrementalCacheKind["IMAGE"] = "IMAGE";
return IncrementalCacheKind;
}({});
//# sourceMappingURL=types.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
import { IncrementalCacheKind, type IncrementalResponseCacheEntry, type ResponseCacheEntry } from './types';
import { RouteKind } from '../route-kind';
export declare function fromResponseCacheEntry(cacheEntry: ResponseCacheEntry): Promise<IncrementalResponseCacheEntry>;
export declare function toResponseCacheEntry(response: IncrementalResponseCacheEntry | null): Promise<ResponseCacheEntry | null>;
export declare function routeKindToIncrementalCacheKind(routeKind: RouteKind): Exclude<IncrementalCacheKind, IncrementalCacheKind.FETCH>;

View File

@@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
0 && (module.exports = {
fromResponseCacheEntry: null,
routeKindToIncrementalCacheKind: null,
toResponseCacheEntry: null
});
function _export(target, all) {
for(var name in all)Object.defineProperty(target, name, {
enumerable: true,
get: all[name]
});
}
_export(exports, {
fromResponseCacheEntry: function() {
return fromResponseCacheEntry;
},
routeKindToIncrementalCacheKind: function() {
return routeKindToIncrementalCacheKind;
},
toResponseCacheEntry: function() {
return toResponseCacheEntry;
}
});
const _types = require("./types");
const _renderresult = /*#__PURE__*/ _interop_require_default(require("../render-result"));
const _routekind = require("../route-kind");
const _constants = require("../../lib/constants");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
async function fromResponseCacheEntry(cacheEntry) {
var _cacheEntry_value, _cacheEntry_value1;
return {
...cacheEntry,
value: ((_cacheEntry_value = cacheEntry.value) == null ? void 0 : _cacheEntry_value.kind) === _types.CachedRouteKind.PAGES ? {
kind: _types.CachedRouteKind.PAGES,
html: await cacheEntry.value.html.toUnchunkedString(true),
pageData: cacheEntry.value.pageData,
headers: cacheEntry.value.headers,
status: cacheEntry.value.status
} : ((_cacheEntry_value1 = cacheEntry.value) == null ? void 0 : _cacheEntry_value1.kind) === _types.CachedRouteKind.APP_PAGE ? {
kind: _types.CachedRouteKind.APP_PAGE,
html: await cacheEntry.value.html.toUnchunkedString(true),
postponed: cacheEntry.value.postponed,
rscData: cacheEntry.value.rscData,
headers: cacheEntry.value.headers,
status: cacheEntry.value.status,
segmentData: cacheEntry.value.segmentData
} : cacheEntry.value
};
}
async function toResponseCacheEntry(response) {
var _response_value, _response_value1;
if (!response) return null;
return {
isMiss: response.isMiss,
isStale: response.isStale,
cacheControl: response.cacheControl,
value: ((_response_value = response.value) == null ? void 0 : _response_value.kind) === _types.CachedRouteKind.PAGES ? {
kind: _types.CachedRouteKind.PAGES,
html: _renderresult.default.fromStatic(response.value.html, _constants.HTML_CONTENT_TYPE_HEADER),
pageData: response.value.pageData,
headers: response.value.headers,
status: response.value.status
} : ((_response_value1 = response.value) == null ? void 0 : _response_value1.kind) === _types.CachedRouteKind.APP_PAGE ? {
kind: _types.CachedRouteKind.APP_PAGE,
html: _renderresult.default.fromStatic(response.value.html, _constants.HTML_CONTENT_TYPE_HEADER),
rscData: response.value.rscData,
headers: response.value.headers,
status: response.value.status,
postponed: response.value.postponed,
segmentData: response.value.segmentData
} : response.value
};
}
function routeKindToIncrementalCacheKind(routeKind) {
switch(routeKind){
case _routekind.RouteKind.PAGES:
return _types.IncrementalCacheKind.PAGES;
case _routekind.RouteKind.APP_PAGE:
return _types.IncrementalCacheKind.APP_PAGE;
case _routekind.RouteKind.IMAGE:
return _types.IncrementalCacheKind.IMAGE;
case _routekind.RouteKind.APP_ROUTE:
return _types.IncrementalCacheKind.APP_ROUTE;
case _routekind.RouteKind.PAGES_API:
// Pages Router API routes are not cached in the incremental cache.
throw Object.defineProperty(new Error(`Unexpected route kind ${routeKind}`), "__NEXT_ERROR_CODE", {
value: "E64",
enumerable: false,
configurable: true
});
default:
return routeKind;
}
}
//# sourceMappingURL=utils.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,20 @@
import type { ResponseCacheEntry, ResponseGenerator } from './types';
/**
* In the web server, there is currently no incremental cache provided and we
* always SSR the page.
*/
export default class WebResponseCache {
pendingResponses: Map<string, Promise<ResponseCacheEntry | null>>;
previousCacheItem?: {
key: string;
entry: ResponseCacheEntry | null;
expiresAt: number;
};
minimalMode?: boolean;
constructor(minimalMode: boolean);
get(key: string | null, responseGenerator: ResponseGenerator, context: {
isOnDemandRevalidate?: boolean;
isPrefetch?: boolean;
incrementalCache: any;
}): Promise<ResponseCacheEntry | null>;
}

View File

@@ -0,0 +1,101 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, /**
* In the web server, there is currently no incremental cache provided and we
* always SSR the page.
*/ "default", {
enumerable: true,
get: function() {
return WebResponseCache;
}
});
const _detachedpromise = require("../../lib/detached-promise");
class WebResponseCache {
constructor(minimalMode){
this.pendingResponses = new Map();
// this is a hack to avoid Webpack knowing this is equal to this.minimalMode
// because we replace this.minimalMode to true in production bundles.
Object.assign(this, {
minimalMode
});
}
get(key, responseGenerator, context) {
var _this_previousCacheItem;
// ensure on-demand revalidate doesn't block normal requests
const pendingResponseKey = key ? `${key}-${context.isOnDemandRevalidate ? '1' : '0'}` : null;
const pendingResponse = pendingResponseKey ? this.pendingResponses.get(pendingResponseKey) : null;
if (pendingResponse) {
return pendingResponse;
}
const { promise, resolve: resolver, reject: rejecter } = new _detachedpromise.DetachedPromise();
if (pendingResponseKey) {
this.pendingResponses.set(pendingResponseKey, promise);
}
let hasResolved = false;
const resolve = (cacheEntry)=>{
if (pendingResponseKey) {
// Ensure all reads from the cache get the latest value.
this.pendingResponses.set(pendingResponseKey, Promise.resolve(cacheEntry));
}
if (!hasResolved) {
hasResolved = true;
resolver(cacheEntry);
}
};
// we keep the previous cache entry around to leverage
// when the incremental cache is disabled in minimal mode
if (pendingResponseKey && this.minimalMode && ((_this_previousCacheItem = this.previousCacheItem) == null ? void 0 : _this_previousCacheItem.key) === pendingResponseKey && this.previousCacheItem.expiresAt > Date.now()) {
resolve(this.previousCacheItem.entry);
this.pendingResponses.delete(pendingResponseKey);
return promise;
}
// We wait to do any async work until after we've added our promise to
// `pendingResponses` to ensure that any any other calls will reuse the
// same promise until we've fully finished our work.
;
(async ()=>{
try {
const cacheEntry = await responseGenerator({
hasResolved
});
const resolveValue = cacheEntry === null ? null : {
...cacheEntry,
isMiss: true
};
// for on-demand revalidate wait to resolve until cache is set
if (!context.isOnDemandRevalidate) {
resolve(resolveValue);
}
if (key && cacheEntry && cacheEntry.cacheControl) {
this.previousCacheItem = {
key: pendingResponseKey || key,
entry: cacheEntry,
expiresAt: Date.now() + 1000
};
} else {
this.previousCacheItem = undefined;
}
if (context.isOnDemandRevalidate) {
resolve(resolveValue);
}
} catch (err) {
// while revalidating in the background we can't reject as
// we already resolved the cache entry so log the error here
if (hasResolved) {
console.error(err);
} else {
rejecter(err);
}
} finally{
if (pendingResponseKey) {
this.pendingResponses.delete(pendingResponseKey);
}
}
})();
return promise;
}
}
//# sourceMappingURL=web.js.map

File diff suppressed because one or more lines are too long