Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); const require_middleware_etag_digest = require("./digest.js"); //#region src/middleware/etag/index.ts /** * Default headers to pass through on 304 responses. From the spec: * > The response must not contain a body and must include the headers that * > would have been sent in an equivalent 200 OK response: Cache-Control, * > Content-Location, Date, ETag, Expires, and Vary. */ const RETAINED_304_HEADERS = [ "cache-control", "content-location", "date", "etag", "expires", "vary" ]; const stripWeak = (tag) => tag.replace(/^W\//, ""); function etagMatches(etag, ifNoneMatch) { return ifNoneMatch != null && ifNoneMatch.split(",").some((t) => stripWeak(t.trim()) === stripWeak(etag)); } function initializeGenerator(generator) { if (!generator) { if (crypto && crypto.subtle) generator = (body) => crypto.subtle.digest({ name: "SHA-1" }, body); } return generator; } /** * ETag Middleware for Hono. * * @see {@link https://hono.dev/docs/middleware/builtin/etag} * * @param {ETagOptions} [options] - The options for the ETag middleware. * @param {boolean} [options.weak=false] - Define using or not using a weak validation. If true is set, then `W/` is added to the prefix of the value. * @param {string[]} [options.retainedHeaders=RETAINED_304_HEADERS] - The headers that you want to retain in the 304 Response. * @param {function(Uint8Array): ArrayBuffer | Promise} [options.generateDigest] - * A custom digest generation function. By default, it uses 'SHA-1' * This function is called with the response body as a `Uint8Array` and should return a hash as an `ArrayBuffer` or a Promise of one. * @returns {MiddlewareHandler} The middleware handler function. * * @example * ```ts * const app = new Hono() * * app.use('/etag/*', etag()) * app.get('/etag/abc', (c) => { * return c.text('Hono is hot') * }) * ``` */ const etag = (options) => { const retainedHeaders = new Set((options?.retainedHeaders ?? RETAINED_304_HEADERS).map((header) => header.toLowerCase())); const weak = options?.weak ?? false; const generator = initializeGenerator(options?.generateDigest); return async function etag(c, next) { const ifNoneMatch = c.req.header("If-None-Match") ?? null; await next(); if (!(c.req.method === "GET" || c.req.method === "HEAD" || c.req.method === "QUERY") || !c.res.ok) return; const res = c.res; let etag = res.headers.get("ETag"); if (!etag) { if (!generator) return; const hash = await require_middleware_etag_digest.generateDigest(res.clone().body, generator); if (hash === null) return; etag = weak ? `W/"${hash}"` : `"${hash}"`; } if (ifNoneMatch === "*" || etagMatches(etag, ifNoneMatch)) { c.res = new Response(null, { status: 304, statusText: "Not Modified", headers: { ETag: etag } }); for (const key of Array.from(c.res.headers.keys())) if (!retainedHeaders.has(key.toLowerCase())) c.res.headers.delete(key); } else c.res.headers.set("ETag", etag); }; }; //#endregion exports.RETAINED_304_HEADERS = RETAINED_304_HEADERS; exports.etag = etag;