{
  "$schema": "https://hyperjs.ai/schema/registry-item.json",
  "name": "compress",
  "version": "0.1.0",
  "description": "Content-negotiated gzip/brotli compression plugin for Hyper.",
  "readme": "# @hyper/compress\n\nContent-negotiated gzip/brotli compression plugin for Hyper.\n\n## Install\n\nComponents are installed as source into your repo, not pulled from npm:\n\n```bash\nbunx hyper add compress\n```\n\nWires the alias `@hyper/compress` to `src/hyper/compress/` (configurable in `hyper.config.json`). See [hyperjs.ai](https://hyperjs.ai) for the full registry.\n\n## Usage\n\n```ts\nimport { Hyper } from \"@hyper/core\"\nimport { compress } from \"@hyper/compress\"\n\nexport default new Hyper()\n  .use(compress())\n  .listen(3000)\n```\n\n## Docs\n\nSee the [main README](../../README.md) and [docs/](../../docs) for guides and integration recipes.\n\n## License\n\nMIT\n",
  "registryDeps": [
    "core"
  ],
  "peerDeps": {},
  "optionalPeerDeps": {},
  "files": [
    {
      "path": "compress/index.ts",
      "contents": "/**\n * @hyper/compress — content-negotiated gzip/brotli compression.\n *\n * Wired as middleware (not a plugin) because we need to return a new\n * Response. Inspect the handler's response, negotiate the encoding,\n * compress in a single sync pass with Bun.gzipSync / node:zlib brotli.\n *\n * Defaults follow the \"safe-by-default\" philosophy:\n *   - Only compress text/*, application/json, javascript, xml, svg, wasm.\n *     No images/video/audio.\n *   - Skip responses smaller than 1 KB (overhead dominates).\n *   - Always set `Vary: Accept-Encoding` when we might compress.\n */\n\nimport { brotliCompressSync, gzipSync, constants as zlibConstants } from \"node:zlib\"\nimport { type Middleware, coerce } from \"@hyper/core\"\n\nexport interface CompressConfig {\n  readonly threshold?: number\n  readonly brotli?: boolean\n  readonly types?: readonly string[]\n  readonly level?: { readonly gzip?: number; readonly brotli?: number }\n}\n\nconst DEFAULT_TYPES: readonly string[] = Object.freeze([\n  \"text/\",\n  \"application/json\",\n  \"application/ld+json\",\n  \"application/javascript\",\n  \"application/xml\",\n  \"image/svg+xml\",\n  \"application/wasm\",\n  \"application/manifest+json\",\n])\n\nexport function compress(config: CompressConfig = {}): Middleware {\n  const threshold = config.threshold ?? 1024\n  const preferBrotli = config.brotli ?? true\n  const types = [...DEFAULT_TYPES, ...(config.types ?? [])]\n  const gzipLevel = config.level?.gzip ?? 6\n  const brotliLevel = config.level?.brotli ?? 5\n\n  return async ({ req, next }) => {\n    const out = await next()\n    const res = out instanceof Response ? out : coerce(out)\n    if (res.headers.has(\"content-encoding\")) return res\n\n    const ae = req.headers.get(\"accept-encoding\")?.toLowerCase() ?? \"\"\n    const acceptsBr = preferBrotli && ae.includes(\"br\")\n    const acceptsGz = ae.includes(\"gzip\")\n    if (!acceptsBr && !acceptsGz) return res\n\n    const ct = res.headers.get(\"content-type\")?.toLowerCase() ?? \"\"\n    if (!ct || !types.some((t) => ct.startsWith(t))) return res\n\n    const body = new Uint8Array(await res.arrayBuffer())\n    // Always signal negotiation even if we don't compress.\n    if (body.byteLength < threshold) {\n      const hh = new Headers(res.headers)\n      hh.append(\"vary\", \"Accept-Encoding\")\n      return new Response(body, { status: res.status, statusText: res.statusText, headers: hh })\n    }\n\n    let encoded: Uint8Array\n    let enc: string\n    if (acceptsBr) {\n      encoded = brotliCompressSync(body, {\n        params: { [zlibConstants.BROTLI_PARAM_QUALITY]: brotliLevel },\n      })\n      enc = \"br\"\n    } else {\n      encoded = gzipSync(body, { level: gzipLevel })\n      enc = \"gzip\"\n    }\n\n    const headers = new Headers(res.headers)\n    headers.set(\"content-encoding\", enc)\n    headers.set(\"content-length\", encoded.byteLength.toString())\n    headers.append(\"vary\", \"Accept-Encoding\")\n    return new Response(encoded, { status: res.status, statusText: res.statusText, headers })\n  }\n}\n",
      "sha256": "fe73d81049a3ab3e5ac2b61e82fdfd9eaf82b8ef2b4794c30d0e6ac20709cb90"
    }
  ],
  "subpaths": {}
}