"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/run/handlers/cache.cts
var cache_exports = {};
__export(cache_exports, {
  NetlifyCacheHandler: () => NetlifyCacheHandler,
  default: () => cache_default
});
module.exports = __toCommonJS(cache_exports);
var import_node_buffer = require("node:buffer");
var import_node_path = require("node:path");
var import_posix = require("node:path/posix");
var import_constants = require("next/dist/lib/constants.js");
var import_cache_types = require("../../shared/cache-types.cjs");
var import_storage = require("../storage/storage.cjs");
var import_request_context = require("./request-context.cjs");
var import_tags_handler = require("./tags-handler.cjs");
var import_tracer = require("./tracer.cjs");
var memoizedPrerenderManifest;
var NetlifyCacheHandler = class {
  options;
  revalidatedTags;
  cacheStore;
  tracer = (0, import_tracer.getTracer)();
  constructor(options) {
    this.options = options;
    this.revalidatedTags = options.revalidatedTags;
    this.cacheStore = (0, import_storage.getMemoizedKeyValueStoreBackedByRegionalBlobStore)({ consistency: "strong" });
  }
  getTTL(blob) {
    if (blob.value?.kind === "FETCH" || blob.value?.kind === "ROUTE" || blob.value?.kind === "APP_ROUTE" || blob.value?.kind === "PAGE" || blob.value?.kind === "PAGES" || blob.value?.kind === "APP_PAGE") {
      const { revalidate } = blob.value;
      if (typeof revalidate === "number") {
        const revalidateAfter = revalidate * 1e3 + blob.lastModified;
        return (revalidateAfter - Date.now()) / 1e3;
      }
      if (revalidate === false) {
        return "PERMANENT";
      }
    }
    return "NOT SET";
  }
  captureResponseCacheLastModified(cacheValue, key, getCacheKeySpan) {
    if (cacheValue.value?.kind === "FETCH") {
      return;
    }
    const requestContext = (0, import_request_context.getRequestContext)();
    if (!requestContext) {
      (0, import_tracer.recordWarning)(new Error("CacheHandler was called without a request context"), getCacheKeySpan);
      return;
    }
    if (requestContext.responseCacheKey && requestContext.responseCacheKey !== key) {
      requestContext.responseCacheGetLastModified = void 0;
      (0, import_tracer.recordWarning)(
        new Error(
          `Multiple response cache keys used in single request: ["${requestContext.responseCacheKey}, "${key}"]`
        ),
        getCacheKeySpan
      );
      return;
    }
    requestContext.responseCacheKey = key;
    if (cacheValue.lastModified) {
      requestContext.responseCacheGetLastModified = cacheValue.lastModified;
    }
  }
  captureRouteRevalidateAndRemoveFromObject(cacheValue) {
    const { revalidate, ...restOfRouteValue } = cacheValue;
    const requestContext = (0, import_request_context.getRequestContext)();
    if (requestContext) {
      requestContext.routeHandlerRevalidate = revalidate;
    }
    return restOfRouteValue;
  }
  captureCacheTags(cacheValue, key) {
    const requestContext = (0, import_request_context.getRequestContext)();
    if (!requestContext) {
      return;
    }
    if (requestContext.responseCacheTags) {
      return;
    }
    if (!cacheValue) {
      const cacheTags = [`_N_T_${key === "/index" ? "/" : encodeURI(key)}`];
      requestContext.responseCacheTags = cacheTags;
      return;
    }
    if (cacheValue.kind === "PAGE" || cacheValue.kind === "PAGES" || cacheValue.kind === "APP_PAGE" || cacheValue.kind === "ROUTE" || cacheValue.kind === "APP_ROUTE") {
      if (cacheValue.headers?.[import_constants.NEXT_CACHE_TAGS_HEADER]) {
        const cacheTags = cacheValue.headers[import_constants.NEXT_CACHE_TAGS_HEADER].split(/,|%2c/gi).map(encodeURI);
        requestContext.responseCacheTags = cacheTags;
      } else if ((cacheValue.kind === "PAGE" || cacheValue.kind === "PAGES") && typeof cacheValue.pageData === "object") {
        const cacheTags = [`_N_T_${key === "/index" ? "/" : encodeURI(key)}`];
        requestContext.responseCacheTags = cacheTags;
      }
    }
  }
  async getPrerenderManifest(serverDistDir) {
    if (memoizedPrerenderManifest) {
      return memoizedPrerenderManifest;
    }
    const prerenderManifestPath = (0, import_node_path.join)(serverDistDir, "..", "prerender-manifest.json");
    try {
      const { loadManifest } = await import("next/dist/server/load-manifest.external.js");
      memoizedPrerenderManifest = loadManifest(prerenderManifestPath);
    } catch {
      const { loadManifest } = await import("next/dist/server/load-manifest.js");
      memoizedPrerenderManifest = loadManifest(prerenderManifestPath);
    }
    return memoizedPrerenderManifest;
  }
  async injectEntryToPrerenderManifest(key, { revalidate, cacheControl }) {
    if (this.options.serverDistDir && (typeof revalidate === "number" || revalidate === false || typeof cacheControl !== "undefined")) {
      try {
        const prerenderManifest = await this.getPrerenderManifest(this.options.serverDistDir);
        if (typeof cacheControl !== "undefined") {
          try {
            const { SharedCacheControls } = await import(
              // @ts-expect-error supporting multiple next version, this module is not resolvable with currently used dev dependency
              // eslint-disable-next-line import/no-unresolved, n/no-missing-import
              "next/dist/server/lib/incremental-cache/shared-cache-controls.external.js"
            );
            const sharedCacheControls = new SharedCacheControls(prerenderManifest);
            sharedCacheControls.set(key, cacheControl);
          } catch {
            const { SharedCacheControls } = await import(
              // @ts-expect-error supporting multiple next version, this module is not resolvable with currently used dev dependency
              // eslint-disable-next-line import/no-unresolved, n/no-missing-import
              "next/dist/server/lib/incremental-cache/shared-cache-controls.js"
            );
            const sharedCacheControls = new SharedCacheControls(prerenderManifest);
            sharedCacheControls.set(key, cacheControl);
          }
        } else if (typeof revalidate === "number" || revalidate === false) {
          try {
            const { normalizePagePath } = await import("next/dist/shared/lib/page-path/normalize-page-path.js");
            prerenderManifest.routes[key] = {
              experimentalPPR: void 0,
              dataRoute: (0, import_posix.join)("/_next/data", `${normalizePagePath(key)}.json`),
              srcRoute: null,
              // FIXME: provide actual source route, however, when dynamically appending it doesn't really matter
              initialRevalidateSeconds: revalidate,
              // Pages routes do not have a prefetch data route.
              prefetchDataRoute: void 0
            };
          } catch {
            const { SharedRevalidateTimings } = await import("next/dist/server/lib/incremental-cache/shared-revalidate-timings.js");
            const sharedRevalidateTimings = new SharedRevalidateTimings(prerenderManifest);
            sharedRevalidateTimings.set(key, revalidate);
          }
        }
      } catch {
      }
    }
  }
  async get(...args) {
    return (0, import_tracer.withActiveSpan)(this.tracer, "get cache key", async (span) => {
      const [key, context = {}] = args;
      (0, import_request_context.getLogger)().debug(`[NetlifyCacheHandler.get]: ${key}`);
      span?.setAttributes({ key });
      const blob = await this.cacheStore.get(key, "blobStore.get");
      if (!blob) {
        span?.addEvent("Cache miss", { key });
        return null;
      }
      const ttl = this.getTTL(blob);
      if ((0, import_request_context.getRequestContext)()?.isBackgroundRevalidation && typeof ttl === "number" && ttl < 0) {
        span?.addEvent("Discarding stale entry due to SWR background revalidation request", {
          key,
          ttl
        });
        (0, import_request_context.getLogger)().withFields({
          ttl,
          key
        }).debug(
          `[NetlifyCacheHandler.get] Discarding stale entry due to SWR background revalidation request: ${key}`
        );
        return null;
      }
      const { stale: staleByTags, expired: expiredByTags } = await this.checkCacheEntryStaleByTags(
        blob,
        context.tags,
        context.softTags
      );
      if (expiredByTags) {
        span?.addEvent("Expired", { expiredByTags, key, ttl });
        return null;
      }
      this.captureResponseCacheLastModified(blob, key, span);
      if (staleByTags) {
        span?.addEvent("Stale", { staleByTags, key, ttl });
        blob.lastModified = -1;
      }
      const isDataRequest = Boolean(context.fetchUrl);
      if (!isDataRequest) {
        this.captureCacheTags(blob.value, key);
      }
      switch (blob.value?.kind) {
        case "FETCH":
          span?.addEvent("FETCH", {
            lastModified: blob.lastModified,
            revalidate: context.revalidate,
            ttl
          });
          return {
            lastModified: blob.lastModified,
            value: blob.value
          };
        case "ROUTE":
        case "APP_ROUTE": {
          span?.addEvent(blob.value?.kind, {
            lastModified: blob.lastModified,
            status: blob.value.status,
            revalidate: blob.value.revalidate,
            ttl
          });
          const valueWithoutRevalidate = this.captureRouteRevalidateAndRemoveFromObject(blob.value);
          return {
            lastModified: blob.lastModified,
            value: {
              ...valueWithoutRevalidate,
              body: import_node_buffer.Buffer.from(valueWithoutRevalidate.body, "base64")
            }
          };
        }
        case "PAGE":
        case "PAGES": {
          const { revalidate, ...restOfPageValue } = blob.value;
          const requestContext = (0, import_request_context.getRequestContext)();
          if (requestContext) {
            requestContext.pageHandlerRevalidate = revalidate;
          }
          span?.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl });
          await this.injectEntryToPrerenderManifest(key, blob.value);
          return {
            lastModified: blob.lastModified,
            value: restOfPageValue
          };
        }
        case "APP_PAGE": {
          const requestContext = (0, import_request_context.getRequestContext)();
          if (requestContext && blob.value?.kind === "APP_PAGE") {
            requestContext.isCacheableAppPage = true;
          }
          const { revalidate, rscData, segmentData, ...restOfPageValue } = blob.value;
          span?.addEvent(blob.value?.kind, { lastModified: blob.lastModified, revalidate, ttl });
          await this.injectEntryToPrerenderManifest(key, blob.value);
          return {
            lastModified: blob.lastModified,
            value: {
              ...restOfPageValue,
              rscData: rscData ? import_node_buffer.Buffer.from(rscData, "base64") : void 0,
              segmentData: segmentData ? new Map(
                Object.entries(segmentData).map(([segmentPath, base64EncodedSegment]) => [
                  segmentPath,
                  import_node_buffer.Buffer.from(base64EncodedSegment, "base64")
                ])
              ) : void 0
            }
          };
        }
        default:
          span?.recordException(new Error(`Unknown cache entry kind: ${blob.value?.kind}`));
      }
      return null;
    });
  }
  transformToStorableObject(data, context) {
    if (!data) {
      return null;
    }
    if ((0, import_cache_types.isCachedRouteValue)(data)) {
      return {
        ...data,
        revalidate: context.revalidate ?? context.cacheControl?.revalidate,
        cacheControl: context.cacheControl,
        body: data.body.toString("base64")
      };
    }
    if ((0, import_cache_types.isCachedPageValue)(data)) {
      return {
        ...data,
        revalidate: context.revalidate ?? context.cacheControl?.revalidate,
        cacheControl: context.cacheControl
      };
    }
    if (data?.kind === "APP_PAGE") {
      return {
        ...data,
        revalidate: context.revalidate ?? context.cacheControl?.revalidate,
        cacheControl: context.cacheControl,
        rscData: data.rscData?.toString("base64"),
        segmentData: data.segmentData ? Object.fromEntries(
          [...data.segmentData.entries()].map(([segmentPath, base64EncodedSegment]) => [
            segmentPath,
            base64EncodedSegment.toString("base64")
          ])
        ) : void 0
      };
    }
    return data;
  }
  async set(...args) {
    return (0, import_tracer.withActiveSpan)(this.tracer, "set cache key", async (span) => {
      const [key, data, context] = args;
      const lastModified = Date.now();
      span?.setAttributes({ key, lastModified });
      (0, import_request_context.getLogger)().debug(`[NetlifyCacheHandler.set]: ${key}`);
      const value = this.transformToStorableObject(data, context);
      const isDataReq = Boolean(context.fetchUrl);
      if (!isDataReq) {
        this.captureCacheTags(value, key);
      }
      await this.cacheStore.set(key, { lastModified, value }, "blobStore.set");
      if (data?.kind === "APP_PAGE") {
        const requestContext = (0, import_request_context.getRequestContext)();
        if (requestContext) {
          requestContext.isCacheableAppPage = true;
        }
      }
      if (!data && !isDataReq || data?.kind === "PAGE" || data?.kind === "PAGES") {
        const requestContext = (0, import_request_context.getRequestContext)();
        if (requestContext?.didPagesRouterOnDemandRevalidate) {
          const tag = `_N_T_${key === "/index" ? "/" : encodeURI(key)}`;
          requestContext?.trackBackgroundWork((0, import_tags_handler.purgeEdgeCache)(tag));
        }
      }
    });
  }
  async revalidateTag(tagOrTags, durations) {
    return (0, import_tags_handler.markTagsAsStaleAndPurgeEdgeCache)(tagOrTags, durations);
  }
  resetRequestCache() {
  }
  /**
   * Checks if a cache entry is stale through on demand revalidated tags
   */
  checkCacheEntryStaleByTags(cacheEntry, tags = [], softTags = []) {
    let cacheTags = [];
    if (cacheEntry.value?.kind === "FETCH") {
      cacheTags = [...tags, ...softTags];
    } else if (cacheEntry.value?.kind === "PAGE" || cacheEntry.value?.kind === "PAGES" || cacheEntry.value?.kind === "APP_PAGE" || cacheEntry.value?.kind === "ROUTE" || cacheEntry.value?.kind === "APP_ROUTE") {
      cacheTags = cacheEntry.value.headers?.[import_constants.NEXT_CACHE_TAGS_HEADER]?.split(/,|%2c/gi) || [];
    } else {
      return {
        stale: false,
        expired: false
      };
    }
    if (this.revalidatedTags && this.revalidatedTags.length !== 0) {
      for (const tag of this.revalidatedTags) {
        if (cacheTags.includes(tag)) {
          return {
            stale: true,
            expired: true
          };
        }
      }
    }
    return (0, import_tags_handler.isAnyTagStaleOrExpired)(cacheTags, cacheEntry.lastModified);
  }
};
var cache_default = NetlifyCacheHandler;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  NetlifyCacheHandler
});
