{
  "$schema": "https://hyperjs.ai/schema/registry-item.json",
  "name": "core",
  "version": "0.1.0",
  "description": "Hyper core — Bun-first, AI-native API framework runtime.",
  "readme": "# @hyper/core\n\nHyper core — the only hard dependency across the Hyper ecosystem. Zero runtime\ndependencies. Bun-native.\n\n## Install\n\nComponents are installed as source into your repo, not pulled from npm:\n\n```bash\nbunx hyper add core\n```\n\nWires the alias `@hyper/core` to `src/hyper/core/` (configurable in `hyper.config.json`). See [hyperjs.ai](https://hyperjs.ai) for the full registry.\n\n## Usage\n\n```ts\nimport { Hyper, ok } from \"@hyper/core\"\n\nexport default new Hyper()\n  .get(\"/\", () => ok({ hello: \"world\" }))\n  .listen(3000)\n```\n\nCompose sub-apps, plugins, middleware, or raw `Route` values through a single\npolymorphic `.use()`:\n\n```ts\nimport { Hyper } from \"@hyper/core\"\nimport users from \"./routes/users.ts\"\n\nexport default new Hyper()\n  .use(users)           // honors sub-app's own prefix\n  .use(\"/v1\", users)    // or re-prefix explicitly\n  .listen(3000)\n```\n\nCLI tools (`hyper openapi`, `hyper routes`, `hyper bench`, `hyper dev`) set\n`HYPER_SKIP_LISTEN=1` before importing, so the same file serves as both\nserver entrypoint and introspection manifest.\n\n## Docs\n\nSee the [main README](../../README.md) and [docs/](../../docs) for guides,\nsecurity defaults, and integration recipes.\n\n## License\n\nMIT\n",
  "registryDeps": [],
  "peerDeps": {},
  "optionalPeerDeps": {},
  "files": [
    {
      "path": "core/adapters/bun.ts",
      "contents": "/**\n * Bun adapter helpers.\n *\n * Thin wrappers around `Bun.serve` that use the native `routes` map\n * emitted by the app + fall through to `fetch` for anything the map\n * cannot express (e.g. catch-alls, middleware-only paths).\n */\n\nimport type { HyperApp } from \"../types.ts\"\n\nexport interface ServeOptions {\n  readonly port?: number\n  readonly hostname?: string\n  readonly idleTimeout?: number\n  readonly tls?: import(\"bun\").TLSOptions\n  readonly development?: boolean\n}\n\n/** Convenience wrapper. Callers may always prefer `Bun.serve` directly. */\nexport function serve(app: HyperApp, opts: ServeOptions = {}): ReturnType<typeof Bun.serve> {\n  const serveOpts: Record<string, unknown> = {\n    routes: app.routes,\n    fetch: app.fetch,\n    idleTimeout: opts.idleTimeout ?? 10,\n  }\n  if (opts.port !== undefined) serveOpts.port = opts.port\n  if (opts.hostname !== undefined) serveOpts.hostname = opts.hostname\n  if (opts.tls !== undefined) serveOpts.tls = opts.tls\n  if (opts.development !== undefined) serveOpts.development = opts.development\n  // Cast: Bun.serve's Options union is too narrow for our generic shape;\n  // the runtime accepts every key we set.\n  return Bun.serve(serveOpts as unknown as Parameters<typeof Bun.serve>[0])\n}\n",
      "sha256": "4f8eb0e6ee1e02aab9cfff7389179c3fdb5b33e64cff1e7448ddfaefb3b9f1e7"
    },
    {
      "path": "core/app.ts",
      "contents": "/**\n * app() — builds a HyperApp from routes, groups, plugins, env, decorate.\n *\n * Boot order:\n *   1. Merge env layers → typed env (throws on bad input with why/fix).\n *   2. Run every decorate(env) → static ctx (singletons like db).\n *   3. Run plugin.build(app) / plugin.context(env) → merge into static ctx.\n *   4. Wire request pipeline: derive(ctx) per-request, plugins.before/after.\n *\n * The app is opaque for end users — everything goes through `.fetch`.\n */\n\nimport { type ContextBlueprint, applyDerive, resolveStaticContext } from \"./decorate.ts\"\nimport { parseEnv, withEnv } from \"./env.ts\"\nimport { HyperError, asHyperError } from \"./error.ts\"\nimport { GroupBuilder, fromPlainRouter } from \"./group.ts\"\nimport {\n  type ClientManifest,\n  type MCPManifest,\n  type OpenAPIManifest,\n  type OpenAPIManifestConfig,\n  toClientManifest,\n  toMCPManifest,\n  toOpenAPI,\n} from \"./projection.ts\"\nimport { parseBodyAuto } from \"./request.ts\"\nimport { coerce, errorResponse } from \"./response.ts\"\nimport { Router } from \"./router.ts\"\nimport {\n  DEFAULT_SECURITY,\n  METHOD_OVERRIDE_HEADERS,\n  METHOD_OVERRIDE_QUERY_KEYS,\n  applyDefaultHeaders,\n} from \"./security.ts\"\nimport { SchemaValidationError, type StandardSchemaV1, parseStandard } from \"./standard-schema.ts\"\nimport type {\n  AppConfig,\n  AppContext,\n  BunRoutes,\n  HyperApp,\n  HyperPlugin,\n  InternalHandlerCtx,\n  InvokeInput,\n  InvokeResult,\n  Route,\n  SecurityDefaults,\n} from \"./types.ts\"\n\nexport function app(config: AppConfig = {}): HyperApp {\n  const security: SecurityDefaults = { ...DEFAULT_SECURITY, ...config.security }\n\n  // 1. Collect routes -------------------------------------------------------\n  const allRoutes: Route[] = []\n  if (config.routes) allRoutes.push(...config.routes)\n  if (config.groups) {\n    for (const g of config.groups) {\n      // Accept either a GroupBuilder (has .build()) or a RouteGroup literal.\n      const built: import(\"./types.ts\").RouteGroup =\n        typeof (g as { build?: unknown }).build === \"function\"\n          ? (g as import(\"./types.ts\").GroupConfigEntry).build()\n          : (g as import(\"./types.ts\").RouteGroup)\n      for (const r of built.routes) allRoutes.push(r)\n    }\n  }\n  if (config.router) {\n    const built = fromPlainRouter(config.router).build()\n    for (const r of built.routes) allRoutes.push(r)\n  }\n\n  const router = new Router()\n  for (const r of allRoutes) router.add(r)\n\n  // 2. Env (lazy — the first request triggers boot). In a real app, boot\n  //    should be eager; but keeping it lazy makes the library usable in\n  //    edge/test environments where no process.env is available.\n  //\n  //    Once boot resolves, we cache the state directly and bypass the\n  //    promise on subsequent requests — the hot path becomes sync.\n  let bootedPromise: Promise<BootedState> | undefined\n  let bootedCache: BootedState | undefined\n  let bootedError: unknown\n  const plugins: readonly HyperPlugin[] = config.plugins ?? []\n\n  // Precompute per-hook plugin arrays so the request pipeline skips\n  // any hook category that has zero installed callbacks — no empty\n  // `for (const p of plugins)` loops on the hot path.\n  const pluginsPreRoute: readonly HyperPlugin[] = plugins.filter((p) => p.request?.preRoute)\n  const pluginsBefore: readonly HyperPlugin[] = plugins.filter((p) => p.request?.before)\n  const pluginsAfter: readonly HyperPlugin[] = plugins.filter((p) => p.request?.after)\n  const pluginsOnError: readonly HyperPlugin[] = plugins.filter((p) => p.request?.onError)\n\n  // Whether the app declares any env schema. When false we skip the\n  // AsyncLocalStorage (`withEnv`) wrapping per request — saves a Map\n  // alloc + ALS snapshot per fetch on plaintext-style routes.\n  const envRequired = config.env?.schema !== undefined\n\n  const boot = async (): Promise<BootedState> => {\n    const envLayers: StandardSchemaV1[] = []\n    if (config.env?.schema) envLayers.push(config.env.schema as StandardSchemaV1)\n    const env = await parseEnv(envLayers, config.env?.source)\n\n    const blueprint: ContextBlueprint = {\n      decorators: config.decorate ?? [],\n      derives: config.derive ?? [],\n    }\n    const { ctx: staticCtx, dispose } = await resolveStaticContext(blueprint, env)\n\n    // Plugin-installed context\n    for (const p of plugins) {\n      if (p.context) {\n        const added = await p.context(env)\n        Object.assign(staticCtx, added)\n      }\n      if (p.build) await p.build(instance)\n    }\n\n    return { env, staticCtx, dispose, blueprint }\n  }\n\n  const fetch = async (req: Request): Promise<Response> => {\n    let booted = bootedCache\n    if (!booted) {\n      if (bootedError) {\n        return finalize(errorResponse(asHyperError(bootedError)), isHttps(req), security)\n      }\n      if (!bootedPromise) {\n        bootedPromise = boot().then(\n          (s) => {\n            bootedCache = s\n            return s\n          },\n          (e) => {\n            bootedError = e\n            throw e\n          },\n        )\n      }\n      try {\n        booted = await bootedPromise\n      } catch (e) {\n        return finalize(errorResponse(asHyperError(e)), isHttps(req), security)\n      }\n    }\n    const hooks: HookPlugins = {\n      preRoute: pluginsPreRoute,\n      before: pluginsBefore,\n      after: pluginsAfter,\n      onError: pluginsOnError,\n    }\n    // Skip the AsyncLocalStorage wrap when no env schema is declared —\n    // `useEnv()` is opt-in so the cost is unjustified on plaintext\n    // throughput benchmarks that never call it.\n    if (!envRequired) return handleRequest(req, booted, router, security, hooks)\n    return withEnv(booted.env, () => handleRequest(req, booted!, router, security, hooks))\n  }\n\n  const routes = buildBunRoutes(allRoutes, fetch)\n\n  const invoke = async (input: InvokeInput): Promise<InvokeResult> => {\n    const rawPath = input.path.startsWith(\"/\") ? input.path : `/${input.path}`\n    const resolvedPath = input.params\n      ? rawPath.replace(/:([A-Za-z0-9_]+)/g, (_, k: string) => {\n          const v = input.params?.[k]\n          if (v === undefined) throw new Error(`invoke: missing path param :${k}`)\n          return encodeURIComponent(v)\n        })\n      : rawPath\n    const url = new URL(`http://local${resolvedPath}`)\n    if (input.query) {\n      for (const [k, v] of Object.entries(input.query)) {\n        if (v !== undefined) url.searchParams.set(k, String(v))\n      }\n    }\n    const init: RequestInit = {\n      method: input.method,\n      ...(input.headers ? { headers: input.headers } : {}),\n      ...(input.body !== undefined && hasBody(input.method)\n        ? {\n            body: typeof input.body === \"string\" ? input.body : JSON.stringify(input.body),\n            headers: {\n              \"content-type\": \"application/json\",\n              ...(input.headers ?? {}),\n            },\n          }\n        : {}),\n    }\n    const req = new Request(url, init)\n    const res = await fetch(req)\n    const ct = res.headers.get(\"content-type\") ?? \"\"\n    const data = ct.includes(\"application/json\") ? await res.json() : await res.text()\n    return { status: res.status, data, headers: res.headers }\n  }\n\n  const toOpenAPIFn = (cfg: OpenAPIManifestConfig = {}): OpenAPIManifest =>\n    toOpenAPI(allRoutes, cfg)\n  const toMCPFn = (): MCPManifest => toMCPManifest(allRoutes)\n  const toClientFn = (): ClientManifest => toClientManifest(allRoutes)\n\n  const instance: HyperApp = {\n    fetch,\n    routes,\n    routeList: Object.freeze([...allRoutes]),\n    invoke,\n    toOpenAPI: toOpenAPIFn,\n    toMCPManifest: toMCPFn,\n    toClientManifest: toClientFn,\n    __config: config,\n    test: (overrides = {}) => makeTestApp(config, overrides),\n  }\n  return instance\n}\n\nfunction makeTestApp(base: AppConfig, overrides: import(\"./types.ts\").TestOverrides): HyperApp {\n  // Merge env: original source + overrides.env (overrides win).\n  const env: AppConfig[\"env\"] | undefined = base.env\n    ? {\n        ...base.env,\n        source: { ...(base.env.source ?? {}), ...(overrides.env ?? {}) },\n      }\n    : overrides.env !== undefined\n      ? { source: { ...overrides.env } }\n      : base.env\n\n  const addDecorators = toArray(overrides.decorate)\n  const decorate =\n    addDecorators.length > 0 ? [...(base.decorate ?? []), ...addDecorators] : base.decorate\n\n  const addDerives = toArray(overrides.derive)\n  const derive = addDerives.length > 0 ? [...(base.derive ?? []), ...addDerives] : base.derive\n\n  let plugins = base.plugins ?? []\n  if (overrides.plugins) {\n    const { skip = [], replace = {}, add = [] } = overrides.plugins\n    plugins = plugins\n      .filter((p) => !skip.includes(p.name))\n      .map((p) => replace[p.name] ?? p)\n      .concat(add)\n  }\n\n  return app({\n    ...base,\n    ...(env !== undefined && { env }),\n    ...(decorate !== undefined && { decorate }),\n    ...(derive !== undefined && { derive }),\n    plugins,\n  })\n}\n\nfunction toArray<T>(x: T | readonly T[] | undefined): readonly T[] {\n  if (x === undefined) return []\n  return Array.isArray(x) ? (x as readonly T[]) : [x as T]\n}\n\ninterface BootedState {\n  readonly env: Record<string, unknown>\n  readonly staticCtx: Record<string, unknown>\n  readonly dispose: () => Promise<void>\n  readonly blueprint: ContextBlueprint\n}\n\ninterface HookPlugins {\n  readonly preRoute: readonly HyperPlugin[]\n  readonly before: readonly HyperPlugin[]\n  readonly after: readonly HyperPlugin[]\n  readonly onError: readonly HyperPlugin[]\n}\n\nasync function handleRequest(\n  req: Request,\n  booted: BootedState,\n  router: Router,\n  security: SecurityDefaults,\n  hooks: HookPlugins,\n): Promise<Response> {\n  // Pathname is extracted via indexOf — we never allocate a URL on the\n  // routing hot path. URL is only built on-demand if the handler reads\n  // `ctx.url` (via a lazy prototype getter on the handler ctx).\n  const rawUrl = req.url\n  const pathname = extractPathname(rawUrl)\n  const https = isHttps(req)\n\n  // Method-override guard — refuse to reinterpret the verb from headers\n  // or query string. CSRF attackers love these; Hyper never honors them.\n  if (security.rejectMethodOverride) {\n    const headers = req.headers\n    for (let i = 0; i < METHOD_OVERRIDE_HEADERS.length; i++) {\n      const h = METHOD_OVERRIDE_HEADERS[i]!\n      if (headers.has(h)) return finalize(methodOverrideRejected(h), https, security)\n    }\n    for (let i = 0; i < METHOD_OVERRIDE_QUERY_KEYS.length; i++) {\n      const q = METHOD_OVERRIDE_QUERY_KEYS[i]!\n      if (urlHasQueryKey(rawUrl, q)) return finalize(methodOverrideRejected(q), https, security)\n    }\n  }\n\n  // Plugin pre-route hooks may short-circuit (CORS preflight, etc.)\n  // before routing, so OPTIONS on unregistered paths still works.\n  if (hooks.preRoute.length > 0) {\n    for (const p of hooks.preRoute) {\n      const r = await p.request!.preRoute!({ req })\n      if (r instanceof Response) return finalize(r, https, security)\n    }\n  }\n\n  const match = router.find(req.method as \"GET\", pathname)\n  if (!match) {\n    return finalize(\n      new Response(\n        JSON.stringify({\n          error: { status: 404, message: `No route for ${req.method} ${pathname}` },\n        }),\n        { status: 404, headers: { \"content-type\": \"application/json; charset=utf-8\" } },\n      ),\n      https,\n      security,\n    )\n  }\n\n  // Per-request ctx — skips the spread when there are no derivers.\n  const ctx = (await applyDerive(booted.blueprint, booted.staticCtx, booted.env, req)) as AppContext\n\n  try {\n    if (hooks.before.length > 0) {\n      for (const p of hooks.before) {\n        await p.request!.before!({ req, ctx, route: match.route })\n      }\n    }\n    const timeoutMs =\n      (match.route.meta.timeoutMs as number | undefined) ?? security.requestTimeoutMs\n    const res =\n      timeoutMs > 0\n        ? await withTimeout(runPipeline(match.route, match.params, req, ctx), timeoutMs)\n        : await runPipeline(match.route, match.params, req, ctx)\n    if (hooks.after.length > 0) {\n      for (const p of hooks.after) {\n        await p.request!.after!({ req, ctx, res, route: match.route })\n      }\n    }\n    return finalize(res, https, security, match.route.meta.headers)\n  } catch (e) {\n    if (hooks.onError.length > 0) {\n      for (const p of hooks.onError) {\n        await p.request!.onError!({ req, ctx, error: e, route: match.route })\n      }\n    }\n    const err = e instanceof SchemaValidationError ? schemaToHyperError(e) : asHyperError(e)\n    return finalize(errorResponse(err), https, security)\n  }\n}\n\nfunction schemaToHyperError(e: SchemaValidationError): HyperError {\n  return new HyperError({\n    status: 400,\n    code: \"validation_failed\",\n    message: \"Request failed validation.\",\n    why: \"One or more inputs did not match the declared schema.\",\n    fix: \"Check the `details` field for per-field issues and correct the payload.\",\n    details: {\n      issues: e.issues.map((i) => ({\n        path: i.path?.map(String) ?? [],\n        message: i.message,\n      })),\n    },\n  })\n}\n\n/**\n * Shared prototype for the per-request handler ctx. Every field that\n * isn't strictly needed up-front is declared as a lazy getter —\n * `ctx.url`, `ctx.query`, `ctx.headers`, `ctx.responseHeaders`. When\n * the route declares a schema we set the parsed value as an own\n * property on the instance, which shadows the getter. The handler\n * pays the cost of allocating a URL / URLSearchParams / Headers only\n * when it actually touches them.\n *\n * Using a shared prototype (not a per-request defineProperty) means\n * every ictx shares one hidden class — JSC specializes it cleanly.\n */\ninterface LazyCtxState {\n  req: Request\n  _url?: URL\n  _query?: unknown\n  _headers?: unknown\n  _rh?: Headers\n  _cookies?: import(\"bun\").CookieMap\n}\n\n/**\n * Each lazy accessor has both a getter (materialize-on-first-read)\n * and a setter (cache override). The setter lets the pipeline write\n * parsed schema output through `ictx.query = parsed` without\n * tripping strict-mode's \"assign to readonly property\" error, while\n * also giving us the hidden-class benefit of a single shared layout.\n */\nconst ICTX_PROTO: PropertyDescriptorMap = {\n  url: {\n    get(this: LazyCtxState): URL {\n      let u = this._url\n      if (u === undefined) {\n        u = new URL(this.req.url)\n        this._url = u\n      }\n      return u\n    },\n    set(this: LazyCtxState, v: URL) {\n      this._url = v\n    },\n    enumerable: true,\n    configurable: true,\n  },\n  query: {\n    get(this: LazyCtxState): unknown {\n      let q = this._query\n      if (q === undefined) {\n        const out: Record<string, string> = {}\n        const url = this.req.url\n        const qi = url.indexOf(\"?\")\n        if (qi >= 0) {\n          const hash = url.indexOf(\"#\", qi)\n          const end = hash < 0 ? url.length : hash\n          const sp = new URLSearchParams(url.slice(qi + 1, end))\n          sp.forEach((v, k) => {\n            out[k] = v\n          })\n        }\n        q = out\n        this._query = q\n      }\n      return q\n    },\n    set(this: LazyCtxState, v: unknown) {\n      this._query = v\n    },\n    enumerable: true,\n    configurable: true,\n  },\n  headers: {\n    get(this: LazyCtxState): unknown {\n      let h = this._headers\n      if (h === undefined) {\n        const out: Record<string, string> = {}\n        ;(this.req as Request).headers.forEach((v, k) => {\n          out[k] = v\n        })\n        h = out\n        this._headers = h\n      }\n      return h\n    },\n    set(this: LazyCtxState, v: unknown) {\n      this._headers = v\n    },\n    enumerable: true,\n    configurable: true,\n  },\n  responseHeaders: {\n    get(this: LazyCtxState): Headers {\n      let rh = this._rh\n      if (rh === undefined) {\n        rh = new Headers()\n        this._rh = rh\n      }\n      return rh\n    },\n    set(this: LazyCtxState, v: Headers) {\n      this._rh = v\n    },\n    enumerable: true,\n    configurable: true,\n  },\n  cookies: {\n    value(this: LazyCtxState): import(\"bun\").CookieMap {\n      let c = this._cookies\n      if (c === undefined) {\n        c = new Bun.CookieMap(this.req.headers.get(\"cookie\") ?? \"\")\n        this._cookies = c\n      }\n      return c\n    },\n    enumerable: true,\n    writable: false,\n    configurable: false,\n  },\n}\n\n// All `ictx` objects share this prototype → one hidden class.\nconst ICTX_PROTOTYPE: object = Object.create(null, ICTX_PROTO)\n\nasync function runPipeline(\n  route: Route,\n  params: Record<string, string>,\n  req: Request,\n  ctx: AppContext,\n): Promise<Response> {\n  const parsedParams = route.params ? await parseStandard(route.params, params) : params\n\n  // The ictx object is laid out with a fixed shape so V8/JSC can\n  // specialize it. Own-property writes for schema-declared inputs\n  // shadow the lazy getters on the prototype.\n  const ictx = Object.create(ICTX_PROTOTYPE) as InternalHandlerCtx & LazyCtxState\n  ictx.req = req\n  ;(ictx as unknown as { params: unknown }).params = parsedParams\n  ;(ictx as unknown as { body: unknown }).body = undefined\n  ;(ictx as unknown as { ctx: AppContext }).ctx = ctx\n\n  if (route.query) {\n    // Schema-declared query → allocate URLSearchParams once, extract\n    // into a plain object, then run Standard Schema over it.\n    const rawUrl = req.url\n    const qi = rawUrl.indexOf(\"?\")\n    const queryInput: Record<string, string> = {}\n    if (qi >= 0) {\n      const hash = rawUrl.indexOf(\"#\", qi)\n      const end = hash < 0 ? rawUrl.length : hash\n      const sp = new URLSearchParams(rawUrl.slice(qi + 1, end))\n      sp.forEach((v, k) => {\n        queryInput[k] = v\n      })\n    }\n    const parsed = await parseStandard(route.query, queryInput)\n    // Writes through the prototype's setter → caches as own state,\n    // shadowing the lazy getter on subsequent reads.\n    ;(ictx as unknown as { query: unknown }).query = parsed\n  }\n\n  if (hasBody(req.method)) {\n    const raw = await parseBodyAuto(req)\n    const parsedBody = route.body ? await parseStandard(route.body, raw) : raw\n    ;(ictx as unknown as { body: unknown }).body = parsedBody\n  }\n\n  if (route.headers) {\n    const headerInput: Record<string, string> = {}\n    req.headers.forEach((v, k) => {\n      headerInput[k] = v\n    })\n    const parsed = await parseStandard(route.headers, headerInput)\n    ;(ictx as unknown as { headers: unknown }).headers = parsed\n  }\n\n  const result = await route.handler(ictx)\n  return coerce(result)\n}\n\nfunction hasBody(method: string): boolean {\n  return method !== \"GET\" && method !== \"HEAD\" && method !== \"OPTIONS\"\n}\n\nfunction finalize(\n  res: Response,\n  https: boolean,\n  security: SecurityDefaults,\n  routeOverrides?: Record<string, string>,\n): Response {\n  if (!security.headers) return res\n  const emitHsts =\n    security.hstsEnv === false\n      ? false\n      : (process.env.NODE_ENV ?? \"development\") === security.hstsEnv\n\n  // Fast path: response helpers pre-bake the secure defaults, so when\n  // there are no route overrides AND we don't need HSTS, we can return\n  // the response unchanged. Probe via `x-content-type-options` — this\n  // is the sentinel that every Hyper helper emits.\n  const needsHsts = https && emitHsts !== false\n  if (\n    !routeOverrides &&\n    !needsHsts &&\n    !res.headers.has(\"server\") &&\n    res.headers.has(\"x-content-type-options\")\n  ) {\n    return res\n  }\n\n  return applyDefaultHeaders(res, {\n    https,\n    emitHsts,\n    ...(routeOverrides ? { overrides: routeOverrides } : {}),\n  })\n}\n\nfunction methodOverrideRejected(which: string): Response {\n  return new Response(\n    JSON.stringify({\n      error: {\n        status: 400,\n        code: \"method_override_rejected\",\n        message: `Hyper refuses to honor method override via '${which}'.`,\n        why: \"Method overrides are a CSRF/verb-smuggling vector and are disabled by default.\",\n        fix: \"Call the endpoint with the real HTTP verb. If you really need overrides, set `app({ security: { rejectMethodOverride: false } })`.\",\n      },\n    }),\n    { status: 400, headers: { \"content-type\": \"application/json; charset=utf-8\" } },\n  )\n}\n\n/**\n * Cheap https detection from `req.url` — avoids allocating a full URL\n * on the boot-error path. `req.url` is always absolute for Bun Request\n * objects produced by `Bun.serve`.\n */\nfunction isHttps(req: Request): boolean {\n  const u = req.url\n  return u.length > 5 && u.charCodeAt(4) === 115 /* 's' */ && u.startsWith(\"https:\")\n}\n\n/**\n * Extract pathname from a fully-qualified request URL without\n * constructing a `URL` object. Returns `/` for inputs without a path.\n * The single allocation is the final `slice()`.\n *\n *   extractPathname(\"http://host:3000/foo/bar?x=1\") === \"/foo/bar\"\n */\nfunction extractPathname(url: string): string {\n  const schemeEnd = url.indexOf(\"://\")\n  if (schemeEnd < 0) return \"/\"\n  const pathStart = url.indexOf(\"/\", schemeEnd + 3)\n  if (pathStart < 0) return \"/\"\n  const q = url.indexOf(\"?\", pathStart)\n  const h = url.indexOf(\"#\", pathStart)\n  const end = q < 0 ? h : h < 0 ? q : Math.min(q, h)\n  return end < 0 ? url.slice(pathStart) : url.slice(pathStart, end)\n}\n\n/**\n * Direct string scan for a query parameter key. Avoids URL /\n * URLSearchParams construction on the method-override guard's hot path.\n */\nfunction urlHasQueryKey(url: string, key: string): boolean {\n  const qStart = url.indexOf(\"?\")\n  if (qStart < 0) return false\n  const hash = url.indexOf(\"#\", qStart)\n  const qEnd = hash < 0 ? url.length : hash\n  const keyLen = key.length\n  let i = qStart + 1\n  while (i < qEnd) {\n    const delim = url.indexOf(\"&\", i)\n    const segEnd = delim < 0 || delim >= qEnd ? qEnd : delim\n    // A key is a match when either \"key=\" starts at i, or the raw key\n    // appears as a flag (no `=`) and runs to segEnd.\n    if (\n      i + keyLen <= segEnd &&\n      url.startsWith(key, i) &&\n      (i + keyLen === segEnd || url.charCodeAt(i + keyLen) === 61) /* '=' */\n    ) {\n      return true\n    }\n    if (delim < 0) return false\n    i = delim + 1\n  }\n  return false\n}\n\nasync function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {\n  let timer: ReturnType<typeof setTimeout> | undefined\n  const timeout = new Promise<never>((_, reject) => {\n    timer = setTimeout(() => {\n      reject(\n        new HyperError({\n          status: 504,\n          code: \"request_timeout\",\n          message: `Handler exceeded ${ms}ms timeout.`,\n          why: \"The handler did not produce a response in time.\",\n          fix: \"Make the handler faster, raise `security.requestTimeoutMs`, or set `.meta({ timeoutMs })` per-route.\",\n        }),\n      )\n    }, ms)\n    if (typeof (timer as { unref?: () => void }).unref === \"function\") {\n      ;(timer as { unref: () => void }).unref()\n    }\n  })\n  try {\n    return await Promise.race([promise, timeout])\n  } finally {\n    if (timer) clearTimeout(timer)\n  }\n}\n\nfunction buildBunRoutes(\n  routes: readonly Route[],\n  fetch: (req: Request) => Promise<Response>,\n): BunRoutes {\n  const map: BunRoutes = {}\n  // Index routes by path so we can detect fully-static paths (every\n  // method on that path is a `.staticResponse()`). Static paths let\n  // Bun.serve's native router short-circuit without calling a fn.\n  const byPath = new Map<string, Route[]>()\n  for (const r of routes) {\n    const list = byPath.get(r.path)\n    if (list) list.push(r)\n    else byPath.set(r.path, [r])\n  }\n  for (const [path, list] of byPath) {\n    const allStatic = list.length > 0 && list.every((r) => r.kind === \"static\")\n    if (allStatic && list.length === 1 && list[0]!.staticResponse) {\n      // Single-method static response → native static route (Response)\n      map[path] = list[0]!.staticResponse\n    } else if (allStatic) {\n      // Method-keyed static responses.\n      const methodMap: Record<string, Response> = {}\n      for (const r of list) {\n        if (r.staticResponse) methodMap[r.method] = r.staticResponse\n      }\n      map[path] = methodMap\n    } else {\n      map[path] = (req: Request) => fetch(req)\n    }\n  }\n  return map\n}\n",
      "sha256": "febb44d5431ee0dfa984f85f2cd16453720ea22b0004456c91ced99114c260e4"
    },
    {
      "path": "core/decorate.ts",
      "contents": "/**\n * Context decoration — three-tier dependency injection.\n *\n * 1. `decorate(env => ({ db, redis }))` at app / group / route level\n *    — static singletons constructed once at boot. Disposed in reverse\n *    order on shutdown via `Symbol.asyncDispose`.\n * 2. `derive(fn)` — runs per-request; computes values from ctx/req\n *    (e.g., `ctx.user` from a JWT claim).\n * 3. Plugin-installed context via `plugin.context`.\n *\n * Types flow via `declare module \"@hyper/core\" { interface AppContext { ... } }`.\n *\n * Recipe (cross-file typing):\n *   // src/ctx.d.ts\n *   import type { Db } from \"./db\"\n *   declare module \"@hyper/core\" { interface AppContext { db: Db } }\n */\n\nimport type { AppContext } from \"./types.ts\"\n\nexport type DecorateFactory<Env = unknown, Added = unknown> = (env: Env) => Added | Promise<Added>\n\nexport type DeriveFactory<\n  Env = unknown,\n  CtxIn extends AppContext = AppContext,\n  Added = unknown,\n> = (args: { ctx: CtxIn; env: Env; req: Request }) => Added | Promise<Added>\n\n/** Registry built at app() time; applied to each request's ctx. */\nexport interface ContextBlueprint<Env = unknown> {\n  readonly decorators: readonly DecorateFactory<Env>[]\n  readonly derives: readonly DeriveFactory<Env>[]\n}\n\n/**\n * Resolve all `decorate()` entries once at boot. Returns the merged\n * static context plus a disposer (async) that runs in reverse order.\n */\nexport async function resolveStaticContext<Env>(\n  bp: ContextBlueprint<Env>,\n  env: Env,\n): Promise<{ ctx: Record<string, unknown>; dispose: () => Promise<void> }> {\n  const merged: Record<string, unknown> = {}\n  const disposers: Array<() => Promise<void>> = []\n  for (const f of bp.decorators) {\n    const added = await f(env)\n    if (added && typeof added === \"object\") {\n      for (const [k, v] of Object.entries(added as Record<string, unknown>)) {\n        merged[k] = v\n        if (isAsyncDisposable(v)) {\n          disposers.push(async () => {\n            await (v as { [Symbol.asyncDispose]: () => PromiseLike<void> })[Symbol.asyncDispose]()\n          })\n        } else if (isDisposable(v)) {\n          disposers.push(async () => {\n            ;(v as { [Symbol.dispose]: () => void })[Symbol.dispose]()\n          })\n        }\n      }\n    }\n  }\n  return {\n    ctx: merged,\n    async dispose() {\n      for (let i = disposers.length - 1; i >= 0; i--) {\n        try {\n          await disposers[i]?.()\n        } catch (err) {\n          console.error(\"hyper: disposer failed:\", err)\n        }\n      }\n    },\n  }\n}\n\n/**\n * Apply per-request `derive()` to an already-static-decorated ctx.\n *\n * Fast path: when there are zero derive functions we return the static\n * ctx as-is — avoiding a per-request shallow clone. Plugins/consumers\n * must treat the ctx as read-only (which the `AppContext` type already\n * implies via declaration-merged readonly surfaces).\n */\nexport async function applyDerive<Env>(\n  bp: ContextBlueprint<Env>,\n  staticCtx: Record<string, unknown>,\n  env: Env,\n  req: Request,\n): Promise<Record<string, unknown>> {\n  if (bp.derives.length === 0) return staticCtx\n  const ctx = { ...staticCtx }\n  for (const f of bp.derives) {\n    const added = await f({ ctx: ctx as AppContext, env, req })\n    if (added && typeof added === \"object\") Object.assign(ctx, added)\n  }\n  return ctx\n}\n\nfunction isAsyncDisposable(v: unknown): boolean {\n  return (\n    typeof v === \"object\" &&\n    v !== null &&\n    typeof (v as Record<PropertyKey, unknown>)[Symbol.asyncDispose] === \"function\"\n  )\n}\n\nfunction isDisposable(v: unknown): boolean {\n  return (\n    typeof v === \"object\" &&\n    v !== null &&\n    typeof (v as Record<PropertyKey, unknown>)[Symbol.dispose] === \"function\"\n  )\n}\n",
      "sha256": "0fae17db271ab3fe0748651d3060c76b085065a99e9b9c8aa8893721264b8cbd"
    },
    {
      "path": "core/env.ts",
      "contents": "/**\n * Environment configuration — layered & parsed once at boot.\n *\n * - `app({ env: schema })` for global, plus `.env(schema)` on group/route.\n * - Layers merge by intersection: the handler sees the union of all\n *   declared fields with narrowed types.\n * - Parse errors throw at boot with a `why`/`fix` shape listing every\n *   field that failed (agents fix all of them in one edit).\n * - Secret marking: paths matching the provided `secret` paths are\n *   redacted by `@hyper/log` and never echoed to error responses.\n * - `useEnv()` via AsyncLocalStorage for deep code.\n */\n\nimport { AsyncLocalStorage } from \"node:async_hooks\"\nimport { HyperError } from \"./error.ts\"\nimport { SchemaValidationError, parseStandard } from \"./standard-schema.ts\"\nimport type { StandardSchemaV1 } from \"./standard-schema.ts\"\n\nexport interface EnvConfig {\n  readonly schema?: StandardSchemaV1\n  /** Dot-paths that should be treated as secret (auto-redacted). */\n  readonly secrets?: readonly string[]\n  /** Source env (defaults to process.env). */\n  readonly source?: Record<string, string | undefined>\n}\n\nconst envStorage = new AsyncLocalStorage<Record<string, unknown>>()\n\n/** Retrieve the current request's env (runs inside an async scope). */\nexport function useEnv<T = Record<string, unknown>>(): T {\n  const env = envStorage.getStore()\n  if (!env) {\n    throw new HyperError({\n      status: 500,\n      message: \"useEnv() called outside a request scope.\",\n      why: \"AsyncLocalStorage has no env; the app is likely not initialized.\",\n      fix: \"Ensure code paths using useEnv() run inside the app.fetch() pipeline.\",\n    })\n  }\n  return env as T\n}\n\nexport function withEnv<T>(env: Record<string, unknown>, fn: () => T): T {\n  return envStorage.run(env, fn)\n}\n\n/**\n * Parse a collection of layer-schemas against `source` once at boot.\n * Returns the merged typed env. Throws `EnvParseError` with a per-field\n * breakdown on failure.\n */\nexport async function parseEnv(\n  layers: readonly StandardSchemaV1[],\n  source: Record<string, string | undefined> = process.env,\n): Promise<Record<string, unknown>> {\n  if (layers.length === 0) return { ...source }\n  const out: Record<string, unknown> = {}\n  const allIssues: Array<{ layer: number; path: string; message: string }> = []\n  for (let i = 0; i < layers.length; i++) {\n    const schema = layers[i]!\n    try {\n      const parsed = (await parseStandard(schema, source)) as Record<string, unknown>\n      Object.assign(out, parsed)\n    } catch (e) {\n      if (e instanceof SchemaValidationError) {\n        for (const issue of e.issues) {\n          allIssues.push({\n            layer: i,\n            path: (issue.path ?? []).map(String).join(\".\") || \"(root)\",\n            message: issue.message,\n          })\n        }\n      } else {\n        throw e\n      }\n    }\n  }\n  if (allIssues.length > 0) throw new EnvParseError(allIssues)\n  return out\n}\n\nexport class EnvParseError extends Error {\n  readonly issues: ReadonlyArray<{ layer: number; path: string; message: string }>\n  constructor(issues: ReadonlyArray<{ layer: number; path: string; message: string }>) {\n    const lines = issues.map((i) => `  layer ${i.layer} ${i.path}: ${i.message}`).join(\"\\n\")\n    super(`Environment did not match declared schema:\\n${lines}`)\n    this.name = \"EnvParseError\"\n    this.issues = issues\n  }\n}\n\n/**\n * Mark secret paths on an env object in-place for @hyper/log consumers.\n * A non-enumerable symbol keyed off the env carries the list.\n */\nexport const SECRET_PATHS: unique symbol = Symbol.for(\"@hyper/core/secret-paths\")\n\nexport function markSecrets<T extends object>(env: T, paths: readonly string[]): T {\n  Object.defineProperty(env, SECRET_PATHS, {\n    value: Object.freeze([...paths]),\n    enumerable: false,\n  })\n  return env\n}\n\nexport function getSecretPaths(env: object): readonly string[] | undefined {\n  const paths = (env as Record<PropertyKey, unknown>)[SECRET_PATHS]\n  if (!paths) return undefined\n  return paths as readonly string[]\n}\n\n/** Helper: wrap a Standard Schema field with a marker string. */\nexport function secret<T>(schema: T): T & { __hyperSecret: true } {\n  return schema as T & { __hyperSecret: true }\n}\n",
      "sha256": "51a24721cd91e60b83dbc64d63c61165c5752c4511c6e88949e6ff8c9647567c"
    },
    {
      "path": "core/error.ts",
      "contents": "/**\n * Structured errors.\n *\n * Hyper distinguishes thrown errors (unexpected) from returned errors\n * (contract-defined). `createError` produces the thrown shape with\n * `why`/`fix` fields that surface in logs, error responses, and the\n * MCP error payload — making failures actionable for both humans\n * and agents.\n */\n\nexport interface HyperErrorInit {\n  /** HTTP status to project (defaults to 500). */\n  status?: number\n  /** Short machine code (e.g., \"email_exists\"). */\n  code?: string\n  /** Human message. */\n  message: string\n  /** Why this happened — explained to the caller (not internal details). */\n  why?: string\n  /** How to fix it — agent-actionable. */\n  fix?: string\n  /** Documentation or recovery links. */\n  links?: readonly string[]\n  /** Arbitrary structured detail (redacted in logs if matching secret paths). */\n  details?: Record<string, unknown>\n  /** Underlying cause (stripped from wire response; kept in logs). */\n  cause?: unknown\n}\n\nexport class HyperError extends Error {\n  readonly status: number\n  readonly code?: string\n  readonly why?: string\n  readonly fix?: string\n  readonly links?: readonly string[]\n  readonly details?: Record<string, unknown>\n\n  constructor(init: HyperErrorInit) {\n    super(init.message, { cause: init.cause })\n    this.name = \"HyperError\"\n    this.status = init.status ?? 500\n    if (init.code !== undefined) this.code = init.code\n    if (init.why !== undefined) this.why = init.why\n    if (init.fix !== undefined) this.fix = init.fix\n    if (init.links !== undefined) this.links = init.links\n    if (init.details !== undefined) this.details = init.details\n  }\n\n  /** Wire shape — safe to serialize to clients and agents. */\n  toJSON(): Record<string, unknown> {\n    const base: Record<string, unknown> = {\n      error: {\n        status: this.status,\n        message: this.message,\n      },\n    }\n    const err = base.error as Record<string, unknown>\n    if (this.code) err.code = this.code\n    if (this.why) err.why = this.why\n    if (this.fix) err.fix = this.fix\n    if (this.links) err.links = this.links\n    if (this.details) err.details = this.details\n    return base\n  }\n}\n\n/** Factory — preferred API. */\nexport function createError(init: HyperErrorInit): HyperError {\n  return new HyperError(init)\n}\n\n/** Project unknown errors into a HyperError at the boundary. */\nexport function asHyperError(e: unknown): HyperError {\n  if (e instanceof HyperError) return e\n  if (e instanceof Error) {\n    return new HyperError({\n      status: 500,\n      message: e.message || \"Internal Server Error\",\n      why: \"Handler threw an unhandled error.\",\n      fix: \"Check server logs for the stack trace.\",\n      cause: e,\n    })\n  }\n  return new HyperError({\n    status: 500,\n    message: \"Internal Server Error\",\n    why: \"Handler threw a non-Error value.\",\n    cause: e,\n  })\n}\n",
      "sha256": "9885ff59ffa0a40c58604eecbf204d6d56185a9cb65080bb306f4552f291be3a"
    },
    {
      "path": "core/example.ts",
      "contents": "/**\n * runExamples(app) — walks every route's `meta.examples` and executes each\n * example against the in-process app.invoke() path. Used by `hyper test`\n * and directly inside consumer test files (see @hyper/testing).\n */\n\nimport type { HttpMethod, HyperApp, RouteExample } from \"./types.ts\"\n\nexport interface ExampleResult {\n  readonly route: string\n  readonly method: string\n  readonly example: string\n  readonly ok: boolean\n  readonly status: number\n  readonly expected?: number\n  readonly actual?: unknown\n  readonly error?: string\n}\n\nexport async function runExamples(app: HyperApp): Promise<readonly ExampleResult[]> {\n  const out: ExampleResult[] = []\n  for (const route of app.routeList) {\n    const examples = route.meta.examples as readonly RouteExample[] | undefined\n    if (!examples || examples.length === 0) continue\n    for (const ex of examples) {\n      const expected = ex.output?.status\n      try {\n        const result = await app.invoke({\n          method: route.method as HttpMethod,\n          path: route.path,\n          ...(ex.input?.params && { params: ex.input.params as Record<string, string> }),\n          ...(ex.input?.query && { query: ex.input.query }),\n          ...(ex.input?.body !== undefined && { body: ex.input.body }),\n          ...(ex.input?.headers && {\n            headers: Object.fromEntries(\n              Object.entries(ex.input.headers).map(([k, v]) => [k, String(v)]),\n            ),\n          }),\n        })\n        const statusOk = expected === undefined ? result.status < 400 : result.status === expected\n        const bodyOk =\n          ex.output?.body === undefined\n            ? true\n            : JSON.stringify(result.data) === JSON.stringify(ex.output.body)\n        out.push({\n          route: route.path,\n          method: route.method,\n          example: ex.name,\n          ok: statusOk && bodyOk,\n          status: result.status,\n          ...(expected !== undefined && { expected }),\n          ...(ex.output?.body !== undefined && { actual: result.data }),\n        })\n      } catch (e) {\n        out.push({\n          route: route.path,\n          method: route.method,\n          example: ex.name,\n          ok: false,\n          status: 0,\n          error: (e as Error).message,\n        })\n      }\n    }\n  }\n  return out\n}\n",
      "sha256": "a2eb72f9af0d96bdeece06b35b2ee9eba3afede8c70e8d1b8cae1b46333ff3df"
    },
    {
      "path": "core/file.ts",
      "contents": "/**\n * file() — serve a file from disk via `Bun.file(path)`.\n *\n * Refuses `..` segments by default to prevent path traversal. Users\n * can opt in explicitly when serving from a safely-sandboxed path.\n */\n\nimport { HyperError } from \"./error.ts\"\n\nexport interface FileOptions {\n  /** Only set this for a path you fully control and sanitize upstream. */\n  readonly allowTraversal?: boolean\n  /** Optional content-type override; otherwise Bun sniffs from extension. */\n  readonly type?: string\n}\n\n/**\n * Return a `Bun.file(path)` response helper. The response layer\n * detects the BunFile shape and passes through via `sendfile`.\n */\nexport function file(path: string, opts: FileOptions = {}): import(\"bun\").BunFile {\n  if (!opts.allowTraversal && hasTraversal(path)) {\n    throw new HyperError({\n      status: 400,\n      code: \"path_traversal\",\n      message: \"Refusing to serve a path containing '..' segments.\",\n      why: \"Path traversal is a common attack; Hyper rejects it at the file helper.\",\n      fix: \"Pass `allowTraversal: true` only when serving from a sandboxed prefix you control.\",\n    })\n  }\n  return Bun.file(path, opts.type ? { type: opts.type } : undefined)\n}\n\nfunction hasTraversal(p: string): boolean {\n  // Normalize forward+backslashes.\n  const normalized = p.replace(/\\\\/g, \"/\")\n  for (const seg of normalized.split(\"/\")) {\n    if (seg === \"..\") return true\n  }\n  return false\n}\n",
      "sha256": "d4bb32c62d9778df1651dc9ed3b9f1a3538c81ed14704302c1369ad822b3c810"
    },
    {
      "path": "core/group.ts",
      "contents": "/**\n * group(prefix) — full composition API.\n *\n * - `.use(middleware)` — prepended to each route's chain\n * - `.meta(obj)` — merged into each route's meta\n * - `.add(...routes)` — register routes (paths rewritten with prefix)\n * - `.merge(otherGroup)` — absorb another group's routes+middleware\n * - `.prefix(more)` — return a new group rooted deeper\n * - `.lazy(() => import(...))` — code-splitting; resolved on first match\n *\n * Plain-object router shape:\n *   app({ router: { users: { create, get } } })\n * is equivalent to a group tree — the nested-object layout maps 1:1\n * into `api.users.create()` at the client.\n */\n\nimport { type Middleware, compileChain } from \"./middleware.ts\"\nimport type { Route, RouteGroup, RouteMeta } from \"./types.ts\"\n\nexport type LazyGroup = () => Promise<{ default: GroupBuilder } | GroupBuilder>\n\nexport class GroupBuilder {\n  readonly #prefix: string\n  readonly #routes: Route[] = []\n  readonly #middleware: Middleware[] = []\n  readonly #meta: RouteMeta = {}\n  readonly #lazyLoaders: LazyGroup[] = []\n\n  constructor(prefix = \"\") {\n    this.#prefix = normalizePrefix(prefix)\n  }\n\n  add(...routes: Route[]): GroupBuilder {\n    for (const r of routes) {\n      this.#routes.push(this.#decorate(r))\n    }\n    return this\n  }\n\n  use(mw: Middleware): GroupBuilder {\n    this.#middleware.push(mw)\n    // Re-decorate: existing routes need the new middleware prepended.\n    for (let i = 0; i < this.#routes.length; i++) {\n      const r = this.#routes[i]!\n      this.#routes[i] = {\n        ...r,\n        handler: wrapWithMiddleware(r.path, r.handler, [mw]),\n      }\n    }\n    return this\n  }\n\n  meta(meta: RouteMeta): GroupBuilder {\n    Object.assign(this.#meta, meta)\n    for (let i = 0; i < this.#routes.length; i++) {\n      const r = this.#routes[i]!\n      this.#routes[i] = { ...r, meta: { ...meta, ...r.meta } }\n    }\n    return this\n  }\n\n  prefix(more: string): GroupBuilder {\n    return new GroupBuilder(joinPath(this.#prefix, more))\n  }\n\n  merge(other: GroupBuilder): GroupBuilder {\n    const built = other.build()\n    // Merged routes already carry their full path; don't re-prefix.\n    for (const r of built.routes) {\n      const handler =\n        this.#middleware.length > 0\n          ? wrapWithMiddleware(r.path, r.handler, this.#middleware)\n          : r.handler\n      this.#routes.push({ ...r, meta: { ...this.#meta, ...r.meta }, handler })\n    }\n    return this\n  }\n\n  lazy(loader: LazyGroup): GroupBuilder {\n    this.#lazyLoaders.push(loader)\n    return this\n  }\n\n  /** Resolve lazy groups (called by app() at construction). */\n  async resolve(): Promise<void> {\n    for (const loader of this.#lazyLoaders) {\n      const mod = await loader()\n      const g = mod instanceof GroupBuilder ? mod : mod.default\n      await g.resolve()\n      this.merge(g)\n    }\n  }\n\n  build(): RouteGroup {\n    return { prefix: this.#prefix, routes: [...this.#routes] }\n  }\n\n  /**\n   * Invoke a route as a function (integration tests, projections).\n   * Walks the group's routes and dispatches to .callable() when present.\n   */\n  async call<T = unknown>(\n    method: string,\n    path: string,\n    input: {\n      params?: Record<string, unknown>\n      query?: Record<string, unknown>\n      body?: unknown\n      headers?: Record<string, string>\n      req?: Request\n    } = {},\n  ): Promise<T> {\n    const routes = [...this.#routes]\n    const full = joinPath(\"\", path)\n    const match = routes.find((r) => r.method === method.toUpperCase() && r.path === full)\n    if (!match) throw new Error(`group.call: no route ${method} ${full}`)\n    // Prefer attached .callable() if present.\n    const callable = (match as { callable?: (i: unknown) => Promise<unknown> }).callable\n    if (callable) return callable(input) as Promise<T>\n    // Fallback: run via internal handler.\n    const req = input.req ?? new Request(`http://local${full}`, { method })\n    const result = await match.handler({\n      req,\n      url: new URL(req.url),\n      params: (input.params ?? {}) as Record<string, string>,\n      query: new URLSearchParams((input.query as Record<string, string> | undefined) ?? {}),\n      headers: new Headers((input.headers ?? {}) as Record<string, string>),\n      body: input.body,\n      ctx: {},\n      cookies: () => new Bun.CookieMap(req.headers.get(\"cookie\") ?? \"\"),\n      responseHeaders: new Headers(),\n    })\n    return result as T\n  }\n\n  #decorate(r: Route): Route {\n    const path = joinPath(this.#prefix, r.path)\n    const meta = { ...this.#meta, ...r.meta }\n    const handler =\n      this.#middleware.length > 0\n        ? wrapWithMiddleware(path, r.handler, this.#middleware)\n        : r.handler\n    return { ...r, path, meta, handler }\n  }\n}\n\nfunction wrapWithMiddleware(\n  path: string,\n  handler: Route[\"handler\"],\n  mws: readonly Middleware[],\n): Route[\"handler\"] {\n  const runner = compileChain(mws)\n  return (ictx) =>\n    runner(\n      {\n        ctx: ictx.ctx,\n        input: { params: ictx.params, query: ictx.query, body: ictx.body, headers: ictx.headers },\n        req: ictx.req,\n        path,\n        params: ictx.params,\n      },\n      () => handler(ictx),\n    ) as ReturnType<Route[\"handler\"]>\n}\n\nexport function group(prefix = \"\"): GroupBuilder {\n  return new GroupBuilder(prefix)\n}\n\n/** Create a lazy group placeholder. */\nexport function lazy(loader: LazyGroup): GroupBuilder {\n  const g = new GroupBuilder()\n  g.lazy(loader)\n  return g\n}\n\nfunction normalizePrefix(p: string): string {\n  if (p === \"\" || p === \"/\") return \"\"\n  let out = p.startsWith(\"/\") ? p : `/${p}`\n  if (out.endsWith(\"/\")) out = out.slice(0, -1)\n  return out\n}\n\nfunction joinPath(prefix: string, rest: string): string {\n  const r = rest.startsWith(\"/\") ? rest : `/${rest}`\n  if (prefix === \"\") return r\n  return `${prefix}${r}`\n}\n\n// ------------------------------------------------------------------\n// Plain-object router → GroupBuilder\n// ------------------------------------------------------------------\n\n/**\n * A plain-object router. Nested records of routes or\n * sub-routers become a group tree. Paths come from the route's own\n * `.path`; object keys provide the typed-client namespace only.\n */\nexport interface PlainRouter {\n  [key: string]: Route | PlainRouter\n}\n\nexport function fromPlainRouter(router: PlainRouter, prefix = \"\"): GroupBuilder {\n  const g = new GroupBuilder(prefix)\n  walk(router, g)\n  return g\n}\n\nfunction walk(router: PlainRouter, g: GroupBuilder): void {\n  for (const v of Object.values(router)) {\n    if (isRoute(v)) g.add(v)\n    else walk(v, g)\n  }\n}\n\nfunction isRoute(x: unknown): x is Route {\n  return (\n    typeof x === \"object\" &&\n    x !== null &&\n    typeof (x as { handler?: unknown }).handler === \"function\" &&\n    typeof (x as { method?: unknown }).method === \"string\" &&\n    typeof (x as { path?: unknown }).path === \"string\"\n  )\n}\n",
      "sha256": "2ecc2a8c85b38a5c1051e4338d7dc2db94a0f6ede3ca21f7e7f0d86a7560266c"
    },
    {
      "path": "core/hash.ts",
      "contents": "/**\n * Hashing helpers — always `Bun.hash.xxHash3` for cache keys, ETags,\n * idempotency keys. Never `crypto.createHash('md5')` or similar.\n *\n * Constant-time compare uses `node:crypto.timingSafeEqual` (Bun's\n * zero-cost polyfill; no userland substitute).\n */\n\nimport { timingSafeEqual as nodeTimingSafeEqual } from \"node:crypto\"\n\nexport function xxh3(input: string | ArrayBufferView | ArrayBufferLike): string {\n  const buf = typeof input === \"string\" ? new TextEncoder().encode(input) : input\n  // Bun.hash.xxHash3 accepts ArrayBuffer/TypedArray/string.\n  return Bun.hash.xxHash3(buf as Parameters<typeof Bun.hash.xxHash3>[0]).toString(16)\n}\n\n/** Build an ETag for a response body. Strong by default. */\nexport function etag(body: string | ArrayBufferView | ArrayBufferLike): string {\n  return `\"${xxh3(body)}\"`\n}\n\n/**\n * Constant-time string equality. Inputs must be the same length;\n * otherwise returns false without any comparison (short-circuit is\n * safe — leaking length is not a secret-material leak).\n */\nexport function timingSafeEqualStr(a: string, b: string): boolean {\n  if (a.length !== b.length) return false\n  return nodeTimingSafeEqual(Buffer.from(a), Buffer.from(b))\n}\n",
      "sha256": "6aca4a7885e1846f66550601ba62755fd995d3045ab0e98534830b05d9586b15"
    },
    {
      "path": "core/hyper.ts",
      "contents": "/**\n * Hyper — the top-level chain API.\n *\n * A thin, ergonomic wrapper around `app({...})` + the `route.<verb>`\n * builder. Construct a server with `new Hyper()`, add routes via verb\n * shortcuts, compose sub-apps / plugins / middleware / namespaces via\n * the polymorphic `.use()`, and boot with `.listen()`.\n *\n *   export default new Hyper()\n *     .get(\"/\", () => \"Hello Hyper\")\n *     .listen(3000)\n *\n * The chain class is additive — everything still lowers to the existing\n * `Route`/`HyperApp` primitives, so plugins, openapi projection,\n * testing, and CLI tooling keep working unchanged.\n */\n\nimport type { Server } from \"bun\"\nimport { app } from \"./app.ts\"\nimport { GroupBuilder, fromPlainRouter } from \"./group.ts\"\nimport { type ChainRunner, type Middleware, compileChain } from \"./middleware.ts\"\nimport { type RouteBuilder, route } from \"./route.ts\"\nimport type { HandlerCtx } from \"./route.ts\"\nimport type { StandardSchemaV1 } from \"./standard-schema.ts\"\nimport type {\n  AppConfig,\n  AppContext,\n  BunRoutes,\n  DecorateFactory,\n  DeriveFactory,\n  EnvConfigLike,\n  HandlerReturn,\n  HttpMethod,\n  HyperApp,\n  HyperPlugin,\n  InternalHandlerCtx,\n  InvokeInput,\n  InvokeResult,\n  PlainRouterConfig,\n  Route,\n  RouteGroup,\n  RouteHandler,\n  RouteMeta,\n  SecurityDefaults,\n  TestOverrides,\n} from \"./types.ts\"\n\n/** Version string for the banner line. Stays in sync with `@hyper/core`. */\nconst HYPER_VERSION = \"0.1.0\"\n\n/** Constructor-time options. */\nexport interface HyperOptions {\n  /** Mount all routes added on this instance under this prefix. */\n  readonly prefix?: string\n  /** Security baseline overrides — merged with secure-by-default. */\n  readonly security?: Partial<SecurityDefaults>\n  /** Env schema + secrets + source. Parsed at boot. */\n  readonly env?: EnvConfigLike\n  /** Optional name — surfaces in banner, logs, and error messages. */\n  readonly name?: string\n}\n\n/** Options for `.listen()`. */\nexport interface ListenOptions {\n  readonly port?: number\n  readonly hostname?: string\n  /** Bun.serve idleTimeout (seconds). Default: 10. */\n  readonly idleTimeout?: number\n  /** Bun.serve development flag. Default: true when NODE_ENV !== \"production\". */\n  readonly development?: boolean\n  /** Print the startup banner. Default: true outside of production. */\n  readonly banner?: boolean\n  /** Wire SIGTERM/SIGINT → drain. Default: true. */\n  readonly drain?: boolean\n}\n\n// ---------------------------------------------------------------------\n// Type helpers\n// ---------------------------------------------------------------------\n\n/**\n * Extract the `:param` tokens from a path literal as a `{ [name]: string }`\n * record. Mirrors the runtime grammar exactly:\n *\n *   - Param names match `[A-Za-z_][A-Za-z0-9_]*`.\n *   - Anything else (including `.`, `@`, `-`, slashes) is a literal that ends\n *     the param name. So `\"/r/:name@:version.json\"` infers `{ name; version }`,\n *     not `{ \"name@:version.json\"; ... }`.\n *\n * Falls back to an empty record when the path has no params — destructuring\n * `({ params })` stays valid on schema-less static paths.\n */\ntype _IdentChar =\n  | \"_\"\n  | \"a\"\n  | \"b\"\n  | \"c\"\n  | \"d\"\n  | \"e\"\n  | \"f\"\n  | \"g\"\n  | \"h\"\n  | \"i\"\n  | \"j\"\n  | \"k\"\n  | \"l\"\n  | \"m\"\n  | \"n\"\n  | \"o\"\n  | \"p\"\n  | \"q\"\n  | \"r\"\n  | \"s\"\n  | \"t\"\n  | \"u\"\n  | \"v\"\n  | \"w\"\n  | \"x\"\n  | \"y\"\n  | \"z\"\n  | \"A\"\n  | \"B\"\n  | \"C\"\n  | \"D\"\n  | \"E\"\n  | \"F\"\n  | \"G\"\n  | \"H\"\n  | \"I\"\n  | \"J\"\n  | \"K\"\n  | \"L\"\n  | \"M\"\n  | \"N\"\n  | \"O\"\n  | \"P\"\n  | \"Q\"\n  | \"R\"\n  | \"S\"\n  | \"T\"\n  | \"U\"\n  | \"V\"\n  | \"W\"\n  | \"X\"\n  | \"Y\"\n  | \"Z\"\n  | \"0\"\n  | \"1\"\n  | \"2\"\n  | \"3\"\n  | \"4\"\n  | \"5\"\n  | \"6\"\n  | \"7\"\n  | \"8\"\n  | \"9\"\n\n/** Walk forward from a `:`, accumulating identifier chars into the name. */\ntype _ReadName<P extends string, Acc extends string = \"\"> = P extends `${infer C}${infer Rest}`\n  ? C extends _IdentChar\n    ? _ReadName<Rest, `${Acc}${C}`>\n    : { name: Acc; rest: P }\n  : { name: Acc; rest: \"\" }\n\n/**\n * Walk the path character-by-character, accumulating each `:name` we hit\n * into a union. We can't use `${string}:${infer After}` because TS\n * template-literal inference is greedy on the prefix and would skip over\n * intermediate params (e.g. dropping `name` from `:name@:version`).\n */\ntype _Walk<P extends string, Acc extends string = never> = P extends `:${infer After}`\n  ? _ReadName<After> extends { name: infer N; rest: infer R }\n    ? N extends \"\"\n      ? // \":\" not followed by an identifier char — treat as literal, advance one char.\n        After extends `${string}${infer Tail}`\n        ? _Walk<Tail, Acc>\n        : Acc\n      : N extends string\n        ? R extends string\n          ? _Walk<R, Acc | N>\n          : Acc | N\n        : Acc\n    : Acc\n  : P extends `${infer _C}${infer Rest}`\n    ? _Walk<Rest, Acc>\n    : Acc\n\nexport type PathParams<P extends string> = [_Walk<P>] extends [never]\n  ? Record<string, never>\n  : { [K in _Walk<P>]: string }\n\n/**\n * Narrow the output of a `StandardSchemaV1` or fall back to `Fallback`\n * when the schema is absent (undefined in the options bag).\n */\nexport type InferSchema<S, Fallback> = S extends StandardSchemaV1<unknown, infer O> ? O : Fallback\n\n/** Per-route options accepted by the verb shortcuts. */\nexport interface RouteOpts<\n  P extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined,\n  Q extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined,\n  B extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined,\n  H extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined,\n> {\n  readonly params?: P\n  readonly query?: Q\n  readonly body?: B\n  readonly headers?: H\n  readonly meta?: RouteMeta\n  readonly use?: readonly Middleware[]\n  /** Declared thrown-error shapes keyed by HTTP status. */\n  readonly throws?: Record<number, StandardSchemaV1>\n  /** Named error-code catalog. */\n  readonly errors?: Record<string, StandardSchemaV1>\n}\n\n/** The typed handler signature for `new Hyper<Ctx>().<verb>(path, [opts,] handler)`. */\nexport type VerbHandler<\n  Path extends string = string,\n  Opts extends RouteOpts | undefined = undefined,\n  Ctx extends AppContext = AppContext,\n> = (\n  ctx: HandlerCtx<\n    Opts extends { params: infer P } ? InferSchema<P, PathParams<Path>> : PathParams<Path>,\n    Opts extends { query: infer Q } ? InferSchema<Q, unknown> : unknown,\n    Opts extends { body: infer B } ? InferSchema<B, unknown> : unknown,\n    Opts extends { headers: infer H } ? InferSchema<H, unknown> : unknown,\n    Ctx\n  >,\n) => HandlerReturn | Promise<HandlerReturn>\n\n/**\n * Polymorphic dispatch — `.use()` accepts any of these shapes.\n *\n * Order of discrimination (see {@link Hyper.use}):\n *   1. `Hyper`  — sub-app composition (its own prefix honored).\n *   2. `GroupBuilder` — flatten to RouteGroup, apply parent prefix.\n *   3. `RouteGroup`   — same as above.\n *   4. `Route` | `Route[]` — register directly.\n *   5. `HyperPlugin`  — install (must have `name: string`).\n *   6. `Middleware`   — `typeof fn === \"function\"`, appended to the stack.\n *   7. Plain object   — walked as an ESM namespace / PlainRouter.\n */\nexport type UseArg =\n  // biome-ignore lint/suspicious/noExplicitAny: variance hole for heterogeneous Hyper<Ctx>\n  | Hyper<any>\n  | GroupBuilder\n  | RouteGroup\n  | Route\n  | readonly Route[]\n  | HyperPlugin\n  | Middleware\n  | Record<string, unknown>\n\n/** Brand used for duck-typed recognition across package boundaries. */\nexport const HYPER_BUILDER_BRAND = \"__hyperBuilder__\" as const\n\n// Shared state for graceful-shutdown handler. One process-level\n// listener per signal no matter how many `Hyper` instances `.listen()`.\n// biome-ignore lint/suspicious/noExplicitAny: heterogeneous by design\nconst activeServers: Set<Hyper<any>> = new Set()\nlet drainInstalled = false\n\nfunction installDrainHandlersOnce(): void {\n  if (drainInstalled) return\n  drainInstalled = true\n  const shutdown = (signal: string): void => {\n    // Drain every live server; do not exit until all have resolved.\n    const pending: Promise<void>[] = []\n    for (const inst of activeServers) {\n      const s = inst.server\n      if (!s) continue\n      pending.push(\n        Promise.resolve(s.stop(false))\n          .then(() => undefined)\n          .catch(() => undefined),\n      )\n    }\n    void Promise.all(pending).then(() => {\n      // Respect the Unix convention: 128 + signal number.\n      const code = signal === \"SIGTERM\" ? 128 + 15 : signal === \"SIGINT\" ? 128 + 2 : 0\n      process.exit(code)\n    })\n  }\n  process.on(\"SIGTERM\", () => shutdown(\"SIGTERM\"))\n  process.on(\"SIGINT\", () => shutdown(\"SIGINT\"))\n}\n\n/**\n * The top-level chain class.\n *\n * Mutable by design — every method returns `this`. Once `.build()` or\n * `.listen()` has produced a `HyperApp`, subsequent mutations transparently\n * invalidate the cache and rebuild on next access.\n */\nexport class Hyper<Ctx extends AppContext = AppContext> {\n  /** Duck-typed brand so CLI tooling can recognize a `Hyper` across package boundaries. */\n  readonly __hyperBuilder__ = true\n\n  readonly #prefix: string\n  readonly #options: HyperOptions\n  readonly #routes: Route[] = []\n  readonly #middleware: Middleware[] = []\n  readonly #decorators: DecorateFactory[] = []\n  readonly #derives: DeriveFactory[] = []\n  readonly #plugins: HyperPlugin[] = []\n  #routerConfig?: PlainRouterConfig\n  #envConfig?: EnvConfigLike\n  #securityOverrides: Partial<SecurityDefaults> = {}\n  #built: HyperApp | undefined\n  #server: Server<unknown> | undefined\n\n  constructor(opts: HyperOptions = {}) {\n    this.#prefix = normalizePrefix(opts.prefix ?? \"\")\n    this.#options = opts\n    if (opts.security) this.#securityOverrides = { ...opts.security }\n    if (opts.env !== undefined) this.#envConfig = opts.env\n  }\n\n  // -----------------------------------------------------------------\n  // Introspection\n  // -----------------------------------------------------------------\n\n  /** The normalized prefix (e.g. \"/users\"). Empty string when unset. */\n  get prefix(): string {\n    return this.#prefix\n  }\n\n  /** The live `Bun.Server` — populated after `.listen()`. */\n  get server(): Server<unknown> | undefined {\n    return this.#server\n  }\n\n  /** Display name (for banners / diagnostics). */\n  get name(): string {\n    return this.#options.name ?? \"hyper\"\n  }\n\n  /** Raw route list — forwards to the built app. */\n  get routeList(): readonly Route[] {\n    return this.build().routeList\n  }\n\n  /** `Bun.serve({ routes })` compatible map — forwards to the built app. */\n  get routes(): BunRoutes {\n    return this.build().routes\n  }\n\n  /** fetch-compatible entry point for any Bun/edge/workers adapter. */\n  get fetch(): (req: Request) => Promise<Response> {\n    return this.build().fetch\n  }\n\n  // -----------------------------------------------------------------\n  // Verb shortcuts\n  // -----------------------------------------------------------------\n\n  get<Path extends string>(path: Path, handler: VerbHandler<Path, undefined, Ctx>): this\n  get<Path extends string, const O extends RouteOpts>(\n    path: Path,\n    opts: O,\n    handler: VerbHandler<Path, O, Ctx>,\n  ): this\n  get(path: string, body: string): this\n  get(path: string, a: unknown, b?: unknown): this {\n    return this.#addRoute(\"GET\", path, a, b)\n  }\n  post<Path extends string>(path: Path, handler: VerbHandler<Path, undefined, Ctx>): this\n  post<Path extends string, const O extends RouteOpts>(\n    path: Path,\n    opts: O,\n    handler: VerbHandler<Path, O, Ctx>,\n  ): this\n  post(path: string, body: string): this\n  post(path: string, a: unknown, b?: unknown): this {\n    return this.#addRoute(\"POST\", path, a, b)\n  }\n  put<Path extends string>(path: Path, handler: VerbHandler<Path, undefined, Ctx>): this\n  put<Path extends string, const O extends RouteOpts>(\n    path: Path,\n    opts: O,\n    handler: VerbHandler<Path, O, Ctx>,\n  ): this\n  put(path: string, body: string): this\n  put(path: string, a: unknown, b?: unknown): this {\n    return this.#addRoute(\"PUT\", path, a, b)\n  }\n  patch<Path extends string>(path: Path, handler: VerbHandler<Path, undefined, Ctx>): this\n  patch<Path extends string, const O extends RouteOpts>(\n    path: Path,\n    opts: O,\n    handler: VerbHandler<Path, O, Ctx>,\n  ): this\n  patch(path: string, body: string): this\n  patch(path: string, a: unknown, b?: unknown): this {\n    return this.#addRoute(\"PATCH\", path, a, b)\n  }\n  delete<Path extends string>(path: Path, handler: VerbHandler<Path, undefined, Ctx>): this\n  delete<Path extends string, const O extends RouteOpts>(\n    path: Path,\n    opts: O,\n    handler: VerbHandler<Path, O, Ctx>,\n  ): this\n  delete(path: string, body: string): this\n  delete(path: string, a: unknown, b?: unknown): this {\n    return this.#addRoute(\"DELETE\", path, a, b)\n  }\n  head<Path extends string>(path: Path, handler: VerbHandler<Path, undefined, Ctx>): this\n  head<Path extends string, const O extends RouteOpts>(\n    path: Path,\n    opts: O,\n    handler: VerbHandler<Path, O, Ctx>,\n  ): this\n  head(path: string, body: string): this\n  head(path: string, a: unknown, b?: unknown): this {\n    return this.#addRoute(\"HEAD\", path, a, b)\n  }\n  options<Path extends string>(path: Path, handler: VerbHandler<Path, undefined, Ctx>): this\n  options<Path extends string, const O extends RouteOpts>(\n    path: Path,\n    opts: O,\n    handler: VerbHandler<Path, O, Ctx>,\n  ): this\n  options(path: string, body: string): this\n  options(path: string, a: unknown, b?: unknown): this {\n    return this.#addRoute(\"OPTIONS\", path, a, b)\n  }\n\n  // -----------------------------------------------------------------\n  // Composition\n  // -----------------------------------------------------------------\n\n  /** Register a plain-object router. Nested keys become a group tree. */\n  router(cfg: PlainRouterConfig): this {\n    this.#invalidate()\n    // Delegate to fromPlainRouter; the prefix flow mirrors sub-app use.\n    const g = fromPlainRouter(cfg as never, \"\")\n    for (const r of g.build().routes) {\n      this.#routes.push(this.#prefixAndWrap(r, \"\"))\n    }\n    // Keep a reference so the raw router config is still available to\n    // tooling that inspects `app.__config.router` (e.g. typed clients).\n    this.#routerConfig = cfg\n    return this\n  }\n\n  /** Polymorphic `.use()` — see {@link UseArg}. */\n  // biome-ignore lint/suspicious/noExplicitAny: sub-app Ctx is opaque at this boundary\n  use(prefix: string, sub: Hyper<any>): this\n  use(arg: UseArg): this\n  use(arg1: unknown, arg2?: unknown): this {\n    this.#invalidate()\n\n    // Two-arg form: (prefix, sub-app)\n    if (typeof arg1 === \"string\") {\n      if (!(arg2 instanceof Hyper)) {\n        throw new Error(\"Hyper.use(prefix, sub): the second argument must be a Hyper instance.\")\n      }\n      return this.#useSubApp(arg2, normalizePrefix(arg1))\n    }\n\n    const arg = arg1\n    if (arg instanceof Hyper) return this.#useSubApp(arg, \"\")\n    if (arg instanceof GroupBuilder) return this.#useGroup(arg.build())\n    if (isRouteGroup(arg)) return this.#useGroup(arg)\n    if (isRoute(arg)) return this.#useRoutes([arg])\n    if (Array.isArray(arg) && arg.every(isRoute)) return this.#useRoutes(arg as readonly Route[])\n    if (isHyperPlugin(arg)) {\n      this.#plugins.push(arg)\n      return this\n    }\n    if (typeof arg === \"function\") {\n      this.#middleware.push(arg as Middleware)\n      return this\n    }\n    // ESM namespace / plain object → walk for Route-shaped values.\n    if (typeof arg === \"object\" && arg !== null) {\n      return this.#useNamespace(arg as Record<string, unknown>)\n    }\n\n    throw new Error(\n      \"Hyper.use: unsupported argument. Accepts Hyper sub-app, GroupBuilder, RouteGroup, Route(s), HyperPlugin, Middleware, or ESM namespace.\",\n    )\n  }\n\n  /** Mount a single plugin by name. Identical to `.use(plugin)`. */\n  plugin(p: HyperPlugin): this {\n    this.#invalidate()\n    this.#plugins.push(p)\n    return this\n  }\n\n  /**\n   * Static context decoration (db, redis, caches) — constructed once at boot.\n   * Returns a `Hyper` with the widened `Ctx` so downstream handlers see the\n   * added shape without casting.\n   */\n  decorate<A extends object>(factory: (env?: unknown) => A | Promise<A>): Hyper<Ctx & Readonly<A>> {\n    this.#invalidate()\n    this.#decorators.push(factory as DecorateFactory)\n    return this as unknown as Hyper<Ctx & Readonly<A>>\n  }\n\n  /**\n   * Per-request context derivation. Runs once per request, after decorators\n   * and before the handler. Returned fields merge into `ctx` and are visible\n   * to the handler with full type inference.\n   */\n  derive<A extends object>(\n    factory: (args: { ctx: Ctx; env?: unknown; req: Request }) => A | Promise<A>,\n  ): Hyper<Ctx & Readonly<A>> {\n    this.#invalidate()\n    this.#derives.push(factory as unknown as DeriveFactory)\n    return this as unknown as Hyper<Ctx & Readonly<A>>\n  }\n\n  /** Declare env schema. Parsed at boot; `parseEnv` throws on bad input. */\n  env(cfg: EnvConfigLike): this {\n    this.#invalidate()\n    this.#envConfig = cfg\n    return this\n  }\n\n  /** Partial overrides over the secure-by-default baseline. */\n  security(overrides: Partial<SecurityDefaults>): this {\n    this.#invalidate()\n    this.#securityOverrides = { ...this.#securityOverrides, ...overrides }\n    return this\n  }\n\n  // -----------------------------------------------------------------\n  // Build / listen\n  // -----------------------------------------------------------------\n\n  /**\n   * Construct (and memoize) the underlying `HyperApp`. Safe to call\n   * many times; re-runs only when chain state has been mutated since\n   * the last call.\n   */\n  build(): HyperApp {\n    if (this.#built) return this.#built\n    const config: AppConfig = {\n      routes: this.#routes,\n      plugins: this.#plugins,\n      decorate: this.#decorators,\n      derive: this.#derives,\n      ...(this.#envConfig && { env: this.#envConfig }),\n      security: this.#securityOverrides,\n      ...(this.#routerConfig && { router: this.#routerConfig }),\n    }\n    this.#built = app(config)\n    return this.#built\n  }\n\n  /**\n   * Boot a real `Bun.serve` unless `process.env.HYPER_SKIP_LISTEN` is\n   * set. Returns `this` so the chain can still be exported cleanly:\n   *\n   *   export default new Hyper().get(\"/\", () => \"hi\").listen(3000)\n   */\n  listen(portOrOpts?: number | ListenOptions): this {\n    const built = this.build()\n\n    if (process.env.HYPER_SKIP_LISTEN) return this\n\n    const opts: ListenOptions =\n      typeof portOrOpts === \"number\" ? { port: portOrOpts } : (portOrOpts ?? {})\n    const isProd = process.env.NODE_ENV === \"production\"\n    const port = opts.port ?? Number(process.env.PORT ?? 3000)\n    const hostname = opts.hostname ?? (isProd ? \"0.0.0.0\" : \"localhost\")\n    const idleTimeout = opts.idleTimeout ?? 10\n    const development = opts.development ?? !isProd\n    const bannerOn = opts.banner ?? !isProd\n    const drainOn = opts.drain !== false\n\n    this.#server = Bun.serve({\n      port,\n      hostname,\n      routes: built.routes,\n      fetch: built.fetch,\n      idleTimeout,\n      development,\n    })\n\n    if (bannerOn) {\n      const n = built.routeList.length\n      const plural = n === 1 ? \"route\" : \"routes\"\n      console.log(\n        `${this.name} ${HYPER_VERSION} listening on http://${this.#server.hostname}:${this.#server.port} (${n} ${plural})`,\n      )\n    }\n\n    if (drainOn) {\n      activeServers.add(this)\n      installDrainHandlersOnce()\n    }\n\n    return this\n  }\n\n  /**\n   * Stop the live server. `drain` (default true) waits for in-flight\n   * requests; pass `false` to immediately force-close.\n   */\n  async stop(drain = true): Promise<void> {\n    const s = this.#server\n    if (!s) return\n    activeServers.delete(this)\n    this.#server = undefined\n    await s.stop(!drain)\n  }\n\n  // -----------------------------------------------------------------\n  // HyperApp proxies\n  // -----------------------------------------------------------------\n\n  invoke(input: InvokeInput): Promise<InvokeResult> {\n    return this.build().invoke(input)\n  }\n  toOpenAPI(cfg?: Parameters<HyperApp[\"toOpenAPI\"]>[0]): ReturnType<HyperApp[\"toOpenAPI\"]> {\n    return this.build().toOpenAPI(cfg)\n  }\n  toMCPManifest(): ReturnType<HyperApp[\"toMCPManifest\"]> {\n    return this.build().toMCPManifest()\n  }\n  toClientManifest(): ReturnType<HyperApp[\"toClientManifest\"]> {\n    return this.build().toClientManifest()\n  }\n  test(overrides?: TestOverrides): HyperApp {\n    return this.build().test(overrides)\n  }\n\n  // -----------------------------------------------------------------\n  // Internal\n  // -----------------------------------------------------------------\n\n  #invalidate(): void {\n    this.#built = undefined\n  }\n\n  #addRoute(\n    method: HttpMethod,\n    path: string,\n    optsOrHandler: unknown,\n    maybeHandler?: unknown,\n  ): this {\n    this.#invalidate()\n\n    // String shortcut: `.get(\"/\", \"Hello\")` → .staticResponse(new Response(\"Hello\"))\n    if (typeof optsOrHandler === \"string\" && maybeHandler === undefined) {\n      const fullPath = joinPaths(this.#prefix, path)\n      const r = this.#verbBuilder(method, fullPath).staticResponse(\n        new Response(optsOrHandler),\n      ) as Route\n      this.#routes.push(r)\n      return this\n    }\n\n    let opts: RouteOpts | undefined\n    let handler: (ctx: HandlerCtx) => HandlerReturn | Promise<HandlerReturn>\n    if (maybeHandler !== undefined) {\n      opts = optsOrHandler as RouteOpts\n      handler = maybeHandler as (ctx: HandlerCtx) => HandlerReturn | Promise<HandlerReturn>\n    } else {\n      handler = optsOrHandler as (ctx: HandlerCtx) => HandlerReturn | Promise<HandlerReturn>\n    }\n\n    const fullPath = joinPaths(this.#prefix, path)\n    let builder: RouteBuilder = this.#verbBuilder(method, fullPath)\n\n    if (opts) {\n      if (opts.params) builder = builder.params(opts.params)\n      if (opts.query) builder = builder.query(opts.query)\n      if (opts.body) builder = builder.body(opts.body)\n      if (opts.headers) builder = builder.headers(opts.headers)\n      if (opts.meta) builder = builder.meta(opts.meta)\n      if (opts.throws) builder = builder.throws(opts.throws)\n      if (opts.errors) builder = builder.errors(opts.errors)\n      if (opts.use) for (const mw of opts.use) builder = builder.use(mw)\n    }\n\n    // Instance-level middleware is applied AFTER per-route opts, so the\n    // chain order is: instance-mw → route-opts-mw → handler. Same\n    // intuition as Express: parent-scoped mw wraps inner.\n    for (const mw of this.#middleware) builder = builder.use(mw)\n\n    const r = builder.handle(handler)\n    this.#routes.push(r)\n    return this\n  }\n\n  #verbBuilder(method: HttpMethod, path: string): RouteBuilder {\n    switch (method) {\n      case \"GET\":\n        return route.get(path)\n      case \"POST\":\n        return route.post(path)\n      case \"PUT\":\n        return route.put(path)\n      case \"PATCH\":\n        return route.patch(path)\n      case \"DELETE\":\n        return route.delete(path)\n      case \"HEAD\":\n        return route.head(path)\n      case \"OPTIONS\":\n        return route.options(path)\n    }\n  }\n\n  // biome-ignore lint/suspicious/noExplicitAny: heterogeneous sub-app Ctx\n  #useSubApp(sub: Hyper<any>, extraPrefix: string): this {\n    // Use the sub-app's already-built route list — its own prefix\n    // (from `new Hyper({ prefix })`) is already baked into each path.\n    const built = sub.build()\n    for (const r of built.routeList) {\n      this.#routes.push(this.#prefixAndWrap(r, extraPrefix))\n    }\n    return this\n  }\n\n  #useGroup(g: RouteGroup): this {\n    // A RouteGroup has its prefix already baked into each route's path.\n    for (const r of g.routes) {\n      this.#routes.push(this.#prefixAndWrap(r, \"\"))\n    }\n    return this\n  }\n\n  #useRoutes(routes: readonly Route[]): this {\n    for (const r of routes) {\n      this.#routes.push(this.#prefixAndWrap(r, \"\"))\n    }\n    return this\n  }\n\n  #useNamespace(ns: Record<string, unknown>): this {\n    // Walk the namespace for Route-shaped values, applying this prefix\n    // + current middleware stack to each. Nested objects recurse.\n    for (const value of Object.values(ns)) {\n      if (isRoute(value)) {\n        this.#routes.push(this.#prefixAndWrap(value, \"\"))\n      } else if (value && typeof value === \"object\" && !Array.isArray(value)) {\n        // Guard against walking primitives or circular structures. A\n        // typical ESM namespace only contains a single level of Routes.\n        this.#useNamespace(value as Record<string, unknown>)\n      }\n    }\n    return this\n  }\n\n  #prefixAndWrap(r: Route, extra: string): Route {\n    const combined = joinPaths(joinPaths(this.#prefix, extra), r.path)\n    if (combined === r.path && this.#middleware.length === 0) return r\n    if (this.#middleware.length === 0) return { ...r, path: combined }\n    const chain: ChainRunner = compileChain(this.#middleware.slice())\n    const wrapped: RouteHandler = (ictx: InternalHandlerCtx) =>\n      chain(\n        {\n          ctx: ictx.ctx,\n          input: {\n            params: ictx.params,\n            query: ictx.query,\n            body: ictx.body,\n            headers: ictx.headers,\n          },\n          req: ictx.req,\n          path: combined,\n          params: ictx.params,\n        },\n        () => r.handler(ictx),\n      ) as ReturnType<RouteHandler>\n    return { ...r, path: combined, handler: wrapped }\n  }\n}\n\n/**\n * `hyper(opts?)` — factory alias for `new Hyper(opts)`. Returns the\n * same class instance, so `instanceof Hyper` continues to work.\n */\nexport function hyper(opts?: HyperOptions): Hyper {\n  return new Hyper(opts)\n}\n\n// ---------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------\n\n/**\n * Combine two path segments, matching Elysia's \"prefix = /users, path\n * = /\" → \"/users\" intuition. Whichever segment is empty is dropped; a\n * path of \"/\" at the tail collapses to the prefix.\n */\nexport function joinPaths(prefix: string, rest: string): string {\n  const p = prefix === \"\" || prefix === \"/\" ? \"\" : prefix\n  const r = rest === \"\" || rest === \"/\" ? \"\" : rest.startsWith(\"/\") ? rest : `/${rest}`\n  if (p === \"\" && r === \"\") return \"/\"\n  if (p === \"\") return r\n  if (r === \"\") return p\n  return `${p}${r}`\n}\n\nfunction normalizePrefix(p: string): string {\n  if (p === \"\" || p === \"/\") return \"\"\n  let out = p.startsWith(\"/\") ? p : `/${p}`\n  if (out.endsWith(\"/\")) out = out.slice(0, -1)\n  return out\n}\n\nfunction isRoute(x: unknown): x is Route {\n  return (\n    typeof x === \"object\" &&\n    x !== null &&\n    typeof (x as Route).method === \"string\" &&\n    typeof (x as Route).path === \"string\" &&\n    typeof (x as Route).handler === \"function\" &&\n    typeof (x as Route).kind === \"string\"\n  )\n}\n\nfunction isRouteGroup(x: unknown): x is RouteGroup {\n  return (\n    typeof x === \"object\" &&\n    x !== null &&\n    typeof (x as RouteGroup).prefix === \"string\" &&\n    Array.isArray((x as RouteGroup).routes)\n  )\n}\n\nfunction isHyperPlugin(x: unknown): x is HyperPlugin {\n  if (typeof x !== \"object\" || x === null) return false\n  if (typeof (x as HyperPlugin).name !== \"string\") return false\n  // A plugin isn't a route (would collide on `.handler`/`.method`).\n  if (isRoute(x)) return false\n  return true\n}\n",
      "sha256": "bdb9dd67d460432c1f7b4ad5dda0a9bfde6d035109cb9a985f613bd517d9c38b"
    },
    {
      "path": "core/index.ts",
      "contents": "/**\n * @hyper/core — public entry.\n */\n\nexport const VERSION: string = \"0.1.0\"\n\n// Core types\nexport type {\n  AppConfig,\n  AppContext,\n  BunFileLike,\n  BunRoutes,\n  EnvConfigLike,\n  ErrorRegistry,\n  HandlerReturn,\n  HttpMethod,\n  HyperApp,\n  HyperPlugin,\n  Infer,\n  InternalHandlerCtx,\n  InvokeInput,\n  InvokeResult,\n  Route,\n  RouteExample,\n  RouteGroup,\n  RouteHandler,\n  RouteMeta,\n  SecurityDefaults,\n} from \"./types.ts\"\n\n// Standard Schema\nexport type {\n  StandardSchemaV1,\n  StandardSchemaV1Issue,\n  StandardSchemaV1Props,\n  StandardSchemaV1Result,\n} from \"./standard-schema.ts\"\nexport { isStandardSchema, parseStandard, SchemaValidationError } from \"./standard-schema.ts\"\n\n// Builder\nexport { route, RouteBuilder } from \"./route.ts\"\nexport type { BuilderState, CallableRoute, HandlerCtx, InferIn } from \"./route.ts\"\n\n// Middleware\nexport { compileChain, onError, onFinish, onStart, onSuccess } from \"./middleware.ts\"\nexport type { ChainRunner } from \"./middleware.ts\"\nexport type { Middleware, MiddlewareArgs } from \"./middleware.ts\"\n\n// Composition\nexport { fromPlainRouter, group, GroupBuilder, lazy } from \"./group.ts\"\nexport type { LazyGroup, PlainRouter } from \"./group.ts\"\n\n// App\nexport { app } from \"./app.ts\"\n\n// Chain API — `new Hyper()` / `hyper()`\nexport { HYPER_BUILDER_BRAND, Hyper, hyper, joinPaths } from \"./hyper.ts\"\nexport type { HyperOptions, ListenOptions, RouteOpts, UseArg, VerbHandler } from \"./hyper.ts\"\n\n// Errors\nexport { asHyperError, createError, HyperError } from \"./error.ts\"\nexport type { HyperErrorInit } from \"./error.ts\"\n\n// Response helpers\nexport {\n  accepted,\n  badRequest,\n  coerce,\n  conflict,\n  created,\n  errorResponse,\n  forbidden,\n  html,\n  jsonResponse,\n  noContent,\n  notFound,\n  ok,\n  redirect,\n  sse,\n  stream,\n  text,\n  tooManyRequests,\n  unauthorized,\n  unprocessable,\n} from \"./response.ts\"\nexport type { TypedResponse } from \"./response.ts\"\n\n// File\nexport { file } from \"./file.ts\"\nexport type { FileOptions } from \"./file.ts\"\n\n// Hash / timing-safe\nexport { etag, timingSafeEqualStr, xxh3 } from \"./hash.ts\"\n\n// Security\nexport {\n  applyDefaultHeaders,\n  assertNoProtoKeys,\n  DEFAULT_BODY_LIMIT_BYTES,\n  DEFAULT_RESPONSE_HEADERS,\n  DEFAULT_SECURITY,\n  FORBIDDEN_JSON_KEYS,\n  PrototypePollutionError,\n  SUPPRESSED_HEADERS,\n} from \"./security.ts\"\n\n// Request\nexport { parseBodyAuto, parseJsonBody, readTextBody } from \"./request.ts\"\n\n// Decorate / derive\nexport { applyDerive, resolveStaticContext } from \"./decorate.ts\"\nexport type { ContextBlueprint, DecorateFactory, DeriveFactory } from \"./decorate.ts\"\n\n// Env\nexport {\n  EnvParseError,\n  getSecretPaths,\n  markSecrets,\n  parseEnv,\n  SECRET_PATHS,\n  secret,\n  useEnv,\n  withEnv,\n} from \"./env.ts\"\nexport type { EnvConfig } from \"./env.ts\"\n\n// Type utilities\nexport type { InferRouterCtx, InferRouterInputs, InferRouterOutputs } from \"./infer.ts\"\n\n// Resource bundles + examples\nexport { resource } from \"./resource.ts\"\nexport type { ResourceHandlers, ResourceMethod, ResourceOptions } from \"./resource.ts\"\nexport { runExamples } from \"./example.ts\"\nexport type { ExampleResult } from \"./example.ts\"\n\n// Projection (OpenAPI / MCP / client manifests)\nexport {\n  projectRoute,\n  projectRoutes,\n  toClientManifest,\n  toMCPManifest,\n  toOpenAPI,\n} from \"./projection.ts\"\nexport type {\n  ClientManifest,\n  MCPManifest,\n  MCPTool,\n  OpenAPIManifest,\n  OpenAPIManifestConfig,\n  ProjectedRoute,\n  SchemaDescriptor,\n} from \"./projection.ts\"\n",
      "sha256": "63e5a349effd109c74ab852807e6ba77ce676b40747ff70fff2680d0234a22b3"
    },
    {
      "path": "core/infer.ts",
      "contents": "/**\n * Type utilities for downstream consumers.\n *\n * These let `@hyper/client` and user code derive input/output/context\n * types from a router tree without reflection.\n *\n * Usage:\n *   import { type InferRouterInputs } from \"@hyper/core\"\n *   type Inputs = InferRouterInputs<typeof router>\n *   type CreateUser = Inputs[\"users\"][\"create\"]\n */\n\nimport type { CallableRoute } from \"./route.ts\"\nimport type { AppContext, Route } from \"./types.ts\"\n\n/** Anything that looks like a plain-object router branch. */\nexport type RouterLike = { [key: string]: Route | RouterLike }\n\n/** Extract inputs from a router tree, preserving namespace structure. */\nexport type InferRouterInputs<R> = {\n  [K in keyof R]: R[K] extends CallableRoute<infer _M, infer P, infer Q, infer B, infer H, infer _O>\n    ? { params: P; query: Q; body: B; headers: H }\n    : R[K] extends Route\n      ? unknown\n      : R[K] extends RouterLike\n        ? InferRouterInputs<R[K]>\n        : never\n}\n\n/** Extract outputs — the handler's resolved return type. */\nexport type InferRouterOutputs<R> = {\n  [K in keyof R]: R[K] extends CallableRoute<\n    infer _M,\n    infer _P,\n    infer _Q,\n    infer _B,\n    infer _H,\n    infer O\n  >\n    ? Awaited<O>\n    : R[K] extends Route\n      ? unknown\n      : R[K] extends RouterLike\n        ? InferRouterOutputs<R[K]>\n        : never\n}\n\n/** The shared context type used by every route in the tree. */\nexport type InferRouterCtx<_R> = AppContext\n",
      "sha256": "f00baa5e87627474c9941607a7b2428ffbe48404b5f55e93d724ad4a9a3be07a"
    },
    {
      "path": "core/middleware.ts",
      "contents": "/**\n * Middleware — with input, output access, and mapInput.\n *\n * Signature:\n *   ({ ctx, input, next, mapInput }) => Promise<Response>\n *\n * `next(transformedInput?)` runs the rest of the chain (or the handler)\n * and returns the output. Middleware can inspect / transform / short-\n * circuit the response.\n *\n * Lifecycle factories (`onStart`, `onSuccess`, `onError`, `onFinish`)\n * are sugar over this primitive. They always produce a regular\n * middleware under the hood — meta is sugar.\n */\n\nimport type { AppContext, HandlerReturn } from \"./types.ts\"\n\nexport interface MiddlewareArgs<C = AppContext, I = unknown> {\n  readonly ctx: C\n  /** Input resolved from `params`/`query`/`body`/`headers`. */\n  readonly input: I\n  /** The actual Request. */\n  readonly req: Request\n  /** Current route path (inc. matched params). */\n  readonly path: string\n  /** Invoke the rest of the chain + handler; optionally mapInput. */\n  readonly next: (mapped?: I) => Promise<HandlerReturn> | HandlerReturn\n  /** Matched params for mapping convenience. */\n  readonly params: Record<string, string>\n}\n\nexport type Middleware<C = AppContext, I = unknown> = (\n  args: MiddlewareArgs<C, I>,\n) => Promise<HandlerReturn> | HandlerReturn\n\n// Lifecycle factories --------------------------------------------------------\n\nexport function onStart<C = AppContext>(\n  fn: (\n    args: Pick<MiddlewareArgs<C, unknown>, \"ctx\" | \"input\" | \"req\" | \"path\" | \"params\">,\n  ) => void | Promise<void>,\n): Middleware<C, unknown> {\n  return async ({ ctx, input, next, req, path, params }) => {\n    await fn({ ctx, input, req, path, params })\n    return next()\n  }\n}\n\nexport function onSuccess<C = AppContext>(\n  fn: (args: {\n    ctx: C\n    output: HandlerReturn\n    req: Request\n  }) => void | Promise<void>,\n): Middleware<C, unknown> {\n  return async ({ ctx, next, req }) => {\n    const output = await next()\n    await fn({ ctx, output, req })\n    return output\n  }\n}\n\nexport function onError<C = AppContext>(\n  fn: (args: { ctx: C; error: unknown; req: Request }) => void | Promise<void>,\n): Middleware<C, unknown> {\n  return async ({ ctx, next, req }) => {\n    try {\n      return await next()\n    } catch (error) {\n      await fn({ ctx, error, req })\n      throw error\n    }\n  }\n}\n\nexport function onFinish<C = AppContext>(\n  fn: (args: {\n    ctx: C\n    output?: HandlerReturn\n    error?: unknown\n    req: Request\n  }) => void | Promise<void>,\n): Middleware<C, unknown> {\n  return async ({ ctx, next, req }) => {\n    try {\n      const output = await next()\n      await fn({ ctx, output, req })\n      return output\n    } catch (error) {\n      await fn({ ctx, error, req })\n      throw error\n    }\n  }\n}\n\n/**\n * Runner produced by `compileChain` — invokes the precompiled pipeline.\n *\n * `next` is captured per-request so each middleware's `next()` call is a\n * single function reference, not a closure rebuilt on every dispatch.\n */\nexport type ChainRunner = (\n  args: Omit<MiddlewareArgs, \"next\">,\n  base: () => Promise<HandlerReturn> | HandlerReturn,\n) => Promise<HandlerReturn> | HandlerReturn\n\n/**\n * Precompile a middleware chain into a single function, once, at\n * route-build time. Eliminates per-request closure allocations that a\n * naive composition would pay. Zero-middleware routes get the fast\n * path — the compiled runner delegates directly to `base`.\n */\nexport function compileChain(middleware: readonly Middleware[]): ChainRunner {\n  if (middleware.length === 0) {\n    return (_args, base) => base()\n  }\n  return (args, base) => {\n    let i = 0\n    const dispatch = (mapped?: unknown): Promise<HandlerReturn> | HandlerReturn => {\n      if (i >= middleware.length) return base()\n      const mw = middleware[i++]!\n      const mwArgs: MiddlewareArgs = {\n        ctx: args.ctx,\n        input: mapped !== undefined ? mapped : args.input,\n        req: args.req,\n        path: args.path,\n        params: args.params,\n        next: dispatch,\n      }\n      return mw(mwArgs)\n    }\n    return dispatch()\n  }\n}\n",
      "sha256": "44b54fb5c7b53901b9ecc660c4bf6f3de305c90d9d0c819accdca177ed9ecfa7"
    },
    {
      "path": "core/projection.ts",
      "contents": "/**\n * Multi-protocol projection infrastructure.\n *\n * One route definition projects to many transports:\n *   HTTP              — always on (the route is HTTP-first).\n *   typed RPC client  — always on (shape is inferred from the route graph).\n *   MCP tool          — opt-in via `meta.mcp`.\n *   server action     — opt-in via `meta.action` or `.actionable()`.\n *   websocket/SSE     — opt-in via the handler return type.\n *\n * The functions in this file walk a route graph and produce serializable\n * manifests. The `app.invoke()` path is shared across protocols so\n * business logic runs exactly once.\n */\n\nimport type { Route, RouteMeta } from \"./types.ts\"\n\n/** Minimal serializable schema descriptor — the full converter lives in @hyper/openapi. */\nexport interface SchemaDescriptor {\n  readonly kind: \"unknown\" | \"object\" | \"string\" | \"number\" | \"boolean\" | \"array\"\n  readonly properties?: Record<string, SchemaDescriptor>\n}\n\n/** A raw route as projected into any manifest. */\nexport interface ProjectedRoute {\n  readonly method: string\n  readonly path: string\n  readonly name?: string\n  readonly tags: readonly string[]\n  readonly deprecated?: boolean\n  readonly version?: string\n  readonly mcp?: RouteMeta[\"mcp\"]\n  readonly action?: boolean\n  readonly internal?: boolean\n  readonly params?: SchemaDescriptor\n  readonly query?: SchemaDescriptor\n  readonly body?: SchemaDescriptor\n  /** Thrown HTTP status codes declared via `.throws(status, schema)`. */\n  readonly throws?: readonly number[]\n  /** Named error codes declared via `.errors({ code: schema })`. */\n  readonly errors?: readonly string[]\n}\n\nfunction descriptorOf(x: unknown): SchemaDescriptor | undefined {\n  if (!x) return undefined\n  return { kind: \"unknown\" }\n}\n\nexport function projectRoute(r: Route): ProjectedRoute {\n  const meta = r.meta\n  const params = descriptorOf(r.params)\n  const query = descriptorOf(r.query)\n  const body = descriptorOf(r.body)\n  const deprecated = meta.deprecated ? true : undefined\n  const throws = r.throws\n    ? Object.keys(r.throws)\n        .map((n) => Number(n))\n        .filter((n) => Number.isFinite(n))\n    : undefined\n  const errors = r.errors ? Object.keys(r.errors) : undefined\n  const base: ProjectedRoute = {\n    method: r.method,\n    path: r.path,\n    tags: meta.tags ?? [],\n    ...(meta.name !== undefined && { name: meta.name }),\n    ...(meta.mcp !== undefined && { mcp: meta.mcp }),\n    ...(meta.action !== undefined && { action: Boolean(meta.action) }),\n    ...(meta.internal !== undefined && { internal: meta.internal }),\n    ...(deprecated !== undefined && { deprecated }),\n    ...(meta.version !== undefined && { version: meta.version }),\n    ...(params && { params }),\n    ...(query && { query }),\n    ...(body && { body }),\n    ...(throws && throws.length > 0 && { throws }),\n    ...(errors && errors.length > 0 && { errors }),\n  }\n  return base\n}\n\nexport function projectRoutes(routes: readonly Route[]): readonly ProjectedRoute[] {\n  return routes.filter((r) => !r.meta.internal).map(projectRoute)\n}\n\n/** Minimal OpenAPI 3.1 manifest. @hyper/openapi adds schema conversion later. */\nexport interface OpenAPIManifest {\n  readonly openapi: \"3.1.0\"\n  readonly info: { title: string; version: string; description?: string }\n  readonly paths: Record<string, Record<string, OpenAPIOperation>>\n}\n\ninterface OpenAPIOperation {\n  readonly operationId?: string\n  readonly tags?: readonly string[]\n  readonly deprecated?: boolean\n  readonly parameters?: readonly OpenAPIParam[]\n  readonly requestBody?: { readonly content: Record<string, unknown> }\n  readonly responses: Record<string, { description: string }>\n}\n\ninterface OpenAPIParam {\n  readonly name: string\n  readonly in: \"path\" | \"query\" | \"header\"\n  readonly required: boolean\n}\n\nexport interface OpenAPIManifestConfig {\n  readonly title?: string\n  readonly version?: string\n  readonly description?: string\n}\n\nfunction openApiPath(path: string): string {\n  // Convert Bun `:param` to OpenAPI `{param}`\n  return path.replace(/:([A-Za-z0-9_]+)/g, \"{$1}\")\n}\n\nexport function toOpenAPI(\n  routes: readonly Route[],\n  cfg: OpenAPIManifestConfig = {},\n): OpenAPIManifest {\n  const paths: Record<string, Record<string, OpenAPIOperation>> = {}\n  for (const r of routes) {\n    if (r.meta.internal) continue\n    const p = openApiPath(r.path)\n    const operation: OpenAPIOperation = {\n      ...(r.meta.name !== undefined && { operationId: r.meta.name }),\n      ...(r.meta.tags !== undefined && { tags: r.meta.tags }),\n      ...(r.meta.deprecated && { deprecated: true }),\n      ...(r.body !== undefined && {\n        requestBody: {\n          content: { \"application/json\": { schema: { $ref: \"#/components/schemas/Body\" } } },\n        },\n      }),\n      responses: {\n        \"200\": { description: \"success\" },\n      },\n    }\n    if (!paths[p]) paths[p] = {}\n    paths[p][r.method.toLowerCase()] = operation\n  }\n  return {\n    openapi: \"3.1.0\",\n    info: {\n      title: cfg.title ?? \"Hyper API\",\n      version: cfg.version ?? \"0.0.0\",\n      ...(cfg.description !== undefined && { description: cfg.description }),\n    },\n    paths,\n  }\n}\n\n/** MCP manifest (JSON-RPC shaped). @hyper/mcp produces the transport. */\nexport interface MCPManifest {\n  readonly version: \"1.0\"\n  readonly tools: readonly MCPTool[]\n}\n\nexport interface MCPTool {\n  readonly name: string\n  readonly description: string\n  readonly method: string\n  readonly path: string\n  readonly inputSchema: {\n    readonly type: \"object\"\n    readonly properties: Record<string, unknown>\n  }\n}\n\nexport function toMCPManifest(routes: readonly Route[]): MCPManifest {\n  const tools: MCPTool[] = []\n  for (const r of routes) {\n    if (r.meta.internal) continue\n    if (!r.meta.mcp) continue\n    const cfg = r.meta.mcp as { description: string }\n    tools.push({\n      name: r.meta.name ?? `${r.method.toLowerCase()}_${r.path.replace(/[^a-z0-9]+/gi, \"_\")}`,\n      description: cfg.description,\n      method: r.method,\n      path: r.path,\n      inputSchema: {\n        type: \"object\",\n        properties: {\n          ...(r.params ? { params: { type: \"object\" } } : {}),\n          ...(r.query ? { query: { type: \"object\" } } : {}),\n          ...(r.body ? { body: { type: \"object\" } } : {}),\n        },\n      },\n    })\n  }\n  return { version: \"1.0\", tools }\n}\n\n/** Typed-client manifest — the serializable contract @hyper/client consumes. */\nexport interface ClientManifest {\n  readonly version: \"1.0\"\n  readonly routes: readonly ProjectedRoute[]\n}\n\nexport function toClientManifest(routes: readonly Route[]): ClientManifest {\n  return { version: \"1.0\", routes: projectRoutes(routes) }\n}\n",
      "sha256": "d4073186cd677ece4b6e5e266214e4594148d71f9e5a5058a235607c3829004a"
    },
    {
      "path": "core/request.ts",
      "contents": "/**\n * Request parsing — lazy, size-capped, prototype-safe.\n *\n * Body parsing only runs when a route declares a `.body(schema)` or a\n * middleware reads `ctx.body`. We count bytes against the body limit\n * before calling `JSON.parse` so oversized payloads fail fast with 413.\n */\n\nimport { HyperError } from \"./error.ts\"\nimport {\n  DEFAULT_BODY_LIMIT_BYTES,\n  FORBIDDEN_JSON_KEYS,\n  PrototypePollutionError,\n  assertNoProtoKeys,\n} from \"./security.ts\"\n\nfunction jsonSafeReviver(key: string, value: unknown): unknown {\n  if (FORBIDDEN_JSON_KEYS.includes(key)) {\n    throw new HyperError({\n      status: 400,\n      code: \"proto_pollution\",\n      message: `Refusing body containing dangerous key \"${key}\".`,\n      why: \"The body contains a prototype-pollution vector.\",\n      fix: \"Remove the __proto__ / constructor / prototype key from the payload.\",\n      cause: new PrototypePollutionError(key, []),\n    })\n  }\n  return value\n}\n\nexport interface ReadBodyOptions {\n  readonly maxBytes?: number\n}\n\n/**\n * Read a request body as text honoring the byte limit. We avoid\n * `req.text()` so we can bail fast before buffering more than allowed.\n */\nexport async function readTextBody(req: Request, opts: ReadBodyOptions = {}): Promise<string> {\n  const max = opts.maxBytes ?? DEFAULT_BODY_LIMIT_BYTES\n  if (!req.body) return \"\"\n  const contentLength = req.headers.get(\"content-length\")\n  if (contentLength !== null) {\n    const declared = Number(contentLength)\n    if (Number.isFinite(declared) && declared > max) {\n      throw payloadTooLarge(declared, max)\n    }\n  }\n\n  const reader = req.body.getReader()\n  const decoder = new TextDecoder()\n  let total = 0\n  let out = \"\"\n  try {\n    while (true) {\n      const { value, done } = await reader.read()\n      if (done) break\n      if (value) {\n        total += value.byteLength\n        if (total > max) throw payloadTooLarge(total, max)\n        out += decoder.decode(value, { stream: true })\n      }\n    }\n    out += decoder.decode()\n  } finally {\n    reader.releaseLock?.()\n  }\n  return out\n}\n\n/**\n * Parse request body as JSON with prototype-pollution guard and size cap.\n * Returns `undefined` for no-body requests.\n */\nexport async function parseJsonBody(req: Request, opts: ReadBodyOptions = {}): Promise<unknown> {\n  const text = await readTextBody(req, opts)\n  if (text === \"\") return undefined\n  let parsed: unknown\n  try {\n    // Reviver catches __proto__ / constructor / prototype keys before\n    // they can pollute the prototype chain. JSON.parse's own __proto__\n    // behavior is to assign to [[Prototype]] directly — so a post-hoc\n    // Object.keys check never sees it. We reject at read time instead.\n    parsed = JSON.parse(text, jsonSafeReviver)\n  } catch (e) {\n    if (e instanceof HyperError) throw e\n    throw new HyperError({\n      status: 400,\n      code: \"invalid_json\",\n      message: \"Request body is not valid JSON.\",\n      why: \"The body failed JSON.parse.\",\n      fix: \"Send a JSON payload matching the declared schema.\",\n      cause: e,\n    })\n  }\n  assertNoProtoKeys(parsed)\n  return parsed\n}\n\n/** Auto-detect the right body parser from content-type. */\nexport async function parseBodyAuto(req: Request, opts: ReadBodyOptions = {}): Promise<unknown> {\n  const ct = (req.headers.get(\"content-type\") ?? \"\").toLowerCase()\n  if (!ct || ct.startsWith(\"application/json\") || ct.startsWith(\"application/ld+json\")) {\n    return parseJsonBody(req, opts)\n  }\n  if (ct.startsWith(\"text/\")) {\n    return readTextBody(req, opts)\n  }\n  if (ct.startsWith(\"application/x-www-form-urlencoded\")) {\n    const text = await readTextBody(req, opts)\n    const out: Record<string, string> = {}\n    const params = new URLSearchParams(text)\n    params.forEach((v, k) => {\n      if (k === \"__proto__\" || k === \"constructor\" || k === \"prototype\") return\n      out[k] = v\n    })\n    return out\n  }\n  if (ct.startsWith(\"multipart/form-data\")) {\n    // Buffer the form; Bun's Request.formData respects body stream.\n    return req.formData()\n  }\n  // Binary passthrough (rare for handlers — they should opt in explicitly).\n  return req.arrayBuffer()\n}\n\nfunction payloadTooLarge(got: number, max: number): HyperError {\n  return new HyperError({\n    status: 413,\n    code: \"payload_too_large\",\n    message: `Request body is ${got} bytes, limit is ${max}.`,\n    why: \"Bodies exceed Hyper's configured maxBytes.\",\n    fix: \"Increase the limit on the route via `.body(schema, { maxBytes })` or trim the payload.\",\n  })\n}\n",
      "sha256": "c4669691fb6589f65a1442518f26ca6695ce3160a0a038165456a63596b4e79a"
    },
    {
      "path": "core/resource.ts",
      "contents": "/**\n * route.resource() — emit a standard CRUD bundle for a collection.\n *\n * Example:\n *   const users = resource(\"/users\", {\n *     list:   () => store.list(),\n *     get:    ({ params }) => store.get(params.id),\n *     create: ({ body }) => store.create(body),\n *     update: ({ params, body }) => store.update(params.id, body),\n *     remove: ({ params }) => store.remove(params.id),\n *   })\n *\n * Returns an array of routes ready to add to `app({ routes })`.\n */\n\nimport { type CallableRoute, route } from \"./route.ts\"\nimport type { HandlerReturn, HttpMethod } from \"./types.ts\"\n\ntype HandlerFn<P, B> = (args: {\n  params: P\n  body: B\n  req: Request\n  url: URL\n  // biome-ignore lint/suspicious/noExplicitAny: ctx is user-augmented\n  ctx: any\n}) => Promise<HandlerReturn> | HandlerReturn\n\nexport interface ResourceHandlers<T, U = Partial<T>> {\n  readonly list?: HandlerFn<Record<string, string>, never>\n  readonly get?: HandlerFn<{ id: string }, never>\n  readonly create?: HandlerFn<Record<string, string>, T>\n  readonly update?: HandlerFn<{ id: string }, U>\n  readonly remove?: HandlerFn<{ id: string }, never>\n}\n\nexport interface ResourceOptions {\n  /** Human-readable resource name (for metadata). */\n  readonly name?: string\n  /** Expose CRUD as MCP tools. */\n  readonly mcp?: boolean\n}\n\nexport function resource<T, U = Partial<T>>(\n  basePath: string,\n  handlers: ResourceHandlers<T, U>,\n  opts: ResourceOptions = {},\n): readonly CallableRoute[] {\n  const name = opts.name ?? basePath.replace(/\\//g, \"\")\n  const mcpMeta = (op: string, desc: string): { mcp: { description: string } } | object =>\n    opts.mcp ? { mcp: { description: `${desc} (${name})` } } : {}\n  const out: CallableRoute[] = []\n\n  if (handlers.list) {\n    out.push(\n      route\n        .get(basePath)\n        .meta({ name: `${name}.list`, tags: [name], ...mcpMeta(\"list\", \"List\") })\n        .handle((c) =>\n          handlers.list!({\n            params: c.params as Record<string, string>,\n            body: undefined as never,\n            req: c.req,\n            url: c.url,\n            ctx: c.ctx,\n          }),\n        ) as CallableRoute,\n    )\n  }\n  if (handlers.get) {\n    out.push(\n      route\n        .get(`${basePath}/:id`)\n        .meta({ name: `${name}.get`, tags: [name], ...mcpMeta(\"get\", \"Get\") })\n        .handle((c) =>\n          handlers.get!({\n            params: c.params as { id: string },\n            body: undefined as never,\n            req: c.req,\n            url: c.url,\n            ctx: c.ctx,\n          }),\n        ) as CallableRoute,\n    )\n  }\n  if (handlers.create) {\n    out.push(\n      route\n        .post(basePath)\n        .meta({ name: `${name}.create`, tags: [name], ...mcpMeta(\"create\", \"Create\") })\n        .handle((c) =>\n          handlers.create!({\n            params: c.params as Record<string, string>,\n            body: c.body as T,\n            req: c.req,\n            url: c.url,\n            ctx: c.ctx,\n          }),\n        ) as CallableRoute,\n    )\n  }\n  if (handlers.update) {\n    out.push(\n      route\n        .patch(`${basePath}/:id`)\n        .meta({ name: `${name}.update`, tags: [name], ...mcpMeta(\"update\", \"Update\") })\n        .handle((c) =>\n          handlers.update!({\n            params: c.params as { id: string },\n            body: c.body as U,\n            req: c.req,\n            url: c.url,\n            ctx: c.ctx,\n          }),\n        ) as CallableRoute,\n    )\n  }\n  if (handlers.remove) {\n    out.push(\n      route\n        .delete(`${basePath}/:id`)\n        .meta({ name: `${name}.remove`, tags: [name], ...mcpMeta(\"remove\", \"Remove\") })\n        .handle((c) =>\n          handlers.remove!({\n            params: c.params as { id: string },\n            body: undefined as never,\n            req: c.req,\n            url: c.url,\n            ctx: c.ctx,\n          }),\n        ) as CallableRoute,\n    )\n  }\n\n  return out\n}\n\n/** Convenience mapping to avoid explicit HttpMethod unions in docs. */\nexport type ResourceMethod = Extract<HttpMethod, \"GET\" | \"POST\" | \"PATCH\" | \"DELETE\">\n",
      "sha256": "4b4e4f5fd0412fc350fc8f200d075c840ae8deb4dfa7c552dcbd7864e0522c17"
    },
    {
      "path": "core/response.ts",
      "contents": "/**\n * Return helpers + response coercion.\n *\n * The philosophy: handlers return values. The framework converts those\n * into `Response` objects with correct status codes. Helpers make the\n * status explicit at the return site and keep TypeScript inference clean.\n *\n * Perf note: helpers pre-merge the secure-by-default response headers so\n * `finalize()` in app.ts can skip the Headers-clone + new-Response cost\n * on the common path (only paying for HSTS / route overrides when those\n * features are actually in play).\n */\n\nimport type { HyperError } from \"./error.ts\"\nimport type { BunFileLike, HandlerReturn } from \"./types.ts\"\n\n/**\n * Bake Hyper's secure-by-default headers directly into the Response\n * constructed by the helpers. The pipeline detects these via a single\n * `headers.has(\"x-content-type-options\")` probe and skips the Headers\n * clone when no other mutation is needed.\n */\nconst DEFAULT_SECURITY_HEADERS: Readonly<Record<string, string>> = Object.freeze({\n  \"x-content-type-options\": \"nosniff\",\n  \"x-frame-options\": \"DENY\",\n  \"referrer-policy\": \"strict-origin-when-cross-origin\",\n  \"cross-origin-opener-policy\": \"same-origin\",\n  \"cross-origin-resource-policy\": \"same-origin\",\n  \"permissions-policy\": \"camera=(), microphone=(), geolocation=(), interest-cohort=()\",\n})\n\nconst JSON_HEADERS_PREBAKED: Readonly<Record<string, string>> = Object.freeze({\n  \"content-type\": \"application/json; charset=utf-8\",\n  ...DEFAULT_SECURITY_HEADERS,\n})\n\nconst TEXT_HEADERS_PREBAKED: Readonly<Record<string, string>> = Object.freeze({\n  \"content-type\": \"text/plain; charset=utf-8\",\n  ...DEFAULT_SECURITY_HEADERS,\n})\n\nconst HTML_HEADERS_PREBAKED: Readonly<Record<string, string>> = Object.freeze({\n  \"content-type\": \"text/html; charset=utf-8\",\n  ...DEFAULT_SECURITY_HEADERS,\n})\n\n/** Branded helper result — carries status so TS can infer response types. */\nexport interface TypedResponse<S extends number, B> extends Response {\n  readonly __hyper?: { status: S; body: B }\n}\n\n// --- 2xx ------------------------------------------------------------------\n\nexport function ok<B>(body: B, init?: ResponseInit): TypedResponse<200, B> {\n  return jsonResponse(200, body, init) as TypedResponse<200, B>\n}\n\nexport function created<B>(body: B, init?: ResponseInit): TypedResponse<201, B> {\n  return jsonResponse(201, body, init) as TypedResponse<201, B>\n}\n\nexport function accepted<B>(body: B, init?: ResponseInit): TypedResponse<202, B> {\n  return jsonResponse(202, body, init) as TypedResponse<202, B>\n}\n\nexport function noContent(init?: ResponseInit): TypedResponse<204, null> {\n  if (!init) {\n    return new Response(null, {\n      status: 204,\n      headers: DEFAULT_SECURITY_HEADERS,\n    }) as TypedResponse<204, null>\n  }\n  const headers = mergeHeaders(init.headers, DEFAULT_SECURITY_HEADERS)\n  return new Response(null, { ...init, status: 204, headers }) as TypedResponse<204, null>\n}\n\n// --- 3xx ------------------------------------------------------------------\n\nexport function redirect(\n  location: string,\n  status: 301 | 302 | 303 | 307 | 308 = 302,\n): TypedResponse<301 | 302 | 303 | 307 | 308, null> {\n  return new Response(null, {\n    status,\n    headers: { location },\n  }) as TypedResponse<301 | 302 | 303 | 307 | 308, null>\n}\n\n// --- 4xx error helpers (returned, not thrown) ----------------------------\n\nexport function badRequest<B extends { code?: string } | undefined = undefined>(\n  body?: B,\n  init?: ResponseInit,\n): TypedResponse<400, B> {\n  return jsonResponse(400, body ?? null, init) as unknown as TypedResponse<400, B>\n}\n\nexport function unauthorized<B extends { code?: string } | undefined = undefined>(\n  body?: B,\n  init?: ResponseInit,\n): TypedResponse<401, B> {\n  return jsonResponse(401, body ?? null, init) as unknown as TypedResponse<401, B>\n}\n\nexport function forbidden<B extends { code?: string } | undefined = undefined>(\n  body?: B,\n  init?: ResponseInit,\n): TypedResponse<403, B> {\n  return jsonResponse(403, body ?? null, init) as unknown as TypedResponse<403, B>\n}\n\nexport function notFound<B extends { code?: string } | undefined = undefined>(\n  body?: B,\n  init?: ResponseInit,\n): TypedResponse<404, B> {\n  return jsonResponse(404, body ?? null, init) as unknown as TypedResponse<404, B>\n}\n\nexport function conflict<B extends { code?: string } | undefined = undefined>(\n  body?: B,\n  init?: ResponseInit,\n): TypedResponse<409, B> {\n  return jsonResponse(409, body ?? null, init) as unknown as TypedResponse<409, B>\n}\n\nexport function unprocessable<B extends { code?: string } | undefined = undefined>(\n  body?: B,\n  init?: ResponseInit,\n): TypedResponse<422, B> {\n  return jsonResponse(422, body ?? null, init) as unknown as TypedResponse<422, B>\n}\n\nexport function tooManyRequests<B extends { code?: string } | undefined = undefined>(\n  body?: B,\n  init?: ResponseInit,\n): TypedResponse<429, B> {\n  return jsonResponse(429, body ?? null, init) as unknown as TypedResponse<429, B>\n}\n\n// --- Body helpers ---------------------------------------------------------\n\nexport function text(body: string, init?: ResponseInit): Response {\n  if (!init) {\n    return new Response(body, { status: 200, headers: TEXT_HEADERS_PREBAKED })\n  }\n  const headers = mergeHeaders(init.headers, TEXT_HEADERS_PREBAKED)\n  return new Response(body, { ...init, status: init.status ?? 200, headers })\n}\n\nexport function html(body: string, init?: ResponseInit): Response {\n  if (!init) {\n    return new Response(body, { status: 200, headers: HTML_HEADERS_PREBAKED })\n  }\n  const headers = mergeHeaders(init.headers, HTML_HEADERS_PREBAKED)\n  return new Response(body, { ...init, status: init.status ?? 200, headers })\n}\n\n/** Server-Sent Events response. Pass an AsyncIterable of {data, event?, id?}. */\nexport function sse(\n  source: AsyncIterable<{ data: string; event?: string; id?: string }>,\n  init?: ResponseInit,\n): Response {\n  const stream = new ReadableStream<Uint8Array>({\n    async start(controller) {\n      const enc = new TextEncoder()\n      try {\n        for await (const ev of source) {\n          let chunk = \"\"\n          if (ev.id) chunk += `id: ${ev.id}\\n`\n          if (ev.event) chunk += `event: ${ev.event}\\n`\n          for (const line of ev.data.split(\"\\n\")) chunk += `data: ${line}\\n`\n          chunk += \"\\n\"\n          controller.enqueue(enc.encode(chunk))\n        }\n      } catch (err) {\n        controller.error(err)\n        return\n      }\n      controller.close()\n    },\n  })\n  const headers = mergeHeaders(init?.headers, {\n    \"content-type\": \"text/event-stream\",\n    \"cache-control\": \"no-cache, no-transform\",\n    \"x-accel-buffering\": \"no\",\n  })\n  return new Response(stream, { ...init, status: init?.status ?? 200, headers })\n}\n\n/** Generic streaming response (AsyncIterable of strings or bytes). */\nexport function stream(source: AsyncIterable<string | Uint8Array>, init?: ResponseInit): Response {\n  const readable = new ReadableStream<Uint8Array>({\n    async start(controller) {\n      const enc = new TextEncoder()\n      try {\n        for await (const chunk of source) {\n          controller.enqueue(typeof chunk === \"string\" ? enc.encode(chunk) : chunk)\n        }\n      } catch (err) {\n        controller.error(err)\n        return\n      }\n      controller.close()\n    },\n  })\n  return new Response(readable, { ...init, status: init?.status ?? 200 })\n}\n\n// --- Internal -------------------------------------------------------------\n\nexport function jsonResponse(status: number, body: unknown, init?: ResponseInit): Response {\n  const payload = body === null || body === undefined ? null : JSON.stringify(body)\n  if (!init) {\n    return new Response(payload, { status, headers: JSON_HEADERS_PREBAKED })\n  }\n  const headers = mergeHeaders(init.headers, JSON_HEADERS_PREBAKED)\n  return new Response(payload, { ...init, status, headers })\n}\n\nfunction mergeHeaders(input: HeadersInit | undefined, defaults?: Record<string, string>): Headers {\n  const h = new Headers(input)\n  if (defaults) {\n    for (const [k, v] of Object.entries(defaults)) {\n      if (!h.has(k)) h.set(k, v)\n    }\n  }\n  return h\n}\n\n/**\n * Coerce a handler return into a Response. Rules:\n * - `Response` → passthrough\n * - `Bun.file`-like (has `.stream`) → streamed passthrough with content-type\n * - `ReadableStream` → 200 with stream body\n * - `string` → 200 text/plain\n * - everything else → 200 JSON\n */\nexport function coerce(value: HandlerReturn): Response {\n  if (value instanceof Response) return value\n  if (value === undefined || value === null) return new Response(null, { status: 204 })\n  if (isBunFile(value)) return bunFileToResponse(value)\n  if (value instanceof ReadableStream) return new Response(value, { status: 200 })\n  if (typeof value === \"string\") return text(value)\n  if (typeof value === \"object\" && value !== null && Symbol.asyncIterator in (value as object)) {\n    return stream(value as AsyncIterable<string | Uint8Array>)\n  }\n  return ok(value)\n}\n\nfunction isBunFile(v: unknown): v is BunFileLike {\n  return (\n    typeof v === \"object\" && v !== null && typeof (v as { stream?: unknown }).stream === \"function\"\n  )\n}\n\nfunction bunFileToResponse(file: BunFileLike): Response {\n  const headers = new Headers()\n  if (file.type) headers.set(\"content-type\", file.type)\n  if (typeof file.size === \"number\") headers.set(\"content-length\", String(file.size))\n  return new Response(file.stream(), { status: 200, headers })\n}\n\n// Error response projection ------------------------------------------------\n\nexport function errorResponse(err: HyperError): Response {\n  return jsonResponse(err.status, err.toJSON())\n}\n",
      "sha256": "8e183f4aadb4139206e27e2105250896dcddad9d700fd6448a87943d2b2e32df"
    },
    {
      "path": "core/route.ts",
      "contents": "/**\n * Route builder — the fluent, immutable DX surface.\n *\n * Every chain step returns a new builder with an updated type state.\n * `.handle(fn)` closes the builder and produces a `Route` value.\n *\n * Surface:\n *   route.<verb>(path)\n *     .params(schema)\n *     .query(schema)\n *     .body(schema)\n *     .headers(schema)\n *     .meta({...})\n *     .use(middleware)             // output access + mapInput\n *     .errors({ CODE: schema })    // named-code catalog\n *     .throws({ 404: schema })     // declared thrown shapes\n *     .handle(fn)                  // closes the builder into a Route\n *\n * Returned Route carries an attached `.callable(ctx, input)` for in-\n * process invocation (testing, Server Actions, projections).\n */\n\nimport type { ChainRunner, Middleware } from \"./middleware.ts\"\nimport { compileChain } from \"./middleware.ts\"\nimport type { StandardSchemaV1 } from \"./standard-schema.ts\"\nimport type {\n  HandlerReturn,\n  HttpMethod,\n  InternalHandlerCtx,\n  Route,\n  RouteHandler,\n  RouteMeta,\n} from \"./types.ts\"\n\nexport interface BuilderState {\n  params?: StandardSchemaV1\n  query?: StandardSchemaV1\n  body?: StandardSchemaV1\n  headers?: StandardSchemaV1\n  meta: RouteMeta\n  middleware: readonly Middleware[]\n  errors?: Record<string, StandardSchemaV1>\n  throws?: Record<number, StandardSchemaV1>\n}\n\nexport type InferIn<S> = S extends StandardSchemaV1<infer _I, infer O> ? O : unknown\n\nexport interface HandlerCtx<\n  Params = unknown,\n  Query = unknown,\n  Body = unknown,\n  HeadersT = unknown,\n  Ctx extends import(\"./types.ts\").AppContext = import(\"./types.ts\").AppContext,\n> {\n  readonly req: Request\n  readonly url: URL\n  readonly params: Params\n  readonly query: Query\n  readonly body: Body\n  readonly headers: HeadersT\n  readonly cookies: () => import(\"bun\").CookieMap\n  /** Decorated app context — `ctx.log`, `ctx.db`, etc. */\n  readonly ctx: Ctx\n}\n\n/** A Route that can also be invoked as a plain async function. */\nexport interface CallableRoute<\n  M extends HttpMethod = HttpMethod,\n  Params = unknown,\n  Query = unknown,\n  Body = unknown,\n  HeadersT = unknown,\n  Output = unknown,\n> extends Route<M> {\n  readonly callable: (input: {\n    params?: Params\n    query?: Query\n    body?: Body\n    headers?: HeadersT\n    req?: Request\n    ctx?: import(\"./types.ts\").AppContext\n  }) => Promise<Output>\n}\n\nexport class RouteBuilder<\n  M extends HttpMethod = HttpMethod,\n  Params = unknown,\n  Query = unknown,\n  Body = unknown,\n  HeadersT = unknown,\n> {\n  readonly #method: M\n  readonly #path: string\n  readonly #state: BuilderState\n\n  constructor(method: M, path: string, state: BuilderState = { meta: {}, middleware: [] }) {\n    this.#method = method\n    this.#path = path\n    this.#state = state\n  }\n\n  params<S extends StandardSchemaV1>(\n    schema: S,\n  ): RouteBuilder<M, InferIn<S>, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, { ...this.#state, params: schema })\n  }\n\n  query<S extends StandardSchemaV1>(\n    schema: S,\n  ): RouteBuilder<M, Params, InferIn<S>, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, { ...this.#state, query: schema })\n  }\n\n  body<S extends StandardSchemaV1>(\n    schema: S,\n  ): RouteBuilder<M, Params, Query, InferIn<S>, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, { ...this.#state, body: schema })\n  }\n\n  headers<S extends StandardSchemaV1>(schema: S): RouteBuilder<M, Params, Query, Body, InferIn<S>> {\n    return new RouteBuilder(this.#method, this.#path, { ...this.#state, headers: schema })\n  }\n\n  meta(meta: RouteMeta): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      meta: { ...this.#state.meta, ...meta },\n    })\n  }\n\n  /** Mark deprecated — emits Sunset header + OpenAPI + client JSDoc. */\n  deprecated(\n    opts: boolean | { reason?: string; sunset?: Date | string } = true,\n  ): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    let value: RouteMeta[\"deprecated\"]\n    if (typeof opts === \"boolean\") {\n      value = opts\n    } else {\n      const out: { reason?: string; sunset?: string } = {}\n      if (opts.reason !== undefined) out.reason = opts.reason\n      if (opts.sunset !== undefined) {\n        out.sunset = opts.sunset instanceof Date ? opts.sunset.toUTCString() : opts.sunset\n      }\n      value = out\n    }\n    const nextHeaders: Record<string, string> = { ...(this.#state.meta.headers ?? {}) }\n    if (typeof value === \"object\" && value?.sunset) {\n      nextHeaders.Sunset = value.sunset\n    } else if (value === true) {\n      nextHeaders.Deprecation = \"true\"\n    }\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      meta: { ...this.#state.meta, deprecated: value, headers: nextHeaders },\n    })\n  }\n\n  /** Tag this route with a version string (header or URL prefix routed). */\n  version(v: string): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      meta: { ...this.#state.meta, version: v },\n    })\n  }\n\n  /** Add an example — surfaced in OpenAPI, MCP few-shot, client JSDoc, tests. */\n  example(ex: import(\"./types.ts\").RouteExample): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      meta: {\n        ...this.#state.meta,\n        examples: [...(this.#state.meta.examples ?? []), ex],\n      },\n    })\n  }\n\n  /** Per-route timeout in milliseconds (overrides `security.requestTimeoutMs`). */\n  timeout(ms: number): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    if (!Number.isFinite(ms) || ms < 0) {\n      throw new Error(`route.timeout(${ms}): must be a non-negative finite number of milliseconds`)\n    }\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      meta: { ...this.#state.meta, timeoutMs: ms },\n    })\n  }\n\n  /**\n   * Short-circuit to a constant Response. Eligible for Bun.serve's static\n   * routes path — no handler invocation, no middleware, near-zero latency.\n   *\n   *   route.get(\"/health\").static(Response.json({ ok: true }))\n   *\n   * Use cases: health checks, robots.txt, static configuration, feature\n   * flags served from a CDN origin, etc. If you need middleware (logging,\n   * auth), fall back to `.handle(() => ...)`.\n   */\n  staticResponse(res: Response): Route<M> {\n    const method = this.#method\n    const path = this.#path\n    const state = this.#state\n    // The hot path is Bun.serve's native static routes, which consume\n    // `staticResponse` directly and never invoke `handler`. The dev\n    // router falls back to `res.clone()` — Bun's clone is O(body) for\n    // string bodies and doesn't materialize a buffer.\n    return {\n      method,\n      path,\n      meta: state.meta,\n      handler: () => res.clone(),\n      kind: \"static\",\n      staticResponse: res,\n    }\n  }\n\n  /** Mark the route as a React Server Action. */\n  actionable(): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      meta: { ...this.#state.meta, action: true },\n    })\n  }\n\n  use(mw: Middleware): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      middleware: [...this.#state.middleware, mw],\n    })\n  }\n\n  /** Declare thrown error shapes per HTTP status (declared-throws contract). */\n  throws(map: Record<number, StandardSchemaV1>): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      throws: { ...(this.#state.throws ?? {}), ...map },\n    })\n  }\n\n  /** Declare named-code error catalog. */\n  errors(map: Record<string, StandardSchemaV1>): RouteBuilder<M, Params, Query, Body, HeadersT> {\n    return new RouteBuilder(this.#method, this.#path, {\n      ...this.#state,\n      errors: { ...(this.#state.errors ?? {}), ...map },\n    })\n  }\n\n  handle(\n    fn: (ctx: HandlerCtx<Params, Query, Body, HeadersT>) => Promise<HandlerReturn> | HandlerReturn,\n  ): CallableRoute<M, Params, Query, Body, HeadersT, HandlerReturn> {\n    const state = this.#state\n    const method = this.#method\n    const path = this.#path\n\n    // Precompile the middleware chain at build time — zero-middleware\n    // routes get a direct-call fast path with no extra allocations.\n    const chain: ChainRunner = compileChain(state.middleware)\n    const hasMiddleware = state.middleware.length > 0\n\n    const handler: RouteHandler = (ictx: InternalHandlerCtx) => {\n      // ictx is shaped by the pipeline to exactly match HandlerCtx —\n      // url / query / headers are either the parsed schema output\n      // (set as own properties by runPipeline) or lazy prototype\n      // getters that materialize on first access. We skip the typed-\n      // copy allocation that the old implementation paid per request.\n      const typed = ictx as unknown as HandlerCtx<Params, Query, Body, HeadersT>\n      if (!hasMiddleware) return fn(typed)\n      return chain(\n        {\n          ctx: ictx.ctx,\n          input: {\n            params: typed.params,\n            query: typed.query,\n            body: typed.body,\n            headers: typed.headers,\n          },\n          req: ictx.req,\n          path,\n          params: ictx.params,\n        },\n        () => fn(typed),\n      )\n    }\n\n    const middlewareTags: string[] = []\n    for (const mw of state.middleware) {\n      const tag = (mw as unknown as { __hyperTag?: string }).__hyperTag\n      if (typeof tag === \"string\") middlewareTags.push(tag)\n    }\n\n    const r: CallableRoute<M, Params, Query, Body, HeadersT, HandlerReturn> = {\n      method,\n      path,\n      ...(state.params !== undefined && { params: state.params }),\n      ...(state.query !== undefined && { query: state.query }),\n      ...(state.body !== undefined && { body: state.body }),\n      ...(state.headers !== undefined && { headers: state.headers }),\n      meta: state.meta,\n      handler,\n      ...(state.throws !== undefined && { throws: state.throws }),\n      ...(state.errors !== undefined && { errors: state.errors }),\n      ...(middlewareTags.length > 0 && { middlewareTags }),\n      kind: \"fn\",\n      callable: async (input) => {\n        const req = input.req ?? new Request(`http://local${path}`, { method })\n        return fn({\n          req,\n          url: new URL(req.url),\n          params: (input.params ?? {}) as Params,\n          query: (input.query ?? {}) as Query,\n          body: (input.body ?? undefined) as Body,\n          headers: (input.headers ?? {}) as HeadersT,\n          cookies: () => new Bun.CookieMap(req.headers.get(\"cookie\") ?? \"\"),\n          ctx: (input.ctx ?? {}) as import(\"./types.ts\").AppContext,\n        })\n      },\n    }\n    return r\n  }\n}\n\nexport const route: {\n  get: (path: string) => RouteBuilder<\"GET\">\n  post: (path: string) => RouteBuilder<\"POST\">\n  put: (path: string) => RouteBuilder<\"PUT\">\n  patch: (path: string) => RouteBuilder<\"PATCH\">\n  delete: (path: string) => RouteBuilder<\"DELETE\">\n  head: (path: string) => RouteBuilder<\"HEAD\">\n  options: (path: string) => RouteBuilder<\"OPTIONS\">\n  lazy: <R extends Route>(loader: () => Promise<{ default: R } | R>) => Promise<R>\n} = {\n  get: (path) => new RouteBuilder(\"GET\", path),\n  post: (path) => new RouteBuilder(\"POST\", path),\n  put: (path) => new RouteBuilder(\"PUT\", path),\n  patch: (path) => new RouteBuilder(\"PATCH\", path),\n  delete: (path) => new RouteBuilder(\"DELETE\", path),\n  head: (path) => new RouteBuilder(\"HEAD\", path),\n  options: (path) => new RouteBuilder(\"OPTIONS\", path),\n  lazy: async <R extends Route>(loader: () => Promise<{ default: R } | R>): Promise<R> => {\n    const mod = await loader()\n    if (mod && typeof mod === \"object\" && \"default\" in mod) {\n      return (mod as { default: R }).default\n    }\n    return mod as R\n  },\n}\n",
      "sha256": "aeede5c10b4bdf6d19bb342a110b776803b3a1bffa99f8fd63ad0f2a2c913b0a"
    },
    {
      "path": "core/router.ts",
      "contents": "/**\n * Dev router — zero-dep trie that mirrors `Bun.serve({ routes })`\n * semantics so dev and prod behave identically.\n *\n * Supported patterns:\n * - Static:           \"/users\"\n * - Param:            \"/users/:id\"\n * - Mixed-segment:    \"/r/:slug.json\", \"/r/:name@:version.json\", \"/posts/:y-:m-:d\", \"/v:version/users\"\n * - Wildcard:         \"/api/*\"\n *\n * A \"mixed\" segment is one with literal characters around or between params.\n * It compiles to a regex once at route-add time; the runtime walker uses\n * `pattern.exec(segment)` only for those nodes. Pure-segment params keep the\n * zero-allocation static fast path unchanged.\n *\n * Multiple mixed patterns may share a parent node — they're tried in\n * descending order of specificity (more literal characters wins), so\n * `/r/:name@:version.json` is preferred over `/r/:name.json` when both\n * match. Within the same specificity, registration order wins.\n *\n * Param names are `[A-Za-z_][A-Za-z0-9_]*`. Anything else in the segment is a\n * literal byte that must match the request path verbatim.\n *\n * Method-keyed dispatch is handled at the Route level (one compiled handler\n * per verb); the router only matches paths.\n */\n\nimport type { HttpMethod, Route } from \"./types.ts\"\n\nexport interface MatchResult {\n  readonly route: Route\n  readonly params: Record<string, string>\n}\n\ninterface PureParam {\n  readonly name: string\n  readonly node: Node\n}\n\ninterface MixedParam {\n  /** Original pattern segment, used for diagnostics. */\n  readonly pattern: string\n  /** Compiled regex anchored to the full segment. */\n  readonly regex: RegExp\n  /** Capture-group names in order. */\n  readonly names: readonly string[]\n  /** Count of literal characters — higher wins ties at match time. */\n  readonly literalChars: number\n  readonly node: Node\n}\n\ninterface Node {\n  /** Static children: \"users\" -> Node */\n  statics: Map<string, Node>\n  /** Single pure-segment param child (`:id`). */\n  pureParam?: PureParam\n  /**\n   * Mixed-segment children (`:slug.json`, `:a@:b`, ...). Multiple may\n   * coexist at the same depth — tried in descending specificity order at\n   * match time.\n   */\n  mixedParams?: MixedParam[]\n  /** Wildcard child: \"*\" */\n  wildcard?: Node\n  /** Routes terminating at this node, keyed by method. */\n  handlers?: Partial<Record<HttpMethod, Route>>\n}\n\nexport class Router {\n  readonly #root: Node = newNode()\n\n  add(route: Route): void {\n    const segments = splitPath(route.path)\n    let cur = this.#root\n    for (const seg of segments) {\n      if (seg === \"*\" || seg.startsWith(\"*\")) {\n        if (!cur.wildcard) cur.wildcard = newNode()\n        cur = cur.wildcard\n        break\n      }\n      const parsed = parsePatternSegment(seg)\n      if (parsed === \"static\") {\n        let child = cur.statics.get(seg)\n        if (!child) {\n          child = newNode()\n          cur.statics.set(seg, child)\n        }\n        cur = child\n        continue\n      }\n      if (parsed.kind === \"pure\") {\n        if (!cur.pureParam) {\n          cur.pureParam = { name: parsed.name, node: newNode() }\n        } else if (cur.pureParam.name !== parsed.name) {\n          throw new Error(\n            `Route conflict: ${route.path} has param :${parsed.name} but trie already uses :${cur.pureParam.name}`,\n          )\n        }\n        cur = cur.pureParam.node\n        continue\n      }\n      // Mixed pattern — multiple may coexist at the same depth.\n      if (!cur.mixedParams) cur.mixedParams = []\n      const existing = cur.mixedParams.find((m) => m.pattern === seg)\n      if (existing) {\n        cur = existing.node\n        continue\n      }\n      const slot: MixedParam = {\n        pattern: seg,\n        regex: parsed.regex,\n        names: parsed.names,\n        literalChars: parsed.literalChars,\n        node: newNode(),\n      }\n      cur.mixedParams.push(slot)\n      // Most specific (highest literal count) first; ties keep registration order.\n      cur.mixedParams.sort((a, b) => b.literalChars - a.literalChars)\n      cur = slot.node\n    }\n    if (!cur.handlers) cur.handlers = {}\n    if (cur.handlers[route.method]) {\n      throw new Error(`Duplicate route: ${route.method} ${route.path}`)\n    }\n    cur.handlers[route.method] = route\n  }\n\n  find(method: HttpMethod, pathname: string): MatchResult | null {\n    const matched = walkInline(this.#root, pathname)\n    if (!matched) return null\n    const route = matched.node.handlers?.[method]\n    if (!route) {\n      // Fallback: HEAD uses GET; OPTIONS handled by caller.\n      if (method === \"HEAD\") {\n        const getRoute = matched.node.handlers?.GET\n        if (getRoute) return { route: getRoute, params: matched.params ?? EMPTY_PARAMS }\n      }\n      return null\n    }\n    return { route, params: matched.params ?? EMPTY_PARAMS }\n  }\n\n  /** Enumerate all routes for introspection. */\n  *all(): Generator<Route> {\n    yield* enumerate(this.#root)\n  }\n}\n\nfunction newNode(): Node {\n  return { statics: new Map() }\n}\n\nconst EMPTY_PARAMS: Record<string, string> = Object.freeze(\n  Object.create(null) as Record<string, string>,\n) as Record<string, string>\n\nfunction splitPath(path: string): string[] {\n  const trimmed = path.startsWith(\"/\") ? path.slice(1) : path\n  if (trimmed === \"\") return []\n  return trimmed.split(\"/\")\n}\n\ninterface WalkHit {\n  readonly node: Node\n  /** Lazily allocated — `null` means \"no params were matched\". */\n  readonly params: Record<string, string> | null\n}\n\n/**\n * Zero-allocation walker for the static fast path.\n *\n * Iterates the pathname by slicing between `/` delimiters directly on the\n * string — no segments array, no params object, no closures. When a `:param`,\n * mixed pattern, or `*` node is encountered we switch to the `walkWithParams`\n * helper which handles backtracking.\n */\nfunction walkInline(root: Node, pathname: string): WalkHit | null {\n  let i = pathname.charCodeAt(0) === 47 /* '/' */ ? 1 : 0\n  const len = pathname.length\n  let node: Node = root\n\n  // Empty path (`/` or ``) matches the root.\n  if (i >= len) return { node, params: null }\n\n  while (i < len) {\n    let j = i\n    while (j < len && pathname.charCodeAt(j) !== 47) j++\n    const seg = pathname.slice(i, j)\n\n    const stat = node.statics.get(seg)\n    if (stat && !node.pureParam && !node.mixedParams && !node.wildcard) {\n      // Unambiguous static step — no backtracking possible.\n      node = stat\n      i = j + 1\n      continue\n    }\n    return walkWithParams(node, pathname, i)\n  }\n  return { node, params: null }\n}\n\nfunction walkWithParams(startNode: Node, pathname: string, startIndex: number): WalkHit | null {\n  const params: Record<string, string> = {}\n  const hit = walkRecur(startNode, pathname, startIndex, params)\n  if (!hit) return null\n  for (const _k in params) return { node: hit, params }\n  return { node: hit, params: null }\n}\n\nfunction walkRecur(\n  node: Node,\n  pathname: string,\n  i: number,\n  params: Record<string, string>,\n): Node | null {\n  const len = pathname.length\n  if (i >= len) return node\n  let j = i\n  while (j < len && pathname.charCodeAt(j) !== 47) j++\n  const seg = pathname.slice(i, j)\n  const nextIndex = j + 1\n\n  // 1) Static (most specific) wins first.\n  const stat = node.statics.get(seg)\n  if (stat) {\n    if (nextIndex > len) return stat\n    const r = walkRecur(stat, pathname, nextIndex, params)\n    if (r) return r\n  }\n\n  // 2a) Mixed-segment params first (most specific to least specific).\n  if (node.mixedParams) {\n    for (const mp of node.mixedParams) {\n      const m = mp.regex.exec(seg)\n      if (!m) continue\n      const captured = mp.names\n      for (let k = 0; k < captured.length; k++) {\n        const name = captured[k]\n        if (name !== undefined) {\n          const value = m[k + 1]\n          params[name] = value === undefined ? \"\" : decodeURIComponent(value)\n        }\n      }\n      if (nextIndex > len) return mp.node\n      const r = walkRecur(mp.node, pathname, nextIndex, params)\n      if (r) return r\n      for (const name of captured) delete params[name]\n    }\n  }\n\n  // 2b) Pure-segment param.\n  if (node.pureParam) {\n    const name = node.pureParam.name\n    params[name] = decodeURIComponent(seg)\n    if (nextIndex > len) return node.pureParam.node\n    const r = walkRecur(node.pureParam.node, pathname, nextIndex, params)\n    if (r) return r\n    delete params[name]\n  }\n\n  // 3) Wildcard catch-all.\n  if (node.wildcard) {\n    params[\"*\"] = decodeURIComponent(pathname.slice(i))\n    return node.wildcard\n  }\n  return null\n}\n\nfunction* enumerate(node: Node): Generator<Route> {\n  if (node.handlers) {\n    for (const v of Object.values(node.handlers)) if (v) yield v\n  }\n  for (const child of node.statics.values()) yield* enumerate(child)\n  if (node.pureParam) yield* enumerate(node.pureParam.node)\n  if (node.mixedParams) for (const mp of node.mixedParams) yield* enumerate(mp.node)\n  if (node.wildcard) yield* enumerate(node.wildcard)\n}\n\n// ---------------------------------------------------------------------------\n// Pattern parsing\n// ---------------------------------------------------------------------------\n\ntype ParsedSegment =\n  | \"static\"\n  | { readonly kind: \"pure\"; readonly name: string }\n  | {\n      readonly kind: \"mixed\"\n      readonly regex: RegExp\n      readonly names: readonly string[]\n      readonly literalChars: number\n    }\n\nconst IDENT_HEAD = /[A-Za-z_]/\nconst IDENT_REST = /[A-Za-z0-9_]/\n\nfunction parsePatternSegment(seg: string): ParsedSegment {\n  // Fast path: no `:` means pure-static.\n  if (seg.indexOf(\":\") === -1) return \"static\"\n\n  // Single `:name` covering the whole segment? -> pure param (fast path).\n  if (seg.length > 1 && seg.charCodeAt(0) === 58 /* ':' */) {\n    let k = 1\n    if (k < seg.length && IDENT_HEAD.test(seg.charAt(k))) {\n      k++\n      while (k < seg.length && IDENT_REST.test(seg.charAt(k))) k++\n      if (k === seg.length) return { kind: \"pure\", name: seg.slice(1) }\n    }\n  }\n\n  // Mixed segment — scan and build a regex, capturing each `:name`.\n  const names: string[] = []\n  let pattern = \"^\"\n  let literalChars = 0\n  let i = 0\n  while (i < seg.length) {\n    const ch = seg.charAt(i)\n    if (ch === \":\" && i + 1 < seg.length && IDENT_HEAD.test(seg.charAt(i + 1))) {\n      let k = i + 1\n      while (k < seg.length && IDENT_REST.test(seg.charAt(k))) k++\n      const name = seg.slice(i + 1, k)\n      if (names.includes(name)) {\n        throw new Error(`Duplicate param :${name} in segment \"${seg}\"`)\n      }\n      names.push(name)\n      pattern += \"([^/]+?)\"\n      i = k\n      continue\n    }\n    pattern += escapeRegexChar(ch)\n    literalChars += 1\n    i++\n  }\n  pattern += \"$\"\n  return { kind: \"mixed\", regex: new RegExp(pattern), names, literalChars }\n}\n\nconst REGEX_META = new Set(\".*+?^${}()|[]\\\\\".split(\"\"))\nfunction escapeRegexChar(ch: string): string {\n  return REGEX_META.has(ch) ? `\\\\${ch}` : ch\n}\n",
      "sha256": "f81563c103792e1b30d75349453368bf4cb5d6c92b5443c4b661e2ab2526784e"
    },
    {
      "path": "core/security.ts",
      "contents": "/**\n * Secure-by-default baseline.\n *\n * Applied by the app's fetch pipeline. Opt-out only — users can\n * override per-route via `.meta({ headers: {...} })` or disable\n * globally in `app({ security: {...} })`.\n */\n\nimport type { SecurityDefaults } from \"./types.ts\"\n\n/** Default 1 MB body size limit. */\nexport const DEFAULT_BODY_LIMIT_BYTES: number = 1_048_576\n\n/** Default response headers (sans HSTS; HSTS only added on HTTPS in prod). */\nexport const DEFAULT_RESPONSE_HEADERS: Readonly<Record<string, string>> = Object.freeze({\n  \"x-content-type-options\": \"nosniff\",\n  \"x-frame-options\": \"DENY\",\n  \"referrer-policy\": \"strict-origin-when-cross-origin\",\n  \"cross-origin-opener-policy\": \"same-origin\",\n  \"cross-origin-resource-policy\": \"same-origin\",\n  \"permissions-policy\": \"camera=(), microphone=(), geolocation=(), interest-cohort=()\",\n})\n\n/** Headers that must never be emitted by Hyper itself. */\nexport const SUPPRESSED_HEADERS: readonly string[] = Object.freeze([\"server\"])\n\n/**\n * Keys that are refused by our JSON parser at the boundary to prevent\n * prototype pollution. Rejection raises a 400 HyperError.\n */\nexport const FORBIDDEN_JSON_KEYS: readonly string[] = Object.freeze([\n  \"__proto__\",\n  \"constructor\",\n  \"prototype\",\n])\n\nexport const DEFAULT_SECURITY: SecurityDefaults = {\n  headers: true,\n  bodyLimitBytes: DEFAULT_BODY_LIMIT_BYTES,\n  rejectProtoKeys: true,\n  serverHeader: false,\n  rejectMethodOverride: true,\n  requestTimeoutMs: 30_000,\n  hstsEnv: \"production\",\n}\n\n/** Headers/body keys used to smuggle verbs via override. */\nexport const METHOD_OVERRIDE_HEADERS: readonly string[] = Object.freeze([\n  \"x-http-method-override\",\n  \"x-method-override\",\n  \"x-http-method\",\n])\nexport const METHOD_OVERRIDE_QUERY_KEYS: readonly string[] = Object.freeze([\"_method\"])\n\n// Precomputed entries of the default response headers — hoisted out of\n// the hot path so we don't pay an Object.entries allocation per request.\nconst DEFAULT_HEADER_ENTRIES: ReadonlyArray<readonly [string, string]> = Object.freeze(\n  Object.entries(DEFAULT_RESPONSE_HEADERS),\n)\n\n/**\n * Apply default headers to a Response. Always returns a new Response\n * with a fresh Headers bag — the previous version tried to short-circuit\n * via `headersEqual`, which was strictly more expensive than cloning\n * (two Array allocations + two sorts every request).\n *\n * HSTS is added only when `https && emitHsts !== false`.\n */\nexport function applyDefaultHeaders(\n  res: Response,\n  opts: {\n    https: boolean\n    overrides?: Readonly<Record<string, string>>\n    /** Emit HSTS. Typically set by the app only in production + HTTPS. */\n    emitHsts?: boolean\n  },\n): Response {\n  const { https, emitHsts, overrides } = opts\n  const headers = new Headers(res.headers)\n  for (let i = 0; i < DEFAULT_HEADER_ENTRIES.length; i++) {\n    const entry = DEFAULT_HEADER_ENTRIES[i]!\n    if (!headers.has(entry[0])) headers.set(entry[0], entry[1])\n  }\n  if (https && emitHsts !== false && !headers.has(\"strict-transport-security\")) {\n    headers.set(\"strict-transport-security\", \"max-age=15552000; includeSubDomains\")\n  }\n  for (let i = 0; i < SUPPRESSED_HEADERS.length; i++) headers.delete(SUPPRESSED_HEADERS[i]!)\n  if (overrides) {\n    for (const k in overrides) {\n      const v = overrides[k]\n      if (v !== undefined) headers.set(k.toLowerCase(), v)\n    }\n  }\n  return new Response(res.body, { status: res.status, statusText: res.statusText, headers })\n}\n\n/**\n * Deep-walk and reject forbidden keys (prototype pollution guard).\n * Throws `PrototypePollutionError` on hit.\n */\nexport function assertNoProtoKeys(value: unknown, path: string[] = []): void {\n  if (value === null || typeof value !== \"object\") return\n  if (Array.isArray(value)) {\n    for (let i = 0; i < value.length; i++) {\n      assertNoProtoKeys(value[i], [...path, String(i)])\n    }\n    return\n  }\n  for (const key of Object.keys(value)) {\n    if (FORBIDDEN_JSON_KEYS.includes(key)) {\n      throw new PrototypePollutionError(key, path)\n    }\n    assertNoProtoKeys((value as Record<string, unknown>)[key], [...path, key])\n  }\n}\n\nexport class PrototypePollutionError extends Error {\n  readonly key: string\n  readonly path: readonly string[]\n  constructor(key: string, path: readonly string[]) {\n    super(`Refusing body containing dangerous key \"${key}\" at ${path.join(\".\") || \"(root)\"}`)\n    this.name = \"PrototypePollutionError\"\n    this.key = key\n    this.path = path\n  }\n}\n",
      "sha256": "ce26fbfbb77fe2ba9df90900935bc079df4dc91e96d777983a3ad0c00b7502b7"
    },
    {
      "path": "core/standard-schema.ts",
      "contents": "/**\n * Standard Schema adapter.\n *\n * Hyper does not depend on any specific validation library; we accept\n * anything implementing the `~standard` contract.\n *\n * Spec: https://github.com/standard-schema/standard-schema\n */\n\nexport interface StandardSchemaV1<Input = unknown, Output = Input> {\n  readonly \"~standard\": StandardSchemaV1Props<Input, Output>\n}\n\nexport interface StandardSchemaV1Props<Input, Output> {\n  readonly version: 1\n  readonly vendor: string\n  readonly validate: (\n    value: unknown,\n  ) => StandardSchemaV1Result<Output> | Promise<StandardSchemaV1Result<Output>>\n  readonly types?: StandardSchemaV1Types<Input, Output> | undefined\n}\n\nexport interface StandardSchemaV1Types<Input, Output> {\n  readonly input: Input\n  readonly output: Output\n}\n\nexport type StandardSchemaV1Result<Output> =\n  | StandardSchemaV1SuccessResult<Output>\n  | StandardSchemaV1FailureResult\n\nexport interface StandardSchemaV1SuccessResult<Output> {\n  readonly value: Output\n  readonly issues?: undefined\n}\n\nexport interface StandardSchemaV1FailureResult {\n  readonly issues: readonly StandardSchemaV1Issue[]\n}\n\nexport interface StandardSchemaV1Issue {\n  readonly message: string\n  readonly path?: readonly (PropertyKey | StandardSchemaV1PathSegment)[] | undefined\n}\n\nexport interface StandardSchemaV1PathSegment {\n  readonly key: PropertyKey\n}\n\n/**\n * Run a Standard Schema against `value`. Returns the parsed value or\n * throws a `SchemaValidationError` with the issues attached so the\n * error mapper can project to a 400 with why/fix.\n */\nexport async function parseStandard<I, O>(\n  schema: StandardSchemaV1<I, O>,\n  value: unknown,\n): Promise<O> {\n  const result = await schema[\"~standard\"].validate(value)\n  if (result.issues && result.issues.length > 0) {\n    throw new SchemaValidationError(result.issues)\n  }\n\n  return (result as StandardSchemaV1SuccessResult<O>).value\n}\n\nexport class SchemaValidationError extends Error {\n  readonly issues: readonly StandardSchemaV1Issue[]\n  constructor(issues: readonly StandardSchemaV1Issue[]) {\n    super(\n      issues\n        .map((i) => `${(i.path ?? []).map(String).join(\".\") || \"(root)\"}: ${i.message}`)\n        .join(\"; \"),\n    )\n    this.name = \"SchemaValidationError\"\n    this.issues = issues\n  }\n}\n\n/** Narrowing guard. */\nexport function isStandardSchema(x: unknown): x is StandardSchemaV1 {\n  return (\n    typeof x === \"object\" &&\n    x !== null &&\n    \"~standard\" in x &&\n    typeof (x as { \"~standard\": unknown })[\"~standard\"] === \"object\"\n  )\n}\n",
      "sha256": "6283de86db2508ac522d3ea59585d0ac0b1ab4e83b99c3a70947e679e2691ac9"
    },
    {
      "path": "core/types.ts",
      "contents": "/**\n * Public types for @hyper/core.\n *\n * Kept in a single file so declaration merging surfaces (AppContext,\n * RouteMeta, ErrorRegistry) are easy to locate and re-export.\n */\n\nimport type { StandardSchemaV1 } from \"./standard-schema.ts\"\n\n/** HTTP verbs supported by the builder. */\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\" | \"HEAD\" | \"OPTIONS\"\n\n/**\n * Consumer-augmentable app context. Decorate/derive/plugins populate\n * this via `declare module \"@hyper/core\" { interface AppContext { ... } }`.\n *\n * Declared as an empty `interface` (not `type = {}`) so that TypeScript\n * honors declaration-merging: every `declare module` contribution adds\n * fields to this shape. Unlike `type = {}`, which matches any non-null\n * value and silently accepts garbage, `interface AppContext {}` enforces\n * the augmented shape in consumer code.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentable declaration-merging surface\n// biome-ignore lint/complexity/noBannedTypes: interface is the correct primitive here\nexport interface AppContext {}\n\n/** A per-route example — surfaced to OpenAPI, MCP few-shot, client JSDoc, tests. */\nexport interface RouteExample {\n  readonly name: string\n  readonly input?: {\n    params?: Record<string, unknown>\n    query?: Record<string, unknown>\n    body?: unknown\n    headers?: Record<string, unknown>\n  }\n  readonly output?: {\n    status?: number\n    body?: unknown\n  }\n}\n\n/** Per-route metadata (OpenAPI, MCP, auth tags, etc.). Augmentable. */\nexport interface RouteMeta {\n  /** Human-readable name. */\n  name?: string\n  /** Free-form tags; plugins may filter on these. */\n  tags?: readonly string[]\n  /** Set by `@hyper/mcp`; if absent, the route is not MCP-exposed. */\n  mcp?: false | { description: string; [k: string]: unknown }\n  /** Reserved for internal tooling (dev MCP etc.). Never projected. */\n  internal?: boolean\n  /** CSRF on/off for cookie-auth routes. Default: on. */\n  csrf?: boolean\n  /** Marks auth-endpoint routes for the default rate-limit recipe. */\n  authEndpoint?: boolean\n  /** Caller-defined overrides to response headers. */\n  headers?: Record<string, string>\n  /** Marks the route as deprecated — surfaces in OpenAPI + Sunset header. */\n  deprecated?: boolean | { readonly reason?: string; readonly sunset?: string }\n  /** API version for header/prefix-based routing. */\n  version?: string\n  /** Server action marker (`.actionable()`). */\n  action?: boolean\n  /** Per-route hard timeout in milliseconds. Overrides `security.requestTimeoutMs`. */\n  timeoutMs?: number\n  /** Examples — OpenAPI, MCP few-shot, client JSDoc, contract tests. */\n  examples?: readonly RouteExample[]\n  [k: string]: unknown\n}\n\n/**\n * Named error codes catalog per route. Augmentable via declaration\n * merging (same pattern as {@link AppContext}).\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentable declaration-merging surface\n// biome-ignore lint/complexity/noBannedTypes: interface is the correct primitive here\nexport interface ErrorRegistry {}\n\n/** The shape returned from every parsing step — Standard Schema aligned. */\nexport type Infer<S> = S extends StandardSchemaV1<unknown, infer O> ? O : unknown\n\n/** A Hyper response can be a real `Response`, a `Bun.file`, or bare data. */\nexport type HandlerReturn =\n  | Response\n  | BunFileLike\n  | object\n  | string\n  | number\n  | boolean\n  | null\n  | ReadableStream\n  | AsyncIterable<string | Uint8Array>\n  | undefined\n\n/** Marker for anything coercible by our response layer (Bun.file(...)). */\nexport interface BunFileLike {\n  readonly stream: () => ReadableStream\n  readonly type?: string\n  readonly size?: number\n  readonly name?: string\n}\n\n/** A compiled route — the normalized shape the app and router consume. */\nexport interface Route<M extends HttpMethod = HttpMethod> {\n  readonly method: M\n  readonly path: string\n  readonly params?: StandardSchemaV1\n  readonly query?: StandardSchemaV1\n  readonly body?: StandardSchemaV1\n  readonly headers?: StandardSchemaV1\n  readonly meta: RouteMeta\n  readonly handler: RouteHandler\n  /** Declared thrown-error shapes keyed by HTTP status (projection surface). */\n  readonly throws?: Record<number, StandardSchemaV1>\n  /** Named error-code catalog (projection surface). */\n  readonly errors?: Record<string, StandardSchemaV1>\n  /** True when the handler is a function (not a pre-built Response). */\n  readonly kind: \"fn\" | \"static\"\n  /** Optional compile-time-static Response for fast path. */\n  readonly staticResponse?: Response\n  /**\n   * Tags for every middleware attached to this route, in order. Middleware\n   * opts in by setting `fn.__hyperTag = \"<name>\"`. Consumed by\n   * `hyper security --check` and other introspection tools.\n   */\n  readonly middlewareTags?: readonly string[]\n}\n\n/** The internal handler shape after builder normalization. */\nexport type RouteHandler = (ctx: InternalHandlerCtx) => Promise<HandlerReturn> | HandlerReturn\n\n/**\n * Context passed into the handler — a superset the framework builds.\n *\n * `url`, `query`, `headers`, and `responseHeaders` are lazy: they're\n * materialized only when the handler reads them. When a route\n * declares a `.query()` / `.headers()` schema the pipeline sets the\n * parsed plain object as an own property (shadowing the lazy getter).\n * `query` / `headers` therefore hold whatever the handler expects —\n * typically a parsed record or `Record<string, string>` for the\n * schema-less case.\n */\nexport interface InternalHandlerCtx {\n  readonly req: Request\n  readonly url: URL\n  readonly params: Record<string, string>\n  readonly query: unknown\n  readonly headers: unknown\n  readonly body: unknown\n  /** Populated by plugin.context / decorate / derive. */\n  readonly ctx: AppContext\n  /** Lazy Bun.CookieMap accessor — parse on first touch. */\n  readonly cookies: () => import(\"bun\").CookieMap\n  /** Mutable response header bag; flushed into the final Response. */\n  readonly responseHeaders: Headers\n}\n\n/** A decorator factory — produces static context from the parsed env. */\nexport type DecorateFactory<Env = unknown, Added extends object = object> = (\n  env: Env,\n) => Added | Promise<Added>\n\n/** A derive factory — produces per-request context from ctx + env + req. */\nexport type DeriveFactory<\n  Env = unknown,\n  CtxIn extends AppContext = AppContext,\n  Added extends object = object,\n> = (args: { ctx: CtxIn; env: Env; req: Request }) => Added | Promise<Added>\n\n/** Input accepted for `AppConfig.groups` — matches the GroupBuilder shape. */\nexport interface GroupConfigEntry {\n  /** The flattened build output consumed by `app()`. */\n  build(): RouteGroup\n}\n\n/** A plain-object router; nested records of routes or sub-routers. */\nexport interface PlainRouterConfig {\n  readonly [key: string]: Route | PlainRouterConfig\n}\n\n/** App-level config. */\nexport interface AppConfig {\n  /** Collected top-level routes. */\n  readonly routes?: readonly Route[]\n  /** Collected groups (flattened at app()). Accepts `GroupBuilder`s or `RouteGroup` literals. */\n  readonly groups?: readonly (GroupConfigEntry | RouteGroup)[]\n  /** Plain-object router (gives the typed-client tree). */\n  readonly router?: PlainRouterConfig\n  /** Feature flags for security defaults. On by default. */\n  readonly security?: Partial<SecurityDefaults>\n  /** Env schema + secrets + source. */\n  readonly env?: EnvConfigLike\n  /** Static context decoration (db, redis, etc.) constructed at boot. */\n  readonly decorate?: readonly DecorateFactory[]\n  /** Per-request derived context. */\n  readonly derive?: readonly DeriveFactory[]\n  /** Plugins installed in priority order. */\n  readonly plugins?: readonly HyperPlugin[]\n}\n\n/** Plugin surface for extending `app()` with lifecycle hooks and ctx decoration. */\nexport interface HyperPlugin {\n  readonly name: string\n  readonly build?: (app: HyperApp) => void | Promise<void>\n  readonly request?: {\n    /**\n     * Fires BEFORE route matching. Returning a Response short-circuits\n     * the rest of the pipeline — ideal for CORS preflight and auth gates.\n     */\n    readonly preRoute?: (args: {\n      req: Request\n    }) => Response | undefined | Promise<Response | undefined>\n    readonly before?: (args: {\n      req: Request\n      ctx: AppContext\n      route?: Route\n    }) => void | Promise<void>\n    readonly after?: (args: {\n      req: Request\n      ctx: AppContext\n      res: Response\n      route?: Route\n    }) => void | Promise<void>\n    readonly onError?: (args: {\n      req: Request\n      ctx: AppContext\n      error: unknown\n      route?: Route\n    }) => void | Promise<void>\n  }\n  readonly context?: (env: unknown) => object | Promise<object>\n}\n\nexport interface EnvConfigLike {\n  readonly schema?: unknown\n  readonly secrets?: readonly string[]\n  readonly source?: Record<string, string | undefined>\n}\n\n/** A single invocation — the shared path between HTTP/MCP/RPC/actions. */\nexport interface InvokeInput {\n  readonly method: HttpMethod\n  readonly path: string\n  readonly params?: Record<string, string>\n  readonly query?: Record<string, unknown>\n  readonly body?: unknown\n  readonly headers?: Record<string, string>\n  /** Optional pre-set AppContext (bypasses decorate/derive). Useful for tests. */\n  readonly ctx?: AppContext\n}\n\nexport interface InvokeResult {\n  readonly status: number\n  readonly data: unknown\n  readonly headers: Headers\n}\n\n/** The built app surface. */\nexport interface HyperApp {\n  /** fetch-compatible entry point for any Bun/edge/workers adapter. */\n  readonly fetch: (req: Request) => Promise<Response>\n  /** Bun.serve({ routes }) shape — static + dynamic routes mounted natively. */\n  readonly routes: BunRoutes\n  /** Raw route list for introspection. */\n  readonly routeList: readonly Route[]\n  /** Shared invoke path — HTTP/MCP/RPC/actions all funnel here. */\n  readonly invoke: (input: InvokeInput) => Promise<InvokeResult>\n  /** OpenAPI 3.1 serializer (schema conversion provided by @hyper/openapi). */\n  readonly toOpenAPI: (cfg?: {\n    title?: string\n    version?: string\n    description?: string\n  }) => import(\"./projection.ts\").OpenAPIManifest\n  /** MCP manifest. @hyper/mcp adds the transport. */\n  readonly toMCPManifest: () => import(\"./projection.ts\").MCPManifest\n  /** Client manifest. @hyper/client consumes this. */\n  readonly toClientManifest: () => import(\"./projection.ts\").ClientManifest\n  /** Original AppConfig — used by `app.test()` to produce scoped clones. */\n  readonly __config: AppConfig\n  /**\n   * Create a test-scoped clone. Replaces env/decorate/derive and can skip\n   * or swap plugins by name. Returns a fresh immutable app.\n   */\n  readonly test: (overrides?: TestOverrides) => HyperApp\n}\n\n/** Overrides accepted by `app.test()`. */\nexport interface TestOverrides {\n  /** Replace env source values (merged into config.env.source). */\n  readonly env?: Record<string, string | undefined>\n  /** Additional decorators appended to config.decorate. */\n  readonly decorate?: DecorateFactory | readonly DecorateFactory[]\n  /** Additional derive functions appended to config.derive. */\n  readonly derive?: DeriveFactory | readonly DeriveFactory[]\n  /** Plugins to skip (by name) or replace (by name → new plugin). */\n  readonly plugins?: {\n    readonly skip?: readonly string[]\n    readonly replace?: Record<string, HyperPlugin>\n    readonly add?: readonly HyperPlugin[]\n  }\n}\n\n/** One mounted-route value in `Bun.serve({ routes })`. */\nexport type BunRouteValue =\n  | Response\n  | Record<string, Response>\n  | ((req: Request) => Response | Promise<Response>)\n\n/** The Bun.serve routes map shape. See https://bun.sh/docs/api/http#routing. */\nexport type BunRoutes = Record<string, BunRouteValue>\n\n/** A RouteGroup is a collection of routes with a shared prefix. */\nexport interface RouteGroup {\n  readonly prefix: string\n  readonly routes: readonly Route[]\n}\n\n/** Security defaults — see ./security.ts for wire values. */\nexport interface SecurityDefaults {\n  readonly headers: boolean\n  readonly bodyLimitBytes: number\n  readonly rejectProtoKeys: boolean\n  readonly serverHeader: false\n  /**\n   * When true, a request carrying `X-HTTP-Method-Override` or\n   * `_method` is rejected with 400. Default: true. Prevents a class of\n   * CSRF/verb-smuggling bugs where attackers bypass safe-method checks.\n   */\n  readonly rejectMethodOverride: boolean\n  /**\n   * Hard request timeout in ms. The framework aborts the handler and\n   * returns 504 if a response isn't produced in time. 0 disables.\n   * Default: 30_000 (30s).\n   */\n  readonly requestTimeoutMs: number\n  /**\n   * Explicit env that allows Hyper to emit HSTS. Default: \"production\".\n   * HSTS is never emitted for HTTP, and only emitted for HTTPS when the\n   * current NODE_ENV (or provided `env`) matches. Prevents accidental\n   * HSTS pinning on dev domains.\n   */\n  readonly hstsEnv: string | false\n}\n",
      "sha256": "b37b2ecf54186938e449559cf4d80d772ef3a4f2a856bc140776ecb8a3f0c288"
    }
  ],
  "subpaths": {
    "bun": "adapters/bun"
  }
}