Initial commit

This commit is contained in:
makearmy 2025-09-22 10:37:53 -04:00
commit 78f8d225ee
21173 changed files with 2907774 additions and 0 deletions

View file

@ -0,0 +1,11 @@
/**
* This is the default "use cache" handler it defaults to an in-memory store.
* In-memory caches are fragile and should not use stale-while-revalidate
* semantics on the caches because it's not worth warming up an entry that's
* likely going to get evicted before we get to use it anyway. However, we also
* don't want to reuse a stale entry for too long so stale entries should be
* considered expired/missing in such cache handlers.
*/
import type { CacheHandlerV2 } from './types';
declare const DefaultCacheHandler: CacheHandlerV2;
export default DefaultCacheHandler;

View file

@ -0,0 +1,117 @@
/**
* This is the default "use cache" handler it defaults to an in-memory store.
* In-memory caches are fragile and should not use stale-while-revalidate
* semantics on the caches because it's not worth warming up an entry that's
* likely going to get evicted before we get to use it anyway. However, we also
* don't want to reuse a stale entry for too long so stale entries should be
* considered expired/missing in such cache handlers.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return _default;
}
});
const _lrucache = require("../lru-cache");
const _tagsmanifestexternal = require("../incremental-cache/tags-manifest.external");
// LRU cache default to max 50 MB but in future track
const memoryCache = new _lrucache.LRUCache(50 * 1024 * 1024, (entry)=>entry.size);
const pendingSets = new Map();
const debug = process.env.NEXT_PRIVATE_DEBUG_CACHE ? console.debug.bind(console, 'DefaultCacheHandler:') : undefined;
const DefaultCacheHandler = {
async get (cacheKey) {
const pendingPromise = pendingSets.get(cacheKey);
if (pendingPromise) {
debug == null ? void 0 : debug('get', cacheKey, 'pending');
await pendingPromise;
}
const privateEntry = memoryCache.get(cacheKey);
if (!privateEntry) {
debug == null ? void 0 : debug('get', cacheKey, 'not found');
return undefined;
}
const entry = privateEntry.entry;
if (performance.timeOrigin + performance.now() > entry.timestamp + entry.revalidate * 1000) {
// In-memory caches should expire after revalidate time because it is
// unlikely that a new entry will be able to be used before it is dropped
// from the cache.
debug == null ? void 0 : debug('get', cacheKey, 'expired');
return undefined;
}
if ((0, _tagsmanifestexternal.isStale)(entry.tags, entry.timestamp)) {
debug == null ? void 0 : debug('get', cacheKey, 'had stale tag');
return undefined;
}
const [returnStream, newSaved] = entry.value.tee();
entry.value = newSaved;
debug == null ? void 0 : debug('get', cacheKey, 'found', {
tags: entry.tags,
timestamp: entry.timestamp,
revalidate: entry.revalidate,
expire: entry.expire
});
return {
...entry,
value: returnStream
};
},
async set (cacheKey, pendingEntry) {
debug == null ? void 0 : debug('set', cacheKey, 'start');
let resolvePending = ()=>{};
const pendingPromise = new Promise((resolve)=>{
resolvePending = resolve;
});
pendingSets.set(cacheKey, pendingPromise);
const entry = await pendingEntry;
let size = 0;
try {
const [value, clonedValue] = entry.value.tee();
entry.value = value;
const reader = clonedValue.getReader();
for(let chunk; !(chunk = await reader.read()).done;){
size += Buffer.from(chunk.value).byteLength;
}
memoryCache.set(cacheKey, {
entry,
isErrored: false,
errorRetryCount: 0,
size
});
debug == null ? void 0 : debug('set', cacheKey, 'done');
} catch (err) {
// TODO: store partial buffer with error after we retry 3 times
debug == null ? void 0 : debug('set', cacheKey, 'failed', err);
} finally{
resolvePending();
pendingSets.delete(cacheKey);
}
},
async refreshTags () {
// Nothing to do for an in-memory cache handler.
},
async getExpiration (...tags) {
const expiration = Math.max(...tags.map((tag)=>_tagsmanifestexternal.tagsManifest.get(tag) ?? 0));
debug == null ? void 0 : debug('getExpiration', {
tags,
expiration
});
return expiration;
},
async expireTags (...tags) {
const timestamp = Math.round(performance.timeOrigin + performance.now());
debug == null ? void 0 : debug('expireTags', {
tags,
timestamp
});
for (const tag of tags){
// TODO: update file-system-cache?
_tagsmanifestexternal.tagsManifest.set(tag, timestamp);
}
}
};
const _default = DefaultCacheHandler;
//# sourceMappingURL=default.js.map

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,109 @@
/**
* A timestamp in milliseconds elapsed since the epoch
*/
export type Timestamp = number;
export interface CacheEntry {
/**
* The ReadableStream can error and only have partial data so any cache
* handlers need to handle this case and decide to keep the partial cache
* around or not.
*/
value: ReadableStream<Uint8Array>;
/**
* The tags configured for the entry excluding soft tags
*/
tags: string[];
/**
* This is for the client, not used to calculate cache entry expiration
* [duration in seconds]
*/
stale: number;
/**
* When the cache entry was created [timestamp in milliseconds]
*/
timestamp: Timestamp;
/**
* How long the entry is allowed to be used (should be longer than revalidate)
* [duration in seconds]
*/
expire: number;
/**
* How long until the entry should be revalidated [duration in seconds]
*/
revalidate: number;
}
/**
* @deprecated Use {@link CacheHandlerV2} instead.
*/
export interface CacheHandler {
/**
* Retrieve a cache entry for the given cache key, if available. The softTags
* should be used to check for staleness.
*/
get(cacheKey: string, softTags: string[]): Promise<undefined | CacheEntry>;
/**
* Store a cache entry for the given cache key. When this is called, the entry
* may still be pending, i.e. its value stream may still be written to. So it
* needs to be awaited first. If a `get` for the same cache key is called
* before the pending entry is complete, the cache handler must wait for the
* `set` operation to finish, before returning the entry, instead of returning
* undefined.
*/
set(cacheKey: string, entry: Promise<CacheEntry>): Promise<void>;
/**
* Next.js will call this method when `revalidateTag` or `revalidatePath()` is
* called. It should update the tags manifest accordingly.
*/
expireTags(...tags: string[]): Promise<void>;
/**
* The `receiveExpiredTags` method is called when an action request sends the
* 'x-next-revalidated-tags' header to indicate which tags have been expired
* by the action. The local tags manifest should be updated accordingly. As
* opposed to `expireTags`, the tags don't need to be propagated to a tags
* service, as this was already done by the server action.
*/
receiveExpiredTags(...tags: string[]): Promise<void>;
}
export interface CacheHandlerV2 {
/**
* Retrieve a cache entry for the given cache key, if available.
*/
get(cacheKey: string): Promise<undefined | CacheEntry>;
/**
* Store a cache entry for the given cache key. When this is called, the entry
* may still be pending, i.e. its value stream may still be written to. So it
* needs to be awaited first. If a `get` for the same cache key is called
* before the pending entry is complete, the cache handler must wait for the
* `set` operation to finish, before returning the entry, instead of returning
* undefined.
*/
set(cacheKey: string, pendingEntry: Promise<CacheEntry>): Promise<void>;
/**
* Next.js will call this method periodically, but always before starting a
* new request. When working with a remote tags service, this method should
* communicate with the tags service to refresh the local tags manifest
* accordingly.
*/
refreshTags(): Promise<void>;
/**
* Next.js will call this method for each set of soft tags that are relevant
* at the start of a request. The result is the maximum timestamp of a
* revalidate event for the tags. Returns `0` if none of the tags were ever
* revalidated.
*/
getExpiration(...tags: string[]): Promise<Timestamp>;
/**
* Next.js will call this method when `revalidateTag` or `revalidatePath()` is
* called. It should update the tags manifest accordingly.
*/
expireTags(...tags: string[]): Promise<void>;
}
/**
* This is a compatibility type to ease migration between cache handler
* versions. Until the old `CacheHandler` type is removed, this type should be
* used for all internal Next.js functions that deal with cache handlers to
* ensure that we are compatible with both cache handler versions. An exception
* is the built-in default cache handler, which implements the
* {@link CacheHandlerV2} interface.
*/
export type CacheHandlerCompat = CacheHandler | CacheHandlerV2;

View file

@ -0,0 +1,8 @@
/**
* A timestamp in milliseconds elapsed since the epoch
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
//# sourceMappingURL=types.js.map

View file

@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/server/lib/cache-handlers/types.ts"],"sourcesContent":["/**\n * A timestamp in milliseconds elapsed since the epoch\n */\nexport type Timestamp = number\n\nexport interface CacheEntry {\n /**\n * The ReadableStream can error and only have partial data so any cache\n * handlers need to handle this case and decide to keep the partial cache\n * around or not.\n */\n value: ReadableStream<Uint8Array>\n\n /**\n * The tags configured for the entry excluding soft tags\n */\n tags: string[]\n\n /**\n * This is for the client, not used to calculate cache entry expiration\n * [duration in seconds]\n */\n stale: number\n\n /**\n * When the cache entry was created [timestamp in milliseconds]\n */\n timestamp: Timestamp\n\n /**\n * How long the entry is allowed to be used (should be longer than revalidate)\n * [duration in seconds]\n */\n expire: number\n\n /**\n * How long until the entry should be revalidated [duration in seconds]\n */\n revalidate: number\n}\n\n/**\n * @deprecated Use {@link CacheHandlerV2} instead.\n */\nexport interface CacheHandler {\n /**\n * Retrieve a cache entry for the given cache key, if available. The softTags\n * should be used to check for staleness.\n */\n get(cacheKey: string, softTags: string[]): Promise<undefined | CacheEntry>\n\n /**\n * Store a cache entry for the given cache key. When this is called, the entry\n * may still be pending, i.e. its value stream may still be written to. So it\n * needs to be awaited first. If a `get` for the same cache key is called\n * before the pending entry is complete, the cache handler must wait for the\n * `set` operation to finish, before returning the entry, instead of returning\n * undefined.\n */\n set(cacheKey: string, entry: Promise<CacheEntry>): Promise<void>\n\n /**\n * Next.js will call this method when `revalidateTag` or `revalidatePath()` is\n * called. It should update the tags manifest accordingly.\n */\n expireTags(...tags: string[]): Promise<void>\n\n /**\n * The `receiveExpiredTags` method is called when an action request sends the\n * 'x-next-revalidated-tags' header to indicate which tags have been expired\n * by the action. The local tags manifest should be updated accordingly. As\n * opposed to `expireTags`, the tags don't need to be propagated to a tags\n * service, as this was already done by the server action.\n */\n receiveExpiredTags(...tags: string[]): Promise<void>\n}\n\nexport interface CacheHandlerV2 {\n /**\n * Retrieve a cache entry for the given cache key, if available.\n */\n get(cacheKey: string): Promise<undefined | CacheEntry>\n\n /**\n * Store a cache entry for the given cache key. When this is called, the entry\n * may still be pending, i.e. its value stream may still be written to. So it\n * needs to be awaited first. If a `get` for the same cache key is called\n * before the pending entry is complete, the cache handler must wait for the\n * `set` operation to finish, before returning the entry, instead of returning\n * undefined.\n */\n set(cacheKey: string, pendingEntry: Promise<CacheEntry>): Promise<void>\n\n /**\n * Next.js will call this method periodically, but always before starting a\n * new request. When working with a remote tags service, this method should\n * communicate with the tags service to refresh the local tags manifest\n * accordingly.\n */\n refreshTags(): Promise<void>\n\n /**\n * Next.js will call this method for each set of soft tags that are relevant\n * at the start of a request. The result is the maximum timestamp of a\n * revalidate event for the tags. Returns `0` if none of the tags were ever\n * revalidated.\n */\n getExpiration(...tags: string[]): Promise<Timestamp>\n\n /**\n * Next.js will call this method when `revalidateTag` or `revalidatePath()` is\n * called. It should update the tags manifest accordingly.\n */\n expireTags(...tags: string[]): Promise<void>\n}\n\n/**\n * This is a compatibility type to ease migration between cache handler\n * versions. Until the old `CacheHandler` type is removed, this type should be\n * used for all internal Next.js functions that deal with cache handlers to\n * ensure that we are compatible with both cache handler versions. An exception\n * is the built-in default cache handler, which implements the\n * {@link CacheHandlerV2} interface.\n */\nexport type CacheHandlerCompat = CacheHandler | CacheHandlerV2\n"],"names":[],"mappings":"AAAA;;CAEC"}