UNPKG

astro

Version:

Astro is a modern site builder with web best practices, performance, and DX front-of-mind.

121 lines (120 loc) • 4.59 kB
import { markFeatureUsed, FetchFeatures } from "../fetch/features.js"; import { getEnvironment } from "../environment/index.js"; import { createManifestMemo } from "../manifest/memo.js"; import { getCacheProvider } from "./provider.js"; import { AstroCache, applyCacheHeaders } from "./runtime/cache.js"; import { NoopAstroCache, DisabledAstroCache } from "./runtime/noop.js"; import { compileCacheRoutes, matchCacheRoute } from "./runtime/route-matching.js"; const CACHE_KEY = "cache"; /** * Registers a cache provider on the given `FetchState`. When * `state.resolve('cache')` is first called, the appropriate cache * implementation is created (disabled, noop for dev, or real). * * Returns synchronously when cache is not configured or in dev mode. */ export function provideCache(state) { const manifest = state.manifest; if (!manifest.cacheConfig) { // Cache not configured — provide a disabled cache that warns once state.provide(CACHE_KEY, { create: () => new DisabledAstroCache(state.logger) }); return; } if (getEnvironment(manifest).runtimeMode === "development") { state.provide(CACHE_KEY, { create: () => new NoopAstroCache() }); return; } return provideCacheAsync(state, manifest); } async function provideCacheAsync(state, manifest) { const cacheProvider = await getCacheProvider(manifest); state.provide(CACHE_KEY, { create() { const cache = new AstroCache(cacheProvider); // Apply config-level cache route matching as initial state. The // compiled routes are derived lazily by the manifest memo, so the // compile still happens on the first cache-seeded request. if (manifest.cacheConfig?.routes) { const matched = matchCacheRoute(state.pathname, getCompiledCacheRoutes(manifest)); if (matched) { cache.set(matched); } } return cache; } }); } /** * Wraps a render callback with cache provider logic. Handles both * runtime caching (onRequest) and CDN-based providers (headers only). * * - When a cache provider with `onRequest` is configured, the callback * is wrapped so the provider can serve from cache or fall through. * - When only CDN headers are needed, the callback runs directly and * cache headers are applied to the response. * - When no cache provider is configured, the callback runs as-is. * * Cache headers (`CDN-Cache-Control`, `Cache-Tag`) are stripped from * the final response after the runtime provider has read them. */ export async function handleCache(state, next) { markFeatureUsed(state.manifest, FetchFeatures.cache); if (!state.manifest.cacheProvider) { return next(); } const cache = state.resolve(CACHE_KEY); const cacheProvider = await getCacheProvider(state.manifest); if (cacheProvider?.onRequest) { const response = await cacheProvider.onRequest({ request: state.request, url: new URL(state.request.url), waitUntil: state.renderOptions.waitUntil, logger: { info(msg) { state.logger.info("cache", msg); }, warn(msg) { state.logger.warn("cache", msg); }, error(msg) { state.logger.error("cache", msg); } } }, async () => { const res = await next(); applyCacheHeaders(cache, res, state.request); return res; }); // Strip CDN headers after the runtime provider has read them response.headers.delete("CDN-Cache-Control"); response.headers.delete("Cache-Tag"); return response; } const response = await next(); // Apply cache headers for CDN-based providers (no onRequest) applyCacheHeaders(cache, response, state.request); return response; } /** * Registers the cache provider via `provideCache`, then runs `handleCache`. * Used by the composable `cache()` handlers, where no earlier stage has * registered the provider. Must run before the API context is built so * `ctx.cache` / `Astro.cache` are defined downstream. */ export async function provideAndHandleCache(state, next) { await provideCache(state); return handleCache(state, next); } const compiledCacheRoutesMemo = createManifestMemo((manifest) => manifest.cacheConfig?.routes ? compileCacheRoutes(manifest.cacheConfig.routes, manifest.base, manifest.trailingSlash) : []); /** * Config-level cache routes compiled from `manifest.cacheConfig`, derived * lazily on the first cache-seeded request. */ export function getCompiledCacheRoutes(manifest) { return compiledCacheRoutesMemo.get(manifest); } /** * Internal raw setter — exists only so the transitional `Pipeline` bridge can * expose `compiledCacheRoutes` as a get/set pair. */ export function setCompiledCacheRoutes(manifest, routes) { compiledCacheRoutesMemo.set(manifest, routes); }