@ozanarslan/corpus 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/LICENSE.txt +20 -0
  2. package/README.md +337 -0
  3. package/dist/App/index.d.ts +377 -0
  4. package/dist/App/index.d.ts.map +1 -0
  5. package/dist/C.namespace.d.ts +27 -0
  6. package/dist/C.namespace.d.ts.map +1 -0
  7. package/dist/Config/index.d.ts +163 -0
  8. package/dist/Config/index.d.ts.map +1 -0
  9. package/dist/Context/ContextAccess/index.d.ts +50 -0
  10. package/dist/Context/ContextAccess/index.d.ts.map +1 -0
  11. package/dist/Context/index.d.ts +162 -0
  12. package/dist/Context/index.d.ts.map +1 -0
  13. package/dist/Controller/index.d.ts +164 -0
  14. package/dist/Controller/index.d.ts.map +1 -0
  15. package/dist/Cookies/index.d.ts +81 -0
  16. package/dist/Cookies/index.d.ts.map +1 -0
  17. package/dist/Cors/index.d.ts +135 -0
  18. package/dist/Cors/index.d.ts.map +1 -0
  19. package/dist/Exception/index.d.ts +85 -0
  20. package/dist/Exception/index.d.ts.map +1 -0
  21. package/dist/Globals/AppsRegistry/index.d.ts +48 -0
  22. package/dist/Globals/AppsRegistry/index.d.ts.map +1 -0
  23. package/dist/Globals/ParsersRegistry/index.d.ts +93 -0
  24. package/dist/Globals/ParsersRegistry/index.d.ts.map +1 -0
  25. package/dist/Globals/index.d.ts +109 -0
  26. package/dist/Globals/index.d.ts.map +1 -0
  27. package/dist/Headers/index.d.ts +208 -0
  28. package/dist/Headers/index.d.ts.map +1 -0
  29. package/dist/Middleware/index.d.ts +125 -0
  30. package/dist/Middleware/index.d.ts.map +1 -0
  31. package/dist/ParserBase/BodyParser/index.d.ts +138 -0
  32. package/dist/ParserBase/BodyParser/index.d.ts.map +1 -0
  33. package/dist/ParserBase/FormDataParser/index.d.ts +57 -0
  34. package/dist/ParserBase/FormDataParser/index.d.ts.map +1 -0
  35. package/dist/ParserBase/SchemaParser/index.d.ts +143 -0
  36. package/dist/ParserBase/SchemaParser/index.d.ts.map +1 -0
  37. package/dist/ParserBase/SearchParamsParser/index.d.ts +61 -0
  38. package/dist/ParserBase/SearchParamsParser/index.d.ts.map +1 -0
  39. package/dist/ParserBase/URLParamsParser/index.d.ts +32 -0
  40. package/dist/ParserBase/URLParamsParser/index.d.ts.map +1 -0
  41. package/dist/ParserBase/index.d.ts +92 -0
  42. package/dist/ParserBase/index.d.ts.map +1 -0
  43. package/dist/RateLimiter/index.d.ts +321 -0
  44. package/dist/RateLimiter/index.d.ts.map +1 -0
  45. package/dist/Request/index.d.ts +81 -0
  46. package/dist/Request/index.d.ts.map +1 -0
  47. package/dist/Res/index.d.ts +353 -0
  48. package/dist/Res/index.d.ts.map +1 -0
  49. package/dist/RouteBase/BundleRoute/index.d.ts +268 -0
  50. package/dist/RouteBase/BundleRoute/index.d.ts.map +1 -0
  51. package/dist/RouteBase/FileRoute/index.d.ts +121 -0
  52. package/dist/RouteBase/FileRoute/index.d.ts.map +1 -0
  53. package/dist/RouteBase/Route/index.d.ts +83 -0
  54. package/dist/RouteBase/Route/index.d.ts.map +1 -0
  55. package/dist/RouteBase/StaticRoute/index.d.ts +149 -0
  56. package/dist/RouteBase/StaticRoute/index.d.ts.map +1 -0
  57. package/dist/RouteBase/WebSocketRoute/index.d.ts +117 -0
  58. package/dist/RouteBase/WebSocketRoute/index.d.ts.map +1 -0
  59. package/dist/RouteBase/index.d.ts +206 -0
  60. package/dist/RouteBase/index.d.ts.map +1 -0
  61. package/dist/Server/index.d.ts +44 -0
  62. package/dist/Server/index.d.ts.map +1 -0
  63. package/dist/XFile/index.d.ts +202 -0
  64. package/dist/XFile/index.d.ts.map +1 -0
  65. package/dist/exports.d.ts +11 -0
  66. package/dist/exports.d.ts.map +1 -0
  67. package/dist/index.d.ts +5 -0
  68. package/dist/index.d.ts.map +1 -0
  69. package/dist/index.js +12 -0
  70. package/dist/index.js.map +47 -0
  71. package/dist/initialize.d.ts +22 -0
  72. package/dist/initialize.d.ts.map +1 -0
  73. package/dist/utils/assert.d.ts +7 -0
  74. package/dist/utils/assert.d.ts.map +1 -0
  75. package/dist/utils/is.d.ts +17 -0
  76. package/dist/utils/is.d.ts.map +1 -0
  77. package/dist/utils/lazy.d.ts +27 -0
  78. package/dist/utils/lazy.d.ts.map +1 -0
  79. package/dist/utils/logger.d.ts +31 -0
  80. package/dist/utils/logger.d.ts.map +1 -0
  81. package/dist/utils/object.d.ts +20 -0
  82. package/dist/utils/object.d.ts.map +1 -0
  83. package/dist/utils/path.d.ts +6 -0
  84. package/dist/utils/path.d.ts.map +1 -0
  85. package/dist/utils/tuple.d.ts +3 -0
  86. package/dist/utils/tuple.d.ts.map +1 -0
  87. package/package.json +57 -0
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../utils/is.ts", "../../utils/assert.ts", "../src/Globals/index.ts", "../src/Globals/AppsRegistry/index.ts", "../src/Cookies/index.ts", "../src/Headers/index.ts", "../../utils/lazy.ts", "../../utils/tuple.ts", "../src/XFile/index.ts", "../src/Res/index.ts", "../src/Exception/index.ts", "../../utils/object.ts", "../src/ParserBase/BodyParser/index.ts", "../src/ParserBase/index.ts", "../src/ParserBase/FormDataParser/index.ts", "../src/ParserBase/SchemaParser/index.ts", "../src/ParserBase/SearchParamsParser/index.ts", "../src/ParserBase/URLParamsParser/index.ts", "../src/Globals/ParsersRegistry/index.ts", "../src/Request/index.ts", "../src/initialize.ts", "../src/Context/index.ts", "../../utils/path.ts", "../src/RouteBase/index.ts", "../src/Config/index.ts", "../src/Context/ContextAccess/index.ts", "../../utils/logger.ts", "../src/App/index.ts", "../src/RouteBase/BundleRoute/index.ts", "../src/RouteBase/FileRoute/index.ts", "../src/RouteBase/Route/index.ts", "../src/RouteBase/StaticRoute/index.ts", "../src/RouteBase/WebSocketRoute/index.ts", "../src/Controller/index.ts", "../src/Cors/index.ts", "../src/Middleware/index.ts", "../src/RateLimiter/index.ts", "../src/index.ts"],
4
+ "sourcesContent": [
5
+ "export type Nullable<T> = T | null;\nexport type Optional<T> = T | undefined;\nexport type Maybe<T> = Optional<Nullable<T>>;\nexport type MaybePromise<T> = T | Promise<T>;\nexport type MaybeArray<T> = T | Array<T>;\nexport type OrString<T> = T | (string & {});\nexport type Primitive = string | number | boolean | bigint;\n\nexport const EMPTY: unique symbol = Symbol(\"empty\");\nexport type EMPTY = typeof EMPTY;\n\nexport function isPresent<T>(input: T): input is Exclude<T, null | undefined | EMPTY> {\n\treturn input !== undefined && input !== null && input !== EMPTY;\n}\n\nexport function isAbsent<T>(input: T): input is Extract<T, null | undefined | EMPTY> {\n\treturn !isPresent(input);\n}\n\nexport function isPrimitive<T>(input: T): input is Extract<T, Primitive> {\n\treturn isOneOf(typeof input, [\"string\", \"number\", \"boolean\", \"bigint\"]);\n}\n\nexport function isObject(input: unknown): input is Record<string, unknown> {\n\tif (isAbsent(input) || typeof input !== \"object\" || Array.isArray(input)) return false;\n\treturn Object.getPrototypeOf(input) === Object.prototype;\n}\n\nexport function isEmpty<T>(input: T): input is Extract<T, EMPTY | null | undefined | \"\" | 0> {\n\tif (isAbsent(input)) return true;\n\tif (typeof input === \"string\") return input.trim() === \"\";\n\tif (typeof input === \"number\") return input === 0;\n\tif (isObject(input)) return Object.keys(input).length === 0;\n\tif (Array.isArray(input)) return input.length === 0;\n\treturn false;\n}\n\nexport function isOneOf<const T extends readonly unknown[]>(\n\tinput: unknown,\n\tarray: T,\n): input is T[number] {\n\treturn array.includes(input);\n}\n\nexport function isSomeArray<T = string>(input: unknown): input is Array<T> {\n\treturn isPresent(input) && Array.isArray(input) && input.length > 0;\n}\n",
6
+ "import { isAbsent } from \"./is\";\n\ninterface AssertFn {\n\t<T>(condition: T | null | undefined, msg: string): asserts condition;\n\tpresent<T>(value: T | undefined, msg: string): asserts value is T;\n}\n\nfunction assertBase<T>(condition: T | null | undefined, msg: string): asserts condition {\n\tif (!condition) throw new Error(msg);\n}\n\nfunction assertPresent<T>(value: T | undefined, msg: string): asserts value is T {\n\tif (isAbsent(value)) throw new Error(msg);\n}\n\nconst assert: AssertFn = Object.assign(assertBase, { present: assertPresent });\n\nexport { assert };\n",
7
+ "/**\n * Typed process-wide state, stored on `globalThis` under package-namespaced\n * symbols.\n *\n * Two things in the framework must be singletons regardless of how many times\n * the module is evaluated: the {@link AppsRegistry} that {@link getNearestApp}\n * resolves against, and the {@link ParsersRegistry} that every route reads while\n * compiling. Module-level variables cannot guarantee that — a duplicated\n * dependency, a re-import under a different resolution, or a test runner\n * reloading modules each produces a second copy, and two apps registries means\n * routes registering onto an app that is never served.\n *\n * The keys are `Symbol.for(\"@ozanarslan/corpus:<key>\")`, so they are shared\n * across every copy of the package in the process and cannot collide with\n * another library's globals.\n *\n * @module Globals\n */\n\nimport type { AppsRegistry } from \"@/Globals/AppsRegistry\";\nimport type { ParsersRegistry } from \"@/Globals/ParsersRegistry\";\nimport { assert } from \"@/utils/assert\";\n\nimport pkg from \"../../package.json\";\n\n/**\n * What lives in the global store, and the type of each entry. Adding a key here\n * requires a matching entry in {@link GLOBAL_SYMBOLS}, which the `satisfies`\n * clause enforces.\n */\ninterface GlobalRegistry {\n\t// key: Type;\n\t/** Whether one-time framework setup has run. */\n\tinitialized: boolean;\n\t/** The shared {@link AppsRegistry} — every {@link App} constructed in this process. */\n\tapps: AppsRegistry;\n\t/** The shared {@link ParsersRegistry} — the parsers routes resolve at compile time. */\n\tparsers: ParsersRegistry;\n}\n\n/**\n * Prefixes a key with the package name, so global symbols cannot collide with\n * another library's.\n *\n * @param key - The unprefixed key.\n * @returns The namespaced string the symbol is registered under.\n */\nconst namespace = (key: string) => `${pkg.name}:${key}`;\n\n/**\n * The symbol for each {@link GlobalRegistry} entry.\n *\n * Registered through `Symbol.for`, so every copy of the package in the process\n * resolves to the same symbol and therefore to the same stored value.\n */\nconst GLOBAL_SYMBOLS = {\n\tinitialized: Symbol.for(namespace(\"initialized\")),\n\tapps: Symbol.for(namespace(\"apps\")),\n\tparsers: Symbol.for(namespace(\"parsers\")),\n} as const satisfies Record<keyof GlobalRegistry, symbol>;\n\n/**\n * Typed accessor for the global store.\n *\n * Keys are constrained to {@link GlobalRegistry}, so every read and write is\n * checked against the type declared for that entry.\n */\nclass Globals {\n\t/** The backing store: `globalThis`, typed for symbol access. */\n\tprivate static readonly store = globalThis as Record<symbol, unknown>;\n\n\t/**\n\t * Returns the symbol an entry is stored under.\n\t *\n\t * Useful for inspecting or clearing global state directly — in a test that\n\t * needs a clean process, for instance.\n\t *\n\t * @param key - The {@link GlobalRegistry} entry.\n\t * @returns Its symbol from {@link GLOBAL_SYMBOLS}.\n\t */\n\tstatic getSymbol<K extends keyof GlobalRegistry>(key: K): (typeof GLOBAL_SYMBOLS)[K] {\n\t\treturn GLOBAL_SYMBOLS[key];\n\t}\n\n\t/**\n\t * Reads an entry, creating it on first access.\n\t *\n\t * The initialiser runs only when the entry is absent, which is what makes this\n\t * safe to call from anywhere: the first caller creates the value and every\n\t * later one gets that same instance. This is how\n\t * {@link getOrInitAppsRegistry} and {@link getOrInitParsersRegistry} work.\n\t *\n\t * @param key - The {@link GlobalRegistry} entry.\n\t * @param init - Builds the initial value. Not called if the entry exists.\n\t * @returns The stored value.\n\t */\n\tstatic create<K extends keyof GlobalRegistry>(\n\t\tkey: K,\n\t\tinit: () => GlobalRegistry[K],\n\t): GlobalRegistry[K] {\n\t\tconst symbolKey = GLOBAL_SYMBOLS[key];\n\t\tif (!(symbolKey in this.store)) this.store[symbolKey] = init();\n\t\treturn this.store[symbolKey] as GlobalRegistry[K];\n\t}\n\n\t/**\n\t * Reads an entry that is expected to exist.\n\t *\n\t * @param key - The {@link GlobalRegistry} entry.\n\t * @returns The stored value.\n\t * @throws {@link Error} when the entry has not been created. Use\n\t * {@link Globals.create} when the caller may be the first.\n\t */\n\tstatic get<K extends keyof GlobalRegistry>(key: K): GlobalRegistry[K] {\n\t\tconst symbolKey = GLOBAL_SYMBOLS[key];\n\t\tconst value = this.store[symbolKey];\n\t\tassert.present(value, `Global \"${String(key)}\" was accessed before being created`);\n\t\treturn value as GlobalRegistry[K];\n\t}\n\n\t/**\n\t * Writes an entry, replacing any existing value.\n\t *\n\t * @param key - The {@link GlobalRegistry} entry.\n\t * @param value - The value to store.\n\t */\n\tstatic set<K extends keyof GlobalRegistry>(key: K, value: GlobalRegistry[K]): void {\n\t\tconst symbolKey = GLOBAL_SYMBOLS[key];\n\t\tthis.store[symbolKey] = value;\n\t}\n\n\t/**\n\t * Reports whether an entry has been created, without creating it or throwing.\n\t *\n\t * @param key - The {@link GlobalRegistry} entry.\n\t * @returns `true` when the entry exists.\n\t */\n\tstatic has<K extends keyof GlobalRegistry>(key: K): boolean {\n\t\tconst symbolKey = GLOBAL_SYMBOLS[key];\n\t\treturn symbolKey in this.store;\n\t}\n\n\t/**\n\t * Removes an entry, so the next {@link Globals.create} rebuilds it from\n\t * scratch. Mainly useful for resetting state between tests.\n\t *\n\t * @param key - The {@link GlobalRegistry} entry.\n\t */\n\tstatic delete<K extends keyof GlobalRegistry>(key: K): void {\n\t\tconst symbolKey = GLOBAL_SYMBOLS[key];\n\t\tdelete this.store[symbolKey];\n\t}\n}\n\nexport { type GlobalRegistry, Globals, GLOBAL_SYMBOLS };\n",
8
+ "/**\n * Process-wide registry of every {@link AppInterface} instance that has been\n * constructed.\n *\n * The registry is what lets {@link RouteBase} implementations and\n * {@link Middleware} attach themselves without being handed an app: they call\n * {@link getNearestApp} and find the most recently constructed one. The array\n * lives on {@link Globals}, so it survives module re-evaluation and is shared\n * across the whole process.\n *\n * @module AppsRegistry\n */\n\nimport type { AppInterface } from \"@/App\";\nimport { Globals } from \"@/Globals\";\nimport { assert } from \"@/utils/assert\";\n\n/**\n * The registry itself: every {@link AppInterface} in construction order, oldest\n * first. The last entry is the one {@link getNearestApp} resolves to.\n */\ntype AppsRegistry = Array<AppInterface>;\n\n/**\n * Reads the registry from {@link Globals}, creating an empty one on first\n * access.\n *\n * @returns The shared {@link AppsRegistry}. The array is live — mutating it\n * mutates the registry.\n */\nfunction getOrInitAppsRegistry(): AppsRegistry {\n\ttry {\n\t\treturn Globals.get(\"apps\");\n\t} catch {\n\t\treturn Globals.create(\"apps\", () => []);\n\t}\n}\n\n/**\n * Resolves the {@link AppInterface} that newly constructed routes and\n * middlewares should attach to: the most recently registered one.\n *\n * With a single app — the common case — this is simply that app. With several,\n * \"nearest\" means last constructed, so an app must be instantiated before the\n * routes belonging to it.\n *\n * @returns The most recently registered {@link AppInterface}.\n * @throws {@link Error} when no app has been constructed yet.\n */\nfunction getNearestApp(): AppInterface {\n\tconst apps = getOrInitAppsRegistry();\n\tconst current = apps[apps.length - 1];\n\tassert.present(current, \"No active App, instantiate one first\");\n\treturn current;\n}\n\n/**\n * Appends an app to the registry, making it the one {@link getNearestApp}\n * returns. Called by the {@link App} constructor; there is no reason to call it\n * by hand.\n *\n * @param app - The {@link AppInterface} to register.\n */\nfunction registerApp(app: AppInterface): void {\n\tconst apps = getOrInitAppsRegistry();\n\tapps.push(app);\n}\n\nexport { type AppsRegistry, getOrInitAppsRegistry, getNearestApp, registerApp };\n",
9
+ "/**\n * Cookie reading and writing, built on Bun's native cookie primitives.\n *\n * {@link Cookies} extends `Bun.CookieMap`, so it keeps the whole native API —\n * `get`, `set`, `delete`, iteration — and adds the two constructions the\n * framework needs: parsing an inbound `Cookie` header and parsing outbound\n * `Set-Cookie` headers back into a map.\n *\n * The two header formats are not interchangeable, which is why they get separate\n * entry points: a `Cookie` header is a list of name/value pairs on one line with\n * no attributes, while each `Set-Cookie` header is a single cookie carrying its\n * own `Path`, `Max-Age`, `HttpOnly` and the rest.\n *\n * @module Cookies\n */\n\n/**\n * Parses an inbound `Cookie` header into individual cookies.\n *\n * The header is a semicolon-separated list of name/value pairs with no\n * attributes, so each pair is re-serialised on its own before being handed to\n * Bun's parser. Malformed fragments that yield no name are skipped rather than\n * throwing — a single bad pair from a client should not fail the request.\n *\n * @param cookieHeader - The raw `Cookie` header value.\n * @returns The parsed cookies, in header order. Values keep whatever encoding\n * the client sent.\n */\nfunction parseCookieHeader(cookieHeader: string): Array<Bun.Cookie> {\n\tconst array: Array<Bun.Cookie> = [];\n\tconst pairs = cookieHeader.matchAll(/([^=;\\s]+)=([^;]*)/g);\n\tfor (const [, name, value] of pairs) {\n\t\tif (!name) continue;\n\t\tarray.push(Bun.Cookie.parse(`${name}=${value}`));\n\t}\n\treturn array;\n}\n\n/**\n * Parses `Set-Cookie` header values into individual cookies, attributes\n * included.\n *\n * Each header carries exactly one cookie, so no splitting is needed — unlike\n * {@link parseCookieHeader}, which has to break one header into many pairs.\n *\n * @param headers - The raw `Set-Cookie` header values, one per cookie. Use\n * `Headers.getSetCookie()` to obtain them; a plain `get` collapses repeated\n * headers into one string and loses the boundaries.\n * @returns The parsed cookies, in the order given.\n */\nfunction parseSetCookieHeaders(headers: string[]): Array<Bun.Cookie> {\n\tconst array: Array<Bun.Cookie> = [];\n\tfor (const header of headers) {\n\t\tarray.push(Bun.Cookie.parse(header));\n\t}\n\treturn array;\n}\n\n/**\n * A cookie map with header-parsing constructors.\n *\n * Everything `Bun.CookieMap` offers is available unchanged; the two static\n * methods are the additions. Use {@link Cookies.fromHeader} for what a client\n * sent and {@link Cookies.fromSetCookieHeaders} for what a server is sending\n * back — reading a response's own cookies, or following them in a client.\n */\nclass Cookies extends Bun.CookieMap {\n\t/**\n\t * Creates a cookie map, forwarding to the native constructor.\n\t *\n\t * `Bun.CookieMap` is a native class that ignores `new.target`, so instances\n\t * come back with the base prototype and none of the subclass members attached.\n\t * The prototype is reattached here, which is what makes subclassing work at\n\t * all.\n\t *\n\t * @param args - The native {@link Bun.CookieMap} constructor arguments.\n\t */\n\tconstructor(...args: ConstructorParameters<typeof Bun.CookieMap>) {\n\t\tsuper(...args);\n\t\t// Bun.CookieMap is native and ignores new.target.\n\t\t// Reattach the subclass prototype.\n\t\tObject.setPrototypeOf(this, new.target.prototype);\n\t}\n\n\t/**\n\t * Builds a map from an inbound `Cookie` header.\n\t *\n\t * @param cookieHeader - The raw `Cookie` header value, as sent by the client.\n\t * @returns The cookies it carried. Repeated names resolve to the last\n\t * occurrence.\n\t */\n\tstatic fromHeader(cookieHeader: string): Cookies {\n\t\tconst cookies = new Cookies();\n\t\tfor (const cookie of parseCookieHeader(cookieHeader)) cookies.set(cookie);\n\t\treturn cookies;\n\t}\n\n\t/**\n\t * Builds a map from outbound `Set-Cookie` headers, preserving each cookie's\n\t * attributes.\n\t *\n\t * @param headers - The raw `Set-Cookie` values, one per cookie.\n\t * @returns The cookies they describe. Repeated names resolve to the last\n\t * occurrence.\n\t */\n\tstatic fromSetCookieHeaders(headers: string[]): Cookies {\n\t\tconst cookies = new Cookies();\n\t\tfor (const cookie of parseSetCookieHeaders(headers)) cookies.set(cookie);\n\t\treturn cookies;\n\t}\n}\n\nexport { Cookies, parseCookieHeader, parseSetCookieHeaders };\n",
10
+ "/**\n * Header names, header-value builders, and the `Headers` extensions the\n * framework relies on.\n *\n * Two things happen here. {@link HeaderKey} gives the common header names as\n * documented constants, so header access is autocompleted and typo-free rather\n * than stringly typed. {@link patchGlobalHeaders} then extends the global\n * `Headers` class with conveniences the native API lacks: non-string values,\n * bulk assignment through {@link Headers.setMany}, and structured builders for\n * `Cache-Control` and `Content-Disposition`.\n *\n * The patch is what lets {@link Res}, {@link FileRoute} and {@link BundleRoute}\n * write headers without stringifying every value by hand. It applies to the\n * process's own `Headers`, so it must be installed once at startup, before any\n * request is served.\n *\n * @module Headers\n */\n\nimport type { MaybeArray, Nullable, Optional, OrString } from \"@/utils/is\";\nimport type { ValueOf } from \"@/utils/object\";\n\n/**\n * A value accepted by the patched header setters. Numbers and booleans are\n * stringified on the way in, so a byte length or a flag can be passed as-is.\n */\ntype HeadersInitValue = string | number | boolean;\n\n/**\n * What {@link Headers.setMany} accepts: entry pairs, a plain object, or another\n * `Headers` instance.\n */\ntype CustomHeadersInit = [string, HeadersInitValue][] | Record<string, HeadersInitValue> | Headers;\n\n/** Just some common headers. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers) for the full spec. */\nconst HeaderKey = {\n\t/** Controls caching mechanisms for requests and responses. */\n\tCacheControl: \"Cache-Control\",\n\t/** Specifies the media type of the resource or data. */\n\tContentType: \"Content-Type\",\n\t/** Indicates the size of the entity-body in bytes. */\n\tContentLength: \"Content-Length\",\n\t/** Whether to display payload inline within the page or prompt the user to download it as an attachment. */\n\tContentDisposition: \"Content-Disposition\",\n\t/** Specifies the character encodings that are acceptable. */\n\tAcceptEncoding: \"Accept-Encoding\",\n\t/** Informs the server about the types of data that can be sent back. */\n\tAccept: \"Accept\",\n\t/** Contains the credentials to authenticate with the server. */\n\tAuthorization: \"Authorization\",\n\t/** The user agent string of the client software. */\n\tUserAgent: \"User-Agent\",\n\t/** The domain name of the server and port number. */\n\tHost: \"Host\",\n\t/** The address of the previous web page from which the current request originated. */\n\tReferer: \"Referer\",\n\t/** Indicates whether the connection should be kept alive. */\n\tConnection: \"Connection\",\n\t/** Requests that the server switch to a different protocol (e.g. WebSocket). */\n\tUpgrade: \"Upgrade\",\n\t/** Used to specify directives that must be obeyed by caching mechanisms. */\n\tPragma: \"Pragma\",\n\t/** The date and time at which the message was sent. */\n\tDate: \"Date\",\n\t/** Makes the request conditional based on the ETag of the resource. */\n\tIfNoneMatch: \"If-None-Match\",\n\t/** Makes the request conditional based on the last modification date. */\n\tIfModifiedSince: \"If-Modified-Since\",\n\t/** An identifier for a specific version of a resource. */\n\tETag: \"ETag\",\n\t/** The date and time after which the response is considered stale. */\n\tExpires: \"Expires\",\n\t/** The last modification date of the resource. */\n\tLastModified: \"Last-Modified\",\n\t/** Indicates the URL to redirect a page to. */\n\tLocation: \"Location\",\n\t/** Defines the authentication method that should be used. */\n\tWWWAuthenticate: \"WWW-Authenticate\",\n\t/** Determines how long the results of a preflight request can be cached. */\n\tAccessControlMaxAge: \"Access-Control-Max-Age\",\n\t/** Indicates whether the response can be shared with resources with credentials. */\n\tAccessControlAllowCredentials: \"Access-Control-Allow-Credentials\",\n\t/** Indicates which HTTP method will be used in the actual CORS request. */\n\tAccessControlRequestMethod: \"Access-Control-Request-Method\",\n\t/** Indicates which headers can be exposed to the browser in a CORS response. */\n\tAccessControlExposeHeaders: \"Access-Control-Expose-Headers\",\n\t/** Indicates which origins are allowed to access the resource. */\n\tAccessControlAllowOrigin: \"Access-Control-Allow-Origin\",\n\t/** Specifies the HTTP methods allowed when accessing the resource in a CORS request. */\n\tAccessControlAllowMethods: \"Access-Control-Allow-Methods\",\n\t/** Specifies the HTTP headers allowed in a CORS request. */\n\tAccessControlAllowHeaders: \"Access-Control-Allow-Headers\",\n\t/** Sends cookies from the server to the client. */\n\tSetCookie: \"Set-Cookie\",\n\t/** Sends cookies from the client to the server. */\n\tCookie: \"Cookie\",\n\t/** Determines which headers should be used to select a response from cache when content negotiation is in use. */\n\tVary: \"Vary\",\n\t/** Set to \"nosniff\" by default in {@link Res}. */\n\tXContentTypeOptions: \"X-Content-Type-Options\",\n} as const;\n\n/**\n * A header name. The {@link HeaderKey} constants are suggested, but\n * {@link OrString} keeps any custom header name assignable — the enum is a\n * convenience, not a restriction.\n */\ntype HeaderKey = OrString<ValueOf<typeof HeaderKey>>;\n\n/**\n * A caching policy, rendered into a `Cache-Control` value by\n * {@link createCacheControlHeader}. Used by {@link FileRoute.cache} and by each\n * entry of a {@link BundleRouteDefinition}.\n */\ninterface CacheControlDefinition {\n\t/** Allows shared caches — proxies and CDNs — to store the response, not just the browser. */\n\tpublic?: boolean;\n\t/** How long the response stays fresh, in seconds. */\n\tmaxAge?: number;\n\t/** Promises the response will never change, so the browser skips revalidation entirely. Only meaningful with a content hash in the URL. */\n\timmutable?: boolean;\n\t/** Caches the response but revalidates before every use. Overrides the other directives except `noStore`. */\n\tnoCache?: boolean;\n\t/** Forbids storing the response anywhere. Overrides every other directive. */\n\tnoStore?: boolean;\n}\n\n/**\n * Renders a {@link CacheControlDefinition} into a `Cache-Control` header value.\n *\n * The two prohibitive directives take precedence and are emitted alone, since\n * combining them with freshness directives is contradictory: `noStore` wins over\n * everything, then `noCache`. Otherwise the remaining directives are joined in\n * order.\n *\n * @param def - The caching policy to render.\n * @returns The header value, or an empty string when the definition sets\n * nothing.\n */\nfunction createCacheControlHeader(def: CacheControlDefinition): string {\n\tif (def.noStore) return \"no-store\";\n\tif (def.noCache) return \"no-cache\";\n\n\tconst parts: string[] = [];\n\n\tif (def.public) parts.push(\"public\");\n\tif (def.maxAge !== undefined) parts.push(`max-age=${def.maxAge}`);\n\tif (def.immutable) parts.push(\"immutable\");\n\n\treturn parts.join(\", \");\n}\n\n/**\n * How a response body should be presented, rendered by\n * {@link createContentDispositionHeader}.\n */\ninterface ContentDispositionDefinition {\n\t/** `\"inline\"` to display in the browser, `\"attachment\"` to prompt a download. */\n\tdisposition: \"attachment\" | \"inline\";\n\t/** The name to save the file under. Omit it to let the client decide. */\n\tfilename?: string;\n}\n\n/**\n * Renders a {@link ContentDispositionDefinition} into a `Content-Disposition`\n * header value.\n *\n * @param def - The disposition to render.\n * @returns The header value, with a quoted `filename` parameter when one is\n * given.\n */\nfunction createContentDispositionHeader(def: ContentDispositionDefinition) {\n\tif (def.filename === undefined) return def.disposition;\n\treturn `${def.disposition}; filename=\"${def.filename}\"`;\n}\n\n/**\n * Reads a header from any `HeadersInit` shape, without constructing a `Headers`.\n *\n * Header names are case-insensitive, so every shape is matched case-insensitively:\n * a `Headers` instance is probed with both casings, and the array and object\n * forms are scanned with lowercased comparison.\n *\n * @param headers - The headers to read, in any accepted form. `undefined` and\n * `null` are allowed and yield `null`.\n * @param name - The header to look for.\n * @returns The value, or `null` when absent.\n */\nfunction readHeader(headers: Optional<HeadersInit>, name: HeaderKey): Nullable<string> {\n\tif (!headers) return null;\n\tif (headers instanceof Headers) {\n\t\treturn headers.get(name) ?? headers.get(name.toLowerCase());\n\t}\n\n\tconst lower = name.toLowerCase();\n\tif (Array.isArray(headers)) {\n\t\tfor (const [key, value] of headers) {\n\t\t\tif (key.toLowerCase() === lower) return value;\n\t\t}\n\t\treturn null;\n\t}\n\n\t// plain Record<string, string>\n\tfor (const key in headers) {\n\t\tif (key.toLowerCase() === lower) return headers[key] ?? null;\n\t}\n\treturn null;\n}\n\n/**\n * Installs the extended `Headers` behaviour declared above onto the global\n * class. Call it once at startup, before any request is served.\n *\n * The patch is applied twice over, deliberately. The prototype methods are\n * replaced so headers *the runtime* created — those on an incoming `Request`, or\n * on a `Response` built elsewhere — gain the same behaviour. The global class is\n * then also replaced with a subclass, so instances constructed after the patch\n * carry the methods as own class members rather than only as prototype\n * assignments.\n *\n * The subclass overrides `Symbol.hasInstance` to defer to the native class, so\n * `instanceof Headers` stays true for objects the runtime created before or\n * outside the patch. Without it, the swap would silently break every\n * `instanceof` check against headers the framework did not construct.\n */\nfunction patchGlobalHeaders() {\n\tconst NativeHeaders = globalThis.Headers;\n\n\tconst setNative = NativeHeaders.prototype.set;\n\tconst getNative = NativeHeaders.prototype.get;\n\tconst appendNative = Headers.prototype.append;\n\tconst hasNative = Headers.prototype.has;\n\n\t/**\n\t * Converts a header value to its string form.\n\t *\n\t * @param value - The value to stringify.\n\t * @returns The string, with booleans rendered by {@link boolString}.\n\t */\n\tfunction strHeaderValue(value: HeadersInitValue): string {\n\t\tif (typeof value === \"string\") return value;\n\t\telse if (typeof value === \"number\") return value.toString();\n\t\telse return String(value);\n\t}\n\n\t/**\n\t * Case-tolerant `has`, falling back to the lowercased name.\n\t *\n\t * @param name - The header to check.\n\t * @returns `true` when present under either casing.\n\t */\n\tfunction hasHeader(this: Headers, name: HeaderKey): boolean {\n\t\treturn hasNative.call(this, name) ?? hasNative.call(this, name.toLowerCase());\n\t}\n\n\t/**\n\t * Case-tolerant `get`, falling back to the lowercased name.\n\t *\n\t * @param name - The header to read.\n\t * @returns The value, or `null` when absent under both casings.\n\t */\n\tfunction getHeader(this: Headers, name: HeaderKey): Nullable<string> {\n\t\treturn getNative.call(this, name) ?? getNative.call(this, name.toLowerCase());\n\t}\n\n\t/**\n\t * `set` that accepts numbers and booleans as well as strings.\n\t *\n\t * @param name - The header to set.\n\t * @param value - The value, stringified by {@link strHeaderValue}.\n\t */\n\tfunction setHeader(this: Headers, name: HeaderKey, value: HeadersInitValue): void {\n\t\tsetNative.call(this, name, strHeaderValue(value));\n\t}\n\n\t/**\n\t * `append` that accepts non-string values and arrays.\n\t *\n\t * An array appends each item as its own header line, which is what\n\t * multi-valued headers such as `Set-Cookie` require — a joined string would be\n\t * one header, not several.\n\t *\n\t * @param name - The header to append to.\n\t * @param value - A value or an array of values.\n\t */\n\tfunction appendHeader(this: Headers, name: HeaderKey, value: MaybeArray<HeadersInitValue>) {\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const v of value) {\n\t\t\t\tappendNative.call(this, name, strHeaderValue(v));\n\t\t\t}\n\t\t} else {\n\t\t\tappendNative.call(this, name, strHeaderValue(value));\n\t\t}\n\t}\n\n\t/**\n\t * Sets many headers in one call.\n\t *\n\t * Copying from another `Headers` appends `Set-Cookie` rather than setting it,\n\t * so multiple cookies survive the copy instead of collapsing into one. The\n\t * array and object forms skip empty and whitespace-only values, so an unset\n\t * optional header is simply not written — which is why the file-serving routes\n\t * can pass a blank cache header without emitting one.\n\t *\n\t * @param init - The headers to copy in. See {@link CustomHeadersInit}.\n\t */\n\tfunction setManyHeaders(this: Headers, init: CustomHeadersInit) {\n\t\tif (init instanceof Headers) {\n\t\t\tinit.forEach((value, key) => {\n\t\t\t\tif (key.toLowerCase() === HeaderKey.SetCookie.toLowerCase()) this.append(key, value);\n\t\t\t\telse this.set(key, value);\n\t\t\t});\n\t\t} else {\n\t\t\tconst entries = Array.isArray(init) ? init : Object.entries(init);\n\t\t\tfor (const [key, val] of entries) {\n\t\t\t\tconst value = strHeaderValue(val);\n\t\t\t\tif (!value || !value.trim()) continue;\n\t\t\t\tthis.set(key, value);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Sets `Cache-Control` from a structured definition.\n\t *\n\t * @param def - The {@link CacheControlDefinition} to render with\n\t * {@link createCacheControlHeader}.\n\t */\n\tfunction setCacheControl(this: Headers, def: CacheControlDefinition): void {\n\t\tthis.set(HeaderKey.CacheControl, createCacheControlHeader(def));\n\t}\n\n\t/**\n\t * Sets `Content-Disposition` from a structured definition.\n\t *\n\t * @param def - The {@link ContentDispositionDefinition} to render with\n\t * {@link createContentDispositionHeader}.\n\t */\n\tfunction setContentDisposition(this: Headers, def: ContentDispositionDefinition): void {\n\t\tthis.set(HeaderKey.ContentDisposition, createContentDispositionHeader(def));\n\t}\n\n\tHeaders.prototype.get = getHeader;\n\tHeaders.prototype.set = setHeader;\n\tHeaders.prototype.append = appendHeader;\n\tHeaders.prototype.setMany = setManyHeaders;\n\tHeaders.prototype.setCacheControl = setCacheControl;\n\tHeaders.prototype.setContentDisposition = setContentDisposition;\n\n\tglobalThis.Headers = class extends NativeHeaders {\n\t\t/**\n\t\t * Defers `instanceof` to the native class, so headers created by the runtime\n\t\t * — before this patch, or outside it — still satisfy `instanceof Headers`.\n\t\t *\n\t\t * @param value - The value being tested.\n\t\t * @returns `true` when it is a native `Headers`.\n\t\t */\n\t\tstatic override [Symbol.hasInstance](value: unknown): boolean {\n\t\t\treturn value instanceof NativeHeaders;\n\t\t}\n\n\t\t/**\n\t\t * Reports whether a header is present, falling back to the lowercased name.\n\t\t *\n\t\t * @param name - The header to check.\n\t\t * @returns `true` when present.\n\t\t */\n\t\toverride has(name: HeaderKey): boolean {\n\t\t\treturn hasHeader.call(this, name);\n\t\t}\n\n\t\t/**\n\t\t * Reads a header, falling back to the lowercased name.\n\t\t *\n\t\t * @param name - The header to read.\n\t\t * @returns The value, or `null` when absent.\n\t\t */\n\t\toverride get(name: HeaderKey): Nullable<string> {\n\t\t\treturn getHeader.call(this, name);\n\t\t}\n\n\t\t/**\n\t\t * Sets a header, replacing any existing value.\n\t\t *\n\t\t * @param name - The header to set.\n\t\t * @param value - The value; numbers and booleans are stringified.\n\t\t */\n\t\toverride set(name: HeaderKey, value: HeadersInitValue): void {\n\t\t\tsetHeader.call(this, name, value);\n\t\t}\n\n\t\t/**\n\t\t * Adds a header value without replacing existing ones.\n\t\t *\n\t\t * @param name - The header to append to.\n\t\t * @param value - A value, or an array appended as separate header lines.\n\t\t */\n\t\toverride append(name: HeaderKey, value: MaybeArray<HeadersInitValue>): void {\n\t\t\tappendHeader.call(this, name, value);\n\t\t}\n\n\t\t/**\n\t\t * Sets many headers in one call.\n\t\t *\n\t\t * @param init - The headers to copy in. See {@link CustomHeadersInit}.\n\t\t */\n\t\toverride setMany(init: CustomHeadersInit): void {\n\t\t\tsetManyHeaders.call(this, init);\n\t\t}\n\n\t\t/**\n\t\t * Sets `Cache-Control` from a structured definition.\n\t\t *\n\t\t * @param def - The {@link CacheControlDefinition} to render.\n\t\t */\n\t\toverride setCacheControl(def: CacheControlDefinition): void {\n\t\t\tsetCacheControl.call(this, def);\n\t\t}\n\n\t\t/**\n\t\t * Sets `Content-Disposition` from a structured definition.\n\t\t *\n\t\t * @param def - The {@link ContentDispositionDefinition} to render.\n\t\t */\n\t\toverride setContentDisposition(def: ContentDispositionDefinition): void {\n\t\t\tsetContentDisposition.call(this, def);\n\t\t}\n\t};\n}\n\ndeclare global {\n\t/**\n\t * The `Headers` surface after {@link patchGlobalHeaders} has run.\n\t *\n\t * The existing methods are re-declared to accept a {@link HeaderKey} and a\n\t * {@link HeadersInitValue}; the rest are additions.\n\t */\n\tinterface Headers {\n\t\t/** Adds a value without replacing existing ones. An array appends each item as its own header line. */\n\t\tappend(name: HeaderKey, value: MaybeArray<HeadersInitValue>): void;\n\t\t/** Sets a header, replacing any existing value. Numbers and booleans are stringified. */\n\t\tset(name: HeaderKey, value: HeadersInitValue): void;\n\t\t/** Reads a header, falling back to the lowercased name. */\n\t\tget(name: HeaderKey): Nullable<string>;\n\t\t/** Reports whether a header is present, falling back to the lowercased name. */\n\t\thas(name: HeaderKey): boolean;\n\t\t/** Sets many headers at once. See {@link CustomHeadersInit}. */\n\t\tsetMany(init: CustomHeadersInit): void;\n\t\t/** Sets `Cache-Control` from a {@link CacheControlDefinition}. */\n\t\tsetCacheControl(def: CacheControlDefinition): void;\n\t\t/** Sets `Content-Disposition` from a {@link ContentDispositionDefinition}. */\n\t\tsetContentDisposition(def: ContentDispositionDefinition): void;\n\t}\n}\n\nexport {\n\tpatchGlobalHeaders,\n\tcreateCacheControlHeader,\n\tcreateContentDispositionHeader,\n\treadHeader,\n\tHeaderKey,\n\ttype ContentDispositionDefinition,\n\ttype CacheControlDefinition,\n};\n",
11
+ "import { EMPTY } from \"./is\";\n\ninterface LazyFn {\n\t<T>(init: () => T): Lazy<T>;\n\tmut<T>(init: () => T): LazyMut<T>;\n\tsynced<T>(init: LazySyncedInit<T>): LazyMut<T>;\n}\n\ninterface Lazy<T> {\n\t(): T;\n}\n\ninterface LazyMut<T> extends Lazy<T> {\n\tset(value: T): void;\n}\n\n/**\n * Hooks that intercept reads and writes. Both return the value to store,\n * so they can rewrite state rather than just observe it - return the\n * argument unchanged for a pure side effect.\n */\ninterface SyncCallbacks<T> {\n\tonGet?(value: T): T;\n\tonSet?(value: T, prev: T): T;\n}\n\ninterface LazySyncedInit<T> extends SyncCallbacks<T> {\n\tinit(): T;\n}\n\n/**\n * Defers `init` until the first call, then caches it forever.\n * `EMPTY` marks the uninitialized state so that a legitimately\n * `undefined` value still counts as initialized.\n */\nfunction lazyBase<T>(init: () => T): Lazy<T> {\n\tlet _state: T | EMPTY = EMPTY;\n\treturn () => {\n\t\tif (_state === EMPTY) _state = init();\n\t\treturn _state;\n\t};\n}\n\n/**\n * Lazy value that can be overwritten via `set`. Setting before the\n * first read skips `init` entirely - it will never run.\n */\nfunction lazyMut<T>(init: () => T): LazyMut<T> {\n\tlet _state: T | EMPTY = EMPTY;\n\tconst fn = (() => {\n\t\tif (_state === EMPTY) _state = init();\n\t\treturn _state;\n\t}) as LazyMut<T>;\n\tfn.set = (value: T) => {\n\t\t_state = value;\n\t};\n\treturn fn;\n}\n\n/**\n * Lazy value with read/write hooks. Unlike `lazy.mut`, `set` forces\n * initialization first so that `onSet` always receives a real `prev`.\n * `onGet` runs on every read, including the one that triggers `init`.\n */\nfunction lazySynced<T>({ init, onGet, onSet }: LazySyncedInit<T>): LazyMut<T> {\n\tlet _state: T | EMPTY = EMPTY;\n\tconst fn = (() => {\n\t\tif (_state === EMPTY) _state = init();\n\t\tif (onGet) _state = onGet(_state);\n\t\treturn _state;\n\t}) as LazyMut<T>;\n\tfn.set = (value: T) => {\n\t\tif (_state === EMPTY) _state = init();\n\t\tconst prev = _state;\n\t\t_state = onSet ? onSet(value, prev) : value;\n\t};\n\treturn fn;\n}\n\nconst lazy: LazyFn = Object.assign(lazyBase, { mut: lazyMut, synced: lazySynced });\n\nexport type { Lazy, LazyMut };\nexport { lazy };\n",
12
+ "export type Tuple<T, U> = [T, U];\n\nexport function tuple<T, U>(arg1: T, arg2: U): Tuple<T, U> {\n\treturn [arg1, arg2];\n}\n",
13
+ "/**\n * File handling for the routes that serve files.\n *\n * {@link XFile} wraps a path with the reads, writes and path manipulation the\n * framework needs, so {@link FileRoute}, {@link StaticRoute},\n * {@link BundleRoute} and {@link Res.file} all go through one place instead of\n * each reaching for `fs` and `path` themselves.\n *\n * It sits on both Bun's file API and Node's `fs`, choosing per operation: Bun's\n * for streaming, Node's for everything where Bun's caching would give a stale\n * answer.\n *\n * @module XFile\n */\n\nimport fs from \"fs\";\nimport path from \"path\";\n\n/**\n * A file at a path.\n *\n * The path is the only state; nothing is read at construction, so an XFile\n * doesn't officially exist (get it?) and can be written from scratch.\n * The path-manipulation members — {@link XFile.name}, {@link XFile.extension},\n * {@link XFile.sibling} — are pure string work and never touch the disk.\n *\n * Reads are synchronous. The routes that use it read either once at startup or\n * per request from the OS page cache, so the simpler API is worth more than the\n * async one.\n */\nclass XFile {\n\t/**\n\t * Creates a file handle. Nothing is read or checked until an operation asks\n\t * for it.\n\t *\n\t * @param pathOrBunFile - The path of the file or BunFile directly.\n\t * @param fallbackExtension - Fallback extension for extension-less files,\n\t * defaults to \"txt\". It decides what {@link XFile.mimeType} reports for a file\n\t * whose name carries no extension.\n\t */\n\tconstructor(\n\t\t/** The path of the file or BunFile directly. */\n\t\tpathOrBunFile: string | Bun.BunFile,\n\t\t/** Fallback extension for extension-less files, defaults to \"txt\" */\n\t\tprivate readonly fallbackExtension: string = \"txt\",\n\t) {\n\t\tif (typeof pathOrBunFile === \"string\") {\n\t\t\tthis.path = pathOrBunFile;\n\t\t\tthis.bunFile = Bun.file(pathOrBunFile);\n\t\t} else {\n\t\t\tthis.path = pathOrBunFile.name ?? \"\";\n\t\t\tthis.bunFile = pathOrBunFile;\n\t\t}\n\t}\n\n\t/** The underlying Bun file, used for streaming. */\n\treadonly bunFile: Bun.BunFile;\n\n\t/** The path this handle points at, as given. */\n\treadonly path: string;\n\n\t/** Matches either path separator, so paths split correctly on any platform. */\n\tprivate readonly SEP = /[\\\\/]/;\n\n\t/** The extension separator. */\n\tprivate readonly DOT = \".\";\n\n\t/** The empty string, used as a join separator and a replacement target. */\n\tprivate readonly EMPTY = \"\";\n\n\t/**\n\t * Joins parts with no separator.\n\t *\n\t * @param parts - The strings to concatenate.\n\t * @returns The joined string.\n\t */\n\tprivate readonly concat = (...parts: string[]) => parts.join(this.EMPTY);\n\n\t/**\n\t * Reads the file content and returns it as a string.\n\t * @param encoding defaults to \"utf8\"\n\t * @returns The decoded contents.\n\t * @throws {@link Error} when the file does not exist.\n\t */\n\ttext(encoding: BufferEncoding = \"utf8\"): string {\n\t\treturn fs.readFileSync(this.path, { encoding });\n\t}\n\n\t/**\n\t * Opens a readable stream to the file's content.\n\t *\n\t * @returns The stream. This is what the file-serving routes return for large\n\t * bodies, so nothing is buffered in memory.\n\t */\n\tstream(): ReadableStream<Uint8Array> {\n\t\treturn this.bunFile.stream();\n\t}\n\n\t/**\n\t * Checks if the file exists in the file system.\n\t *\n\t * Deliberately uses Node's `fs` rather than Bun's: `Bun.file().exists()`\n\t * caches, and this handle's `BunFile` was created before the file may have\n\t * been deleted, so it would report a stale answer.\n\t *\n\t * @returns `true` when the file exists right now.\n\t */\n\texists(): boolean {\n\t\t// Bun.file().exists() caches and the bunFile instance is\n\t\t// created before the file gets deleted\n\t\t// needs a fresh instance\n\t\treturn fs.existsSync(this.path);\n\t}\n\n\t/**\n\t * Writes to the file, directories are created recursively.\n\t *\n\t * @param data - The contents to write, replacing anything already there.\n\t */\n\twrite(data: string | ArrayBuffer | Uint8Array): void {\n\t\tfs.mkdirSync(path.dirname(this.path), { recursive: true });\n\t\tfs.writeFileSync(this.path, data instanceof ArrayBuffer ? Buffer.from(data) : data);\n\t}\n\n\t/**\n\t * Deletes the file.\n\t *\n\t * @throws {@link Error} when the file does not exist.\n\t */\n\tunlink(): void {\n\t\tfs.unlinkSync(this.path);\n\t}\n\n\t/**\n\t * Reads the file content and returns it as a Uint8Array.\n\t *\n\t * @returns The raw bytes. Used where an exact `Content-Length` is wanted; see\n\t * {@link XFile.stream} for the alternative.\n\t * @throws {@link Error} when the file does not exist.\n\t */\n\tbytes(): Buffer<ArrayBuffer> {\n\t\treturn fs.readFileSync(this.path);\n\t}\n\n\t/**\n\t * Returns file metadata (size, dates, etc.)\n\t *\n\t * @returns The stats.\n\t * @throws {@link Error} when the file does not exist.\n\t */\n\tstat(): fs.Stats {\n\t\treturn fs.statSync(this.path);\n\t}\n\n\t/**\n\t * Returns the file size in bytes, or null if the file doesn't exist. Unlike\n\t * {@link XFile.stat}, a missing file is not an error here.\n\t *\n\t * @returns The size in bytes, or `null`.\n\t */\n\tsize(): number | null {\n\t\tif (!fs.existsSync(this.path)) return null;\n\t\treturn fs.statSync(this.path).size;\n\t}\n\n\t/**\n\t * Copies the file to a destination path, creating directories recursively.\n\t *\n\t * @param dest - Where to copy it.\n\t * @returns A handle to the copy, carrying this handle's fallback extension.\n\t */\n\tcopyTo(dest: string): XFile {\n\t\tfs.mkdirSync(path.dirname(dest), { recursive: true });\n\t\tfs.copyFileSync(this.path, dest);\n\t\treturn new XFile(dest, this.fallbackExtension);\n\t}\n\n\t/**\n\t * Moves (renames) the file to a destination path, creating directories recursively.\n\t *\n\t * @param dest - Where to move it.\n\t * @returns A handle to the new location. This handle still points at the old\n\t * path, which no longer exists.\n\t */\n\tmoveTo(dest: string): XFile {\n\t\tfs.mkdirSync(path.dirname(dest), { recursive: true });\n\t\tfs.renameSync(this.path, dest);\n\t\treturn new XFile(dest, this.fallbackExtension);\n\t}\n\n\t/**\n\t * Appends data to the file.\n\t *\n\t * @param data - The contents to add at the end. Unlike {@link XFile.write},\n\t * parent directories are not created.\n\t */\n\tappend(data: string | Uint8Array): void {\n\t\tfs.appendFileSync(this.path, data);\n\t}\n\n\t/**\n\t * Returns a new XFile pointing to a sibling path (same directory, different name).\n\t *\n\t * @param filename - The sibling's full name, extension included.\n\t * @returns A handle to the sibling. Nothing is created on disk.\n\t */\n\tsibling(filename: string): XFile {\n\t\treturn new XFile(path.join(path.dirname(this.path), filename), this.fallbackExtension);\n\t}\n\n\t/**\n\t * Returns a new XFile with a different extension.\n\t *\n\t * @param ext - The new extension, without the leading dot.\n\t * @returns A handle to the renamed path. Nothing is moved on disk.\n\t */\n\twithExtension(ext: string): XFile {\n\t\treturn new XFile(\n\t\t\tpath.join(path.dirname(this.path), this.concat(this.name, this.DOT, ext)),\n\t\t\tthis.fallbackExtension,\n\t\t);\n\t}\n\n\t/**\n\t * The absolute directory path containing this file.\n\t *\n\t * @returns The directory path.\n\t */\n\tget dir(): string {\n\t\treturn path.dirname(this.path);\n\t}\n\n\t/**\n\t * The name of the file without the extension.\n\t *\n\t * @returns The bare name.\n\t */\n\tget name(): string {\n\t\tconst last = this.path.split(this.SEP).pop() ?? this.path;\n\t\treturn last.replace(this.concat(this.DOT, this.extension), this.EMPTY);\n\t}\n\n\t/**\n\t * The file extension (e.g., \"html\", \"md\"), excluding the leading dot.\n\t *\n\t * @returns The lowercased extension, or the fallback extension when the name\n\t * has none.\n\t */\n\tget extension(): string {\n\t\tconst last = this.path.split(this.SEP).pop() ?? this.EMPTY;\n\t\tif (!last.includes(this.DOT)) return this.fallbackExtension.toLowerCase();\n\t\tconst ext = last.split(this.DOT).pop() ?? this.fallbackExtension;\n\t\treturn ext.toLowerCase();\n\t}\n\n\t/**\n\t * The full name of the file, including the extension.\n\t *\n\t * @returns The name with its extension. This is what the file-serving routes\n\t * send as the `Content-Disposition` filename.\n\t */\n\tget fullname(): string {\n\t\treturn this.concat(this.name, this.DOT, this.extension);\n\t}\n\n\t/**\n\t * Gets the parent directory names as an array, ordered from the immediate parent up to the root.\n\t *\n\t * @returns The directory names, nearest first. Empty segments are dropped, so\n\t * a leading or doubled separator does not produce blanks.\n\t */\n\tget parentDirs(): string[] {\n\t\tconst parts = this.path.split(this.SEP);\n\t\tparts.pop();\n\t\treturn parts.filter((seg) => seg.length > 0).reverse();\n\t}\n\n\t/**\n\t * The standard MIME type associated with the file's extension.\n\t *\n\t * Resolved from the extension alone — the file's contents are never read, and\n\t * it need not exist. Any parameters such as `charset` are stripped, so the\n\t * result is safe to use as a bare `Content-Type`.\n\t *\n\t * @returns The MIME type, or `\"application/octet-stream\"` when the extension\n\t * maps to nothing known.\n\t */\n\tget mimeType(): string {\n\t\tconst type = Bun.file(`file.${this.extension}`).type;\n\t\tif (!type || type === \"application/octet-stream\") return \"application/octet-stream\";\n\t\treturn type.split(\";\")[0]?.trim() ?? \"application/octet-stream\";\n\t}\n}\n\nexport { XFile };\n",
14
+ "/**\n * Response building: status codes, body serialization, streaming, and cookies.\n *\n * A {@link Res} is what a handler shapes on its way to a native `Response`. It\n * is created lazily on {@link Context.res}, so a handler that just returns a\n * value never constructs one; reach for it when the response needs a status,\n * headers, cookies, or a body form that a plain return value cannot express —\n * a file, a redirect, or a stream.\n *\n * Body serialization is inferred from the value's type at\n * {@link Res.toNativeResponse} time, so an object becomes JSON, a typed array\n * stays binary, and a stream passes through untouched — each with a matching\n * `Content-Type` unless one was set explicitly.\n *\n * ```ts\n * import { Res, Status } from \"@ozanarslan/corpus\";\n *\n * new Res({ id: 1 }, { status: Status.CREATED });\n * new Res().file(\"./report.pdf\");\n * new Res().redirect(\"/login\");\n * ```\n *\n * @module Res\n */\n\nimport { Cookies } from \"@/Cookies\";\nimport { Exception } from \"@/Exception\";\nimport {\n\tcreateContentDispositionHeader,\n\tHeaderKey,\n\ttype ContentDispositionDefinition,\n} from \"@/Headers\";\nimport {\n\ttype MaybePromise,\n\ttype Nullable,\n\ttype Optional,\n\tisPresent,\n\tisAbsent,\n\tisPrimitive,\n} from \"@/utils/is\";\nimport { lazy, type Lazy, type LazyMut } from \"@/utils/lazy\";\nimport type { ValueOf } from \"@/utils/object\";\nimport { type Tuple, tuple } from \"@/utils/tuple\";\nimport { XFile } from \"@/XFile\";\n\n/** Commonly used HTTP status codes. */\nconst Status = {\n\t/** Continue: Request received, please continue */\n\tCONTINUE: 100,\n\t/** Switching Protocols: Protocol change request approved */\n\tSWITCHING_PROTOCOLS: 101,\n\t/** Processing (WebDAV) */\n\tPROCESSING: 102,\n\t/** Early Hints */\n\tEARLY_HINTS: 103,\n\t/** OK: Request succeeded */\n\tOK: 200,\n\t/** Created: Resource created */\n\tCREATED: 201,\n\t/** Accepted: Request accepted but not completed */\n\tACCEPTED: 202,\n\t/** Non-Authoritative Information */\n\tNON_AUTHORITATIVE_INFORMATION: 203,\n\t/** No Content: Request succeeded, no body returned */\n\tNO_CONTENT: 204,\n\t/** Reset Content: Clear form or view */\n\tRESET_CONTENT: 205,\n\t/** Partial Content: Partial GET successful (e.g. range requests) */\n\tPARTIAL_CONTENT: 206,\n\t/** Multi-Status (WebDAV) */\n\tMULTI_STATUS: 207,\n\t/** Already Reported (WebDAV) */\n\tALREADY_REPORTED: 208,\n\t/** IM Used (HTTP Delta encoding) */\n\tIM_USED: 226,\n\t/** Multiple Choices */\n\tMULTIPLE_CHOICES: 300,\n\t/** Moved Permanently: Resource moved to a new URL */\n\tMOVED_PERMANENTLY: 301,\n\t/** Found: Resource temporarily under different URI */\n\tFOUND: 302,\n\t/** See Other: Redirect to another URI using GET */\n\tSEE_OTHER: 303,\n\t/** Not Modified: Cached version is still valid */\n\tNOT_MODIFIED: 304,\n\t/** Use Proxy: Deprecated */\n\tUSE_PROXY: 305,\n\t/** Temporary Redirect: Resource temporarily at another URI */\n\tTEMPORARY_REDIRECT: 307,\n\t/** Permanent Redirect: Resource permanently at another URI */\n\tPERMANENT_REDIRECT: 308,\n\t/** Bad Request: Malformed request */\n\tBAD_REQUEST: 400,\n\t/** Unauthorized: Missing or invalid auth credentials */\n\tUNAUTHORIZED: 401,\n\t/** Payment Required: Reserved for future use */\n\tPAYMENT_REQUIRED: 402,\n\t/** Forbidden: Authenticated but no permission */\n\tFORBIDDEN: 403,\n\t/** Not Found: Resource does not exist */\n\tNOT_FOUND: 404,\n\t/** Method Not Allowed: HTTP method not allowed */\n\tMETHOD_NOT_ALLOWED: 405,\n\t/** Not Acceptable: Response not acceptable by client */\n\tNOT_ACCEPTABLE: 406,\n\t/** Proxy Authentication Required */\n\tPROXY_AUTHENTICATION_REQUIRED: 407,\n\t/** Request Timeout: Server timeout waiting for client */\n\tREQUEST_TIMEOUT: 408,\n\t/** Conflict: Request conflict (e.g. duplicate resource) */\n\tCONFLICT: 409,\n\t/** Gone: Resource is no longer available */\n\tGONE: 410,\n\t/** Length Required: Missing Content-Length header */\n\tLENGTH_REQUIRED: 411,\n\t/** Precondition Failed */\n\tPRECONDITION_FAILED: 412,\n\t/** Payload Too Large */\n\tPAYLOAD_TOO_LARGE: 413,\n\t/** URI Too Long */\n\tURI_TOO_LONG: 414,\n\t/** Unsupported Media Type */\n\tUNSUPPORTED_MEDIA_TYPE: 415,\n\t/** Range Not Satisfiable */\n\tRANGE_NOT_SATISFIABLE: 416,\n\t/** Expectation Failed */\n\tEXPECTATION_FAILED: 417,\n\t/** I'm a teapot: Joke response for coffee machines */\n\tIM_A_TEAPOT: 418,\n\t/** Misdirected Request: Sent to the wrong server */\n\tMISDIRECTED_REQUEST: 421,\n\t/** Unprocessable Entity (WebDAV) */\n\tUNPROCESSABLE_ENTITY: 422,\n\t/** Locked (WebDAV) */\n\tLOCKED: 423,\n\t/** Failed Dependency (WebDAV) */\n\tFAILED_DEPENDENCY: 424,\n\t/** Too Early: Request might be replayed */\n\tTOO_EARLY: 425,\n\t/** Upgrade Required */\n\tUPGRADE_REQUIRED: 426,\n\t/** Precondition Required */\n\tPRECONDITION_REQUIRED: 428,\n\t/** Too Many Requests: Rate limiting */\n\tTOO_MANY_REQUESTS: 429,\n\t/** Request Header Fields Too Large */\n\tREQUEST_HEADER_FIELDS_TOO_LARGE: 431,\n\t/** Unavailable For Legal Reasons */\n\tUNAVAILABLE_FOR_LEGAL_REASONS: 451,\n\t/** Internal Server Error: Unhandled server error */\n\tINTERNAL_SERVER_ERROR: 500,\n\t/** Not Implemented: Endpoint/method not implemented */\n\tNOT_IMPLEMENTED: 501,\n\t/** Bad Gateway: Invalid response from upstream server */\n\tBAD_GATEWAY: 502,\n\t/** Service Unavailable: Server temporarily overloaded/down */\n\tSERVICE_UNAVAILABLE: 503,\n\t/** Gateway Timeout: No response from upstream server */\n\tGATEWAY_TIMEOUT: 504,\n\t/** HTTP Version Not Supported */\n\tHTTP_VERSION_NOT_SUPPORTED: 505,\n\t/** Variant Also Negotiates */\n\tVARIANT_ALSO_NEGOTIATES: 506,\n\t/** Insufficient Storage (WebDAV) */\n\tINSUFFICIENT_STORAGE: 507,\n\t/** Loop Detected (WebDAV) */\n\tLOOP_DETECTED: 508,\n\t/** Not Extended */\n\tNOT_EXTENDED: 510,\n\t/** Network Authentication Required */\n\tNETWORK_AUTHENTICATION_REQUIRED: 511,\n} as const;\n\n/**\n * An HTTP status code. The {@link Status} constants are suggested, but any\n * number is assignable.\n */\ntype Status = ValueOf<typeof Status> | (number & {});\n\n/**\n * Converts a body value into something `Response` accepts, and infers the\n * content type that goes with it.\n *\n * Types are checked in a deliberate order. Binary views are matched before the\n * JSON catch-all, because `Buffer` defines its own `toJSON` and would otherwise\n * serialize to `{\"type\":\"Buffer\",\"data\":[…]}` instead of being sent as bytes.\n * Streams get no inferred type at all — the producer knows what it is streaming,\n * so {@link FileRoute} and {@link StaticRoute} set it themselves.\n *\n * @param b - The value to send as the body.\n * @returns A {@link Tuple} of the `BodyInit` and the inferred content type,\n * either of which may be `null`.\n */\nfunction resolveResBody(b: unknown): Tuple<Nullable<BodyInit>, Nullable<string>> {\n\tif (isAbsent(b)) return tuple(null, null);\n\n\tif (isPrimitive(b)) return tuple(String(b), \"text/plain\");\n\n\t// Any typed array or DataView (Uint8Array, Int32Array, Float64Array, DataView,\n\t// Node's Buffer, etc.) is raw binary and must be forwarded as-is - NOT\n\t// JSON.stringify'd. This check must stay ahead of the catch-all at the bottom.\n\t// QUIRK: fs.readFileSync/Bun's byte-reading APIs return Uint8Array<ArrayBufferLike>,\n\t// while lib.dom.d.ts's BodyInit/ArrayBufferView are pinned to Uint8Array<ArrayBuffer>\n\t// in newer TS versions - hence the cast. This is a type-checker-only mismatch;\n\t// both are the same thing at runtime and Bun/fetch accept it fine.\n\tif (ArrayBuffer.isView(b)) return tuple(b as BodyInit, \"application/octet-stream\");\n\n\tif (typeof b !== \"object\") return tuple(String(b), null);\n\n\t// The raw backing buffer itself (not a view over it) is also valid BodyInit.\n\tif (b instanceof ArrayBuffer) return tuple(b, \"application/octet-stream\");\n\n\t// Blobs store type\n\tif (b instanceof Blob) return tuple(b, b.type || null);\n\n\tif (b instanceof FormData) return tuple(b, \"multipart/form-data\");\n\n\tif (b instanceof URLSearchParams) return tuple(b, \"application/x-www-form-urlencoded\");\n\n\t// No content-type is inferred for streams - the producer (e.g. StaticRoute,\n\t// FileRoute) is expected to set Content-Type explicitly before returning one.\n\tif (b instanceof ReadableStream) return tuple(b, null);\n\n\t// This is a deliberate convenience conversion, not a passthrough.\n\t// Serialized to an ISO string, same as any other object below.\n\tif (b instanceof Date) return tuple(b.toISOString(), \"text/plain\");\n\n\t// Catch-all: plain objects/arrays/other class instances get JSON-stringified.\n\t// IMPORTANT: this used to silently swallow Buffer/Uint8Array too, since Buffer's\n\t// own toJSON() serializes to {\"type\":\"Buffer\",\"data\":[...]}. The ArrayBuffer.isView\n\t// check above must always run before this line, or that regression comes back.\n\treturn tuple(JSON.stringify(b), \"application/json\");\n}\n\n/**\n * Produces the events for a server-sent event stream, used by {@link Res.sse}.\n *\n * @param send - Emits one event. `data` is JSON-serialized; `event` names the\n * event type for a client listening on something other than `message`, and `id`\n * lets a client resume from where it left off.\n * @returns Nothing when the source is finite — the stream is closed for you once\n * it resolves — or a cleanup function when it is open-ended, in which case the\n * stream stays open and the function runs if the client disconnects.\n */\ntype SseSource = (\n\tsend: (item: { data: unknown; event?: string; id?: string }) => void,\n) => MaybePromise<void | (() => void)>;\n\n/**\n * Produces the lines for a newline-delimited JSON stream, used by\n * {@link Res.ndjson}.\n *\n * @param send - Emits one item, JSON-serialized on its own line.\n * @returns Nothing to close the stream when the source resolves, or a cleanup\n * function to keep it open and be told when the client disconnects.\n */\ntype NdjsonSource = (send: (item: unknown) => void) => MaybePromise<void | (() => void)>;\n\n/**\n * Wraps a producer function into a `ReadableStream` with disconnect handling.\n *\n * Whether the stream closes on its own is decided by what the producer returns:\n * no cleanup function means it had a finite amount to send, so the stream closes\n * when it resolves; a cleanup function means it is open-ended and the stream\n * stays open until the client goes away.\n *\n * The cancelled flag is passed in rather than checked here, because a producer\n * that is mid-loop needs to notice the disconnect itself — sending after\n * cancellation would throw on a closed controller.\n *\n * @param execute - Fills the stream. Receives the controller to enqueue through,\n * and a predicate reporting whether the client has disconnected.\n * @returns The stream. Anything the producer throws surfaces as a stream error.\n */\nfunction createStream(\n\texecute: (\n\t\tcontroller: ReadableStreamDefaultController,\n\t\tisCancelled: () => boolean,\n\t) => MaybePromise<(() => void) | void>,\n): ReadableStream {\n\tlet cancelled = false;\n\tlet cleanupPromise: Optional<Promise<(() => void) | void>>;\n\treturn new ReadableStream({\n\t\tstart(controller) {\n\t\t\tcleanupPromise = (async () => {\n\t\t\t\ttry {\n\t\t\t\t\tconst cleanup = await execute(controller, () => cancelled);\n\t\t\t\t\tif (typeof cleanup !== \"function\") controller.close();\n\t\t\t\t\treturn cleanup;\n\t\t\t\t} catch (err) {\n\t\t\t\t\tcontroller.error(err);\n\t\t\t\t}\n\t\t\t})();\n\t\t},\n\t\tasync cancel() {\n\t\t\tcancelled = true;\n\t\t\tconst cleanup = await cleanupPromise;\n\t\t\tcleanup?.();\n\t\t},\n\t});\n}\n\n/**\n * Resolves a path or file into a readable {@link XFile}.\n *\n * @param fileOrPath - An {@link XFile} or a path to one.\n * @returns The file.\n * @throws {@link Exception} with {@link Status.NOT_FOUND} when it does not\n * exist, carrying the path as exception data.\n */\nfunction resolveFile(fileOrPath: XFile | string): XFile {\n\tconst file = fileOrPath instanceof XFile ? fileOrPath : new XFile(fileOrPath);\n\tif (!file.exists()) {\n\t\tthrow new Exception(Status.NOT_FOUND.toString(), Status.NOT_FOUND, { filePath: file.path });\n\t}\n\treturn file;\n}\n\n/**\n * Wraps a response's headers so that writing `Set-Cookie` also updates the\n * cookie map.\n *\n * {@link Res} exposes cookies two ways — as headers and as a {@link Cookies} map\n * — and they must not disagree. The map wins at serialization time, so a\n * `Set-Cookie` written directly as a header would be dropped unless it is\n * mirrored into the map, which is what this does. An array append is mirrored\n * item by item, since each is its own cookie.\n *\n * @param headers - The headers to wrap. Mutated in place.\n * @param getCookies - Resolves the cookie map lazily, so wrapping does not force\n * it into existence.\n * @returns The same headers object.\n */\nfunction resHeadersWrapper(headers: Headers, getCookies: () => Cookies): Headers {\n\tconst set = headers.set.bind(headers);\n\tconst append = headers.append.bind(headers);\n\n\tfunction sync(cookieString: string) {\n\t\tgetCookies().set(Bun.Cookie.parse(cookieString));\n\t}\n\n\theaders.set = ((...args: Parameters<Headers[\"set\"]>) => {\n\t\tset(...args);\n\t\tif (args[0].toLowerCase() === HeaderKey.SetCookie.toLowerCase()) {\n\t\t\tsync(String(args[1]));\n\t\t}\n\t}) as Headers[\"set\"];\n\n\theaders.append = ((...args: Parameters<Headers[\"append\"]>) => {\n\t\tappend(...args);\n\t\tif (args[0].toLowerCase() === HeaderKey.SetCookie.toLowerCase()) {\n\t\t\tif (Array.isArray(args[1])) {\n\t\t\t\tfor (const cs of args[1]) sync(String(cs));\n\t\t\t} else {\n\t\t\t\tsync(String(args[1]));\n\t\t\t}\n\t\t}\n\t}) as Headers[\"append\"];\n\n\treturn headers;\n}\n\n/** A `ResponseInit` that can also carry cookies. */\ninterface ResInit extends ResponseInit {\n\t/** Cookies to seed {@link Res.cookies} with. */\n\tcookies?: Cookies;\n}\n\n/**\n * A response under construction.\n *\n * Everything is mutable until {@link Res.toNativeResponse} is called, so a\n * {@link Middleware} can adjust a response a route handler already built. The\n * body is kept as the original value rather than serialized eagerly, which is\n * what lets the content type be inferred from it at the very end.\n *\n * Headers and cookies are both lazy, so an untouched response allocates neither.\n * The chainable methods — {@link Res.file}, {@link Res.redirect},\n * {@link Res.sse} and the rest — set the body and its headers together and\n * return `this`.\n *\n * @typeParam R - The body type, carried from the route's own response type.\n */\nclass Res<R = unknown> {\n\t/**\n\t * Creates a response.\n\t *\n\t * @param body - The body value. Serialized by {@link resolveResBody} at\n\t * {@link Res.toNativeResponse} time, not now.\n\t * @param init - Status, status text, headers and cookies. See\n\t * {@link ResInit}.\n\t */\n\tconstructor(body?: Nullable<BodyInit | R>, init?: ResInit) {\n\t\tthis.body = isAbsent(body) ? null : body;\n\n\t\tthis.status = init?.status ?? Status.OK;\n\n\t\tthis.statusText = init?.statusText ?? \"\";\n\n\t\tthis._cookies = lazy(() => init?.cookies ?? new Cookies());\n\n\t\tthis._headers = lazy.synced({\n\t\t\tinit: () =>\n\t\t\t\tresHeadersWrapper(\n\t\t\t\t\tinit?.headers instanceof Headers ? init.headers : new Headers(init?.headers),\n\t\t\t\t\t() => this._cookies(),\n\t\t\t\t),\n\t\t\tonGet: (value) => {\n\t\t\t\tconst cookies = this._cookies();\n\t\t\t\t// Cookie values are percent-encoded on serialize - that is what keeps\n\t\t\t\t// a CRLF in a value from splitting the response. The encoding must\n\t\t\t\t// happen exactly once: see the double-encoding tests in Res.\n\t\t\t\t// Use the prototype methods, not the patched ones: writing the\n\t\t\t\t// map into the header must not feed the header back into the\n\t\t\t\t// map. Bun.Cookie.parse does not decode what serialize encoded,\n\t\t\t\t// so each round-trip re-encodes (\"%3A\" -> \"%253A\").\n\t\t\t\tHeaders.prototype.delete.call(value, HeaderKey.SetCookie);\n\t\t\t\tfor (const header of cookies.toSetCookieHeaders()) {\n\t\t\t\t\tHeaders.prototype.append.call(value, HeaderKey.SetCookie, header);\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * The body to send. Assign any value — objects become JSON, typed arrays stay\n\t * binary, streams pass through — and {@link resolveResBody} works out the rest\n\t * at serialization time.\n\t */\n\tbody: Nullable<BodyInit | R> = null;\n\n\t/** The status code. Defaults to {@link Status.OK}. */\n\tstatus: number;\n\n\t/** The status text. Empty by default, which lets the runtime supply the standard phrase. */\n\tstatusText: string;\n\n\t/** Backing store for {@link Res.headers}, created on first access. */\n\tprivate _headers: LazyMut<Headers>;\n\n\t/**\n\t * The response headers.\n\t *\n\t * Reading them rewrites the `Set-Cookie` lines from {@link Res.cookies} first,\n\t * so the two views never disagree — and writing `Set-Cookie` here feeds back\n\t * into the cookie map. Values may be numbers or booleans, since\n\t * {@link patchGlobalHeaders} has stringified setters.\n\t *\n\t * @returns The headers, created on first access.\n\t */\n\tget headers(): Headers {\n\t\treturn this._headers();\n\t}\n\n\t/** Backing store for {@link Res.cookies}, created on first access. */\n\tprivate _cookies: Lazy<Cookies>;\n\n\t/**\n\t * The cookies to send, as a mutable map.\n\t *\n\t * This is the authoritative view: the map is serialized into `Set-Cookie`\n\t * whenever {@link Res.headers} is read, so deleting a cookie here removes its\n\t * header. Values are percent-encoded on serialization, which is what keeps a\n\t * CRLF in a cookie value from splitting the response.\n\t *\n\t * @returns The {@link Cookies} map, created on first access.\n\t */\n\tget cookies(): Cookies {\n\t\treturn this._cookies();\n\t}\n\n\t/**\n\t * Serializes everything into a native `Response`.\n\t *\n\t * The content type inferred from the body is applied only when none was set\n\t * explicitly, so a handler's own choice always wins. `X-Content-Type-Options:\n\t * nosniff` is set unconditionally — without it a browser will sniff a\n\t * `text/plain` body that looks like markup and render it as HTML, turning any\n\t * reflected value into XSS.\n\t *\n\t * @returns The response to send over the wire. Called by\n\t * {@link App.respond}.\n\t */\n\ttoNativeResponse(): Response {\n\t\tconst data = this.body;\n\t\tconst headers = this.headers;\n\t\tconst status = this.status;\n\t\tconst statusText = this.statusText;\n\t\tconst [body, contentType] = resolveResBody(data);\n\t\tif (isPresent(contentType) && !headers.has(HeaderKey.ContentType)) {\n\t\t\theaders.set(HeaderKey.ContentType, contentType);\n\t\t}\n\t\t// Browsers will otherwise MIME-sniff a text/plain body that looks like\n\t\t// markup and render it as HTML, turning any reflected value into XSS.\n\t\t// Set unconditionally: there is no case where sniffing is wanted.\n\t\tif (!headers.has(HeaderKey.XContentTypeOptions)) {\n\t\t\theaders.set(HeaderKey.XContentTypeOptions, \"nosniff\");\n\t\t}\n\t\treturn new Response(body, { headers, status, statusText });\n\t}\n\n\t/**\n\t * Turns the response into a server-sent event stream.\n\t *\n\t * Sets the body to a stream and the headers browsers require for `EventSource`\n\t * to work — the event stream type, no caching, and a kept-alive connection.\n\t *\n\t * @param source - The {@link SseSource} producing events. Return a cleanup\n\t * function from it to keep the stream open indefinitely.\n\t * @param retry - Reconnection delay in milliseconds, sent with every event to\n\t * tell the client how long to wait before reconnecting.\n\t * @returns This response, for chaining.\n\t */\n\tsse(source: SseSource, retry?: number): this {\n\t\tconst encoder = new TextEncoder();\n\t\tconst stream = createStream((controller, isCancelled) => {\n\t\t\treturn source((event) => {\n\t\t\t\tif (isCancelled()) return;\n\t\t\t\tlet chunk = \"\";\n\t\t\t\tif (isPresent(retry)) chunk += `retry: ${retry}\\n`;\n\t\t\t\tif (event.id) chunk += `id: ${event.id}\\n`;\n\t\t\t\tif (event.event) chunk += `event: ${event.event}\\n`;\n\t\t\t\tchunk += `data: ${JSON.stringify(event.data)}\\n\\n`;\n\t\t\t\tcontroller.enqueue(encoder.encode(chunk));\n\t\t\t});\n\t\t});\n\t\tthis.body = stream;\n\t\tthis.headers.set(HeaderKey.ContentType, \"text/event-stream\");\n\t\tthis.headers.set(HeaderKey.CacheControl, \"no-cache\");\n\t\tthis.headers.set(HeaderKey.Connection, \"keep-alive\");\n\t\treturn this;\n\t}\n\n\t/**\n\t * Turns the response into a newline-delimited JSON stream.\n\t *\n\t * Each item is serialized onto its own line, so a client can parse results as\n\t * they arrive instead of waiting for a whole array. Useful for large result\n\t * sets and progressive output where the event semantics of {@link Res.sse} are\n\t * not needed.\n\t *\n\t * @param source - The {@link NdjsonSource} producing items. Return a cleanup\n\t * function from it to keep the stream open indefinitely.\n\t * @returns This response, for chaining.\n\t */\n\tndjson(source: NdjsonSource): this {\n\t\tconst encoder = new TextEncoder();\n\t\tconst stream = createStream((controller, isCancelled) => {\n\t\t\treturn source((item) => {\n\t\t\t\tif (isCancelled()) return;\n\t\t\t\tcontroller.enqueue(encoder.encode(`${JSON.stringify(item)}\\n`));\n\t\t\t});\n\t\t});\n\t\tthis.body = stream;\n\t\tthis.headers.set(HeaderKey.ContentType, \"application/x-ndjson\");\n\t\tthis.headers.set(HeaderKey.CacheControl, \"no-cache\");\n\t\treturn this;\n\t}\n\n\t/**\n\t * Streams a file as the response body, without reading it into memory. Prefer\n\t * this over {@link Res.file} for anything large.\n\t *\n\t * @param fileOrPath - An {@link XFile} or a path to one.\n\t * @param disposition - `\"inline\"` to display in the browser, `\"attachment\"` to\n\t * prompt a download under the file's own name.\n\t * @returns This response, for chaining.\n\t * @throws {@link Exception} with {@link Status.NOT_FOUND} when the file does\n\t * not exist.\n\t */\n\tstreamFile(\n\t\tfileOrPath: XFile | string,\n\t\tdisposition: ContentDispositionDefinition[\"disposition\"],\n\t): this {\n\t\tconst file = resolveFile(fileOrPath);\n\t\tconst stream = file.stream();\n\t\tthis.body = stream;\n\t\tthis.headers.set(HeaderKey.ContentType, file.mimeType);\n\t\tthis.headers.set(\n\t\t\tHeaderKey.ContentDisposition,\n\t\t\tcreateContentDispositionHeader({\n\t\t\t\tdisposition: disposition,\n\t\t\t\tfilename: file.fullname,\n\t\t\t}),\n\t\t);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Sends a file as the response body, read into memory so it can carry an exact\n\t * `Content-Length`. Use {@link Res.streamFile} instead for large files.\n\t *\n\t * @param fileOrPath - An {@link XFile} or a path to one.\n\t * @returns This response, for chaining.\n\t * @throws {@link Exception} with {@link Status.NOT_FOUND} when the file does\n\t * not exist.\n\t */\n\tfile(fileOrPath: XFile | string): this {\n\t\tconst file = resolveFile(fileOrPath);\n\t\tconst bytes = file.bytes();\n\t\tthis.body = bytes;\n\t\tthis.headers.set(HeaderKey.ContentType, file.mimeType);\n\t\tthis.headers.set(HeaderKey.ContentLength, bytes.byteLength.toString());\n\t\treturn this;\n\t}\n\n\t/**\n\t * Redirects the client to another URL.\n\t *\n\t * @param url - Where to send the client, absolute or relative.\n\t * @param status - Which redirect to use. Defaults to {@link Status.FOUND}, a\n\t * temporary redirect that browsers do not cache. See\n\t * {@link Res.permanentRedirect}, {@link Res.temporaryRedirect} and\n\t * {@link Res.seeOther} for the named alternatives.\n\t * @returns This response, for chaining.\n\t */\n\tredirect(url: string | URL, status: 301 | 302 | 303 | 307 | 308 = 302): this {\n\t\tthis.status = status;\n\t\tconst urlString = url instanceof URL ? url.toString() : url;\n\t\tthis.headers.set(HeaderKey.Location, urlString);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Redirects with {@link Status.MOVED_PERMANENTLY}, which browsers and search\n\t * engines cache indefinitely. Use it only when the resource has really moved\n\t * for good.\n\t *\n\t * @param url - Where to send the client.\n\t * @returns This response, for chaining.\n\t */\n\tpermanentRedirect(url: string | URL): this {\n\t\treturn this.redirect(url, Status.MOVED_PERMANENTLY);\n\t}\n\n\t/**\n\t * Redirects with {@link Status.TEMPORARY_REDIRECT}, which preserves the\n\t * original method and body — unlike {@link Status.FOUND}, which clients\n\t * commonly turn into a GET.\n\t *\n\t * @param url - Where to send the client.\n\t * @returns This response, for chaining.\n\t */\n\ttemporaryRedirect(url: string | URL): this {\n\t\treturn this.redirect(url, Status.TEMPORARY_REDIRECT);\n\t}\n\n\t/**\n\t * Redirects with {@link Status.SEE_OTHER}, which explicitly switches the\n\t * client to a GET. This is the correct redirect after a successful POST, since\n\t * it stops a refresh from resubmitting the form.\n\t *\n\t * @param url - Where to send the client.\n\t * @returns This response, for chaining.\n\t */\n\tseeOther(url: string | URL): this {\n\t\treturn this.redirect(url, Status.SEE_OTHER);\n\t}\n}\n\nexport { Res, Status };\n",
15
+ "/**\n * Throwing HTTP errors with a status attached.\n *\n * All errors are caught by {@link App.handleError}, whatever their type. What an\n * {@link Exception} adds is the {@link Status} to answer with: it is rendered\n * through {@link Exception.toRes}, message and detail intact, while an ordinary\n * error becomes an opaque {@link Status.INTERNAL_SERVER_ERROR} so an accidental\n * `TypeError` does not leak its message to the client.\n *\n * Throwing one is the intended way to end a request early:\n *\n * ```ts\n * import { Exception, Status } from \"@ozanarslan/corpus\";\n *\n * if (!user) throw new Exception(\"User not found\", Status.NOT_FOUND);\n * ```\n *\n * Subclass it for errors you raise often, so the status and message live in one\n * place rather than at every throw site.\n *\n * @module Exception\n */\n\nimport { Res, Status } from \"@/Res\";\nimport { assert } from \"@/utils/assert\";\n\n/**\n * An error carrying the {@link Status} it should be answered with.\n *\n * Beyond a status, an exception can carry {@link Exception.data} — either\n * arbitrary detail to include in the error body, or a fully formed {@link Res}\n * when the error response needs its own headers or shape.\n */\nclass Exception extends Error {\n\t/**\n\t * Creates an exception for subclasses, which assign\n\t * {@link Exception.message}, {@link Exception.status} and\n\t * {@link Exception.data} themselves.\n\t */\n\tconstructor();\n\t/**\n\t * Creates an exception to throw from a handler.\n\t *\n\t * @param message - Human-readable description. It is sent to the client, so\n\t * write it for whoever receives the response.\n\t * @param status - The {@link Status} to answer with.\n\t * @param data - Optional detail attached to the response body under `error`,\n\t * such as validation failures. Pass a {@link Res} instead to control the whole\n\t * error response — see {@link Exception.toRes}.\n\t */\n\tconstructor(message: string, status: Status, data?: unknown);\n\tconstructor(message?: string, status?: Status, data?: unknown) {\n\t\tsuper(message);\n\t\tif (new.target !== Exception) return;\n\t\tconst msg = \"Exception must be constructed with (message, status, data?) or extended.\";\n\t\tassert.present(message, msg);\n\t\tassert.present(status, msg);\n\t\tthis.message = message;\n\t\tthis.status = status;\n\t\tthis.data = data;\n\t}\n\n\t/** Human-readable description, sent to the client in the error body. */\n\toverride message!: string;\n\n\t/** The {@link Status} the response is sent with. */\n\tstatus!: Status;\n\n\t/**\n\t * Optional detail. An arbitrary value is placed under `error` in the response\n\t * body; a {@link Res} is used as the response itself.\n\t */\n\tdata?: unknown;\n\n\t/**\n\t * Renders the exception as the response to send.\n\t *\n\t * When {@link Exception.data} is a {@link Res}, that response is used directly:\n\t * its status is overwritten with {@link Exception.status}, the Exception\n\t * message gets discarded. Otherwise a fresh {@link Res} is built with the\n\t * message and the detail under `error`.\n\t *\n\t * @returns The {@link Res} for this error. Called by\n\t * {@link App.handleError}.\n\t */\n\ttoRes(): Res {\n\t\tif (this.data instanceof Res) {\n\t\t\tthis.data.status = this.status;\n\t\t\treturn this.data;\n\t\t}\n\t\treturn new Res({ error: this.data, message: this.message }, { status: this.status });\n\t}\n\n\t/**\n\t * Checks the exception's status, by numeric value or by {@link Status} name.\n\t *\n\t * Useful when catching an exception to branch on what went wrong:\n\t *\n\t * ```ts\n\t * if (err instanceof Exception && err.isStatusOf(\"NOT_FOUND\")) { … }\n\t * ```\n\t *\n\t * @param status - A {@link Status} value or one of its keys.\n\t * @returns `true` when {@link Exception.status} matches.\n\t */\n\tisStatusOf(status: Status | keyof typeof Status): boolean {\n\t\treturn typeof status === \"number\" ? this.status === status : this.status === Status[status];\n\t}\n}\n\nexport { Exception };\n",
16
+ "export type ValueOf<T> = T[keyof T];\nexport type Prettify<T> = { [K in keyof T]: T[K] } & {};\nexport type PartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\nexport type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;\nexport type DeepRequired<T> = T extends object ? { [K in keyof T]-?: DeepRequired<T[K]> } : T;\n\nexport function createSafeObject<T = Record<string, unknown>>(): T {\n\treturn Object.create(null);\n}\n\ndeclare global {\n\tinterface ObjectConstructor {\n\t\tkeys<O extends object>(o: O): Array<keyof O>;\n\t\tvalues<O extends object>(o: O): Array<O[keyof O]>;\n\t\tentries<O extends object>(o: O): Array<[keyof O, O[keyof O]]>;\n\t}\n}\n",
17
+ "/**\n * Content-type-driven body reading for requests and responses alike.\n *\n * {@link BodyParser} is the implementation behind\n * {@link ParsersRegistry.bodyParser}, which {@link App} uses to fill\n * {@link Context.body}. It classifies the `Content-Type` header, then delegates\n * structured payloads to {@link SearchParamsParser} and {@link FormDataParser}\n * through the registry, so nesting and coercion rules stay identical across\n * query strings, form posts and JSON.\n *\n * @module BodyParser\n */\n\nimport { Exception } from \"@/Exception\";\nimport { getOrInitParsersRegistry } from \"@/Globals/ParsersRegistry\";\nimport { HeaderKey } from \"@/Headers\";\nimport { Res, Status } from \"@/Res\";\nimport type { Nullable } from \"@/utils/is\";\nimport { createSafeObject } from \"@/utils/object\";\n\n/**\n * The family a `Content-Type` header belongs to, as resolved by\n * {@link getContentTypeDisco}. Each value selects one of the read strategies on\n * {@link BodyParser}.\n */\ntype ContentTypeDisco = \"json\" | \"form-urlencoded\" | \"form-data\" | \"text\" | \"xml\" | \"binary\";\n\n/**\n * What {@link BodyParser.parse} can produce.\n *\n * Structured types yield an object or an array, textual types a string, binary\n * types the undrained stream so it can be piped rather than buffered, and a\n * binary body that turned out to be absent yields `null`.\n */\ntype ParsedBody =\n\t| Record<string, unknown>\n\t| Array<unknown>\n\t| string\n\t| ReadableStream<Uint8Array>\n\t| null;\n\n/**\n * The public shape of a body parser, implemented by {@link BodyParser}.\n *\n * Assign an alternative implementation to\n * {@link ParsersRegistry.bodyParser} through {@link setParsersRegistry} to\n * replace body handling framework-wide.\n */\ninterface BodyParserInterface {\n\t/**\n\t * Reads and decodes a body according to its `Content-Type`.\n\t *\n\t * @param received - The request, response or {@link Res} to read from.\n\t * @returns The decoded body.\n\t */\n\tparse(received: Request | Response | Res): Promise<ParsedBody>;\n}\n\n/**\n * Ordered `Content-Type` patterns mapped to their {@link ContentTypeDisco}.\n *\n * The first match wins, so narrower patterns must precede broader ones.\n */\nconst CONTENT_TYPE_MAP: Array<[ContentTypeDisco, RegExp]> = [\n\t[\"json\", /application\\/json/],\n\t[\"form-urlencoded\", /application\\/x-www-form-urlencoded/],\n\t[\"form-data\", /multipart\\/form-data/],\n\t[\"text\", /text\\/plain/],\n\t[\"xml\", /application\\/xml|text\\/xml/],\n\t[\"binary\", /application\\/octet-stream|application\\/pdf|^image\\/|^audio\\/|^video\\//],\n];\n\n/**\n * Classifies a raw `Content-Type` header value against {@link CONTENT_TYPE_MAP}.\n *\n * Matching is substring-based, so parameters such as `charset` or a multipart\n * `boundary` do not prevent a match.\n *\n * @param contentType - The full header value, parameters included.\n * @returns The matching {@link ContentTypeDisco}, or `null` when nothing matches\n * — which sends the body to {@link BodyParser.getUnknownBody}.\n */\nfunction getContentTypeDisco(contentType: string): Nullable<ContentTypeDisco> {\n\tfor (const [disco, pattern] of CONTENT_TYPE_MAP) {\n\t\tif (pattern.test(contentType)) return disco;\n\t}\n\treturn null;\n}\n\n/**\n * Default {@link BodyParserInterface} implementation.\n *\n * Reads are non-destructive for caller-supplied inputs: the body is cloned\n * before being drained, so a {@link Middleware} can inspect a body and still\n * leave it readable downstream. Empty bodies resolve to a null-prototype object\n * from {@link createSafeObject} rather than a plain `{}`, which is what keeps a\n * `__proto__` key in a payload from reaching `Object.prototype`.\n */\nclass BodyParser implements BodyParserInterface {\n\t/**\n\t * Reads a body and decodes it according to its `Content-Type`. This can be\n\t * used for both request and response bodies.\n\t *\n\t * A {@link Res} is converted with {@link Res.toNativeResponse} first. The\n\t * content type is read from that source rather than from the clone, because\n\t * Bun derives it lazily for `FormData`-backed requests and a clone taken\n\t * before the derivation does not carry it. The clone itself is skipped for a\n\t * {@link Res}-derived response, which is freshly constructed and unshared.\n\t *\n\t * @param received - The request, response or {@link Res} whose body to read.\n\t * @returns The decoded {@link ParsedBody}. A body-less input yields an empty\n\t * safe object.\n\t * @throws {@link Exception} with {@link Status.BAD_REQUEST} when the payload is\n\t * malformed for its declared type — a `SyntaxError` from decoding is\n\t * attributed to the client, and the offending content type is attached to the\n\t * exception. Any other error propagates unchanged.\n\t */\n\tasync parse(received: Request | Response | Res): Promise<ParsedBody> {\n\t\tconst isRes = received instanceof Res;\n\t\tconst source = isRes ? received.toNativeResponse() : received;\n\n\t\t// Read the content-type from the source: Bun derives it lazily for\n\t\t// FormData-backed requests, and a clone taken before that derivation\n\t\t// does not carry it.\n\t\tconst contentType = source.headers.get(HeaderKey.ContentType) ?? \"\";\n\t\tconst contentTypeDisco = getContentTypeDisco(contentType);\n\n\t\tif (!source.body) return createSafeObject();\n\n\t\t// A Res-derived Response is freshly constructed and unshared, so it\n\t\t// needs no clone; anything caller-supplied must stay readable.\n\t\tconst input = isRes ? source : source.clone();\n\n\t\ttry {\n\t\t\tswitch (contentTypeDisco) {\n\t\t\t\tcase \"json\":\n\t\t\t\t\treturn await this.getJsonBody(input);\n\t\t\t\tcase \"form-urlencoded\":\n\t\t\t\t\treturn await this.getFormUrlEncodedBody(input);\n\t\t\t\tcase \"form-data\":\n\t\t\t\t\treturn await this.getFormDataBody(input);\n\t\t\t\tcase \"text\":\n\t\t\t\tcase \"xml\":\n\t\t\t\t\treturn await this.getTextBody(input, contentType);\n\t\t\t\tcase \"binary\":\n\t\t\t\t\treturn this.getBinaryBody(input);\n\t\t\t\tdefault:\n\t\t\t\t\treturn await this.getUnknownBody(input, contentType);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (err instanceof SyntaxError) {\n\t\t\t\tthrow new Exception(err.message, Status.BAD_REQUEST, { contentType });\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/**\n\t * Decodes an `application/json` body.\n\t *\n\t * @param input - The cloned request or response to drain.\n\t * @returns The parsed value, or an empty safe object when the body is blank —\n\t * an empty payload is treated as \"nothing sent\" rather than as a syntax error.\n\t * @throws `SyntaxError` on malformed JSON, which {@link BodyParser.parse}\n\t * converts into a {@link Status.BAD_REQUEST} {@link Exception}.\n\t */\n\tprivate async getJsonBody(\n\t\tinput: Request | Response,\n\t): Promise<Record<string, unknown> | Array<unknown>> {\n\t\tconst text = await input.text();\n\t\tif (!text || text.trim().length === 0) return createSafeObject();\n\t\treturn JSON.parse(text);\n\t}\n\n\t/**\n\t * Decodes an `application/x-www-form-urlencoded` body.\n\t *\n\t * The pairs are handed to {@link ParsersRegistry.searchParamsParser}, so a\n\t * form post and a query string with the same shape produce the same object,\n\t * including bracket nesting and repeated keys.\n\t *\n\t * @param input - The cloned request or response to drain.\n\t * @returns The parsed object, or an empty safe object when the body is blank.\n\t * Falls back to flat `URLSearchParams` entries if no search params parser is\n\t * registered.\n\t */\n\tprivate async getFormUrlEncodedBody(input: Request | Response): Promise<Record<string, unknown>> {\n\t\tconst text = await input.text();\n\t\tif (!text || text.trim().length === 0) return createSafeObject();\n\t\tconst searchParams = new URLSearchParams(text);\n\t\tconst searchParamsParser = getOrInitParsersRegistry().searchParamsParser;\n\t\tif (!searchParamsParser) return Object.fromEntries(searchParams.entries());\n\t\treturn searchParamsParser.parse(searchParams);\n\t}\n\n\t/**\n\t * Decodes a `multipart/form-data` body, delegating to\n\t * {@link ParsersRegistry.formDataParser} so uploaded files and nested field\n\t * names are handled consistently.\n\t *\n\t * @param input - The cloned request or response to drain.\n\t * @returns The parsed object, or flat `FormData` entries if no form data\n\t * parser is registered.\n\t */\n\tprivate async getFormDataBody(input: Request | Response): Promise<Record<string, unknown>> {\n\t\tconst formData = await input.formData();\n\t\tconst formDataParser = getOrInitParsersRegistry().formDataParser;\n\t\tif (!formDataParser) return Object.fromEntries(formData.entries());\n\t\treturn formDataParser.parse(formData);\n\t}\n\n\t/**\n\t * Decodes a textual body, honouring the `charset` parameter of the content\n\t * type.\n\t *\n\t * `Body.text()` always decodes as UTF-8 per the fetch spec, ignoring the\n\t * declared charset, so anything other than UTF-8 is decoded from the raw bytes\n\t * with a matching `TextDecoder`. An unrecognised charset label falls back to\n\t * UTF-8 rather than failing the request.\n\t *\n\t * @param input - The cloned request or response to drain.\n\t * @param contentType - The full header value, used to extract `charset`.\n\t * @returns The decoded text.\n\t */\n\tprivate async getTextBody(input: Request | Response, contentType: string): Promise<string> {\n\t\tconst charset =\n\t\t\tcontentType\n\t\t\t\t.match(/charset=([^;]+)/i)?.[1]\n\t\t\t\t?.trim()\n\t\t\t\t.toLowerCase() ?? null;\n\n\t\t// Per the fetch spec, Body.text() always decodes as UTF-8,\n\t\t// ignoring the Content-Type charset.\n\t\tif (!charset || charset === \"utf-8\" || charset === \"utf8\") {\n\t\t\treturn input.text();\n\t\t}\n\n\t\tlet decoder: TextDecoder;\n\t\ttry {\n\t\t\tdecoder = new TextDecoder(charset);\n\t\t} catch {\n\t\t\t// unknown charset label, fall back to utf-8\n\t\t\tdecoder = new TextDecoder(\"utf-8\");\n\t\t}\n\t\treturn decoder.decode(await input.arrayBuffer());\n\t}\n\n\t/**\n\t * Passes a binary body through undrained, so large uploads can be streamed\n\t * instead of buffered into memory.\n\t *\n\t * @param input - The cloned request or response.\n\t * @returns The body stream, or `null` if there is none.\n\t */\n\tprivate getBinaryBody(input: Request | Response): ReadableStream<Uint8Array> | null {\n\t\treturn input.body;\n\t}\n\n\t/**\n\t * Handles a body whose content type matched nothing in\n\t * {@link CONTENT_TYPE_MAP}, including requests that declared no type at all.\n\t *\n\t * The body is decoded as text and JSON is attempted opportunistically, so a\n\t * client that omits its `Content-Type` still gets structured data. A failed\n\t * parse is not an error here — the raw text is returned instead.\n\t *\n\t * @param input - The cloned request or response to drain.\n\t * @param contentType - The full header value, forwarded for charset handling.\n\t * @returns The parsed JSON value, the raw text, or an empty safe object when\n\t * the body is blank.\n\t */\n\tprivate async getUnknownBody(\n\t\tinput: Request | Response,\n\t\tcontentType: string,\n\t): Promise<Record<string, unknown> | Array<unknown> | string> {\n\t\tconst text = await this.getTextBody(input, contentType);\n\t\tif (!text || text.trim().length === 0) return createSafeObject();\n\t\ttry {\n\t\t\treturn JSON.parse(text);\n\t\t} catch {\n\t\t\treturn text;\n\t\t}\n\t}\n}\n\nexport { type BodyParserInterface, BodyParser };\n",
18
+ "/**\n * The shared base for the input parsers that turn flat key/value pairs into\n * nested objects.\n *\n * {@link URLParamsParser}, {@link SearchParamsParser} and\n * {@link FormDataParser} all face the same problem: a source that only carries\n * strings paired with strings, where structure has to be recovered from the key\n * itself and types from the value. {@link ParserBase} supplies both halves —\n * {@link ParserBase.parseKey} for the bracket notation and\n * {@link ParserBase.tryParseJSON} for value coercion — so the three agree on\n * what `user[roles][0]=admin` means regardless of which surface it arrived on.\n *\n * Subclass it to add a parser of your own, then register it through\n * {@link setParsersRegistry}.\n *\n * @module ParserBase\n */\n\n/**\n * The public shape of an input parser. This is the type\n * {@link ParsersRegistry.urlParamsParser}, {@link ParsersRegistry.searchParamsParser}\n * and {@link ParsersRegistry.formDataParser} hold, so a replacement need only\n * satisfy the contract rather than extend {@link ParserBase}.\n *\n * @typeParam T - The input the parser accepts — `URLSearchParams`, `FormData`,\n * or a record of raw path parameters.\n */\ninterface ParserBaseInterface<T> {\n\t/**\n\t * Converts the input into a nested object.\n\t *\n\t * @param input - The source to parse.\n\t * @returns The parsed object.\n\t */\n\tparse(input: T): Record<string, unknown>;\n}\n\n/**\n * Bracket segments that parse as an integer become array indices, so\n * `a[999999999]=1` would otherwise produce a billion-length array whose\n * JSON serialization is gigabytes of `null,` — a 15-byte body that OOMs\n * the process. Past this bound the segment stays a string key, and the\n * container is built as a plain object instead of an array.\n */\nconst ARRAY_INDEX_LIMIT = 1_000;\n\n/**\n * Base class for the input parsers.\n *\n * It provides the key and value handling; each subclass supplies\n * {@link ParserBase.parse} for its own source and does the writing, since how\n * repeated keys collect differs between a query string and a form.\n *\n * @typeParam T - The input the parser accepts.\n */\nabstract class ParserBase<T> implements ParserBaseInterface<T> {\n\t/**\n\t * Converts the input into a nested object. Implemented per source.\n\t *\n\t * @param input - The source to parse.\n\t * @returns The parsed object.\n\t */\n\tabstract parse(input: T): Record<string, unknown>;\n\n\t/**\n\t * Narrows a value to an indexable container so a path segment can be written\n\t * into it.\n\t *\n\t * Purely a readability helper for the traversal loops — the value is used\n\t * as-is, nothing is constructed.\n\t *\n\t * @param current - The level currently being written into.\n\t * @returns The same value, typed for index access.\n\t */\n\tprotected newContainer(current: unknown): Record<string | number, unknown> {\n\t\treturn current as Record<string | number, unknown>;\n\t}\n\n\t/**\n\t * Splits a key into the path it describes.\n\t *\n\t * Dot and bracket notation are treated as equivalent, so `a[0].b[1][c]` and\n\t * `a.0.b.1.c` both yield `[\"a\", 0, \"b\", 1, \"c\"]`. A purely numeric segment\n\t * becomes a number, which is how the subclasses know to create an array rather\n\t * than an object at that level — subject to {@link ARRAY_INDEX_LIMIT}, above\n\t * which the segment stays a string key.\n\t *\n\t * @param key - The raw field name.\n\t * @returns The path segments: numbers for array indices, strings for object\n\t * keys.\n\t */\n\tprotected parseKey(key: string): (string | number)[] {\n\t\t// split \"a[0].b[1][c]\" into [\"a\", 0, \"b\", 1, \"c\"]\n\t\t// \"next\" is used to assigned to correct index\n\t\tconst parts: (string | number)[] = [];\n\t\tfor (const part of key.split(/[.[\\]]+/).filter(Boolean)) {\n\t\t\tif (!/^\\d+$/.test(part)) {\n\t\t\t\tparts.push(part);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst index = parseInt(part);\n\t\t\tparts.push(index <= ARRAY_INDEX_LIMIT ? index : part);\n\t\t}\n\t\treturn parts;\n\t}\n\n\t/**\n\t * Coerces a raw string value into whatever it represents.\n\t *\n\t * Since every value from these sources arrives as a string, `\"42\"`, `\"true\"`\n\t * and `\"null\"` are decoded to their JSON equivalents. Anything that is not\n\t * valid JSON — ordinary text, most of the time — is returned unchanged rather\n\t * than treated as an error.\n\t *\n\t * @param value - The raw string value.\n\t * @returns The decoded value, or the original string.\n\t */\n\tprotected tryParseJSON(value: string): unknown {\n\t\ttry {\n\t\t\treturn JSON.parse(value);\n\t\t} catch {\n\t\t\treturn value;\n\t\t}\n\t}\n}\n\nexport { type ParserBaseInterface, ParserBase };\n",
19
+ "/**\n * Parsing `multipart/form-data` into a nested object.\n *\n * {@link FormDataParser} is the default {@link ParsersRegistry.formDataParser},\n * reached through {@link BodyParser} rather than called directly. It applies the\n * same bracket-notation nesting as {@link SearchParamsParser}, so a form post\n * and a query string with matching field names produce the same shape — the\n * difference is that a form can also carry files, which are kept as `File`\n * objects instead of being coerced.\n *\n * @module FormDataParser\n */\n\nimport { ParserBase } from \"@/ParserBase\";\nimport { createSafeObject } from \"@/utils/object\";\n\n/**\n * Turns a `FormData` into a nested object.\n *\n * Field names are read as paths through {@link ParserBase.parseKey}, so\n * `user[address][city]` and `tags[0]` build the objects and arrays they\n * describe. Repeated names collect into an array, which is how a multi-select or\n * a multi-file input arrives without any bracket notation at all.\n *\n * Values are coerced through {@link ParserBase.tryParseJSON}, so `\"true\"` and\n * `\"42\"` arrive as a boolean and a number rather than as strings. Files are\n * exempt — a `File` is passed through untouched.\n */\nclass FormDataParser extends ParserBase<FormData> {\n\t/**\n\t * Parses form data into a nested object.\n\t *\n\t * @param formData - The form data to parse, typically from\n\t * `Request.formData()`.\n\t * @returns The nested object, built on a null prototype by\n\t * {@link createSafeObject} so a `__proto__` field name cannot reach\n\t * `Object.prototype`.\n\t */\n\tparse(formData: FormData): Record<string, unknown> {\n\t\tconst result = createSafeObject();\n\n\t\tformData.forEach((entry, key) => {\n\t\t\tconst parts = this.parseKey(key);\n\t\t\tconst value = entry instanceof File ? entry : this.tryParseJSON(entry);\n\t\t\tthis.setDeep(result, parts, value);\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Writes a value at a path, creating the containers it passes through.\n\t *\n\t * Each missing level is created as an array when the next path part is a\n\t * number and as an object otherwise, so the shape follows the field name\n\t * rather than being guessed after the fact.\n\t *\n\t * The final slot collects rather than overwrites: an empty slot takes the\n\t * value, a slot already holding an array appends, and a slot holding a single\n\t * value is promoted to an array. That is what makes repeated field names\n\t * accumulate — order of arrival is preserved.\n\t *\n\t * @param result - The object being built up.\n\t * @param parts - The path from {@link ParserBase.parseKey}; numbers mean array\n\t * indices, strings mean object keys.\n\t * @param value - The value to write, already coerced or left as a `File`.\n\t */\n\tprivate setDeep(result: Record<string, unknown>, parts: (string | number)[], value: unknown) {\n\t\tlet current = result;\n\n\t\tfor (let i = 0; i < parts.length - 1; i++) {\n\t\t\tconst part = parts[i]!;\n\t\t\tconst next = parts[i + 1];\n\n\t\t\t// just for readability, current can be used directly as well\n\t\t\tconst container = this.newContainer(current);\n\n\t\t\t// each part needs an entry\n\t\t\t// container[part] is undefined so we assign it as inner container\n\t\t\tif (container[part] === undefined) {\n\t\t\t\tconst isIndexAssigned = typeof next === \"number\";\n\t\t\t\tcontainer[part] = isIndexAssigned ? [] : createSafeObject();\n\t\t\t}\n\n\t\t\t// if container[part] defined, it is a value assigned directly\n\t\t\t(current as unknown) = container[part];\n\t\t}\n\n\t\tconst last = parts[parts.length - 1]!;\n\t\tconst container = this.newContainer(current);\n\t\tconst existing = container[last];\n\n\t\tif (existing === undefined) {\n\t\t\t// first write at this slot\n\t\t\tcontainer[last] = value;\n\t\t} else if (Array.isArray(existing)) {\n\t\t\t// slot already holds an array, append\n\t\t\tcontainer[last] = [...existing, value];\n\t\t} else {\n\t\t\t// slot holds a single value, promote to array\n\t\t\tcontainer[last] = [existing, value];\n\t\t}\n\t}\n}\n\nexport { FormDataParser };\n",
20
+ "/**\n * Request validation against the schemas declared in a {@link RouteConfig}.\n *\n * Schemas are taken as [Standard Schema](https://standardschema.dev), so any\n * library implementing that spec works — Zod, Valibot, ArkType and others —\n * without corpus depending on any of them. The same interface also supplies the\n * inferred types that flow into {@link Context}, so declaring a schema both\n * validates the request and types the handler.\n *\n * {@link App} calls this after each surface is parsed, so what a handler sees on\n * {@link Context.body}, {@link Context.search} and {@link Context.params} is\n * already validated. A failure raises {@link Status.UNPROCESSABLE_ENTITY} with a\n * message naming the offending fields, so the client is told what was wrong\n * rather than just that something was.\n *\n * @module SchemaParser\n */\n\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\nimport { Exception } from \"@/Exception\";\nimport { Status } from \"@/Res\";\nimport type { RouteConfig } from \"@/RouteBase\";\nimport type { Prettify } from \"@/utils/object\";\n\n/**\n * Any Standard Schema validator producing `T`. This is the type\n * {@link RouteConfig} fields accept.\n *\n * @typeParam T - What the schema validates to.\n */\ntype Schema<T = unknown> = StandardSchemaV1<unknown, T>;\n\n/**\n * The type a schema accepts as input, before transformation.\n *\n * @typeParam T - The schema.\n */\ntype InferSchemaIn<T extends Schema> = StandardSchemaV1.InferInput<T>;\n\n/**\n * The type a schema produces after validation. This is what a route's\n * {@link Context} fields are typed as.\n *\n * @typeParam T - The schema.\n */\ntype InferSchemaOut<T extends Schema> = StandardSchemaV1.InferOutput<T>;\n\n/** The validation failures a schema reports. */\ntype ValidationIssues = readonly StandardSchemaV1.Issue[];\n\n/** If you prefer to put all schemas into a single object, this will be helpful */\ntype InferModel<T extends Record<string, any>> = {\n\t[K in keyof T as K extends \"prototype\" ? never : K]: T[K] extends RouteConfig<any, any, any, any>\n\t\t? Prettify<\n\t\t\t\t(T[K][\"body\"] extends Schema ? { body: InferSchemaOut<T[K][\"body\"]> } : {}) &\n\t\t\t\t\t(T[K][\"search\"] extends Schema ? { search: InferSchemaOut<T[K][\"search\"]> } : {}) &\n\t\t\t\t\t(T[K][\"params\"] extends Schema ? { params: InferSchemaOut<T[K][\"params\"]> } : {}) &\n\t\t\t\t\t(T[K][\"response\"] extends Schema ? { response: InferSchemaOut<T[K][\"response\"]> } : {})\n\t\t\t>\n\t\t: T[K] extends Schema\n\t\t\t? InferSchemaOut<T[K]>\n\t\t\t: never;\n};\n\n/**\n * The public shape of a schema parser, implemented by {@link SchemaParser}.\n * Assign an alternative to {@link ParsersRegistry.schemaParser} to change how\n * validation failures are reported.\n */\ninterface SchemaParserInterface {\n\t/**\n\t * Validates a value against a schema.\n\t *\n\t * @param label - Names the surface being validated, for the error message.\n\t * @param input - The value to validate.\n\t * @param schema - The schema. Omitting it passes the value through unchanged.\n\t * @returns The validated value.\n\t */\n\tparse<T = Record<string, unknown>>(label: string, input: unknown, schema?: Schema<T>): Promise<T>;\n\t/**\n\t * Validates a value against a synchronous schema.\n\t *\n\t * @param label - Names the surface being validated, for the error message.\n\t * @param input - The value to validate.\n\t * @param schema - The schema. Omitting it passes the value through unchanged.\n\t * @returns The validated value.\n\t */\n\tparseSync<T = Record<string, unknown>>(label: string, input: unknown, schema?: Schema<T>): T;\n}\n\n/**\n * Default {@link SchemaParserInterface} implementation.\n *\n * A missing schema is not an error — the value passes through untouched, which\n * is what makes {@link RouteConfig} entirely optional.\n */\nclass SchemaParser implements SchemaParserInterface {\n\t/**\n\t * Validates a value against a schema.\n\t *\n\t * This is what {@link App} uses during the request lifecycle, since a schema\n\t * may validate asynchronously.\n\t *\n\t * @param label - Names the surface being validated — `\"body\"`, `\"search\"` or\n\t * `\"params\"` — and appears in the error message.\n\t * @param data - The value to validate, already parsed from the request.\n\t * @param schema - The schema from the route's {@link RouteConfig}. Omitting it\n\t * returns the data as-is.\n\t * @returns The validated value, with whatever transformations the schema\n\t * applies.\n\t * @throws {@link Exception} with {@link Status.UNPROCESSABLE_ENTITY} when\n\t * validation fails, carrying the rejected data as exception data.\n\t */\n\tasync parse<T = Record<string, unknown>>(\n\t\tlabel: string,\n\t\tdata: unknown,\n\t\tschema?: Schema<T>,\n\t): Promise<T> {\n\t\tif (!schema) return data as T;\n\t\tconst result = await schema[\"~standard\"].validate(data);\n\t\tif (result.issues !== undefined) {\n\t\t\tconst msg = this.issuesToErrorMessage(label, data, result.issues);\n\t\t\tthrow new Exception(msg, Status.UNPROCESSABLE_ENTITY, data);\n\t\t}\n\t\treturn result.value;\n\t}\n\n\t/**\n\t * Validates a value without awaiting, for callers that cannot be async.\n\t *\n\t * Whether a schema validates synchronously is not visible in its type, so it\n\t * is detected at runtime: a validator that returns a promise is rejected\n\t * outright rather than having its result silently used as a value.\n\t *\n\t * @param label - Names the surface being validated, and appears in the error\n\t * message.\n\t * @param data - The value to validate.\n\t * @param schema - The schema. Omitting it returns the data as-is.\n\t * @returns The validated value.\n\t * @throws {@link Error} when the schema validates asynchronously — use\n\t * {@link SchemaParser.parse} instead.\n\t * @throws {@link Exception} with {@link Status.UNPROCESSABLE_ENTITY} when\n\t * validation fails.\n\t */\n\tparseSync<T = Record<string, unknown>>(label: string, data: unknown, schema?: Schema<T>): T {\n\t\tif (!schema) return data as T;\n\t\tconst result = schema[\"~standard\"].validate(data);\n\t\tconst isThenable = \"then\" in result && typeof result?.then === \"function\";\n\t\tif (result instanceof Promise || isThenable) {\n\t\t\tthrow new Error(\"parseSync called with async validator — use a sync schema library\");\n\t\t}\n\t\tif (result.issues !== undefined) {\n\t\t\tconst msg = this.issuesToErrorMessage(label, data, result.issues);\n\t\t\tthrow new Exception(msg, Status.UNPROCESSABLE_ENTITY, data);\n\t\t}\n\t\treturn result.value;\n\t}\n\n\t/**\n\t * Renders validation issues into the message sent to the client.\n\t *\n\t * Each issue is reported as `in <label> <path> (received <value>): <message>`,\n\t * so a client can see which field failed and what it actually sent — a\n\t * schema's own message alone rarely says which field it came from. Path\n\t * segments are joined with dots, and the offending value is looked up by\n\t * walking the original data along that path. Issues with no path are global to\n\t * the surface and keep their message unadorned.\n\t *\n\t * Override this to change the wording or to withhold the received values.\n\t *\n\t * @param label - Names the surface being validated.\n\t * @param data - The value that failed, walked to find each reported value.\n\t * @param issues - The failures the schema reported.\n\t * @returns One line per issue, newline-joined, or an empty string when there\n\t * are none.\n\t */\n\tissuesToErrorMessage(label: string, data: unknown, issues: ValidationIssues): string {\n\t\tif (issues.length === 0) return \"\";\n\t\treturn issues\n\t\t\t.map((issue) => {\n\t\t\t\t// Handle global issues without a path\n\t\t\t\tif (!issue.path || issue.path.length === 0) {\n\t\t\t\t\treturn issue.message;\n\t\t\t\t}\n\t\t\t\t// Extract the string representation of the path\n\t\t\t\tconst pathKeys = issue.path.map((segment) =>\n\t\t\t\t\ttypeof segment === \"object\" && segment !== null && \"key\" in segment\n\t\t\t\t\t\t? String(segment.key)\n\t\t\t\t\t\t: String(segment as string),\n\t\t\t\t);\n\t\t\t\tconst key = pathKeys.join(\".\");\n\t\t\t\t// Traverse the input data to find the specific value at this path\n\t\t\t\tconst value = pathKeys.reduce<unknown>((acc, segment) => {\n\t\t\t\t\tif (acc && typeof acc === \"object\") {\n\t\t\t\t\t\treturn (acc as Record<string, unknown>)[segment];\n\t\t\t\t\t}\n\t\t\t\t\treturn undefined;\n\t\t\t\t}, data);\n\t\t\t\t// Format: \"key (received value): message\"\n\t\t\t\tconst received = value !== undefined ? ` (received ${JSON.stringify(value)})` : \"\";\n\t\t\t\treturn `in ${label} ${key}${received}: ${issue.message}`;\n\t\t\t})\n\t\t\t.join(\"\\n\");\n\t}\n}\n\nexport {\n\ttype SchemaParserInterface,\n\tSchemaParser,\n\ttype Schema,\n\ttype InferSchemaIn,\n\ttype InferSchemaOut,\n\ttype ValidationIssues,\n\ttype InferModel,\n};\n",
21
+ "/**\n * Parsing a query string into a nested object.\n *\n * {@link SearchParamsParser} is the default\n * {@link ParsersRegistry.searchParamsParser}. It fills {@link Context.search},\n * and {@link BodyParser} also routes `application/x-www-form-urlencoded` bodies\n * through it, so a query string and a form post with matching field names\n * produce the same shape.\n *\n * @module SearchParamsParser\n */\n\nimport { ParserBase } from \"@/ParserBase\";\nimport { createSafeObject } from \"@/utils/object\";\n\n/**\n * Turns `URLSearchParams` into a nested object.\n *\n * Keys are read as paths through {@link ParserBase.parseKey}, so\n * `?filter[status]=open&tags[0]=a` builds the objects and arrays it describes.\n * Repeated keys collect into an array, which is how `?tag=a&tag=b` arrives\n * without any bracket notation.\n *\n * Values are coerced through {@link ParserBase.tryParseJSON}, so `?page=2` and\n * `?active=true` arrive as a number and a boolean rather than as strings.\n */\nclass SearchParamsParser extends ParserBase<URLSearchParams> {\n\t/**\n\t * Parses a query string into a nested object.\n\t *\n\t * @param searchParams - The parameters to parse.\n\t * @returns The nested object, built on a null prototype by\n\t * {@link createSafeObject} so a `__proto__` key in the query string cannot\n\t * reach `Object.prototype`.\n\t */\n\tparse(searchParams: URLSearchParams): Record<string, unknown> {\n\t\tconst result = createSafeObject();\n\n\t\tsearchParams.forEach((entry, key) => {\n\t\t\tconst parts = this.parseKey(key);\n\t\t\tconst value = this.tryParseJSON(entry);\n\t\t\tthis.setDeep(result, parts, value);\n\t\t});\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Writes a value at a path, creating the containers it passes through.\n\t *\n\t * Each missing level is created as an array when the next path part is a\n\t * number and as an object otherwise, so the shape follows the key rather than\n\t * being guessed after the fact. A scalar already occupying a level that must\n\t * be descended into is replaced — query strings are client-controlled and\n\t * arrive in arbitrary order, so `?a=1&a[b]=2` is resolved by letting the\n\t * structured key win rather than throwing.\n\t *\n\t * The final slot collects rather than overwrites: an empty slot takes the\n\t * value, a slot already holding an array appends, and a slot holding a single\n\t * value is promoted to an array. That is what makes repeated keys accumulate,\n\t * in arrival order.\n\t *\n\t * Deliberately kept separate from {@link FormDataParser}'s near-identical\n\t * method: the two sources differ in what they can carry, and sharing the code\n\t * would tie their behaviour together.\n\t *\n\t * @param result - The object being built up.\n\t * @param parts - The path from {@link ParserBase.parseKey}; numbers mean array\n\t * indices, strings mean object keys.\n\t * @param value - The value to write, already coerced.\n\t */\n\tprivate setDeep(result: Record<string, unknown>, parts: (string | number)[], value: unknown) {\n\t\t// same as formdata but good to keep separate\n\t\tlet current = result;\n\n\t\tfor (let i = 0; i < parts.length - 1; i++) {\n\t\t\tconst part = parts[i]!;\n\t\t\tconst next = parts[i + 1];\n\t\t\t// just for readability, current can be used directly as well\n\t\t\tconst container = this.newContainer(current);\n\t\t\t// each part needs an entry\n\t\t\tconst isIndexAssigned = typeof next === \"number\";\n\t\t\tif (container[part] === undefined) {\n\t\t\t\tcontainer[part] = isIndexAssigned ? [] : createSafeObject();\n\t\t\t} else if (typeof container[part] !== \"object\" || container[part] === null) {\n\t\t\t\t// scalar already sitting where we need to descend: replace it\n\t\t\t\tcontainer[part] = isIndexAssigned ? [] : createSafeObject();\n\t\t\t}\n\t\t\t(current as unknown) = container[part];\n\t\t}\n\n\t\tconst last = parts[parts.length - 1]!;\n\t\tconst container = this.newContainer(current);\n\t\tconst existing = container[last];\n\n\t\tif (existing === undefined) {\n\t\t\t// first write at this slot\n\t\t\tcontainer[last] = value;\n\t\t} else if (Array.isArray(existing)) {\n\t\t\t// slot already holds an array, append\n\t\t\tcontainer[last] = [...existing, value];\n\t\t} else {\n\t\t\t// slot holds a single value, promote to array\n\t\t\tcontainer[last] = [existing, value];\n\t\t}\n\t}\n}\n\nexport { SearchParamsParser };\n",
22
+ "/**\n * Parsing path parameters into typed values.\n *\n * {@link URLParamsParser} is the default\n * {@link ParsersRegistry.urlParamsParser}. It fills {@link Context.params} from\n * the raw values Bun's router matched, decoding and coercing each one.\n *\n * @module URLParamsParser\n */\n\nimport { ParserBase } from \"@/ParserBase\";\nimport { createSafeObject } from \"@/utils/object\";\n\n/**\n * Turns matched path parameters into a typed object.\n *\n * The flattest of the parsers: path parameters are named by the route pattern,\n * so there is no nesting to recover — no bracket notation, no repeated keys.\n * Each value is percent-decoded and then coerced through\n * {@link ParserBase.tryParseJSON}, so `/users/42` yields a number rather than a\n * string.\n */\nclass URLParamsParser extends ParserBase<Record<string, string>> {\n\t/**\n\t * Parses matched path parameters.\n\t *\n\t * @param input - The raw parameters from the router, including the `*` key\n\t * that {@link App} lifts out of a wildcard endpoint.\n\t * @returns The decoded, coerced parameters, on a null prototype from\n\t * {@link createSafeObject}.\n\t */\n\tparse(input: Record<string, string>): Record<string, unknown> {\n\t\tconst data: Record<string, unknown> = createSafeObject();\n\t\tfor (const [key, value] of Object.entries(input)) {\n\t\t\tdata[key] = this.tryParseJSON(decodeURIComponent(value));\n\t\t}\n\t\treturn data;\n\t}\n}\n\nexport { URLParamsParser };\n",
23
+ "/**\n * The single place every parsing decision in the framework is resolved from —\n * and the single place to replace them.\n *\n * Nothing in corpus constructs a parser inline. {@link App} resolves the\n * registry while compiling routes, and {@link BodyParser} reaches back into it\n * for {@link FormDataParser} and {@link SearchParamsParser} rather than owning\n * instances of its own. Replacing an entry therefore changes how the whole\n * framework parses that surface, across every {@link RouteBase} on every\n * {@link App}, without touching a single route definition.\n *\n * Every slot is plug and play. Each is typed as an interface\n * ({@link ParserBaseInterface}, {@link BodyParserInterface},\n * {@link SchemaParserInterface}), never as a concrete class, so a replacement\n * only has to satisfy the contract — subclassing the default is optional.\n *\n * ```ts\n * // Replace one parser; the rest keep their defaults.\n * setParsersRegistry({ bodyParser: new MyBodyParser() });\n *\n * await app.listen();\n * ```\n *\n * Overrides must be in place before {@link App.listen}: routes read the registry\n * as they are compiled, so anything swapped in afterwards is never reached.\n *\n * The registry lives on {@link Globals}, so it is created once per process and\n * shared by every {@link App}.\n *\n * @module ParsersRegistry\n */\n\nimport { Globals } from \"@/Globals\";\nimport type { ParserBaseInterface } from \"@/ParserBase\";\nimport { type BodyParserInterface, BodyParser } from \"@/ParserBase/BodyParser\";\nimport { FormDataParser } from \"@/ParserBase/FormDataParser\";\nimport { type SchemaParserInterface, SchemaParser } from \"@/ParserBase/SchemaParser\";\nimport { SearchParamsParser } from \"@/ParserBase/SearchParamsParser\";\nimport { URLParamsParser } from \"@/ParserBase/URLParamsParser\";\n\n/**\n * The set of parsers the framework resolves at request time. Every field is an\n * interface, so each one can be replaced independently through\n * {@link setParsersRegistry}.\n */\ninterface ParsersRegistry {\n\t/**\n\t * Turns the raw path parameters matched by a {@link RouteBase} into\n\t * {@link Context.params}. Defaults to {@link URLParamsParser}.\n\t */\n\turlParamsParser: ParserBaseInterface<Record<string, string>>;\n\t/**\n\t * Turns a query string into {@link Context.search}. Defaults to\n\t * {@link SearchParamsParser}, which {@link BodyParser} also reuses for\n\t * `application/x-www-form-urlencoded` bodies.\n\t */\n\tsearchParamsParser: ParserBaseInterface<URLSearchParams>;\n\t/**\n\t * Turns `multipart/form-data` into an object. Defaults to\n\t * {@link FormDataParser} and is reached through {@link BodyParser} rather than\n\t * called directly.\n\t */\n\tformDataParser: ParserBaseInterface<FormData>;\n\t/**\n\t * Reads and decodes request and response bodies by content type. Defaults to\n\t * {@link BodyParser}.\n\t */\n\tbodyParser: BodyParserInterface;\n\t/**\n\t * Validates already-parsed params, search and body against the schemas\n\t * declared in a route's {@link Config}. Defaults to {@link SchemaParser},\n\t * which supports Zod and ArkType.\n\t */\n\tschemaParser: SchemaParserInterface;\n}\n\n/**\n * Reads the registry from {@link Globals}, constructing the defaults on first\n * access.\n *\n * Called by {@link App} during route compilation and by {@link BodyParser} when\n * it needs a sibling parser.\n *\n * @returns The shared {@link ParsersRegistry}.\n */\nfunction getOrInitParsersRegistry(): ParsersRegistry {\n\ttry {\n\t\treturn Globals.get(\"parsers\");\n\t} catch {\n\t\treturn Globals.create(\"parsers\", () => {\n\t\t\tconst urlParamsParser = new URLParamsParser();\n\t\t\tconst searchParamsParser = new SearchParamsParser();\n\t\t\tconst formDataParser = new FormDataParser();\n\t\t\tconst bodyParser = new BodyParser();\n\t\t\tconst schemaParser = new SchemaParser();\n\t\t\treturn { urlParamsParser, searchParamsParser, formDataParser, bodyParser, schemaParser };\n\t\t});\n\t}\n}\n\n/**\n * Overrides one or more parsers. Only the provided keys are replaced;\n * unspecified parsers keep their current implementation.\n *\n * Must be called before {@link App.listen} — routes resolve their parsers as\n * they are compiled, so a later override has no effect on an already-listening\n * app.\n *\n * @param overrides - The {@link ParsersRegistry} entries to swap in. Each value\n * need only satisfy its interface; it does not have to extend the default class.\n */\nfunction setParsersRegistry(overrides: Partial<ParsersRegistry>): void {\n\tObject.assign(getOrInitParsersRegistry(), overrides);\n}\n\nexport { type ParsersRegistry, getOrInitParsersRegistry, setParsersRegistry };\n",
24
+ "/**\n * HTTP method constants, and the `Request` extensions the framework relies on.\n *\n * {@link Method} gives the verbs as documented constants. {@link patchGlobalRequest}\n * then adds two properties to the global `Request`: `params`, which {@link App}\n * fills with the matched path parameters before parsing, and `cookies`, which\n * parses the `Cookie` header into a {@link Cookies} map on first access.\n *\n * Like {@link patchGlobalHeaders}, this must be installed once at startup,\n * before any request is served.\n *\n * @module Request\n */\n\nimport { Cookies, parseCookieHeader } from \"@/Cookies\";\nimport { readHeader, HeaderKey } from \"@/Headers\";\nimport type { OrString } from \"@/utils/is\";\nimport { lazy, type LazyMut } from \"@/utils/lazy\";\nimport { createSafeObject, type ValueOf } from \"@/utils/object\";\n\n/** Commonly used HTTP verbs. See [MDN Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods) for the full spec. */\nconst Method = {\n\t/** Retrieve a resource from the server. */\n\tGET: \"GET\",\n\t/** Submit data to create a new resource. */\n\tPOST: \"POST\",\n\t/** Replace an entire resource with new data. */\n\tPUT: \"PUT\",\n\t/** Apply partial modifications to a resource. */\n\tPATCH: \"PATCH\",\n\t/** Remove a resource from the server. */\n\tDELETE: \"DELETE\",\n\t/** Get response headers without body. */\n\tHEAD: \"HEAD\",\n\t/** Discover communication options. */\n\tOPTIONS: \"OPTIONS\",\n\t/** Establish tunnel to server. */\n\tCONNECT: \"CONNECT\",\n\t/** Echo back received request. */\n\tTRACE: \"TRACE\",\n} as const;\n\n/**\n * An HTTP method. The {@link Method} constants are suggested, but\n * {@link OrString} keeps any custom verb assignable.\n */\ntype Method = OrString<ValueOf<typeof Method>>;\n\n/**\n * Builds the lazy {@link Cookies} accessor a request exposes.\n *\n * Parsing is deferred until something reads `request.cookies`, so a request that\n * never touches cookies never pays for the header. Cookies passed in the init\n * are used as the starting map, with anything from the `Cookie` header layered\n * on top.\n *\n * @param init - The request init, read for an existing {@link Cookies} map and\n * for the `Cookie` header.\n * @returns A mutable lazy holding the cookie map, so assigning\n * `request.cookies` can replace it wholesale.\n */\nfunction requestCookiesFactory(init?: RequestInit): LazyMut<Cookies> {\n\treturn lazy.mut(() => {\n\t\tconst cookies = init?.cookies ?? new Cookies();\n\t\tconst cookieHeader = readHeader(init?.headers, HeaderKey.Cookie);\n\t\tif (!cookieHeader) return cookies;\n\t\tfor (const cookie of parseCookieHeader(cookieHeader)) {\n\t\t\tcookies.set(cookie);\n\t\t}\n\t\treturn cookies;\n\t});\n}\n\n/**\n * Installs `params` and `cookies` on the global `Request`. Call it once at\n * startup, before any request is served.\n *\n * The patch is applied twice over, for the same reason as\n * {@link patchGlobalHeaders}. The prototype is extended so requests *the runtime*\n * creates — every incoming request Bun hands to the server — carry the\n * properties, since those never pass through a constructor the framework\n * controls. The global class is then replaced too, so manually constructed\n * requests can seed their cookies from {@link RequestInit.cookies}, which the\n * prototype path has no access to.\n *\n * The subclass overrides `Symbol.hasInstance` to defer to the native class, so\n * `instanceof Request` stays true for requests created outside the patch.\n */\nfunction patchGlobalRequest() {\n\tconst NativeRequest = globalThis.Request;\n\n\t/** The request as seen internally, with its lazy cookie backing store. */\n\tinterface WithPrivates extends Request {\n\t\t/** Backing store for `cookies`, created on first access. */\n\t\t_cookies: LazyMut<Cookies>;\n\t}\n\n\t// For Request objects constructed with Bun\n\tObject.defineProperties(Request.prototype, {\n\t\tparams: {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: false,\n\t\t\twritable: true,\n\t\t\tvalue: createSafeObject(),\n\t\t},\n\t\t_cookies: {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: false,\n\t\t\twritable: true,\n\t\t\tvalue: undefined,\n\t\t},\n\t\tcookies: {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: false,\n\t\t\tget(this: WithPrivates) {\n\t\t\t\tif (!this._cookies) {\n\t\t\t\t\tthis._cookies = requestCookiesFactory({ headers: this.headers });\n\t\t\t\t}\n\t\t\t\treturn this._cookies();\n\t\t\t},\n\t\t\tset(this: WithPrivates, value: Cookies) {\n\t\t\t\tif (!this._cookies) {\n\t\t\t\t\tthis._cookies = requestCookiesFactory({ headers: this.headers });\n\t\t\t\t}\n\t\t\t\tthis._cookies.set(value);\n\t\t\t},\n\t\t},\n\t});\n\n\t// For Request objects constructed manually\n\tglobalThis.Request = class extends NativeRequest {\n\t\t/**\n\t\t * Creates a request, seeding its cookies from the init.\n\t\t *\n\t\t * @param input - The URL or request to build from.\n\t\t * @param init - The request init. {@link RequestInit.cookies} and the\n\t\t * `Cookie` header both feed the cookie map.\n\t\t */\n\t\tconstructor(input: RequestInfo | URL, init?: RequestInit) {\n\t\t\tsuper(input, init);\n\t\t\tthis._cookies = requestCookiesFactory(init);\n\t\t\tthis.params = createSafeObject();\n\t\t}\n\n\t\t/**\n\t\t * Defers `instanceof` to the native class, so requests created by the\n\t\t * runtime still satisfy `instanceof Request`.\n\t\t *\n\t\t * @param value - The value being tested.\n\t\t * @returns `true` when it is a native `Request`.\n\t\t */\n\t\tstatic override [Symbol.hasInstance](value: unknown): boolean {\n\t\t\treturn value instanceof NativeRequest;\n\t\t}\n\n\t\t/**\n\t\t * The raw path parameters matched by the route, populated by Bun's router.\n\t\t * The declaration here only types them and supplies an empty default for\n\t\t * manually constructed requests. Read {@link Context.params} instead for the\n\t\t * parsed and validated values; {@link App} additionally lifts a wildcard\n\t\t * segment into the `*` key, which Bun does not provide.\n\t\t */\n\t\toverride params: { [x: string]: string };\n\n\t\t/** Backing store for {@link Request.cookies}, parsed on first access. */\n\t\tprivate _cookies: LazyMut<Cookies>;\n\n\t\t/**\n\t\t * The request's cookies, parsed from the `Cookie` header on first access.\n\t\t *\n\t\t * @returns The {@link Cookies} map.\n\t\t */\n\t\toverride get cookies(): Cookies {\n\t\t\treturn this._cookies();\n\t\t}\n\n\t\toverride set cookies(value: Cookies) {\n\t\t\tthis._cookies.set(value);\n\t\t}\n\t};\n}\n\ndeclare global {\n\tinterface RequestInit {\n\t\t/**\n\t\t * Cookies to seed the request with. Anything in the `Cookie` header is\n\t\t * layered on top of these.\n\t\t */\n\t\tcookies?: Cookies;\n\t}\n\t/** The `Request` surface after {@link patchGlobalRequest} has run. */\n\tinterface Request {\n\t\t/**\n\t\t * The raw path parameters matched by the route, populated by Bun's router.\n\t\t * {@link Context.params} holds the parsed, validated version.\n\t\t */\n\t\tparams: { [x: string]: string };\n\t\t/** The request's cookies, parsed from the `Cookie` header on first access. */\n\t\tcookies: Cookies;\n\t}\n}\n\nexport { patchGlobalRequest, Method };\n",
25
+ "/**\n * One-time framework setup.\n *\n * {@link initialize} creates the global registries and installs the `Request`\n * and `Headers` extensions the rest of corpus assumes are present. It runs when\n * the package is imported, so nothing normally needs to call it.\n *\n * @module initialize\n */\n\nimport { Globals } from \"@/Globals\";\nimport { getOrInitAppsRegistry } from \"@/Globals/AppsRegistry\";\nimport { getOrInitParsersRegistry } from \"@/Globals/ParsersRegistry\";\nimport { patchGlobalHeaders } from \"@/Headers\";\nimport { patchGlobalRequest } from \"@/Request\";\n\n/**\n * Prepares the process for corpus: creates the {@link AppsRegistry} and\n * {@link ParsersRegistry}, then applies {@link patchGlobalRequest} and\n * {@link patchGlobalHeaders}.\n *\n * Guarded through {@link Globals}, so it runs at most once per process however\n * many times it is called — which matters because the patches wrap the previous\n * implementation, and applying them twice would layer one wrapper on another.\n * The flag is set before the work rather than after, so a re-entrant call during\n * setup is caught too.\n */\nexport function initialize() {\n\tif (Globals.has(\"initialized\")) return;\n\tGlobals.set(\"initialized\", true);\n\tgetOrInitAppsRegistry();\n\tgetOrInitParsersRegistry();\n\tpatchGlobalRequest();\n\tpatchGlobalHeaders();\n}\n",
26
+ "/**\n * The per-request object every handler receives.\n *\n * A {@link Context} carries the incoming request alongside the parsed views of\n * it — {@link Context.body}, {@link Context.params}, {@link Context.search} —\n * and the {@link Res} being built up in reply. It is created once per request by\n * the {@link ContextFactory} on {@link App.contextFactory} and threaded through\n * the whole {@link Middleware} chain, so it is also the place to hang state that\n * one handler produces and another consumes.\n *\n * The parsed fields start out empty and are filled by {@link App} during the\n * request lifecycle, but only for the surfaces a route's handlers actually read\n * — {@link getContextAccess} decides that, so an untouched body is never parsed.\n *\n * @module Context\n */\n\nimport { Res } from \"@/Res\";\nimport type { Server } from \"@/Server\";\nimport type { Maybe, MaybePromise } from \"@/utils/is\";\nimport { lazy, type Lazy, type LazyMut } from \"@/utils/lazy\";\nimport { createSafeObject } from \"@/utils/object\";\n\n/**\n * Declaration target for {@link Context.data}, the request-scoped state shared\n * between {@link Middleware} and route handlers.\n *\n * Empty by design. Augment it from your own code so everything a middleware sets\n * is typed where a handler reads it:\n *\n * ```ts\n * declare module \"@ozanarslan/corpus\" {\n * interface ContextDataInterface {\n * user: User;\n * }\n * }\n * ```\n */\ninterface ContextDataInterface {}\n\n/**\n * Builds the {@link Context} for an incoming request. Assigned to\n * {@link App.contextFactory}; replace it to have an app construct a\n * {@link Context} subclass.\n *\n * @param request - The incoming request.\n * @param server - The {@link Server} that accepted it, absent when the request\n * was dispatched without one.\n * @returns The context the request will be handled with.\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n * @typeParam R - Response body type carried by {@link Context.res}.\n */\ntype ContextFactory<B = unknown, S = unknown, P = unknown, R = unknown> = (\n\trequest: Request,\n\tserver: Maybe<Server>,\n) => Context<B, S, P, R>;\n\n/**\n * A function that handles a request given its {@link Context}. This is the shape\n * of a {@link RouteBase} handler and of the app-level hooks\n * {@link App.handleNotFound} and {@link App.handlePreflight}.\n *\n * A {@link Middleware} handler is a {@link MiddlewareHandler} instead, since it\n * additionally receives `next`.\n *\n * @param context - The {@link Context} for the request.\n * @returns The response body, a {@link Res}, or a promise of either. Returning\n * `undefined` leaves {@link Context.res} as the handler mutated it.\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n * @typeParam R - Response body type.\n */\ntype ContextHandler<B = unknown, S = unknown, P = unknown, R = unknown> = (\n\tcontext: Context<B, S, P, R>,\n) => MaybePromise<R>;\n\n/**\n * Everything a handler needs to know about one request, and everything it uses\n * to answer it.\n *\n * The type parameters are supplied by the route the context belongs to, so a\n * handler sees its own validated shapes rather than `unknown`.\n *\n * {@link Context.res} and {@link Context.url} are lazy: neither the response\n * object nor the parsed URL is constructed until something reads it, so a\n * handler that returns a body without touching either pays for neither.\n *\n * The parsed containers are created with {@link createSafeObject}, so a payload\n * carrying a `__proto__` key cannot reach `Object.prototype` through them.\n *\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n * @typeParam R - Response body type carried by {@link Context.res}.\n */\nclass Context<B = unknown, S = unknown, P = unknown, R = unknown> {\n\t/**\n\t * Creates a context with empty parsed containers; {@link App} fills them\n\t * during the request lifecycle.\n\t *\n\t * @param req - The incoming request.\n\t * @param server - The {@link Server} that accepted it. Absent when the request\n\t * was dispatched without one, which is why {@link Context.server} is optional\n\t * at every use site — including the WebSocket upgrade.\n\t */\n\tconstructor(req: Request, server: Maybe<Server>) {\n\t\tthis.req = req;\n\t\tthis.server = server;\n\t\tthis._res = lazy.mut(() => new Res<R>());\n\t\tthis._url = lazy(() => new URL(this.req.url));\n\t\tthis.body = createSafeObject<B>();\n\t\tthis.params = createSafeObject<P>();\n\t\tthis.search = createSafeObject<S>();\n\t\tthis.data = createSafeObject<ContextDataInterface>();\n\t}\n\n\t/**\n\t * The parsed request body, decoded by {@link BodyParser} and validated against\n\t * the route's {@link RouteConfig} body schema.\n\t *\n\t * Empty for {@link Method.GET} and {@link Method.HEAD} requests, and for any\n\t * route whose handlers never read it.\n\t */\n\tbody: B;\n\n\t/**\n\t * The path parameters matched by the route, parsed by\n\t * {@link ParsersRegistry.urlParamsParser} and validated against the route's\n\t * params schema. A wildcard segment is available under the `*` key.\n\t */\n\tparams: P;\n\n\t/**\n\t * The query string, parsed by {@link ParsersRegistry.searchParamsParser} and\n\t * validated against the route's search schema. Empty when the request carried\n\t * no query string.\n\t */\n\tsearch: S;\n\n\t/**\n\t * Free-form request-scoped state, shared across the whole\n\t * {@link Middleware} chain and the route handler. This is how a middleware\n\t * hands something — an authenticated user, a request id — to what runs after\n\t * it. Type it by augmenting {@link ContextDataInterface}.\n\t */\n\tdata: ContextDataInterface;\n\n\t/**\n\t * The {@link Server} that accepted the request. Needed to upgrade a connection\n\t * to a {@link WebSocketRoute}; absent when the request was dispatched without\n\t * a server.\n\t */\n\treadonly server: Maybe<Server>;\n\n\t/**\n\t * The untouched incoming request. Read it for headers and for the raw body;\n\t * the parsed views live on {@link Context.body} and its siblings.\n\t */\n\treadonly req: Request;\n\n\t/** Backing store for {@link Context.res}, constructed on first access. */\n\tprivate _res: LazyMut<Res<R>>;\n\n\t/**\n\t * The response under construction. Mutate it to set status, headers or body\n\t * before returning, or assign a whole new {@link Res} to replace it.\n\t *\n\t * A {@link Middleware} that replaces this after `next()` resolves wins over\n\t * whatever the downstream handler returned — see {@link composeHandlerChain}.\n\t *\n\t * @returns The response object, created on first access.\n\t */\n\tget res(): Res<R> {\n\t\treturn this._res();\n\t}\n\n\tset res(value: Res<R>) {\n\t\tthis._res.set(value);\n\t}\n\n\t/** Backing store for {@link Context.url}, parsed on first access. */\n\tprivate _url: Lazy<URL>;\n\n\t/**\n\t * The request URL, parsed once and reused.\n\t *\n\t * @returns The parsed `URL`. Prefer {@link Context.params} and\n\t * {@link Context.search} for path and query values; reach for this when you\n\t * need the pathname or origin itself, as {@link BundleRoute} does.\n\t */\n\tget url(): URL {\n\t\treturn this._url();\n\t}\n}\n\nexport { Context, type ContextDataInterface, type ContextFactory, type ContextHandler };\n",
27
+ "import type { Optional } from \"./is\";\n\nexport type WithLeadingSlash<E extends string> = E extends `/${string}` ? E : `/${E}`;\n\nexport type WithPrefix<Px extends Optional<string>, E extends string> = Px extends string\n\t? `${WithLeadingSlash<Px>}${WithLeadingSlash<E>}`\n\t: WithLeadingSlash<E>;\n\nexport function joinPathSegments<P extends string>(\n\t...segments: Array<string | undefined | number>\n): P {\n\tconst joined = segments\n\t\t.map((segment) => (typeof segment === \"number\" ? `${segment}` : segment))\n\t\t.filter((segment): segment is string => !!segment)\n\t\t.map((segment) => segment.replace(/^\\/+|\\/+$/g, \"\"))\n\t\t.filter((segment) => segment.length > 0)\n\t\t.join(\"/\");\n\n\treturn `/${joined}` as P;\n}\n\nexport function withLeadingSlash<E extends string>(rawEndpoint: E): WithLeadingSlash<E> {\n\treturn (rawEndpoint.startsWith(\"/\") ? rawEndpoint : `/${rawEndpoint}`) as WithLeadingSlash<E>;\n}\n",
28
+ "/**\n * The base every route kind extends, and the address and config types they\n * share.\n *\n * {@link RouteBase} defines what {@link App} needs from a route to compile it —\n * a {@link RouteVariant}, a {@link Method}, an endpoint, an optional\n * {@link RouteConfig}, and a handler — plus registration and the direct-call\n * helpers. The concrete kinds are {@link Route}, {@link StaticRoute},\n * {@link FileRoute}, {@link BundleRoute} and {@link WebSocketRoute}; extend this\n * class only to add a kind of your own.\n *\n * {@link RouteBase.handle} is worth knowing about even if you never subclass:\n * it invokes a route's handler directly, without a server, which is how routes\n * are unit tested and how one route can call another in process.\n *\n * @module RouteBase\n */\n\nimport { Context, type ContextHandler } from \"@/Context\";\nimport { getNearestApp } from \"@/Globals/AppsRegistry\";\nimport { HeaderKey } from \"@/Headers\";\nimport type { Schema } from \"@/ParserBase/SchemaParser\";\nimport { Method } from \"@/Request\";\nimport { assert } from \"@/utils/assert\";\nimport { isObject, isOneOf, type MaybePromise } from \"@/utils/is\";\nimport type { ValueOf } from \"@/utils/object\";\nimport { joinPathSegments } from \"@/utils/path\";\n\n/**\n * What kind of route this is. {@link App.composeRoutes} branches on it — a\n * `websocket` route upgrades instead of responding, and a `bundle` route is\n * excluded from {@link RateLimiter} by default.\n */\nconst RouteVariant = {\n\t/** A file whose contents feed a handler. See {@link StaticRoute}. */\n\tstatic: \"static\",\n\t/** A single file served as-is. See {@link FileRoute}. */\n\tfile: \"file\",\n\t/** A handler function. See {@link Route}. */\n\tdynamic: \"dynamic\",\n\t/** A connection upgrade. See {@link WebSocketRoute}. */\n\twebsocket: \"websocket\",\n\t/** A directory of built files. See {@link BundleRoute}. */\n\tbundle: \"bundle\",\n} as const;\n\n/** One of the {@link RouteVariant} values. */\ntype RouteVariant = ValueOf<typeof RouteVariant>;\n\n/**\n * The request to simulate when calling a route directly through\n * {@link RouteBase.handle}. Every field is optional — supply only what the\n * handler reads.\n *\n * @typeParam B - Body type.\n * @typeParam S - Search type.\n * @typeParam P - Params type.\n */\ntype RouteHandleInput<B, S, P> = {\n\t/** The request body, already in parsed form. */\n\tbody?: B;\n\t/** The query values, already in parsed form. */\n\tsearch?: S;\n\t/** The path parameters, which are also substituted into the endpoint. */\n\tparams?: P;\n\t/** Headers for the synthesized request. */\n\theaders?: HeadersInit;\n};\n\n/**\n * A route's schemas and per-route limits.\n *\n * The schemas do double duty: {@link SchemaParser} validates against them at\n * request time, and their inferred output types become the {@link Context}\n * types the handler sees. {@link getContextAccess} also reads this to decide\n * what to parse — a surface with a schema is always parsed, since it must be\n * validated.\n *\n * @typeParam B - Body type.\n * @typeParam S - Search type.\n * @typeParam P - Params type.\n * @typeParam R - Response type.\n */\ntype RouteConfig<B = unknown, S = unknown, P = unknown, R = unknown> = {\n\t/**\n\t * Body size ceiling in bytes for this route, enforced by\n\t * {@link enforceBodyLimit}. Tightens {@link App.maxRequestBodySize} for a\n\t * single endpoint — an upload route and a JSON route rarely want the same\n\t * limit.\n\t */\n\tmaxRequestBodySize?: number;\n\t/** Schema for the response body. Types the handler's return value. */\n\tresponse?: Schema<R>;\n\t/** Schema for the request body. Validated before the handler runs. */\n\tbody?: Schema<B>;\n\t/** Schema for the query string. Validated before the handler runs. */\n\tsearch?: Schema<S>;\n\t/** Schema for the path parameters. Validated before the handler runs. */\n\tparams?: Schema<P>;\n};\n\n/**\n * Where a route lives, in any of the forms the constructors accept.\n *\n * A bare path means {@link Method.GET}. A `\"METHOD /path\"` string is the usual\n * form, and the verb may be written in either case. The object form is what\n * {@link resolveRouteAddress} normalizes everything into, and what\n * {@link Controller} passes through once it has joined its prefix on.\n *\n * @typeParam E - The literal endpoint type.\n */\ntype RouteAddress<E extends string = string> =\n\t| E\n\t| `${Method} ${E}`\n\t| `${Lowercase<Method>} ${E}`\n\t| { method: Method; endpoint: E };\n\n/**\n * Normalizes any {@link RouteAddress} form into a method and an endpoint.\n *\n * The space in `\"POST /users\"` is what separates the verb from the path, so a\n * string containing a space must start with a valid verb — otherwise it is a\n * malformed address rather than a path that happens to contain a space, and it\n * is rejected loudly at registration time instead of quietly never matching.\n *\n * @param address - The address to resolve, in any accepted form.\n * @returns The endpoint and its {@link Method}, with the verb uppercased.\n * @throws {@link Error} when a string contains a space but does not begin with\n * an HTTP verb followed by a path.\n */\nfunction resolveRouteAddress<E extends string>(\n\taddress: RouteAddress<E>,\n): { endpoint: E; method: Method } {\n\tif (typeof address !== \"string\") return address;\n\tif (!address.includes(\" \")) return { method: Method.GET, endpoint: address as E };\n\n\tconst [method, endpoint] = address.split(\" \");\n\tassert(\n\t\tisOneOf(method?.toUpperCase(), Object.values(Method)),\n\t\t`Route address cannot include whitespaces unless it starts with an HTTP verb. Received: ${address}`,\n\t);\n\tassert(\n\t\ttypeof endpoint === \"string\" && endpoint.trim() !== \"\",\n\t\t`Route address cannot include whitespaces unless it starts with an HTTP verb and ends with a path. Received: ${address}`,\n\t);\n\treturn { method: method.toUpperCase() as Method, endpoint: endpoint as E };\n}\n\n/**\n * The contract every route kind implements.\n *\n * Subclasses declare what they are and how they answer; this class supplies\n * {@link RouteBase.id}, registration, and the direct-invocation helpers.\n *\n * The abstract members exist because {@link App} reads them while compiling —\n * {@link RouteBase.variant} decides the pipeline shape,\n * {@link RouteBase.config} decides what gets parsed and validated, and the rest\n * decide where the route sits in the route map.\n *\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n * @typeParam R - Response body type.\n * @typeParam E - The literal endpoint type.\n */\nabstract class RouteBase<B = any, S = any, P = any, R = any, E extends string = string> {\n\t/** Which kind of route this is. */\n\tabstract readonly variant: RouteVariant;\n\n\t/** The HTTP method this route answers. */\n\tabstract readonly method: Method;\n\n\t/** The path this route answers, before {@link App.prefix} is applied. */\n\tabstract readonly endpoint: E;\n\n\t/** Schemas and limits for this route, if any. */\n\tabstract readonly config?: RouteConfig<B, S, P, R>;\n\n\t// Has to be a method to circumvent any/never typescript errors\n\t/**\n\t * Answers the request. Declared as a method rather than a property so\n\t * subclasses can narrow its parameter types — a property declaration would\n\t * make the variance error out on routes that take `never` for their inputs.\n\t *\n\t * @param args - The {@link ContextHandler} arguments: the {@link Context} for\n\t * the request.\n\t * @returns The response body, a {@link Res}, or nothing.\n\t */\n\tabstract handler(\n\t\t...args: Parameters<ContextHandler<B, S, P, R>>\n\t): ReturnType<ContextHandler<B, S, P, R>>;\n\n\t/**\n\t * The route's identity, as `\"METHOD /endpoint\"`.\n\t *\n\t * This is what {@link Middleware.useOn} targets and what\n\t * {@link Controller.routeIds} collects, so two routes sharing a method and\n\t * endpoint share middlewares as well.\n\t *\n\t * @returns The id.\n\t */\n\tget id(): string {\n\t\treturn `${this.method.toUpperCase()} ${this.endpoint}`;\n\t}\n\n\t/**\n\t * Appends this route to the nearest {@link App}. Called by every concrete\n\t * route's constructor; a subclass that declares its members as class fields\n\t * must call it itself, after those fields are initialized.\n\t */\n\tregister() {\n\t\tgetNearestApp().routes.push(this);\n\t}\n\n\t/**\n\t * Invokes the route's handler directly, with no server and no HTTP.\n\t *\n\t * The inputs are taken as already-parsed values, so nothing is decoded or\n\t * validated — and no {@link Middleware} runs, since middlewares are folded in\n\t * by {@link App} at compile time, not by the route. What this exercises is the\n\t * handler itself, which is what makes it useful for unit tests and for calling\n\t * one route's logic from another.\n\t *\n\t * @param data - The {@link RouteHandleInput} to build the {@link Context}\n\t * from.\n\t * @returns Whatever the handler returns.\n\t */\n\thandle(data: RouteHandleInput<B, S, P>): MaybePromise<R> {\n\t\tconst context = new Context<B, S, P, R>(this.request(data), null);\n\n\t\tif (isObject(data.body)) context.body = data.body;\n\t\tif (isObject(data.params)) context.params = data.params;\n\t\tif (isObject(data.search)) context.search = data.search;\n\n\t\treturn this.handler(context);\n\t}\n\n\t/**\n\t * Builds the request a {@link RouteHandleInput} describes, so the\n\t * {@link Context} that {@link RouteBase.handle} creates has a real request\n\t * behind it — a handler reading {@link Context.url} or a header gets what it\n\t * would have got over the wire.\n\t *\n\t * Params are substituted into the endpoint, so `/users/:id` becomes a concrete\n\t * URL. A `FormData` body is passed through untouched, since the runtime sets\n\t * its own multipart content type with the boundary; anything else is\n\t * JSON-encoded.\n\t *\n\t * @param data - The {@link RouteHandleInput} to build from.\n\t * @returns The synthesized request, addressed against {@link App.baseUrl}.\n\t */\n\trequest(data: RouteHandleInput<B, S, P>): Request {\n\t\tconst app = getNearestApp();\n\t\tlet endpoint = joinPathSegments(app.prefix, this.endpoint);\n\n\t\tif (isObject(data.params)) {\n\t\t\tfor (const [key, value] of Object.entries(data.params)) {\n\t\t\t\tendpoint = endpoint.replace(`:${String(key)}`, String(value));\n\t\t\t}\n\t\t}\n\n\t\tconst url = new URL(endpoint, app.baseUrl);\n\n\t\tif (isObject(data.search)) {\n\t\t\tfor (const [key, value] of Object.entries(data.search)) {\n\t\t\t\turl.searchParams.set(String(key), String(value));\n\t\t\t}\n\t\t}\n\n\t\tconst headers = new Headers(data.headers);\n\n\t\tlet body: BodyInit | undefined = undefined;\n\n\t\tif (data.body instanceof FormData) {\n\t\t\tbody = data.body;\n\t\t} else if (isObject(data.body)) {\n\t\t\tbody = JSON.stringify(data.body);\n\t\t\theaders.set(HeaderKey.ContentType, \"application/json\");\n\t\t}\n\n\t\treturn new Request(url, { method: this.method, body, headers });\n\t}\n}\n\nexport { RouteBase, RouteVariant, type RouteConfig, type RouteAddress, resolveRouteAddress };\n",
29
+ "/**\n * Typed access to environment variables and the current runtime environment.\n *\n * {@link Config} is a static façade over `process.env` — nothing is cached, so a\n * read always reflects the current value. Beyond convenience, it is what the\n * framework itself consults for environment-dependent behaviour: {@link App}\n * checks {@link Config.nodeEnv} before exiting the process on\n * {@link App.close}.\n *\n * Declare your variables on {@link Env} in your own code to get autocompletion\n * and a checked key for every lookup.\n *\n * ```ts\n * const port = Config.get(\"PORT\", { parser: Number, fallback: 3000 });\n * const secret = Config.require(\"JWT_SECRET\");\n * ```\n *\n * @module Config\n */\n\nimport { isPresent, isAbsent, type OrString } from \"@/utils/is\";\n\n/**\n * Declaration target for an application's environment variables.\n *\n * Empty by design. Augment it from your own code and every key you add becomes a\n * suggested {@link EnvKey}:\n *\n * ```ts\n * declare module \"corpus\" {\n * interface Env {\n * DATABASE_URL: string;\n * JWT_SECRET: string;\n * }\n * }\n * ```\n */\ninterface Env {}\n\n/**\n * The value of `NODE_ENV`. The three known environments are suggested, but any\n * string is accepted — {@link OrString} keeps the literals as hints rather than\n * as a closed set.\n */\ntype NodeEnv = OrString<\"development\" | \"production\" | \"test\">;\n\n/**\n * A variable name accepted by the {@link Config} lookups. Keys declared on\n * {@link Env} are suggested; any other string still resolves.\n */\ntype EnvKey = OrString<keyof Env>;\n\n/**\n * Static accessor for environment variables.\n *\n * Every read goes straight to `process.env`, so a variable set after startup —\n * by a test, or by {@link Config.set} — is visible immediately.\n *\n * The three lookups differ in how they treat a missing value:\n * {@link Config.get} returns `undefined` or a fallback, {@link Config.require}\n * throws, and {@link Config.has} only reports presence.\n */\nclass Config {\n\t/**\n\t * The live `process.env` object.\n\t *\n\t * @returns The environment, unwrapped. Reading it directly bypasses the\n\t * parsing and fallback handling of {@link Config.get}.\n\t */\n\tstatic get env(): NodeJS.ProcessEnv {\n\t\treturn process.env;\n\t}\n\n\t/**\n\t * The current environment name.\n\t *\n\t * @returns The value of `NODE_ENV`, or `\"development\"` when it is unset — an\n\t * unconfigured process is treated as a development one.\n\t */\n\tstatic get nodeEnv(): NodeEnv {\n\t\treturn this.env.NODE_ENV ?? \"development\";\n\t}\n\n\t/**\n\t * @returns `true` when {@link Config.nodeEnv} is `\"production\"`.\n\t */\n\tstatic get isProd(): boolean {\n\t\treturn this.nodeEnv === \"production\";\n\t}\n\n\t/**\n\t * @returns `true` when {@link Config.nodeEnv} is `\"development\"`, including\n\t * when `NODE_ENV` is unset.\n\t */\n\tstatic get isDev(): boolean {\n\t\treturn this.nodeEnv === \"development\";\n\t}\n\n\t/**\n\t * @returns `true` when {@link Config.nodeEnv} is `\"test\"`. {@link App.close}\n\t * checks this to avoid exiting the process out from under a test runner.\n\t */\n\tstatic get isTest(): boolean {\n\t\treturn this.nodeEnv === \"test\";\n\t}\n\n\t/**\n\t * Reports whether a variable is set, without reading its value.\n\t *\n\t * @param key - The variable name to check.\n\t * @returns `true` when the variable is defined. An empty string counts as\n\t * defined.\n\t */\n\tstatic has(key: EnvKey): boolean {\n\t\treturn isPresent(this.env[key]);\n\t}\n\n\t/**\n\t * Reads a variable as a raw string.\n\t *\n\t * @param key - The variable name to read.\n\t * @returns The value, or `undefined` when unset.\n\t */\n\tstatic get(key: EnvKey): string | undefined;\n\t/**\n\t * Reads a variable with a guaranteed result, since a fallback covers the unset\n\t * case.\n\t *\n\t * @param key - The variable name to read.\n\t * @param opts - Lookup options.\n\t * @param opts.parser - Converts the raw string into the value you want. Not\n\t * applied to the fallback, which is used as given.\n\t * @param opts.fallback - Returned when the variable is unset.\n\t * @returns The parsed value, or the fallback.\n\t */\n\tstatic get<T = string>(key: EnvKey, opts: { parser?: (raw: string) => T; fallback: T }): T;\n\t/**\n\t * Reads and converts a variable with no fallback.\n\t *\n\t * @param key - The variable name to read.\n\t * @param opts - Lookup options.\n\t * @param opts.parser - Converts the raw string into the value you want.\n\t * @param opts.fallback - Returned when the variable is unset.\n\t * @returns The parsed value, or `undefined` when the variable is unset.\n\t */\n\tstatic get<T = string>(\n\t\tkey: EnvKey,\n\t\topts: { parser: (raw: string) => T; fallback?: T },\n\t): T | undefined;\n\tstatic get<T = string>(\n\t\tkey: EnvKey,\n\t\topts?: { parser?: (raw: string) => T; fallback?: T },\n\t): T | undefined {\n\t\tconst value = this.env[key];\n\t\tif (isPresent(value)) {\n\t\t\treturn opts?.parser ? opts.parser(value) : (value as T);\n\t\t}\n\t\tif (opts && \"fallback\" in opts) {\n\t\t\treturn opts.fallback;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Reads a variable that the application cannot run without.\n\t *\n\t * @param key - The variable name to read.\n\t * @returns The raw value.\n\t * @throws {@link Error} when the variable is unset.\n\t */\n\tstatic require(key: EnvKey): string;\n\t/**\n\t * Reads and converts a variable that the application cannot run without.\n\t *\n\t * @param key - The variable name to read.\n\t * @param parser - Converts the raw string into the value you want.\n\t * @returns The parsed value.\n\t * @throws {@link Error} when the variable is unset. The parser is never called\n\t * in that case, so it can assume a real value.\n\t */\n\tstatic require<T = string>(key: EnvKey, parser: (raw: string) => T): T;\n\tstatic require<T = string>(key: EnvKey, parser?: (raw: string) => T): T | string {\n\t\tconst value = parser ? this.get(key, { parser }) : this.get(key);\n\t\tif (isAbsent(value)) {\n\t\t\tthrow new Error(`Required environment variable \"${key}\" is not set`);\n\t\t}\n\t\treturn value;\n\t}\n\n\t/**\n\t * Writes a variable into the environment, stringifying the value the way the\n\t * environment stores everything.\n\t *\n\t * Mutates the real `process.env`, so the change is visible to every reader in\n\t * the process, not only to {@link Config}.\n\t *\n\t * @param key - The variable name to write.\n\t * @param value - The value to store; numbers and booleans are converted to\n\t * their string form.\n\t */\n\tstatic set(key: string, value: string | number | boolean): void {\n\t\tthis.env[key] = String(value);\n\t}\n}\n\nexport { Config, type Env };\n",
30
+ "/**\n * Works out which parts of a request a route actually uses, so the rest can be\n * skipped.\n *\n * Parsing a body, a query string and path parameters costs work, and most\n * handlers only use one of them. Before serving a route, {@link App} asks this\n * module what the handler chain reads and then parses only that.\n *\n * The answer comes from reading the handler's own source: `fn.toString()` gives\n * back the function's text, and every mention of the context parameter is\n * checked. `c.body` is a read of the body. `({ params }) => …` is a read of the\n * params. Anything less obvious — passing `c` to another function, returning it,\n * indexing it with a variable — means the handler could reach anything, so\n * everything gets parsed.\n *\n * That fallback is the rule the whole module follows: when in doubt, parse it\n * all. Guessing \"unused\" wrongly would hand a handler an empty body it was\n * counting on. Guessing \"used\" wrongly just does some work nobody needed.\n *\n * @module ContextAccess\n */\n\nimport type { RouteConfig } from \"@/RouteBase\";\nimport { isPresent, type Nullable, type Optional } from \"@/utils/is\";\n\n/**\n * The context properties whose population can be skipped. Order is irrelevant;\n * this exists so the key list, the record type, and the runtime loops can never\n * drift apart.\n */\nconst KEYS = [\"body\", \"search\", \"params\"] as const;\n\n/** One of the skippable {@link Context} properties: `body`, `search` or `params`. */\ntype ContextAccessKey = (typeof KEYS)[number];\n\n/** Which properties to parse. `true` means the handler chain might read it. */\ntype ContextAccess = Record<ContextAccessKey, boolean>;\n\n/**\n * The conservative fallback: every property is parsed. Returned whenever the\n * analysis cannot prove a property is unused.\n *\n * @returns A record with every key set to `true`.\n */\nfunction all(): ContextAccess {\n\treturn { body: true, search: true, params: true };\n}\n\n/**\n * Fresh zeroed record. A factory, not a shared constant — callers mutate it.\n *\n * @returns A record with every key set to `false`.\n */\nfunction none(): ContextAccess {\n\treturn { body: false, search: false, params: false };\n}\n\n/**\n * Scans forward through source text looking for a character, ignoring anything\n * inside brackets or string literals.\n *\n * Used to find the end of a parameter list without being fooled by a comma in a\n * default value or a closing paren inside a string.\n *\n * @param source - The text to scan.\n * @param from - Index to start at.\n * @param stop - Called for each candidate character with the current bracket\n * depth. Return anything other than `null` to stop here; the value itself is\n * ignored.\n * @returns The index where the scan stopped, or -1 if it ran off the end.\n */\nfunction scan(\n\tsource: string,\n\tfrom: number,\n\tstop: (char: string, depth: number) => Nullable<number>,\n): number {\n\tlet depth = 0;\n\t// Empty string means \"not currently inside a literal\"; otherwise it holds\n\t// the quote character that will close the literal.\n\tlet quote = \"\";\n\n\tfor (let i = from; i < source.length; i++) {\n\t\tconst char = source[i]!;\n\n\t\tif (quote) {\n\t\t\t// Skip the escaped character wholesale so an escaped quote does not\n\t\t\t// read as a terminator.\n\t\t\tif (char === \"\\\\\") i++;\n\t\t\telse if (char === quote) quote = \"\";\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"' || char === \"'\" || char === \"`\") {\n\t\t\tquote = char;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"(\" || char === \"[\" || char === \"{\") {\n\t\t\tdepth++;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \")\" || char === \"]\" || char === \"}\") {\n\t\t\tdepth--;\n\t\t\t// Closers are only offered to `stop` when they return to the top\n\t\t\t// level, so a nested `)` cannot be mistaken for the one that ends\n\t\t\t// the parameter list.\n\t\t\tif (depth === 0 && stop(char, depth) !== null) return i;\n\t\t\tcontinue;\n\t\t}\n\t\tif (stop(char, depth) !== null) return i;\n\t}\n\n\treturn -1;\n}\n\n/**\n * Splits a function's source text into its first parameter and its body.\n *\n * The first parameter is the context; the body is where reads of it are\n * counted. Handles both `(c) => …` and the parenless `c => …`.\n *\n * @param source - The function's `toString()` output.\n * @returns The first parameter and the body text, or `null` when no parameter\n * list could be found — which the caller treats as unanalyzable.\n */\nfunction getSignature(source: string): Nullable<{ firstParam: string; body: string }> {\n\t// Parenless arrow: `c => ...` or `async c => ...`. There is no bracket pair\n\t// to scan for, so the identifier is captured directly.\n\tconst bare = /^\\s*(?:async\\s+)?([A-Za-z_$][\\w$]*)\\s*=>/.exec(source);\n\tif (bare && bare[1]) {\n\t\tconst firstParam = bare[1].trim();\n\t\tconst body = source.slice(bare[0].length);\n\t\treturn { firstParam, body };\n\t}\n\n\tconst opening = source.indexOf(\"(\");\n\tif (opening === -1) return null;\n\n\t// Depth starts at 0 and the opening paren immediately raises it, so depth\n\t// returning to 0 marks the matching close.\n\tconst closing = scan(source, opening, (_char, depth) => (depth === 0 ? -1 : null));\n\tif (closing === -1) return null;\n\n\tconst params = source.slice(opening + 1, closing);\n\t// A top-level comma ends the first parameter. Commas nested inside a\n\t// destructuring pattern or a default value sit at depth > 0 and are ignored.\n\tconst comma = scan(params, 0, (char, depth) => (char === \",\" && depth === 0 ? -1 : null));\n\t// -1 means no top-level comma was found, i.e. the whole list is one parameter.\n\tconst firstParam = (comma === -1 ? params : params.slice(0, comma)).trim();\n\tconst body = source.slice(closing + 1);\n\n\treturn { firstParam, body };\n}\n\n/**\n * Reads which keys a destructuring pattern pulls out, as in\n * `({ body, params }) => …`.\n *\n * @param pattern - The pattern text, braces included.\n * @returns Which keys it binds, or `null` when the pattern has a rest element —\n * that captures everything left over, so no key can be ruled out.\n */\nfunction getPatternAccess(pattern: string): Nullable<ContextAccess> {\n\tif (pattern.includes(\"...\")) return null;\n\n\tconst access = none();\n\tfor (const key of KEYS) {\n\t\t// The trailing class matches the four ways a destructured binding can\n\t\t// end: `body,` `body:` `body}` `body=`. The lookbehind rejects\n\t\t// `req.body` and the lookahead rejects `bodyParser`.\n\t\tif (new RegExp(`(?<![\\\\w$.])${key}(?![\\\\w$])\\\\s*[,:}=]`).test(pattern)) access[key] = true;\n\t}\n\treturn access;\n}\n\n/**\n * Finds the `{` that opens the block ending at `closing`, by counting brackets\n * backwards.\n *\n * @param source - The text to search.\n * @param closing - Index of the closing brace.\n * @returns The opening brace's index, or -1 when the brackets do not pair up —\n * which sends the caller to {@link all}.\n */\nfunction getOpeningBrace(source: string, closing: number): number {\n\tlet depth = 0;\n\tfor (let i = closing; i >= 0; i--) {\n\t\tconst char = source[i]!;\n\t\tif (char === \"}\" || char === \")\" || char === \"]\") depth++;\n\t\telse if (char === \"{\" || char === \"(\" || char === \"[\") {\n\t\t\tdepth--;\n\t\t\tif (depth === 0) return char === \"{\" ? i : -1;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Analyzes one function.\n *\n * Finds the context parameter, then walks every mention of it in the body. A\n * mention it can read as a property access records that key; a mention it\n * cannot falls back to {@link all}.\n *\n * @param fn - The handler to analyze.\n * @returns Which properties this function might read.\n */\nfunction getSingleContextAccess(fn: Function): ContextAccess {\n\tconst source = fn.toString();\n\t// Bound, native, or otherwise opaque functions expose no readable body.\n\tif (source.includes(\"[native code]\")) return all();\n\n\tconst signature = getSignature(source);\n\tif (signature === null) return all();\n\n\tconst { firstParam, body } = signature;\n\n\t// Destructured context: `({ body, search }) => ...`. The pattern itself\n\t// names everything the function can reach, so the body needs no scanning.\n\tif (firstParam.startsWith(\"{\")) {\n\t\tconst patternAccess = getPatternAccess(firstParam);\n\t\treturn patternAccess ?? all();\n\t}\n\n\t// Strip any default value or type annotation to leave a bare identifier.\n\tconst name = firstParam.split(/[\\s=]/)[0] ?? \"\";\n\t// Anything that is not a plain identifier — an array pattern, an empty\n\t// parameter list, minifier output this parser does not model — is unanalyzable.\n\tif (!/^[A-Za-z_$][\\w$]*$/.test(name)) return all();\n\n\tconst access = none();\n\t// Property positions (`x.c`) are excluded below rather than in the pattern,\n\t// because a spread's trailing dot is indistinguishable from member access\n\t// to a lookbehind. Longer identifiers that merely start with the name\n\t// (`ctx2`) are still rejected here.\n\tconst identifier = new RegExp(`(?<![\\\\w$])${name}(?![\\\\w$])`, \"g\");\n\n\tfor (const match of body.matchAll(identifier)) {\n\t\t// Bun's transpiler merges consecutive property reads into one\n\t\t// destructuring assignment, so `const a = c.body; const b = c.params`\n\t\t// arrives here as `const { body: a, params: b } = c`. The pattern names\n\t\t// everything the statement can reach, so it is read rather than\n\t\t// treated as an escape.\n\t\tconst head = body.slice(0, match.index).trimEnd();\n\n\t\t// `x.c` is a property that happens to share the parameter's name. `...c`\n\t\t// is the parameter itself being spread, and its third dot must not be\n\t\t// mistaken for member access.\n\t\tif (head.endsWith(\".\") && !head.endsWith(\"...\")) continue;\n\n\t\tconst tail = body.slice(match.index + name.length);\n\n\t\t// `c.body`, `c?.body`\n\t\tconst dot = /^\\s*\\??\\.\\s*([A-Za-z_$][\\w$]*)/.exec(tail);\n\t\tif (dot) {\n\t\t\tconst key = dot[1] as ContextAccessKey;\n\t\t\t// A property outside KEYS is a read this module does not care about.\n\t\t\tif (KEYS.includes(key)) access[key] = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// `c[\"body\"]`, `c?.[\"body\"]`. The backreference forces matching quotes\n\t\t// and the character class rejects escapes, so only literal static keys\n\t\t// are accepted.\n\t\tconst bracket = /^\\s*\\??\\.?\\[\\s*([\"'])([^\"'\\\\]*)\\1\\s*\\]/.exec(tail);\n\t\tif (bracket) {\n\t\t\tconst key = bracket[2] as ContextAccessKey;\n\t\t\tif (KEYS.includes(key)) access[key] = true;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// A bare `=` only. `==`, `===`, `!=`, `>=` and `<=` are comparisons, in\n\t\t// which the context is being used as a value.\n\t\tif (head.endsWith(\"=\") && !/[=!<>]=$/.test(head)) {\n\t\t\tconst target = head.slice(0, -1).trimEnd();\n\t\t\tif (target.endsWith(\"}\")) {\n\t\t\t\tconst opening = getOpeningBrace(target, target.length - 1);\n\t\t\t\tif (opening !== -1) {\n\t\t\t\t\tconst patternAccess = getPatternAccess(target.slice(opening));\n\t\t\t\t\tif (patternAccess === null) return all();\n\t\t\t\t\tfor (const key of KEYS) access[key] ||= patternAccess[key];\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// The context escapes as a value here: passed to another function,\n\t\t// destructured, returned, or indexed with a computed key. Nothing\n\t\t// can be ruled out, so everything is parsed.\n\t\treturn all();\n\t}\n\n\treturn access;\n}\n\n/**\n * Combines every handler in a route's chain into one answer.\n *\n * A property is parsed if any handler reads it, so a {@link Middleware} that\n * needs the body still gets one even when the route handler ignores it. A key\n * with a schema in the route's {@link RouteConfig} is always parsed — skipping\n * it would let an invalid payload through unvalidated.\n *\n * @param handlers - Every function in the chain, middleware and route handler\n * alike.\n * @param config - The route's config, read for its schemas.\n * @returns Which properties {@link App} should populate on the\n * {@link Context}.\n */\nfunction getContextAccess(\n\thandlers: ReadonlyArray<Function>,\n\tconfig: Optional<RouteConfig>,\n): ContextAccess {\n\tconst result = none();\n\n\tfor (const handler of handlers) {\n\t\tconst access = getSingleContextAccess(handler);\n\t\tfor (const key of KEYS) result[key] ||= access[key];\n\t\t// Every key is already set; no later handler can widen this further.\n\t\tif (KEYS.every((key) => result[key])) break;\n\t}\n\n\t// A configured schema runs whether or not a handler reads the value —\n\t// skipping it would let an invalid payload through unvalidated.\n\treturn {\n\t\tparams: result.params || isPresent(config?.params),\n\t\tsearch: result.search || isPresent(config?.search),\n\t\tbody: result.body || isPresent(config?.body),\n\t};\n}\n\nexport { getContextAccess };\n",
31
+ "// oxlint-disable typescript/no-explicit-any\nexport type Logger = {\n\tlog(...args: any[]): void;\n\tbold(...args: any[]): void;\n\tinfo(...args: any[]): void;\n\tsuccess(...args: any[]): void;\n\terror(...args: any[]): void;\n\tdebug(...args: any[]): void;\n\twarn(...args: any[]): void;\n\tstep(...args: any[]): void;\n};\n\nconst col = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tred: \"\\x1b[31m\",\n\tcyan: \"\\x1b[36m\",\n\tyellow: \"\\x1b[33m\",\n\tgray: \"\\x1b[90m\",\n\tbold: \"\\x1b[1m\",\n\tmagenta: \"\\x1b[35m\",\n\tblue: \"\\x1b[34m\",\n} as const;\n\nexport function strColor(color: keyof typeof col, str: string): string {\n\treturn col[color] + str + col.reset;\n}\n\nexport function makeLogger(): Logger {\n\tconst logger = {} as Logger;\n\tlogger.log = (...a: any[]) => console.log(...a);\n\tlogger.bold = (...a: any[]) => console.log(col.bold, ...a, col.reset);\n\tlogger.info = (...a: any[]) => console.log(strColor(\"cyan\", \"i\"), ...a);\n\tlogger.success = (...a: any[]) => console.log(strColor(\"green\", \"✓\"), ...a);\n\tlogger.error = (...a: any[]) => console.error(strColor(\"red\", \"✗\"), ...a);\n\tlogger.debug = (...a: any[]) => console.log(strColor(\"gray\", \"·\"), ...a);\n\tlogger.warn = (...a: any[]) => console.warn(strColor(\"yellow\", \"⚠\"), ...a);\n\tlogger.step = (...a: any[]) => console.log(strColor(\"magenta\", \">\"), ...a);\n\n\treturn logger;\n}\n\nexport function makeNoopLogger(): Logger {\n\treturn {\n\t\tbold() {},\n\t\tlog() {},\n\t\tinfo() {},\n\t\tsuccess() {},\n\t\tdebug() {},\n\t\twarn() {},\n\t\tstep() {},\n\t\terror() {},\n\t};\n}\n\n// mutable holder — `logger` proxies to whatever is currently active\nlet active: Logger = makeLogger();\n\nexport const logger: Logger = new Proxy({} as Logger, {\n\tget(_target, prop: keyof Logger) {\n\t\treturn active[prop];\n\t},\n});\n\nexport function setLogger(custom: Logger): void {\n\tactive = custom;\n}\n\nexport function setLoggerNoop(): void {\n\tactive = makeNoopLogger();\n}\n\nexport function resetLogger(): void {\n\tactive = makeLogger();\n}\n\nexport function logFatal(...args: any[]): never {\n\tif (process.env.NODE_ENV === \"test\") {\n\t\tthrow new Error(JSON.stringify(args));\n\t} else {\n\t\tlogger.error(...args);\n\t\tprocess.exit(1);\n\t}\n}\n",
32
+ "/**\n * The application root: owns the {@link Server}, the route table, the\n * {@link Middleware} registry, and the request lifecycle that ties them\n * together.\n *\n * An {@link App} is the only thing that talks to Bun's HTTP server.\n * {@link RouteBase} implementations and {@link Middleware} instances register\n * themselves against an app, and at {@link App.listen} time the app compiles\n * them into a single {@link ServerRouteMap} plus a fallback {@link ServerHandler}.\n *\n * ```ts\n * const app = new App({ port: 3000, prefix: \"/api\" });\n * await app.listen();\n * ```\n *\n * @module App\n */\n\nimport { Config } from \"@/Config\";\nimport { type ContextFactory, type ContextHandler, Context } from \"@/Context\";\nimport { getContextAccess } from \"@/Context/ContextAccess\";\nimport type { CorsInterface } from \"@/Cors\";\nimport { Exception } from \"@/Exception\";\nimport { registerApp } from \"@/Globals/AppsRegistry\";\nimport { getOrInitParsersRegistry } from \"@/Globals/ParsersRegistry\";\nimport { HeaderKey } from \"@/Headers\";\nimport type { MiddlewareHandler, Middleware } from \"@/Middleware\";\nimport { Method } from \"@/Request\";\nimport { Res, Status } from \"@/Res\";\nimport { RouteVariant, type RouteBase } from \"@/RouteBase\";\nimport type { WebSocketRoute } from \"@/RouteBase/WebSocketRoute\";\nimport type { Server, ServerHandler, ServerRouteMap } from \"@/Server\";\nimport {\n\tisEmpty,\n\tisAbsent,\n\tisPresent,\n\ttype MaybePromise,\n\ttype Nullable,\n\ttype Optional,\n\tisOneOf,\n\ttype OrString,\n} from \"@/utils/is\";\nimport { logger } from \"@/utils/logger\";\nimport { withLeadingSlash } from \"@/utils/path\";\n\nconst noop = () => {};\n\n/**\n * TLS material used to serve an {@link App} over HTTPS.\n *\n * Passing this to {@link App} switches {@link App.baseUrl} to the `https`\n * scheme and hands the certificate straight to Bun's server.\n */\ninterface TlsOptions {\n\t/** PEM-encoded certificate chain, as a string or a file buffer. */\n\tcert: string | Buffer;\n\t/** PEM-encoded private key matching {@link TlsOptions.cert}. */\n\tkey: string | Buffer;\n\t/** Optional PEM-encoded certificate authority bundle for client verification. */\n\tca?: string | Buffer;\n}\n\n/**\n * Construction options for {@link App}. Every field is optional; omitted fields\n * keep the defaults declared on {@link App}.\n */\ninterface AppOptions {\n\t/** TCP port to bind. See {@link App.port}. */\n\tport?: number;\n\t/**\n\t * Path prefix prepended to the endpoint of every {@link RouteBase} registered\n\t * on the app. See {@link App.prefix}.\n\t */\n\tprefix?: string;\n\t/** Interface to bind. See {@link App.hostname}. */\n\thostname?: OrString<\"0.0.0.0\" | \"127.0.0.1\" | \"localhost\">;\n\t/** Seconds a connection may stay idle before Bun closes it. See {@link App.idleTimeout}. */\n\tidleTimeout?: number;\n\t/** {@link TlsOptions} to serve the app over HTTPS. See {@link App.tls}. */\n\ttls?: TlsOptions;\n\t/**\n\t * App-wide request body ceiling in bytes, handed to Bun's server. An\n\t * individual {@link RouteBase} can tighten this through its own\n\t * {@link Config}, which {@link enforceBodyLimit} applies per request.\n\t */\n\tmaxRequestBodySize?: number;\n}\n\n/**\n * Handles an {@link Error} raised anywhere in the request lifecycle. Assigned to\n * {@link App.handleError}.\n *\n * @param error - The thrown error, typically an {@link Exception}.\n * @param context - The {@link Context} the error was raised in. Absent only when\n * the failure happened before a {@link Context} could be built.\n * @returns The value to respond with — a {@link Res}, a plain body that\n * {@link App.respond} will assign to {@link Res.body}, or a promise of either.\n */\ntype ErrorHandler<R = unknown> = (error: Error, context?: Context) => MaybePromise<R>;\n\n/**\n * Folds {@link Middleware} handlers and a terminal {@link RouteBase} handler\n * into one {@link MiddlewareHandler}, giving each link an `await next()` that\n * runs the rest of the chain.\n *\n * Resolution order for what a link contributes to the response:\n *\n * 1. A returned value wins — it is either the terminal body or a\n * {@link Middleware} short-circuiting the chain with a {@link Res}.\n * 2. If `next()` was never called, it is called implicitly so the chain still\n * reaches its terminal handler.\n * 3. If the handler replaced {@link Context.res} after `next()` resolved, that\n * outbound mutation wins over the downstream result.\n * 4. Otherwise the downstream result passes through untouched.\n *\n * @param handlers - {@link MiddlewareHandler} functions in execution order; the\n * last one is normally the {@link RouteBase} handler.\n * @returns A single {@link MiddlewareHandler} that runs the whole chain,\n * delegating to its own `next` once the chain is exhausted.\n * @throws {@link Exception} with {@link Status.INTERNAL_SERVER_ERROR} if a\n * handler calls `next()` more than once.\n */\nfunction composeHandlerChain(...handlers: Array<MiddlewareHandler>): MiddlewareHandler {\n\treturn (c, outerNext) => {\n\t\tlet index = -1;\n\t\tconst dispatch = (i: number): ReturnType<MiddlewareHandler> => {\n\t\t\tif (i <= index) {\n\t\t\t\tthrow new Exception(\"next() called multiple times\", Status.INTERNAL_SERVER_ERROR);\n\t\t\t}\n\t\t\tindex = i;\n\n\t\t\tconst handler = handlers[i];\n\t\t\tif (!handler) return outerNext();\n\n\t\t\tlet called = false;\n\t\t\tlet downstream: unknown | undefined;\n\t\t\tconst next = async () => {\n\t\t\t\tcalled = true;\n\t\t\t\tdownstream = await dispatch(i + 1);\n\t\t\t\treturn downstream;\n\t\t\t};\n\n\t\t\treturn (async () => {\n\t\t\t\tconst resBefore = c.res;\n\t\t\t\tconst result = await handler(c, next);\n\t\t\t\tif (result !== undefined) return result; // terminal body OR middleware Res short-circuit\n\t\t\t\tif (!called) return await next();\n\t\t\t\tif (c.res !== resBefore) return c.res; // outbound mutation wins\n\t\t\t\treturn downstream;\n\t\t\t})();\n\t\t};\n\t\treturn dispatch(0);\n\t};\n}\n\n/**\n * Enforces the body size limit declared by a {@link RouteBase}'s {@link Config}.\n * Runs whether or not the body is parsed — the limit is a property of the\n * request, not of what the handler happens to read.\n *\n * {@link HeaderKey.ContentLength} is trusted: Bun's parser stops reading at the\n * declared length regardless of how much the client actually writes (verified —\n * a request declaring 10 and sending 10MB yields 10 bytes), so a declared length\n * under the limit needs no buffering. Chunked bodies carry no declared length,\n * so those are counted while streaming.\n *\n * Binary content types are exempt from the streaming count: they are handed to\n * the handler as a live stream, and draining them here would leave the handler\n * with a consumed body. Only the declared-length check applies to them.\n *\n * @param request - The incoming request to measure.\n * @param maxRequestBodySize - Ceiling in bytes for the request body.\n * @param retain - Whether the drained chunks are kept so the body can be read\n * again by the parsers. Pass `true` only when the body will actually be parsed;\n * `false` counts bytes without holding them in memory.\n * @returns The request the body should be read from — the original when nothing\n * was consumed, or a re-wrapped one carrying the buffered chunks when a chunked\n * body had to be drained and `retain` asked for it back.\n * @throws {@link Exception} with {@link Status.PAYLOAD_TOO_LARGE} when the\n * declared or observed size exceeds `maxRequestBodySize`.\n */\nasync function enforceBodyLimit(\n\trequest: Request,\n\tmaxRequestBodySize: number,\n\tretain: boolean,\n): Promise<Request | Response> {\n\tconst header = request.headers.get(HeaderKey.ContentLength);\n\tconst contentLength = header === null ? NaN : parseInt(header);\n\n\tif (!isNaN(contentLength)) {\n\t\tif (contentLength > maxRequestBodySize) {\n\t\t\tthrow new Exception(\"Payload too large\", Status.PAYLOAD_TOO_LARGE);\n\t\t}\n\t\treturn request;\n\t}\n\n\tif (!request.body) return request;\n\n\t// Binary bodies are handed to the handler as a live stream, so draining\n\t// them here to count bytes would leave the handler with a consumed stream.\n\t// They are exempt from the chunked count; only the declared-length check\n\t// above applies to them.\n\tconst contentType = request.headers.get(HeaderKey.ContentType) ?? \"\";\n\tif (/application\\/octet-stream|application\\/pdf|^image\\/|^audio\\/|^video\\//.test(contentType)) {\n\t\treturn request;\n\t}\n\n\tconst reader = request.body.getReader();\n\tconst chunks: Array<Uint8Array<ArrayBuffer>> = [];\n\tlet total = 0;\n\n\twhile (true) {\n\t\tconst { done, value } = await reader.read();\n\t\tif (done) break;\n\t\ttotal += value.byteLength;\n\t\tif (total > maxRequestBodySize) {\n\t\t\tawait reader.cancel();\n\t\t\tthrow new Exception(\"Payload too large\", Status.PAYLOAD_TOO_LARGE);\n\t\t}\n\t\t// Chunks are only accumulated when the body will be parsed, so\n\t\t// enforcing a limit on a body nobody reads costs no memory.\n\t\tif (retain) chunks.push(value as Uint8Array<ArrayBuffer>);\n\t}\n\n\tif (!retain) return request;\n\treturn new Response(new Blob(chunks), { headers: request.headers });\n}\n\n/**\n * The public shape of an application instance, implemented by {@link App}.\n *\n * Depend on this type rather than the {@link App} class when you need to accept\n * an app without pinning the implementation.\n */\ninterface AppInterface {\n\t/** The running {@link Server}, or `null` before {@link AppInterface.listen} and after {@link AppInterface.close}. */\n\tserver: Nullable<Server>;\n\t/** {@link CorsInterface} policy applied to every response and to preflight requests. */\n\tcors: Optional<CorsInterface>;\n\t/** {@link RouteBase} instances registered on this app, in registration order. */\n\troutes: Array<RouteBase>;\n\t/** {@link Middleware} instances keyed by the {@link RouteBase.id} they target; `\"*\"` holds the global ones. */\n\tmiddlewares: Map<string, Array<Middleware>>;\n\t/** TCP port to bind. */\n\tport: number;\n\t/** Path prefix prepended to every {@link RouteBase} endpoint on this app. */\n\tprefix: string;\n\t/** Interface to bind. */\n\thostname: OrString<\"0.0.0.0\" | \"127.0.0.1\" | \"localhost\">;\n\t/** Seconds a connection may stay idle before Bun closes it. */\n\tidleTimeout?: number;\n\t/** {@link TlsOptions} material; when set, the app is served over HTTPS. */\n\ttls?: TlsOptions;\n\t/** Origin the app is reachable at. */\n\tget baseUrl(): string;\n\t/** Compiles {@link RouteBase} and {@link Middleware} registrations and starts the {@link Server}. */\n\tlisten(): Promise<void>;\n\t/** Stops the {@link Server} and releases the port. */\n\tclose(closeActiveConnections?: boolean): Promise<void>;\n\t/** Hook run just before the {@link Server} starts. */\n\thandleBeforeListen: Optional<() => MaybePromise<void>>;\n\t/** Hook run just before the {@link Server} stops. */\n\thandleBeforeClose: Optional<() => MaybePromise<void>>;\n\t/** {@link ErrorHandler} that converts a thrown {@link Error} into a response value. */\n\thandleError: ErrorHandler;\n\t/** {@link ContextHandler} that produces the response for requests matching no {@link RouteBase}. */\n\thandleNotFound: ContextHandler;\n\t/** {@link ContextHandler} that produces the response for CORS preflight requests. */\n\thandlePreflight: ContextHandler;\n\t/** {@link ContextFactory} that builds the {@link Context} for each incoming request. */\n\tcontextFactory: ContextFactory;\n\t/** Registers a {@link Middleware} against each {@link RouteBase.id} it targets. */\n\taddMiddleware(middleware: Middleware): void;\n\t/** Resolves the {@link Middleware} instances that apply to a {@link RouteBase.id}. */\n\tfindMiddlewares(routeId: string): Array<Middleware>;\n}\n\n/**\n * An HTTP application.\n *\n * The app is a container first: {@link RouteBase} implementations and\n * {@link Middleware} instances attach to it as they are constructed. Nothing is\n * compiled until {@link App.listen}, at which point {@link App.composeRoutes}\n * turns routes into a {@link ServerRouteMap} and folds the matching\n * {@link Middleware} handlers into each route's chain.\n *\n * Every request follows the same path — build a {@link Context}, resolve\n * {@link Context.params}, {@link Context.search} and {@link Context.body}\n * through the parsers registry, run the {@link composeHandlerChain} chain, apply\n * {@link CorsInterface}, and serialise the {@link Res}. Anything thrown along\n * the way is routed to {@link App.handleError}.\n *\n * Constructing an app calls {@link registerApp}, so it is discoverable without\n * being passed around.\n */\nclass App implements AppInterface {\n\t/**\n\t * Creates an app and registers it globally with {@link registerApp}.\n\t *\n\t * @param opts - {@link AppOptions} overriding port, hostname, prefix, idle\n\t * timeout, {@link TlsOptions} and body size. Any field left out keeps the\n\t * default declared on the corresponding {@link App} property.\n\t */\n\tconstructor(opts?: AppOptions) {\n\t\tif (opts?.port) this.port = opts.port;\n\t\tif (opts?.hostname) this.hostname = opts.hostname;\n\t\tif (opts?.idleTimeout) this.idleTimeout = opts.idleTimeout;\n\t\tif (opts?.tls) this.tls = opts.tls;\n\t\tif (opts?.prefix) this.prefix = opts.prefix;\n\t\tif (opts?.maxRequestBodySize) this.maxRequestBodySize = opts.maxRequestBodySize;\n\t\tregisterApp(this);\n\t}\n\n\t/**\n\t * The live {@link Server}. `null` until {@link App.listen} is called and again\n\t * after {@link App.close}.\n\t */\n\tserver: Nullable<Server> = null;\n\n\t/**\n\t * {@link CorsInterface} policy for this app. When set,\n\t * {@link CorsInterface.handler} runs after every handler chain in\n\t * {@link App.respond} and {@link CorsInterface.handlePreflight} answers\n\t * preflight requests. Left unset, {@link App.handlePreflight} replies with\n\t * {@link Status.NO_CONTENT} and no CORS headers are added.\n\t */\n\tcors: Optional<CorsInterface>;\n\n\t/** {@link RouteBase} instances attached to this app, in registration order. */\n\troutes: Array<RouteBase> = [];\n\n\t/**\n\t * {@link Middleware} instances indexed by the {@link RouteBase.id} they\n\t * target. The `\"*\"` key holds middlewares that run on every route as well as\n\t * on the {@link App.handleNotFound} path.\n\t */\n\tmiddlewares: Map<string, Array<Middleware>> = new Map();\n\n\t/** Port the {@link Server} binds to. Defaults to `3000`. */\n\tport: number = 3000;\n\n\t/** Prefix prepended to every {@link RouteBase} endpoint on this app. Defaults to `\"\"`. */\n\tprefix: string = \"\";\n\n\t/** Interface the {@link Server} binds to. Defaults to `\"0.0.0.0\"`. */\n\thostname: OrString<\"0.0.0.0\" | \"127.0.0.1\" | \"localhost\"> = \"0.0.0.0\";\n\n\t/** Seconds an idle connection is kept open before Bun closes it. */\n\tidleTimeout?: number;\n\n\t/** {@link TlsOptions} material. When present the app is served over HTTPS. */\n\ttls?: TlsOptions;\n\n\t/**\n\t * App-wide body ceiling in bytes passed to Bun. A {@link RouteBase} may\n\t * declare a tighter limit in its {@link Config}, enforced per request by\n\t * {@link enforceBodyLimit}.\n\t */\n\tmaxRequestBodySize?: number;\n\n\t/**\n\t * Origin the app is reachable at, for example `http://0.0.0.0:3000`.\n\t *\n\t * @returns The {@link Server} URL once listening; otherwise a URL derived from\n\t * {@link App.tls}, {@link App.hostname} and {@link App.port}.\n\t */\n\tget baseUrl(): string {\n\t\tif (isPresent(this.server)) return this.server.url.toString();\n\t\tconst protocol = this.tls ? \"https\" : \"http\";\n\t\treturn `${protocol}://${this.hostname}${this.port ? `:${this.port}` : \"\"}`;\n\t}\n\n\t/**\n\t * Compiles {@link App.composeRoutes} and {@link App.composeFetch} and hands\n\t * them to `Bun.serve`, wiring the WebSocket callbacks through to the handlers\n\t * carried by each {@link WebSocketRoute}. Unroutable middlewares are reported\n\t * first by {@link App.warnUnmatchedMiddlewares}.\n\t *\n\t * Calling this when {@link App.server} already exists is a no-op that returns\n\t * the existing one, so it is safe to reach for lazily.\n\t *\n\t * @returns The running {@link Server}.\n\t */\n\tprotected createServer(): Server {\n\t\tif (isPresent(this.server)) return this.server;\n\n\t\tthis.warnUnmatchedMiddlewares();\n\n\t\tthis.server = Bun.serve({\n\t\t\tport: this.port,\n\t\t\thostname: this.hostname,\n\t\t\tidleTimeout: this?.idleTimeout,\n\t\t\ttls: this?.tls,\n\t\t\tmaxRequestBodySize: this.maxRequestBodySize,\n\t\t\tfetch: this.composeFetch(),\n\t\t\troutes: this.composeRoutes(),\n\t\t\twebsocket: {\n\t\t\t\topen: (ws) => ws.data.onOpen?.(ws),\n\t\t\t\tmessage: (ws, msg) => ws.data.onMessage?.(ws, msg),\n\t\t\t\tclose: (ws, code, reason) => ws.data.onClose?.(ws, code, reason),\n\t\t\t},\n\t\t});\n\n\t\treturn this.server;\n\t}\n\n\t/**\n\t * Starts the app.\n\t *\n\t * Installs `SIGINT` and `SIGTERM` handlers that call {@link App.close}, runs\n\t * {@link App.handleBeforeListen}, then compiles and starts the {@link Server}\n\t * via {@link App.createServer}. A failure at any of these steps is logged and\n\t * the app is closed rather than left half-started.\n\t *\n\t * @returns A promise that resolves once the {@link Server} is listening.\n\t */\n\tasync listen(): Promise<void> {\n\t\ttry {\n\t\t\tprocess.on(\"SIGINT\", () => this.close());\n\t\t\tprocess.on(\"SIGTERM\", () => this.close());\n\n\t\t\tawait this.handleBeforeListen?.();\n\n\t\t\tthis.createServer();\n\t\t} catch (err) {\n\t\t\tlogger.error(err);\n\t\t\tawait this.close();\n\t\t}\n\t}\n\n\t/**\n\t * Stops the app.\n\t *\n\t * Runs {@link App.handleBeforeClose}, stops the {@link Server}, and clears\n\t * {@link App.server}. Outside the `test` value of {@link Config.nodeEnv} this\n\t * also exits the process, so tests can close apps without tearing down the\n\t * runner.\n\t *\n\t * @param closeActiveConnections - Whether in-flight connections are severed\n\t * immediately rather than allowed to drain. Defaults to `true`.\n\t * @returns A promise that resolves once the {@link Server} has stopped.\n\t */\n\tasync close(closeActiveConnections: boolean = true): Promise<void> {\n\t\tawait this.handleBeforeClose?.();\n\t\tawait this.server?.stop(closeActiveConnections);\n\t\tthis.server = null;\n\t\tif (Config.nodeEnv !== \"test\") process.exit(0);\n\t}\n\n\t// TODO: async handle(request: Request, server?: Maybe<Server>): Promise<Response>\n\n\t/**\n\t * Compiles {@link App.routes} into the {@link ServerRouteMap} Bun expects,\n\t * keyed by endpoint and then by {@link Method}.\n\t *\n\t * Each entry is a full request pipeline wrapped in {@link App.finalize}:\n\t * wildcard segments are lifted into {@link Req.params} (Bun does not treat\n\t * them as params), then params, search and body are parsed and validated\n\t * against the {@link RouteBase} {@link Config} — but only the ones the handler\n\t * chain actually reads, as reported by {@link getContextAccess}. Routes with\n\t * {@link RouteVariant.websocket} upgrade the connection into a\n\t * {@link WebSocketRoute} instead of responding, and {@link Method.GET} and\n\t * {@link Method.HEAD} skip body work entirely.\n\t *\n\t * @returns A {@link ServerRouteMap} ready to hand to `Bun.serve`.\n\t * @throws {@link Exception} with {@link Status.UPGRADE_REQUIRED} when a\n\t * WebSocket upgrade is rejected.\n\t */\n\tprotected composeRoutes(): ServerRouteMap {\n\t\tconst routes: ServerRouteMap = {};\n\n\t\tfor (const route of this.routes) {\n\t\t\tconst endpoint = withLeadingSlash(route.endpoint);\n\t\t\tconst isWebSocket = route.variant === RouteVariant.websocket;\n\t\t\tconst isWildcard = endpoint.endsWith(\"*\");\n\t\t\tconst isMethodWithoutBody = isOneOf(route.method, [Method.GET, Method.HEAD]);\n\t\t\tconst maxRequestBodySize = route.config?.maxRequestBodySize;\n\n\t\t\tconst handlers = [...this.findMiddlewares(route.id).map((m) => m.handler), route.handler];\n\n\t\t\tconst access = getContextAccess(handlers, route.config);\n\t\t\tconst parsers = getOrInitParsersRegistry();\n\n\t\t\tconst handler = composeHandlerChain(...handlers);\n\n\t\t\troutes[endpoint] ??= {};\n\t\t\troutes[endpoint]![route.method] = this.finalize(async (c) => {\n\t\t\t\t// Bun doesn't handle wildcards as params\n\t\t\t\tif (isWildcard) {\n\t\t\t\t\tconst prefix = endpoint.slice(0, -1);\n\t\t\t\t\tconst prefixIndex = c.req.url.indexOf(prefix);\n\t\t\t\t\tconst wildcardValue = c.req.url.slice(prefixIndex + prefix.length).split(\"?\")[0];\n\t\t\t\t\tif (wildcardValue) c.req.params[\"*\"] = decodeURIComponent(wildcardValue);\n\t\t\t\t}\n\n\t\t\t\tif (access.params && !isEmpty(c.req.params)) {\n\t\t\t\t\tc.params = parsers.urlParamsParser.parse(c.req.params);\n\t\t\t\t\tc.params = await parsers.schemaParser.parse(\"params\", c.params, route.config?.params);\n\t\t\t\t}\n\n\t\t\t\tconst qIndex = access.search ? c.req.url.indexOf(\"?\") : -1;\n\t\t\t\tif (qIndex !== -1) {\n\t\t\t\t\tconst search = new URLSearchParams(c.req.url.slice(qIndex + 1));\n\t\t\t\t\tc.search = parsers.searchParamsParser.parse(search);\n\t\t\t\t\tc.search = await parsers.schemaParser.parse(\"search\", c.search, route.config?.search);\n\t\t\t\t}\n\n\t\t\t\t// TODO: upgrade in WebSocketRoute\n\t\t\t\tif (isWebSocket) {\n\t\t\t\t\tconst upgraded = c.server?.upgrade(c.req, {\n\t\t\t\t\t\tdata: (await handler(c, noop)) as WebSocketRoute,\n\t\t\t\t\t});\n\t\t\t\t\tif (upgraded === false) throw new Exception(\"Upgrade failed\", Status.UPGRADE_REQUIRED);\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\n\t\t\t\tif (!isMethodWithoutBody) {\n\t\t\t\t\tlet input: Request | Response = c.req;\n\n\t\t\t\t\tif (isPresent(maxRequestBodySize)) {\n\t\t\t\t\t\tinput = await enforceBodyLimit(c.req, maxRequestBodySize, access.body);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (access.body) {\n\t\t\t\t\t\tc.body = await parsers.bodyParser.parse(input);\n\t\t\t\t\t\tc.body = await parsers.schemaParser.parse(\"body\", c.body, route.config?.body);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn await handler(c, noop);\n\t\t\t});\n\t\t}\n\n\t\treturn routes;\n\t}\n\n\t/**\n\t * Builds the {@link ServerHandler} Bun uses for requests that matched no\n\t * {@link RouteBase}.\n\t *\n\t * Preflight requests — {@link Method.OPTIONS} carrying\n\t * {@link HeaderKey.AccessControlRequestMethod} — go to\n\t * {@link App.handlePreflight}. Everything else runs the global (`\"*\"`)\n\t * {@link Middleware} chain followed by {@link App.handleNotFound}, so global\n\t * middlewares still observe traffic to unknown endpoints.\n\t *\n\t * @returns The `fetch` handler for `Bun.serve`, wrapped by\n\t * {@link App.finalize}.\n\t */\n\tprotected composeFetch(): ServerHandler {\n\t\tconst notFoundChain = composeHandlerChain(\n\t\t\t...this.findMiddlewares(\"*\").map((m) => m.handler),\n\t\t\tthis.handleNotFound,\n\t\t);\n\n\t\treturn this.finalize((c) => {\n\t\t\tconst isPreflight =\n\t\t\t\tc.req.method === Method.OPTIONS && c.req.headers.has(HeaderKey.AccessControlRequestMethod);\n\t\t\tif (isPreflight) {\n\t\t\t\treturn this.handlePreflight(c);\n\t\t\t}\n\t\t\treturn notFoundChain(c, noop);\n\t\t});\n\t}\n\n\t/**\n\t * Wraps a {@link ContextHandler} into the {@link ServerHandler} Bun calls,\n\t * giving it a {@link Context} from {@link App.contextFactory} and guaranteeing\n\t * that every outcome — value or throw — leaves as a `Response`.\n\t *\n\t * This is the single boundary where errors are caught, so every throw reaches\n\t * {@link App.handleError} through {@link App.respondWithError}.\n\t *\n\t * @param handler - The {@link ContextHandler} to run for the request.\n\t * @returns A {@link ServerHandler} suitable for a {@link ServerRouteMap} entry\n\t * or for `Bun.serve`'s `fetch`.\n\t */\n\tprotected finalize(handler: ContextHandler): ServerHandler {\n\t\treturn async (request, server) => {\n\t\t\tconst context = this.contextFactory(request, server);\n\n\t\t\ttry {\n\t\t\t\treturn await this.respond(context, await handler(context));\n\t\t\t} catch (err) {\n\t\t\t\treturn await this.respondWithError(context, err as Error);\n\t\t\t}\n\t\t};\n\t}\n\n\t/**\n\t * Turns a handler's return value into the response sent over the wire.\n\t *\n\t * A returned {@link Res} replaces {@link Context.res} wholesale; any other\n\t * defined value becomes {@link Res.body}; `undefined` leaves the existing\n\t * {@link Res} untouched, which is how handlers that mutate\n\t * {@link Context.res} directly are supported. {@link CorsInterface.handler}\n\t * runs last and separately from the {@link Middleware} chain, so CORS headers\n\t * cannot be clobbered by a short-circuiting middleware.\n\t *\n\t * @param context - The {@link Context} for the request.\n\t * @param result - Whatever the handler chain returned.\n\t * @returns The native `Response` produced by {@link Res.toNativeResponse}.\n\t */\n\tprotected async respond(context: Context, result: unknown): Promise<Response> {\n\t\tif (result instanceof Res) context.res = result;\n\t\telse if (result !== undefined) context.res.body = result;\n\n\t\t// CORS must come last and be separate from other middlewares\n\t\tawait this.cors?.handler(context);\n\n\t\treturn context.res.toNativeResponse();\n\t}\n\n\t/**\n\t * Runs {@link App.handleError} and responds with its result.\n\t *\n\t * If the error handler itself throws, that second failure is logged and a bare\n\t * {@link Status.INTERNAL_SERVER_ERROR} is returned — the request never escapes\n\t * without a response.\n\t *\n\t * @param context - The {@link Context} the failure occurred in.\n\t * @param err - The {@link Error} thrown by the handler chain.\n\t * @returns The error response.\n\t */\n\tprotected async respondWithError(context: Context, err: Error): Promise<Response> {\n\t\ttry {\n\t\t\treturn await this.respond(context, await this.handleError(err, context));\n\t\t} catch (fatal) {\n\t\t\tlogger.error(fatal);\n\t\t\treturn new Response(null, { status: Status.INTERNAL_SERVER_ERROR });\n\t\t}\n\t}\n\n\t/**\n\t * Hook run inside {@link App.listen}, before the {@link Server} is created.\n\t * Use it for setup that must complete before traffic is accepted; throwing\n\t * here aborts startup and closes the app.\n\t */\n\thandleBeforeListen: Optional<() => MaybePromise<void>>;\n\n\t/**\n\t * Hook run inside {@link App.close}, before the {@link Server} is stopped. Use\n\t * it to release resources the app owns.\n\t */\n\thandleBeforeClose: Optional<() => MaybePromise<void>>;\n\n\t/**\n\t * Default {@link ErrorHandler}. An {@link Exception} is rendered through\n\t * {@link Exception.toRes}; anything else becomes an opaque\n\t * {@link Status.INTERNAL_SERVER_ERROR} {@link Res}, so internal failures never\n\t * leak their message. Replace it to customise error output.\n\t *\n\t * @param err - The thrown {@link Error}.\n\t * @returns The {@link Res} to send.\n\t */\n\thandleError: ErrorHandler = (err) => {\n\t\tif (err instanceof Exception) return err.toRes();\n\t\treturn new Res({ message: \"INTERNAL_SERVER_ERROR\" }, { status: Status.INTERNAL_SERVER_ERROR });\n\t};\n\n\t/**\n\t * Default {@link ContextHandler} for unmatched requests. Replace it to\n\t * customise the 404 body.\n\t *\n\t * @param c - The {@link Context} for the unmatched request.\n\t * @returns A {@link Status.NOT_FOUND} {@link Res} naming the method and URL\n\t * that did not resolve.\n\t */\n\thandleNotFound: ContextHandler = (c) => {\n\t\treturn new Res(\n\t\t\t{ message: `${c.req.method} on ${c.req.url} does not exist.` },\n\t\t\t{ status: Status.NOT_FOUND },\n\t\t);\n\t};\n\n\t/**\n\t * Default {@link ContextHandler} for CORS preflight requests. Delegates to\n\t * {@link CorsInterface.handlePreflight} when {@link App.cors} is configured.\n\t *\n\t * @param c - The {@link Context} for the preflight request.\n\t * @returns The CORS preflight response, or an empty\n\t * {@link Status.NO_CONTENT} {@link Res} when no {@link CorsInterface} is set.\n\t */\n\thandlePreflight: ContextHandler = (c) => {\n\t\tif (isAbsent(this.cors)) {\n\t\t\treturn new Res(undefined, { status: Status.NO_CONTENT });\n\t\t}\n\t\treturn this.cors.handlePreflight(c);\n\t};\n\n\t/**\n\t * Default {@link ContextFactory}. Replace it to have the app build a\n\t * {@link Context} subclass carrying your own per-request state.\n\t *\n\t * @param request - The incoming request.\n\t * @param server - The {@link Server} that accepted it.\n\t * @returns A new {@link Context}.\n\t */\n\tcontextFactory: ContextFactory = (request, server) => {\n\t\treturn new Context(request, server);\n\t};\n\n\t/**\n\t * Registers a {@link Middleware} under every {@link RouteBase.id} in\n\t * {@link Middleware.routeIds}, so one instance can serve several routes.\n\t *\n\t * @param middleware - The {@link Middleware} to register.\n\t */\n\taddMiddleware(middleware: Middleware): void {\n\t\tfor (const routeId of middleware.routeIds) {\n\t\t\tconst arr = this.middlewares.get(routeId);\n\t\t\tif (arr) arr.push(middleware);\n\t\t\telse this.middlewares.set(routeId, [middleware]);\n\t\t}\n\t}\n\n\t/**\n\t * Resolves the {@link Middleware} instances that apply to a route, global ones\n\t * first so they wrap the route-specific ones.\n\t *\n\t * @param routeId - The {@link RouteBase.id} to resolve for, or `\"*\"` to get\n\t * only the global middlewares without duplicating them.\n\t * @returns The middlewares in execution order.\n\t */\n\tfindMiddlewares(routeId: string): Array<Middleware> {\n\t\tconst global = routeId === \"*\" ? [] : (this.middlewares.get(\"*\") ?? []);\n\t\tconst local = this.middlewares.get(routeId) ?? [];\n\t\treturn [...global, ...local];\n\t}\n\n\t/**\n\t * Logs a warning for every {@link Middleware} whose target\n\t * {@link RouteBase.id} is not registered on this app and which therefore can\n\t * never run — usually a typo or a route that was never attached.\n\t *\n\t * Runs from {@link App.createServer} rather than {@link App.addMiddleware},\n\t * because registration order is not guaranteed and a middleware may legally be\n\t * added before its route.\n\t */\n\tprotected warnUnmatchedMiddlewares(): void {\n\t\tconst routeIds = new Set(this.routes.map((route) => route.id));\n\t\tfor (const routeId of this.middlewares.keys()) {\n\t\t\tif (routeId === \"*\" || routeIds.has(routeId)) continue;\n\t\t\tlogger.warn(\n\t\t\t\t`Middleware targets route id \"${routeId}\", which is not registered on this app. It will never run.`,\n\t\t\t);\n\t\t}\n\t}\n}\n\nexport { App, type AppInterface };\n",
33
+ "/**\n * Serving for a directory of built front-end files: hashed assets, an entry\n * document, and per-file-class caching.\n *\n * {@link BundleRoute} is the {@link RouteVariant.bundle} member of the\n * {@link RouteBase} family. It serves any directory of servable files, deriving\n * cache headers from what kind of file each one is — immutable for build-hashed\n * assets, revalidated for the entry document, and a configurable fallback for\n * everything else. Its distinguishing behaviour is the entry-document fallback:\n * a path that resolves to no file is answered with the entry HTML instead of a\n * 404, which is what makes a single-page app survive a hard refresh on a client\n * route. That fallback is the main use, not the only one — {@link StaticRoute}\n * remains the plainer choice when a bundle's caching and fallback behaviour is\n * not wanted.\n *\n * ```ts\n * new BundleRoute(\"/*\", \"./dist\");\n * ```\n *\n * @module BundleRoute\n */\n\nimport fs from \"fs\";\nimport path from \"path\";\n\nimport type { ContextHandler } from \"@/Context\";\nimport { Exception } from \"@/Exception\";\nimport {\n\tcreateCacheControlHeader,\n\tcreateContentDispositionHeader,\n\tHeaderKey,\n\ttype CacheControlDefinition,\n} from \"@/Headers\";\nimport { Method } from \"@/Request\";\nimport { Status, type Res } from \"@/Res\";\nimport { RouteBase, RouteVariant, type RouteConfig } from \"@/RouteBase\";\nimport { assert } from \"@/utils/assert\";\nimport { isAbsent, type MaybePromise, isPresent } from \"@/utils/is\";\nimport { tuple, type Tuple } from \"@/utils/tuple\";\nimport { XFile } from \"@/XFile\";\n\n/**\n * A {@link CacheControlDefinition} that also names the path it applies to,\n * relative to {@link BundleRoute.dir}.\n */\ninterface CacheControlDefinitionWithPath extends CacheControlDefinition {\n\t/**\n\t * Path relative to {@link BundleRoute.dir}. Falls back to the corresponding\n\t * {@link DEFAULT_DEFINITION} value when omitted.\n\t */\n\tpath?: string;\n}\n\n/**\n * Describes the layout of a bundle directory and the caching policy for each\n * class of file within it. See {@link DEFAULT_DEFINITION} for the values used\n * when this is not supplied.\n */\ninterface BundleRouteDefinition {\n\t/**\n\t * The entry document — its path within the bundle, and how it should be\n\t * cached. Also the file served by the fallback in\n\t * {@link BundleRoute.resolveFile}.\n\t */\n\tindexHtml: CacheControlDefinitionWithPath;\n\t/**\n\t * The directory holding build-hashed assets, and how they should be cached.\n\t * Files below it are matched by path segment, not by extension.\n\t */\n\tassetsDir: CacheControlDefinitionWithPath;\n\t/**\n\t * Caching for every file that is neither the entry document nor under the\n\t * assets directory. Omit it to send no `Cache-Control` for those files.\n\t */\n\tfallback?: CacheControlDefinition;\n}\n\n/**\n * What a {@link BundleRoute} handler resolves to: a stream for non-HTML files,\n * bytes for HTML, or a string or {@link Res} when\n * {@link BundleRoute.onFileNotFound} is overridden to return one.\n */\ntype BundleRouteRes = ReadableStream<Uint8Array> | Uint8Array | string | Res;\n\n/**\n * Caching defaults tuned for a conventional Vite build. Used whole when no\n * {@link BundleRouteDefinition} is given, and per field when\n * {@link CacheControlDefinitionWithPath.path} is omitted.\n */\nconst DEFAULT_DEFINITION = {\n\t// Vite assets are hashed (index-HASH.js), so they are safe to cache forever.\n\tassetsDir: {\n\t\tpath: \"assets\",\n\t\tpublic: true,\n\t\tmaxAge: 31536000, // 1 year\n\t\timmutable: true,\n\t},\n\t// index.html must be checked every time to see if a new version exists.\n\tindexHtml: {\n\t\tpath: \"index.html\",\n\t\tnoCache: true,\n\t},\n\t// Root files (favicon, robots.txt, manifest.json) usually don't have\n\t// hashes in the filename, so we tell the browser to revalidate them.\n\tfallback: {\n\t\tpublic: true,\n\t\tnoCache: true,\n\t},\n} as const;\n\n/**\n * Serves a directory of built files.\n *\n * Register it on a wildcard endpoint so every path below it reaches the route.\n * A request resolves in three steps: the endpoint prefix is stripped to a\n * sub-path by {@link BundleRoute.resolveSubPath}, the sub-path is joined onto\n * {@link BundleRoute.dir} by {@link BundleRoute.resolveTargetPath}, and\n * {@link BundleRoute.resolveFile} serves the result if it exists. When it does\n * not and the request is not for an HTML file, the entry document named by\n * {@link BundleRoute.indexHtmlPath} is served instead.\n *\n * Files are matched against {@link BundleRoute.definition} to pick their\n * `Cache-Control`: the entry document, anything under\n * {@link BundleRoute.assetsDirPath}, and everything else each get their own\n * policy.\n *\n * Paths that escape {@link BundleRoute.dir} are rejected by\n * {@link BundleRoute.isTraversalAttempt} before the filesystem is touched, so\n * `..` segments cannot reach files outside the directory.\n *\n * @typeParam E - The literal endpoint type, carried so the endpoint stays\n * narrowly typed at the call site.\n */\nclass BundleRoute<E extends string = string> extends RouteBase<\n\tnever,\n\tnever,\n\tnever,\n\tBundleRouteRes,\n\tE\n> {\n\t/**\n\t * Creates a bundle route for subclasses, which declare\n\t * {@link BundleRoute.endpoint} and {@link BundleRoute.dir} as class fields and\n\t * call {@link RouteBase.register} themselves.\n\t */\n\tconstructor();\n\t/**\n\t * Creates a bundle route and registers it on the nearest {@link App}.\n\t *\n\t * @param endpoint - The path to serve the directory under. Use a wildcard so\n\t * nested paths reach the route.\n\t * @param dir - Directory to serve from. Every resolved path is confined to it.\n\t * @param definition - Optional {@link BundleRouteDefinition} overriding the\n\t * bundle layout and caching policy. Defaults to {@link DEFAULT_DEFINITION}.\n\t */\n\tconstructor(endpoint: E, dir: string, definition?: BundleRouteDefinition);\n\tconstructor(endpoint?: E, dir?: string, definition?: BundleRouteDefinition) {\n\t\tsuper();\n\n\t\tif (new.target !== BundleRoute) return;\n\t\tassert.present(endpoint, \"endpoint is required when BundleRoute is constructed directly.\");\n\t\tassert.present(dir, \"dir is required when BundleRoute is constructed directly.\");\n\n\t\tthis.endpoint = endpoint;\n\t\tthis.dir = dir;\n\t\tif (isPresent(definition)) this.definition = definition;\n\t\tthis.register();\n\t}\n\n\t/**\n\t * Directory the files are served from. Every path resolved by the route is\n\t * confined to it.\n\t */\n\tdir!: string;\n\n\t/**\n\t * Sub-paths that are answered with the entry document rather than the file\n\t * they name. A trailing `*` makes a pattern a prefix match; leading slashes\n\t * are optional.\n\t *\n\t * Useful when a client-side route collides with a real file in the directory,\n\t * or to keep a build artefact from being reachable.\n\t */\n\tignore: Array<string> = [];\n\n\t/**\n\t * The bundle's layout and caching policy. Defaults to\n\t * {@link DEFAULT_DEFINITION}.\n\t */\n\tdefinition: BundleRouteDefinition = DEFAULT_DEFINITION;\n\n\t// resolved values\n\t/**\n\t * Path of the entry document within {@link BundleRoute.dir}.\n\t *\n\t * @returns The path from {@link BundleRouteDefinition.indexHtml}, or the\n\t * {@link DEFAULT_DEFINITION} value when it declares none.\n\t */\n\tget indexHtmlPath(): string {\n\t\treturn this.definition.indexHtml.path ?? DEFAULT_DEFINITION.indexHtml.path;\n\t}\n\n\t/**\n\t * Path of the hashed-assets directory within {@link BundleRoute.dir}.\n\t *\n\t * @returns The path from {@link BundleRouteDefinition.assetsDir}, or the\n\t * {@link DEFAULT_DEFINITION} value when it declares none.\n\t */\n\tget assetsDirPath(): string {\n\t\treturn this.definition.assetsDir.path ?? DEFAULT_DEFINITION.assetsDir.path;\n\t}\n\n\t/**\n\t * Lists every file this route can serve, as sub-paths relative to\n\t * {@link BundleRoute.dir}.\n\t *\n\t * Walks {@link BundleRoute.dir} recursively and returns each file's path\n\t * with a leading `/`, matching what {@link BundleRoute.resolveSubPath} would\n\t * produce for a request reaching that file. Useful for generating a sitemap\n\t * or verifying what a deployed bundle actually contains.\n\t *\n\t * @returns The sub-paths of every file under {@link BundleRoute.dir}.\n\t */\n\tgetEndpoints(): Array<string> {\n\t\treturn fs\n\t\t\t.readdirSync(this.dir, { recursive: true, encoding: \"utf8\" })\n\t\t\t.filter((entry) => fs.statSync(path.join(this.dir, entry)).isFile())\n\t\t\t.map((entry) => `/${entry.split(path.sep).join(\"/\")}`);\n\t}\n\n\t/**\n\t * Decides what to serve when a request resolves to no readable file — an HTML\n\t * file that is genuinely missing, a directory with no entry document, or a\n\t * path rejected by {@link BundleRoute.isTraversalAttempt}.\n\t *\n\t * Replace it to serve a custom 404 page or redirect instead of throwing.\n\t *\n\t * @param subPath - The request path with the endpoint prefix stripped, as the\n\t * client asked for it.\n\t * @returns The {@link BundleRouteRes} to send instead.\n\t * @throws {@link Exception} with {@link Status.NOT_FOUND} by default.\n\t */\n\tonFileNotFound: (subPath: string) => MaybePromise<BundleRouteRes> = (subPath) => {\n\t\tthrow new Exception(`${subPath} file was not found.`, Status.NOT_FOUND);\n\t};\n\n\t/**\n\t * Resolves a filesystem path to a readable {@link XFile}, applying the\n\t * entry-document fallback.\n\t *\n\t * A missing non-HTML path falls back to the entry document, which is what\n\t * serves client-side routes. A missing HTML path does not fall back — asking\n\t * for a specific document that does not exist is a real 404 rather than a\n\t * client route.\n\t *\n\t * @param targetPath - Path produced by {@link BundleRoute.resolveTargetPath}.\n\t * @returns The {@link XFile} to serve, or `null` when nothing readable was\n\t * found, which sends the request to {@link BundleRoute.onFileNotFound}.\n\t */\n\tprotected resolveFile(targetPath: string): XFile | null {\n\t\tlet file = new XFile(targetPath);\n\t\tlet exists = file.exists();\n\n\t\tif (!exists && file.extension !== \"html\") {\n\t\t\tconst idxPath = path.join(this.dir, this.indexHtmlPath);\n\t\t\tconst idxFile = new XFile(idxPath);\n\n\t\t\tif (idxFile.exists()) {\n\t\t\t\tfile = idxFile;\n\t\t\t\texists = true;\n\t\t\t}\n\t\t}\n\n\t\treturn exists ? file : null;\n\t}\n\n\t/**\n\t * Strips the route's own prefix from a request pathname, leaving the path\n\t * relative to {@link BundleRoute.dir}.\n\t *\n\t * Both `/*` and `*` endpoint suffixes are handled, and a pathname that does\n\t * not start with the prefix is returned untouched. The result is percent-decoded,\n\t * since {@link URL.pathname} leaves escapes like `%20` intact and filesystem\n\t * paths need the literal characters.\n\t *\n\t * @param pathname - The pathname of the incoming request.\n\t * @returns The bundle-relative sub-path, `\"\"` or `/` for the route root.\n\t */\n\tprotected resolveSubPath(pathname: string): string {\n\t\tconst base = this.endpoint.endsWith(\"/*\")\n\t\t\t? this.endpoint.slice(0, -2)\n\t\t\t: this.endpoint.endsWith(\"*\")\n\t\t\t\t? this.endpoint.slice(0, -1)\n\t\t\t\t: this.endpoint;\n\t\tconst subPath = base && pathname.startsWith(base) ? pathname.slice(base.length) : pathname;\n\n\t\ttry {\n\t\t\treturn decodeURIComponent(subPath);\n\t\t} catch {\n\t\t\treturn subPath;\n\t\t}\n\t}\n\n\t/**\n\t * Joins a sub-path onto {@link BundleRoute.dir} to get the file to read.\n\t *\n\t * The route root maps to the entry document, as does any sub-path matching\n\t * {@link BundleRoute.ignore}. The result is not yet known to be safe —\n\t * {@link BundleRoute.isTraversalAttempt} checks it before it is opened.\n\t *\n\t * @param subPath - The sub-path from {@link BundleRoute.resolveSubPath}.\n\t * @returns The joined filesystem path.\n\t */\n\tprotected resolveTargetPath(subPath: string): string {\n\t\tconst indexHtml = this.indexHtmlPath;\n\t\tconst relFilePath = subPath === \"\" || subPath === \"/\" ? indexHtml : subPath;\n\n\t\tconst isIgnored = this.ignore.some((pattern) => {\n\t\t\tif (pattern.endsWith(\"*\")) {\n\t\t\t\tconst prefix = pattern.slice(0, -1);\n\t\t\t\treturn relFilePath.startsWith(prefix) || relFilePath.startsWith(`/${prefix}`);\n\t\t\t}\n\t\t\treturn relFilePath === pattern || relFilePath === `/${pattern}`;\n\t\t});\n\n\t\treturn path.join(this.dir, isIgnored ? indexHtml : relFilePath);\n\t}\n\n\t/**\n\t * Reports whether a resolved path escapes {@link BundleRoute.dir}.\n\t *\n\t * Both sides are fully resolved before comparison, so `..` segments and\n\t * encoded variants are normalised away rather than matched textually. The\n\t * separator check keeps a sibling directory sharing the root's name prefix\n\t * from passing.\n\t *\n\t * @param targetPath - Path from {@link BundleRoute.resolveTargetPath}.\n\t * @returns `true` when the path lies outside the served directory, in which\n\t * case the request goes to {@link BundleRoute.onFileNotFound} without the file\n\t * being opened.\n\t */\n\tprotected isTraversalAttempt(targetPath: string): boolean {\n\t\tconst root = path.resolve(this.dir);\n\t\tconst resolved = path.resolve(targetPath);\n\t\treturn resolved !== root && !resolved.startsWith(root + path.sep);\n\t}\n\n\t/**\n\t * Produces the response body and headers for a resolved file.\n\t *\n\t * `Cache-Control` is chosen by matching the file against\n\t * {@link BundleRoute.definition}: the entry document first, then anything\n\t * under {@link BundleRoute.assetsDirPath}, then\n\t * {@link BundleRouteDefinition.fallback}. No fallback means no header.\n\t *\n\t * Non-HTML files are streamed and carry an inline\n\t * `Content-Disposition`, so large assets are never buffered. HTML is read into\n\t * memory instead, which lets it carry an exact `Content-Length` — the entry\n\t * document is small and served constantly, so the length is worth more than\n\t * the streaming.\n\t *\n\t * @param file - The {@link XFile} resolved for the request.\n\t * @returns A {@link Tuple} of the body and the headers to set on\n\t * {@link Res.headers}.\n\t */\n\tprotected resolveResponseData(\n\t\tfile: XFile,\n\t): Tuple<ReadableStream | Uint8Array, Record<string, string>> {\n\t\tlet cacheHeader: string = \"\";\n\t\tif (file.fullname === this.indexHtmlPath) {\n\t\t\tcacheHeader = createCacheControlHeader(this.definition.indexHtml);\n\t\t} else if (file.path.includes(`/${this.assetsDirPath}/`)) {\n\t\t\tcacheHeader = createCacheControlHeader(this.definition.assetsDir);\n\t\t} else if (this.definition.fallback) {\n\t\t\tcacheHeader = createCacheControlHeader(this.definition.fallback);\n\t\t}\n\n\t\tif (file.extension !== \"html\") {\n\t\t\tconst stream = file.stream();\n\t\t\treturn tuple(stream, {\n\t\t\t\t[HeaderKey.ContentType]: file.mimeType,\n\t\t\t\t[HeaderKey.CacheControl]: cacheHeader,\n\t\t\t\t[HeaderKey.ContentDisposition]: createContentDispositionHeader({\n\t\t\t\t\tdisposition: \"inline\",\n\t\t\t\t\tfilename: file.fullname,\n\t\t\t\t}),\n\t\t\t});\n\t\t}\n\n\t\tconst bytes = file.bytes();\n\t\treturn tuple(bytes, {\n\t\t\t[HeaderKey.ContentType]: file.mimeType,\n\t\t\t[HeaderKey.CacheControl]: cacheHeader,\n\t\t\t[HeaderKey.ContentLength]: bytes.byteLength.toString(),\n\t\t});\n\t}\n\n\t/** Marks this route as {@link RouteVariant.bundle} for {@link App} route compilation. */\n\toverride readonly variant: RouteVariant = RouteVariant.bundle;\n\n\t/** Bundles answer {@link Method.GET} only. */\n\toverride readonly method: Method = Method.GET;\n\n\t/**\n\t * The path the directory is served under. Use a wildcard so nested paths reach\n\t * the route; {@link BundleRoute.resolveSubPath} strips the wildcard suffix.\n\t */\n\toverride endpoint!: E;\n\n\t/**\n\t * Bundles take no params, search or body, so no {@link RouteConfig} schemas\n\t * apply.\n\t */\n\toverride readonly config?: RouteConfig<never, never, never, BundleRouteRes>;\n\n\t/**\n\t * Serves the file a request resolves to.\n\t *\n\t * Resolves the sub-path and target path, rejects traversal attempts, reads the\n\t * file, then sets the headers from {@link BundleRoute.resolveResponseData} on\n\t * {@link Context.res} and returns the body. Anything that fails to resolve\n\t * goes to {@link BundleRoute.onFileNotFound}.\n\t *\n\t * @param c - The {@link Context} for the request.\n\t * @returns The file body — a stream for non-HTML, bytes for HTML — or whatever\n\t * {@link BundleRoute.onFileNotFound} produced.\n\t */\n\toverride handler: ContextHandler<never, never, never, BundleRouteRes> = (c) => {\n\t\tconst subPath = this.resolveSubPath(c.url.pathname);\n\t\tconst targetPath = this.resolveTargetPath(subPath);\n\n\t\tif (this.isTraversalAttempt(targetPath)) {\n\t\t\treturn this.onFileNotFound(subPath);\n\t\t}\n\n\t\tconst file = this.resolveFile(targetPath);\n\t\tif (isAbsent(file)) return this.onFileNotFound(subPath);\n\n\t\tconst [data, headers] = this.resolveResponseData(file);\n\t\tc.res.headers.setMany(headers);\n\t\treturn data;\n\t};\n}\n\nexport { BundleRoute };\n",
34
+ "/**\n * Serving a single file from a single endpoint.\n *\n * {@link FileRoute} is the {@link RouteVariant.file} member of the\n * {@link RouteBase} family, and the narrowest of the file-serving routes: one\n * endpoint, one file, no path resolution. Reach for {@link BundleRoute} to serve\n * a whole directory, or {@link StaticRoute} when the file's contents feed a\n * handler rather than being sent as-is.\n *\n * ```ts\n * import { FileRoute } from \"@ozanarslan/corpus\";\n *\n * new FileRoute(\"GET /robots.txt\", \"./public/robots.txt\");\n * new FileRoute(\"GET /report\", { filePath: \"./report.pdf\", disposition: \"attachment\" });\n * ```\n *\n * @module FileRoute\n */\n\nimport type { ContextHandler } from \"@/Context\";\nimport { Exception } from \"@/Exception\";\nimport {\n\ttype ContentDispositionDefinition,\n\ttype CacheControlDefinition,\n\tcreateCacheControlHeader,\n\tcreateContentDispositionHeader,\n\tHeaderKey,\n} from \"@/Headers\";\nimport { Method } from \"@/Request\";\nimport { Status } from \"@/Res\";\nimport {\n\tRouteBase,\n\ttype RouteAddress,\n\tresolveRouteAddress,\n\tRouteVariant,\n\ttype RouteConfig,\n} from \"@/RouteBase\";\nimport { assert } from \"@/utils/assert\";\nimport { isPresent } from \"@/utils/is\";\nimport { XFile } from \"@/XFile\";\n\n/**\n * Describes the file a {@link FileRoute} serves and how it is sent. Pass a bare\n * path string instead when the defaults suffice.\n */\ninterface FileRouteDefinition {\n\t/** Path to the file, resolved when the request arrives rather than at registration. */\n\tfilePath: string;\n\t/**\n\t * The `Content-Disposition` to send. Setting it also switches the route to\n\t * streaming — see {@link FileRoute.disposition}.\n\t */\n\tdisposition?: ContentDispositionDefinition[\"disposition\"];\n\t/** Caching policy. Defaults to {@link FileRoute.cache}. */\n\tcache?: CacheControlDefinition;\n}\n\n/**\n * What a {@link FileRoute} handler resolves to: a stream when a disposition is\n * set, bytes otherwise, or a string when {@link FileRoute.onFileNotFound} is\n * overridden to return one.\n */\ntype FileRouteRes = ReadableStream<Uint8Array> | Uint8Array | string;\n\n/**\n * Serves one file at one endpoint.\n *\n * The file is read per request, not at registration, so replacing it on disk\n * takes effect without a restart — and a file that does not exist yet is not an\n * error until someone asks for it.\n *\n * How the body is sent depends on {@link FileRoute.disposition}: with one set,\n * the file is streamed and carries a `Content-Disposition`; without one, it is\n * read into memory and carries an exact `Content-Length`.\n *\n * @typeParam E - The literal endpoint type, carried so the endpoint stays\n * narrowly typed at the call site.\n */\nclass FileRoute<E extends string = string> extends RouteBase<never, never, never, FileRouteRes, E> {\n\t/**\n\t * Creates a file route for subclasses, which declare\n\t * {@link FileRoute.endpoint} and {@link FileRoute.filePath} as class fields\n\t * and call {@link RouteBase.register} themselves.\n\t */\n\tconstructor();\n\t/**\n\t * Creates a file route and registers it on the nearest {@link App}.\n\t *\n\t * @param address - The {@link RouteAddress}: a `\"METHOD /endpoint\"` string or a\n\t * method-and-endpoint pair.\n\t * @param definition - A {@link FileRouteDefinition}, or just the file path when\n\t * the default caching and inline sending are fine.\n\t */\n\tconstructor(address: RouteAddress<E>, definition: FileRouteDefinition | string);\n\tconstructor(address?: RouteAddress<E>, definition?: FileRouteDefinition | string) {\n\t\tsuper();\n\t\tif (new.target !== FileRoute) return;\n\t\tassert.present(address, \"address is required when FileRoute is constructed directly.\");\n\t\tassert.present(definition, \"definition is required when FileRoute is constructed directly.\");\n\t\tconst resolved = resolveRouteAddress(address);\n\t\tthis.endpoint = resolved.endpoint;\n\t\tthis.method = resolved.method;\n\t\tif (typeof definition === \"string\") {\n\t\t\tthis.filePath = definition;\n\t\t} else {\n\t\t\tthis.filePath = definition.filePath;\n\t\t\tif (isPresent(definition.disposition)) this.disposition = definition.disposition;\n\t\t\tif (isPresent(definition.cache)) this.cache = definition.cache;\n\t\t}\n\t\tthis.register();\n\t}\n\n\t/** Path to the file to serve, read fresh on every request. */\n\tfilePath!: string;\n\n\t/**\n\t * The `Content-Disposition` to send — `\"inline\"` to display in the browser,\n\t * `\"attachment\"` to download under the file's own name.\n\t *\n\t * Setting it also switches the route to streaming, so large downloads are\n\t * never buffered. Leaving it unset sends the bytes with a `Content-Length`\n\t * instead, which suits small files a client may want to cache or range over.\n\t */\n\tdisposition?: ContentDispositionDefinition[\"disposition\"];\n\n\t/**\n\t * Caching policy, rendered into `Cache-Control` by\n\t * {@link createCacheControlHeader}. Defaults to one hour of public caching.\n\t */\n\tcache: CacheControlDefinition = {\n\t\tpublic: true,\n\t\tmaxAge: 3600,\n\t\tnoCache: false,\n\t};\n\n\t/**\n\t * Decides what to serve when {@link FileRoute.filePath} does not exist at\n\t * request time. Replace it to serve a placeholder or redirect instead of\n\t * throwing.\n\t *\n\t * @returns The {@link FileRouteRes} to send instead.\n\t * @throws {@link Exception} with {@link Status.NOT_FOUND} by default.\n\t */\n\tonFileNotFound: () => Promise<FileRouteRes> = () => {\n\t\tthrow new Exception(Status.NOT_FOUND.toString(), Status.NOT_FOUND);\n\t};\n\n\t/** Marks this route as {@link RouteVariant.file} for {@link App} route compilation. */\n\toverride readonly variant: RouteVariant = RouteVariant.file;\n\n\t/** The method the file is served on. Taken from the {@link RouteAddress}; defaults to {@link Method.GET}. */\n\toverride method: Method = Method.GET;\n\n\t/** The path the file is served at. */\n\toverride endpoint!: E;\n\n\t/**\n\t * File routes take no params, search or body, so no {@link RouteConfig}\n\t * schemas apply.\n\t */\n\toverride config?: RouteConfig<never, never, never, FileRouteRes> | undefined;\n\n\t/**\n\t * Reads the file and sends it, setting the content type, caching and length or\n\t * disposition headers on {@link Context.res}.\n\t *\n\t * @param c - The {@link Context} for the request.\n\t * @returns The file body — a stream when {@link FileRoute.disposition} is set,\n\t * bytes otherwise — or whatever {@link FileRoute.onFileNotFound} produced.\n\t */\n\toverride handler: ContextHandler<never, never, never, FileRouteRes> = async (c) => {\n\t\tconst file = new XFile(this.filePath);\n\t\tconst exists = file.exists();\n\t\tif (!exists) return await this.onFileNotFound();\n\t\tconst cacheHeader = createCacheControlHeader(this.cache);\n\n\t\tif (isPresent(this.disposition)) {\n\t\t\tconst stream = file.stream();\n\t\t\tc.res.headers.setMany({\n\t\t\t\t[HeaderKey.ContentType]: file.mimeType,\n\t\t\t\t[HeaderKey.CacheControl]: cacheHeader,\n\t\t\t\t[HeaderKey.ContentDisposition]: createContentDispositionHeader({\n\t\t\t\t\tdisposition: this.disposition,\n\t\t\t\t\tfilename: file.fullname,\n\t\t\t\t}),\n\t\t\t});\n\t\t\treturn stream;\n\t\t}\n\n\t\tconst content = file.bytes();\n\t\tc.res.headers.setMany({\n\t\t\t[HeaderKey.ContentType]: file.mimeType,\n\t\t\t[HeaderKey.CacheControl]: cacheHeader,\n\t\t\t[HeaderKey.ContentLength]: content.byteLength.toString(),\n\t\t});\n\t\treturn content;\n\t};\n}\n\nexport { FileRoute };\n",
35
+ "/**\n * The ordinary route: a method, an endpoint, and a handler.\n *\n * {@link Route} is the {@link RouteVariant.dynamic} member of the\n * {@link RouteBase} family and the one most code uses. The others exist for\n * responses the framework can produce itself — files, directories, socket\n * upgrades — while this one just runs your function.\n *\n * ```ts\n * import { Route } from \"@ozanarslan/corpus\";\n *\n * new Route(\"GET /users/:id\", (c) => findUser(c.params.id));\n * ```\n *\n * A {@link RouteConfig} adds schemas for the body, search and params, which both\n * validate the request and type {@link Context}, so `c.params.id` is known to be\n * whatever the schema says it is.\n *\n * @module Route\n */\n\nimport type { ContextHandler } from \"@/Context\";\nimport { Method } from \"@/Request\";\nimport {\n\tresolveRouteAddress,\n\tRouteBase,\n\tRouteVariant,\n\ttype RouteAddress,\n\ttype RouteConfig,\n} from \"@/RouteBase\";\nimport { assert } from \"@/utils/assert\";\n\n/**\n * A route handled by a function.\n *\n * Constructing one registers it on the nearest {@link App}, which compiles it\n * into the server's route map at {@link App.listen} time along with whatever\n * {@link Middleware} targets it.\n *\n * The type parameters are usually inferred from the {@link RouteConfig} rather\n * than written out, and flow into the {@link Context} the handler receives.\n *\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n * @typeParam R - Response body type.\n * @typeParam E - The literal endpoint type, carried so the endpoint stays\n * narrowly typed at the call site.\n */\nclass Route<\n\tB = unknown,\n\tS = unknown,\n\tP = unknown,\n\tR = unknown,\n\tE extends string = string,\n> extends RouteBase<B, S, P, R, E> {\n\t/**\n\t * Creates a route for subclasses, which declare\n\t * {@link Route.method}, {@link Route.endpoint} and {@link Route.handler} as\n\t * class fields and call {@link RouteBase.register} themselves.\n\t */\n\tconstructor();\n\t/**\n\t * Creates a route and registers it on the nearest {@link App}.\n\t *\n\t * @param address - The {@link RouteAddress}: a `\"METHOD /endpoint\"` string or a\n\t * method-and-endpoint pair.\n\t * @param callback - The {@link ContextHandler} that answers the request.\n\t * @param model - Optional {@link RouteConfig} declaring validation schemas and\n\t * per-route limits. Its schemas also type the {@link Context} the handler\n\t * receives.\n\t */\n\tconstructor(\n\t\taddress: RouteAddress<E>,\n\t\tcallback: ContextHandler<B, S, P, R>,\n\t\tmodel?: RouteConfig<B, S, P, R>,\n\t);\n\tconstructor(\n\t\taddress?: RouteAddress<E>,\n\t\tcallback?: ContextHandler<B, S, P, R>,\n\t\tmodel?: RouteConfig<B, S, P, R>,\n\t) {\n\t\tsuper();\n\t\tif (new.target !== Route) return;\n\t\tassert.present(address, \"address is required when Route is constructed directly.\");\n\t\tassert.present(callback, \"callback is required when Route is constructed directly.\");\n\t\tconst addr = resolveRouteAddress(address);\n\t\tthis.method = addr.method;\n\t\tthis.endpoint = addr.endpoint;\n\t\tthis.config = model;\n\t\tthis.handler = callback;\n\t\tthis.register();\n\t}\n\n\t/** Marks this route as {@link RouteVariant.dynamic} for {@link App} route compilation. */\n\treadonly variant: RouteVariant = RouteVariant.dynamic;\n\n\t/** The {@link Method} this route answers, taken from the {@link RouteAddress}. */\n\treadonly method!: Method;\n\n\t/**\n\t * The path this route answers. Supports `:name` parameters and a trailing\n\t * wildcard, both of which arrive in {@link Context.params}.\n\t */\n\treadonly endpoint!: E;\n\n\t/**\n\t * Validation schemas and per-route limits. Absent means nothing is validated —\n\t * and, since {@link getContextAccess} inspects it, absent schemas are one of\n\t * the signals that a surface need not be parsed at all.\n\t */\n\treadonly config?: RouteConfig<B, S, P, R> = undefined;\n\n\t/**\n\t * The function that answers the request.\n\t *\n\t * Return a value to send it as the body, a {@link Res} to control the whole\n\t * response, or nothing after mutating {@link Context.res} directly.\n\t */\n\toverride handler!: ContextHandler<B, S, P, R>;\n}\n\nexport { Route };\n",
36
+ "/**\n * Serving a file read once at startup, optionally through a handler.\n *\n * {@link StaticRoute} is the {@link RouteVariant.static} member of the\n * {@link RouteBase} family. Unlike {@link FileRoute}, which reads its file per\n * request, this one reads at construction and holds the bytes — so the file is\n * served from memory, and changes on disk are not picked up until the process\n * restarts.\n *\n * The optional callback receives the file's contents as text, which is what\n * makes this the route for templating: an HTML shell can have values injected\n * into it before being sent.\n *\n * ```ts\n * import { StaticRoute } from \"@ozanarslan/corpus\";\n *\n * new StaticRoute(\"GET /about\", \"./pages/about.html\");\n * new StaticRoute(\"GET /\", \"./pages/index.html\", (c, html) => html.replace(\"{{title}}\", title));\n * ```\n *\n * @module StaticRoute\n */\n\nimport type { Context, ContextHandler } from \"@/Context\";\nimport { Exception } from \"@/Exception\";\nimport { createCacheControlHeader, HeaderKey, type CacheControlDefinition } from \"@/Headers\";\nimport { Method } from \"@/Request\";\nimport { Status, type Res } from \"@/Res\";\nimport {\n\tresolveRouteAddress,\n\tRouteBase,\n\tRouteVariant,\n\ttype RouteAddress,\n\ttype RouteConfig,\n} from \"@/RouteBase\";\nimport { assert } from \"@/utils/assert\";\nimport { type Nullable, isAbsent, type MaybePromise } from \"@/utils/is\";\nimport { XFile } from \"@/XFile\";\n\n/**\n * Transforms a static file's contents before they are sent.\n *\n * @param context - The {@link Context} for the request.\n * @param content - The file's contents, decoded as UTF-8 text.\n * @returns What to send: a string, raw bytes, or a {@link Res} for full control\n * over the response.\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n */\ntype StaticRouteCallback<B = unknown, S = unknown, P = unknown> = (\n\tcontext: Context<B, S, P, StaticRouteRes>,\n\tcontent: string,\n) => MaybePromise<StaticRouteRes>;\n\n/**\n * What a {@link StaticRoute} handler resolves to: the file's bytes when there is\n * no callback, or whatever the {@link StaticRouteCallback} returned.\n */\ntype StaticRouteRes = Uint8Array | string | Res;\n\n/**\n * A {@link RouteConfig} that also carries a caching policy.\n *\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n */\ninterface StaticRouteConfig<B = unknown, S = unknown, P = unknown> extends RouteConfig<\n\tB,\n\tS,\n\tP,\n\tStaticRouteRes\n> {\n\t/**\n\t * Caching policy for the served file. Defaults to one hour of public caching.\n\t */\n\tcache?: CacheControlDefinition;\n}\n\n/**\n * Serves a file held in memory, optionally passing it through a callback first.\n *\n * The file is read once during construction and kept as bytes, so every request\n * is answered without touching the filesystem. A file that does not exist at\n * that point is not an error — the route registers, and requests reach\n * {@link StaticRoute.onFileNotFound} instead.\n *\n * @typeParam B - Parsed {@link Context.body} type.\n * @typeParam S - Parsed {@link Context.search} type.\n * @typeParam P - Parsed {@link Context.params} type.\n * @typeParam E - The literal endpoint type, carried so the endpoint stays\n * narrowly typed at the call site.\n */\nclass StaticRoute<\n\tB = unknown,\n\tS = unknown,\n\tP = unknown,\n\tE extends string = string,\n> extends RouteBase<B, S, P, StaticRouteRes, E> {\n\t/**\n\t * Creates a static route for subclasses, which declare\n\t * {@link StaticRoute.endpoint} and {@link StaticRoute.file} as class fields and\n\t * call {@link RouteBase.register} themselves.\n\t */\n\tconstructor();\n\t/**\n\t * Creates a static route, reads its file, and registers it on the nearest\n\t * {@link App}.\n\t *\n\t * @param address - The {@link RouteAddress}: a `\"METHOD /endpoint\"` string or a\n\t * method-and-endpoint pair.\n\t * @param filePath - Path to the file, read now rather than per request.\n\t * @param callback - Optional {@link StaticRouteCallback} to transform the\n\t * contents before sending. Omit it to send the file as-is.\n\t * @param config - Optional {@link StaticRouteConfig} with validation schemas\n\t * and a caching policy.\n\t */\n\tconstructor(\n\t\taddress: RouteAddress<E>,\n\t\tfilePath: string,\n\t\tcallback?: StaticRouteCallback<B, S, P>,\n\t\tconfig?: StaticRouteConfig<B, S, P>,\n\t);\n\tconstructor(\n\t\taddress?: RouteAddress<E>,\n\t\tfilePath?: string,\n\t\tcallback?: StaticRouteCallback<B, S, P>,\n\t\tconfig?: StaticRouteConfig<B, S, P>,\n\t) {\n\t\tsuper();\n\t\tif (new.target !== StaticRoute) return;\n\t\tassert.present(address, \"address is required when StaticRoute is constructed directly.\");\n\t\tassert.present(filePath, \"filePath is required when StaticRoute is constructed directly.\");\n\t\tconst resolved = resolveRouteAddress(address);\n\t\tthis.endpoint = resolved.endpoint;\n\t\tthis.method = resolved.method;\n\t\tconst { cache, ...model } = config ?? {};\n\t\tthis.config = model;\n\t\tthis.cacheHeader = createCacheControlHeader(\n\t\t\tcache ?? { public: true, maxAge: 3600, noCache: false },\n\t\t);\n\t\tthis.callback = callback;\n\t\tthis.file = new XFile(filePath);\n\t\tif (this.file.exists()) this.bytes = this.file.bytes();\n\t\tthis.register();\n\t}\n\n\t/**\n\t * The file this route serves. Kept for its mime type and name; the contents\n\t * live in {@link StaticRoute.bytes}.\n\t */\n\tfile: Nullable<XFile> = null;\n\n\t/**\n\t * The file's contents, read at construction. `null` when the file did not\n\t * exist then, which is what sends requests to\n\t * {@link StaticRoute.onFileNotFound}.\n\t */\n\tbytes: Nullable<Uint8Array> = null;\n\n\t/**\n\t * The `Cache-Control` value sent with the file, rendered from the config at\n\t * construction rather than per request.\n\t */\n\tcacheHeader: string = \"\";\n\n\t/**\n\t * Transforms the file's contents before they are sent. Absent means the bytes\n\t * are sent unchanged.\n\t */\n\tcallback?: StaticRouteCallback<B, S, P>;\n\n\t/**\n\t * Decides what to serve when the file was missing at construction. Replace it\n\t * to serve a placeholder or redirect instead of throwing.\n\t *\n\t * @returns The {@link StaticRouteRes} to send instead.\n\t * @throws {@link Exception} with {@link Status.NOT_FOUND} by default.\n\t */\n\tonFileNotFound: () => Promise<StaticRouteRes> = () => {\n\t\tthrow new Exception(Status.NOT_FOUND.toString(), Status.NOT_FOUND);\n\t};\n\n\t/** Marks this route as {@link RouteVariant.static} for {@link App} route compilation. */\n\toverride readonly variant: RouteVariant = RouteVariant.static;\n\n\t/** The method the file is served on. Taken from the {@link RouteAddress}; defaults to {@link Method.GET}. */\n\toverride method: Method = Method.GET;\n\n\t/** The path the file is served at. */\n\toverride endpoint!: E;\n\n\t/**\n\t * Validation schemas for the route. The caching policy is stripped out during\n\t * construction, so what remains is a plain {@link RouteConfig}.\n\t */\n\toverride config?: RouteConfig<B, S, P, StaticRouteRes>;\n\n\t/**\n\t * Sends the file, or the callback's transformation of it.\n\t *\n\t * The content type, caching and length headers are set from the file before\n\t * the callback runs, so a callback that changes the content's length — or\n\t * returns a {@link Res} of its own — should set those headers itself.\n\t *\n\t * @param c - The {@link Context} for the request.\n\t * @returns The file's bytes, or whatever the {@link StaticRoute.callback}\n\t * returned. Falls through to {@link StaticRoute.onFileNotFound} when the file\n\t * was missing at construction.\n\t */\n\toverride handler: ContextHandler<B, S, P, StaticRouteRes> = (c) => {\n\t\tif (isAbsent(this.file)) return this.onFileNotFound();\n\t\tif (isAbsent(this.bytes)) return this.onFileNotFound();\n\t\tc.res.headers.set(HeaderKey.ContentType, this.file.mimeType);\n\t\tc.res.headers.set(HeaderKey.CacheControl, this.cacheHeader);\n\t\tc.res.headers.set(HeaderKey.ContentLength, this.bytes.byteLength.toString());\n\t\tif (isAbsent(this.callback)) return this.bytes;\n\t\tconst decoder = new TextDecoder();\n\t\treturn this.callback(c, decoder.decode(this.bytes));\n\t};\n}\n\nexport { StaticRoute };\n",
37
+ "/**\n * WebSocket endpoints.\n *\n * {@link WebSocketRoute} is the {@link RouteVariant.websocket} member of the\n * {@link RouteBase} family. It is the one route kind that does not produce a\n * response: {@link App.composeRoutes} sees the variant and upgrades the\n * connection instead, storing the route itself as the socket's data so that\n * every later event can be dispatched back to its callbacks.\n *\n * ```ts\n * import { WebSocketRoute } from \"@ozanarslan/corpus\";\n *\n * new WebSocketRoute(\"/chat\", {\n * onOpen: (ws) => ws.subscribe(\"room\"),\n * onMessage: (ws, message) => ws.publish(\"room\", message),\n * });\n * ```\n *\n * Since one route instance backs every connection to that endpoint, per-socket\n * state belongs on the socket — through Bun's `subscribe`/`publish` or a map\n * keyed by socket — not on the route.\n *\n * @module WebSocketRoute\n */\n\nimport type { ContextHandler } from \"@/Context\";\nimport { Method } from \"@/Request\";\nimport { RouteBase, RouteVariant, type RouteConfig } from \"@/RouteBase\";\nimport type { ServerWebSocket } from \"@/Server\";\nimport { assert } from \"@/utils/assert\";\nimport type { MaybePromise } from \"@/utils/is\";\n\n/**\n * Runs once a connection has been upgraded and is ready.\n *\n * @param ws - The newly opened {@link ServerWebSocket}.\n */\ntype WebSocketOnOpen = (ws: ServerWebSocket) => MaybePromise<void>;\n\n/**\n * Runs when a connection closes, whichever side ended it.\n *\n * @param ws - The closing {@link ServerWebSocket}.\n * @param code - The WebSocket close code, when one was sent.\n * @param reason - The accompanying reason, when one was sent.\n */\ntype WebSocketOnClose = (ws: ServerWebSocket, code?: number, reason?: string) => MaybePromise<void>;\n\n/**\n * Runs for each message a client sends.\n *\n * @param ws - The {@link ServerWebSocket} the message arrived on.\n * @param message - The payload: a string for text frames, a `Buffer` for binary\n * ones.\n */\ntype WebSocketOnMessage = (ws: ServerWebSocket, message: string | Buffer) => MaybePromise<void>;\n\n/** The socket lifecycle callbacks a {@link WebSocketRoute} is built from. */\ninterface WebSocketRouteDefinition {\n\t// TODO: onBeforeUpgrade\n\t/** Called once per connection, after the upgrade succeeds. */\n\tonOpen?: WebSocketOnOpen;\n\t/** Called once per connection, when it closes. */\n\tonClose?: WebSocketOnClose;\n\t/** Called for every message received. The only required callback — a socket that never reads has nothing to do. */\n\tonMessage: WebSocketOnMessage;\n}\n\n/**\n * A WebSocket endpoint.\n *\n * Constructing one registers it on the nearest {@link App}, which upgrades\n * matching requests rather than responding to them. The callbacks are shared by\n * every connection to the endpoint, and each event receives the socket it\n * concerns.\n *\n * @typeParam E - The literal endpoint type, carried so the endpoint stays\n * narrowly typed at the call site.\n */\nclass WebSocketRoute<E extends string = string> extends RouteBase<\n\tnever,\n\tnever,\n\tnever,\n\tWebSocketRoute,\n\tE\n> {\n\t/**\n\t * Creates a websocket route for subclasses, which declare\n\t * {@link WebSocketRoute.endpoint} and the callbacks as class fields and call\n\t * {@link RouteBase.register} themselves.\n\t */\n\tconstructor();\n\t/**\n\t * Creates a websocket route and registers it on the nearest {@link App}.\n\t *\n\t * @param endpoint - The path clients connect to.\n\t * @param definition - The {@link WebSocketRouteDefinition} holding the socket\n\t * lifecycle callbacks.\n\t */\n\tconstructor(endpoint: E, definition: WebSocketRouteDefinition);\n\tconstructor(endpoint?: E, definition?: WebSocketRouteDefinition) {\n\t\tsuper();\n\t\tif (new.target !== WebSocketRoute) return;\n\t\tconst msg = \"WebSocketRoute must be constructed with (path, definition) or extended.\";\n\t\tassert.present(endpoint, msg);\n\t\tassert.present(definition, msg);\n\t\tthis.endpoint = endpoint;\n\t\tthis.onOpen = definition.onOpen;\n\t\tthis.onClose = definition.onClose;\n\t\tthis.onMessage = definition.onMessage;\n\t\tthis.register();\n\t}\n\n\t/**\n\t * Marks this route as {@link RouteVariant.websocket}, which is what tells\n\t * {@link App.composeRoutes} to upgrade rather than respond.\n\t */\n\toverride readonly variant: RouteVariant = RouteVariant.websocket;\n\n\t/** Always {@link Method.GET} — an upgrade handshake is a GET request. */\n\toverride readonly method: Method = Method.GET;\n\n\t/** The path clients connect to. */\n\toverride endpoint!: E;\n\n\t/** No schemas apply: an upgrade request carries no body, search or params to validate. */\n\toverride readonly config?: RouteConfig<never, never, never, WebSocketRoute<string>> | undefined =\n\t\tundefined;\n\n\t// TODO: upgrade here\n\t/**\n\t * Returns the route itself, which {@link App.composeRoutes} attaches to the\n\t * socket as its data — that is how a message arriving minutes later finds its\n\t * way back to {@link WebSocketRoute.onMessage}.\n\t *\n\t * The upgrade itself still happens in {@link App}; this handler only supplies\n\t * what the socket carries.\n\t *\n\t * @returns This route.\n\t */\n\toverride readonly handler: ContextHandler<never, never, never, WebSocketRoute<string>> = () =>\n\t\tthis;\n\n\t/** Called once per connection, after the upgrade succeeds. */\n\tonOpen?: WebSocketOnOpen | undefined;\n\n\t/** Called once per connection, when it closes. */\n\tonClose?: WebSocketOnClose | undefined;\n\n\t/** Called for every message received. */\n\tonMessage!: WebSocketOnMessage;\n}\n\nexport {\n\tWebSocketRoute,\n\ttype WebSocketOnMessage,\n\ttype WebSocketOnClose,\n\ttype WebSocketOnOpen,\n\ttype WebSocketRouteDefinition,\n};\n",
38
+ "/**\n * Grouping for routes that share a path prefix and a common preamble.\n *\n * A {@link Controller} is a thin factory: each method constructs the\n * corresponding {@link RouteBase} subclass with the prefix already joined on,\n * records its id, and returns it. The routes are ordinary routes registered on\n * the nearest {@link App} exactly as if they had been constructed directly —\n * a controller adds no dispatch layer of its own.\n *\n * ```ts\n * import { Controller } from \"@ozanarslan/corpus\";\n *\n * const users = new Controller(\"/users\");\n * users.beforeEach = (c) => authenticate(c);\n *\n * users.route(\"GET /:id\", (c) => findUser(c.params.id));\n * ```\n *\n * @module Controller\n */\n\nimport type { ContextHandler } from \"@/Context\";\nimport { resolveRouteAddress } from \"@/RouteBase\";\nimport { BundleRoute } from \"@/RouteBase/BundleRoute\";\nimport { FileRoute } from \"@/RouteBase/FileRoute\";\nimport { Route } from \"@/RouteBase/Route\";\nimport { StaticRoute } from \"@/RouteBase/StaticRoute\";\nimport { WebSocketRoute } from \"@/RouteBase/WebSocketRoute\";\nimport type { Optional } from \"@/utils/is\";\nimport { joinPathSegments, type WithPrefix } from \"@/utils/path\";\n\n/**\n * Registers routes under a shared path prefix.\n *\n * Every method mirrors the constructor of the route class it creates, so moving\n * a route into a controller means changing the call site and nothing else. The\n * prefix is folded into the endpoint at construction time and into the endpoint\n * *type* through {@link WithPrefix}, so the returned route stays as narrowly\n * typed as one written out by hand.\n *\n * {@link Controller.beforeEach} runs ahead of the handler for the route kinds\n * that have a user-supplied handler — {@link Controller.route} and\n * {@link Controller.staticRoute}. The other kinds resolve their responses\n * internally and are unaffected.\n *\n * {@link Controller.routeIds} makes the group addressable afterwards, which is\n * how a {@link Middleware} can be attached to every route in a controller at\n * once.\n *\n * @typeParam Px - The literal prefix type, carried into every endpoint type the\n * controller produces.\n */\nclass Controller<Px extends Optional<string> = Optional<string>> {\n\t/**\n\t * Creates a controller.\n\t *\n\t * @param prefix - Path segment prepended to every endpoint registered through\n\t * this controller. Omit it to group routes without changing their paths — the\n\t * shared {@link Controller.beforeEach} and {@link Controller.routeIds} still\n\t * apply.\n\t */\n\tconstructor(public prefix?: Px) {}\n\n\t/**\n\t * Ids of every {@link RouteBase} registered through this controller, in the\n\t * order they were created.\n\t *\n\t * Pass them to a {@link Middleware} to target the whole group:\n\t *\n\t * ```ts\n\t * new Middleware([...users.routeIds], handler);\n\t * ```\n\t */\n\treadonly routeIds: Set<string> = new Set<string>();\n\n\t/**\n\t * Runs before the handler of every {@link Controller.route} and\n\t * {@link Controller.staticRoute} created by this controller.\n\t *\n\t * Its return value is discarded — this is for side effects on the\n\t * {@link Context}, such as authenticating a request or populating\n\t * {@link Context.data}. Throwing here aborts the request before the handler\n\t * runs, which is the intended way to reject one.\n\t *\n\t * Assign it before registering routes: each route captures the controller, not\n\t * the function, but a route registered while it is unset still checks it at\n\t * request time. For behaviour that must wrap the response as well as precede\n\t * it, use a {@link Middleware} targeting {@link Controller.routeIds} instead.\n\t */\n\tbeforeEach?: ContextHandler;\n\n\t/**\n\t * Registers a dynamic route under this controller. Behaves identically to {@link Route}\n\t * but automatically prepends the controller prefix and runs `beforeEach` before the handler.\n\t *\n\t * @param args - The {@link Route} constructor arguments: the route address\n\t * (a `\"METHOD /endpoint\"` string or a method/endpoint pair), the\n\t * {@link ContextHandler}, and an optional {@link RouteConfig}.\n\t * @returns The registered {@link Route}, its endpoint type prefixed.\n\t * @typeParam B - Parsed {@link Context.body} type.\n\t * @typeParam S - Parsed {@link Context.search} type.\n\t * @typeParam P - Parsed {@link Context.params} type.\n\t * @typeParam R - Response body type.\n\t * @typeParam E - The endpoint literal, before the prefix is applied.\n\t */\n\troute<B = unknown, S = unknown, P = unknown, R = unknown, E extends string = string>(\n\t\t...args: ConstructorParameters<typeof Route<B, S, P, R, E>>\n\t): Route<B, S, P, R, WithPrefix<Px, E>> {\n\t\tconst [address, handler, model] = args;\n\t\tconst resolved = resolveRouteAddress(address);\n\t\tconst method = resolved.method;\n\t\tconst endpoint = joinPathSegments<WithPrefix<Px, E>>(this.prefix, resolved.endpoint);\n\t\tconst route = new Route<B, S, P, R, WithPrefix<Px, E>>(\n\t\t\t{ method, endpoint },\n\t\t\tasync (ctx) => {\n\t\t\t\tawait this.beforeEach?.(ctx);\n\t\t\t\treturn await handler(ctx);\n\t\t\t},\n\t\t\tmodel,\n\t\t);\n\t\tthis.routeIds.add(route.id);\n\t\treturn route;\n\t}\n\n\t/**\n\t * Registers a static route under this controller. Behaves identically to {@link StaticRoute}\n\t * but automatically prepends the controller prefix.\n\t *\n\t * {@link Controller.beforeEach} runs only when a callback is supplied, since\n\t * that is where the controller has a handler to wrap; a static route that just\n\t * serves its file is left alone.\n\t *\n\t * @param args - The {@link StaticRoute} constructor arguments: the route\n\t * address, the file path, an optional callback receiving the\n\t * {@link Context} and the file contents, and an optional\n\t * {@link RouteConfig}.\n\t * @returns The registered {@link StaticRoute}, its endpoint type prefixed.\n\t * @typeParam B - Parsed {@link Context.body} type.\n\t * @typeParam S - Parsed {@link Context.search} type.\n\t * @typeParam P - Parsed {@link Context.params} type.\n\t * @typeParam E - The endpoint literal, before the prefix is applied.\n\t */\n\tstaticRoute<B = unknown, S = unknown, P = unknown, E extends string = string>(\n\t\t...args: ConstructorParameters<typeof StaticRoute<B, S, P, E>>\n\t): StaticRoute<B, S, P, WithPrefix<Px, E>> {\n\t\tconst [address, filePath, callback, model] = args;\n\t\tconst resolved = resolveRouteAddress(address);\n\t\tconst method = resolved.method;\n\t\tconst endpoint = joinPathSegments<WithPrefix<Px, E>>(this.prefix, resolved.endpoint);\n\t\tconst route = new StaticRoute<B, S, P, WithPrefix<Px, E>>(\n\t\t\t{ method, endpoint },\n\t\t\tfilePath,\n\t\t\tcallback === undefined\n\t\t\t\t? undefined\n\t\t\t\t: async (ctx, content) => {\n\t\t\t\t\t\tawait this.beforeEach?.(ctx);\n\t\t\t\t\t\treturn await callback(ctx, content);\n\t\t\t\t\t},\n\t\t\tmodel,\n\t\t);\n\t\tthis.routeIds.add(route.id);\n\t\treturn route;\n\t}\n\n\t/**\n\t * Registers a file route under this controller. Behaves identically to {@link FileRoute}\n\t * but automatically prepends the controller prefix.\n\t *\n\t * {@link Controller.beforeEach} does not apply: a file route resolves its\n\t * response internally and takes no handler to wrap.\n\t *\n\t * @param args - The {@link FileRoute} constructor arguments: the route address\n\t * and the file definition.\n\t * @returns The registered {@link FileRoute}, its endpoint type prefixed.\n\t * @typeParam E - The endpoint literal, before the prefix is applied.\n\t */\n\tfileRoute<E extends string = string>(\n\t\t...args: ConstructorParameters<typeof FileRoute<E>>\n\t): FileRoute<WithPrefix<Px, E>> {\n\t\tconst [address, definition] = args;\n\t\tconst resolved = resolveRouteAddress(address);\n\t\tconst method = resolved.method;\n\t\tconst endpoint = joinPathSegments<WithPrefix<Px, E>>(this.prefix, resolved.endpoint);\n\t\tconst route = new FileRoute<WithPrefix<Px, E>>({ method, endpoint }, definition);\n\t\tthis.routeIds.add(route.id);\n\t\treturn route;\n\t}\n\n\t/**\n\t * Registers a websocket route under this controller. Behaves identically to {@link WebSocketRoute}\n\t * but automatically prepends the controller prefix.\n\t *\n\t * The address is a plain endpoint rather than a method-and-endpoint pair, since\n\t * an upgrade is always a {@link Method.GET}.\n\t * {@link Controller.beforeEach} does not apply — the socket lifecycle\n\t * callbacks are not a {@link ContextHandler}.\n\t *\n\t * @param args - The {@link WebSocketRoute} constructor arguments: the endpoint\n\t * followed by the socket lifecycle callbacks.\n\t * @returns The registered {@link WebSocketRoute}, its endpoint type prefixed.\n\t * @typeParam E - The endpoint literal, before the prefix is applied.\n\t */\n\twebsocketRoute<E extends string = string>(\n\t\t...args: ConstructorParameters<typeof WebSocketRoute<E>>\n\t): WebSocketRoute<WithPrefix<Px, E>> {\n\t\tconst [path, ...rest] = args;\n\t\tconst endpoint = joinPathSegments<WithPrefix<Px, E>>(this.prefix, path);\n\t\tconst route = new WebSocketRoute<WithPrefix<Px, E>>(endpoint, ...rest);\n\t\tthis.routeIds.add(route.id);\n\t\treturn route;\n\t}\n\n\t/**\n\t * Registers a bundle route under this controller. Behaves identically to {@link BundleRoute}\n\t * but automatically prepends the controller prefix.\n\t *\n\t * {@link Controller.beforeEach} does not apply: a bundle route resolves files\n\t * internally and takes no handler to wrap.\n\t *\n\t * @param args - The {@link BundleRoute} constructor arguments: the endpoint,\n\t * the directory to serve, and an optional {@link BundleRouteDefinition}.\n\t * @returns The registered {@link BundleRoute}, its endpoint type prefixed.\n\t * @typeParam E - The endpoint literal, before the prefix is applied.\n\t */\n\tbundleRoute<E extends string = string>(\n\t\t...args: ConstructorParameters<typeof BundleRoute<E>>\n\t): BundleRoute<WithPrefix<Px, E>> {\n\t\tconst [endpoint, dir, definition] = args;\n\t\tconst resolvedEndpoint = joinPathSegments<WithPrefix<Px, E>>(this.prefix, endpoint);\n\t\tconst route = new BundleRoute<WithPrefix<Px, E>>(resolvedEndpoint, dir, definition);\n\t\tthis.routeIds.add(route.id);\n\t\treturn route;\n\t}\n}\n\nexport { Controller };\n",
39
+ "/**\n * Cross-origin resource sharing.\n *\n * Constructing a {@link Cors} attaches it to the nearest {@link App}, which then\n * calls it from two places: {@link App.respond} runs {@link Cors.handler} on\n * every outgoing response, after the {@link Middleware} chain has finished, and\n * {@link App.handlePreflight} delegates to {@link Cors.handlePreflight} for\n * `OPTIONS` requests carrying\n * {@link HeaderKey.AccessControlRequestMethod}. Running last is deliberate — a\n * middleware that short-circuits the chain cannot drop the CORS headers.\n *\n * With no {@link Cors} attached, an app answers preflights with\n * {@link Status.NO_CONTENT} and sends no CORS headers at all.\n *\n * ```ts\n * import { Cors } from \"@ozanarslan/corpus\";\n *\n * new Cors({ allowedOrigins: [\"https://example.com\"], credentials: true });\n * ```\n *\n * @module Cors\n */\n\nimport type { ContextHandler } from \"@/Context\";\nimport { getNearestApp } from \"@/Globals/AppsRegistry\";\nimport { HeaderKey } from \"@/Headers\";\nimport { Res, Status } from \"@/Res\";\nimport { isPresent, isSomeArray } from \"@/utils/is\";\n\n/**\n * The CORS policy. Every field is optional; an omitted field means the\n * corresponding header is not sent, with the exception of the origin, which\n * falls back to a wildcard.\n */\ntype CorsOptions = {\n\t/** Which origins are allowed to access the resource. Use [\"*\"] for any origin, or specific domains. */\n\tallowedOrigins?: string[];\n\t/** Which HTTP methods are allowed (GET, POST, etc.) */\n\tallowedMethods?: string[];\n\t/** Which headers can be sent in the request */\n\tallowedHeaders?: HeaderKey[];\n\t/** Which headers should be exposed to the client/browser JavaScript\n\t * These are response headers that the client can read\n\t * @example ['RateLimit-Limit', 'RateLimit-Remaining', 'X-Custom-Header']\n\t */\n\texposedHeaders?: HeaderKey[];\n\t/** Whether to expose cookies and auth headers to the client */\n\tcredentials?: boolean;\n\t/** How long (in seconds) browsers can cache preflight results. Default: 86400 (24 hours) */\n\tmaxAge?: number;\n\t/**\n\t * Whether {@link HeaderKey.AccessControlMaxAge} is sent on ordinary responses\n\t * as well as on preflights. Preflights always carry it; browsers ignore it\n\t * elsewhere.\n\t */\n\tincludeMaxAgeResponseHeader?: boolean;\n};\n\n/**\n * The public shape of a CORS policy, implemented by {@link Cors}. This is the\n * type {@link App.cors} holds, so a custom policy only has to satisfy the\n * contract.\n */\ninterface CorsInterface {\n\t/** The configured {@link CorsOptions}. */\n\topts?: CorsOptions;\n\t/** Preflight handler for OPTIONS requests. */\n\thandlePreflight: ContextHandler;\n\t/** Applies CORS headers to an outgoing response. */\n\thandler: ContextHandler;\n}\n\n/**\n * Default {@link CorsInterface} implementation.\n *\n * Both entry points share {@link Cors.applyHeaders}, so a preflight and a real\n * response describe the same policy. The preflight builds a fresh\n * {@link Status.NO_CONTENT} {@link Res} — it never reaches a route handler —\n * while the response path writes onto the {@link Res} that is already being\n * returned.\n */\nclass Cors implements CorsInterface {\n\t/**\n\t * Creates a policy and attaches it to the nearest {@link App}.\n\t *\n\t * @param opts - The {@link CorsOptions} to enforce. Omitting them yields a\n\t * permissive wildcard origin with no method, header or exposure restrictions.\n\t */\n\tconstructor(public opts?: CorsOptions) {\n\t\tthis.register();\n\t}\n\n\t/**\n\t * Attaches this policy to the nearest {@link App}, replacing any policy\n\t * already set. Called by the constructor; an app holds exactly one.\n\t */\n\tregister(): void {\n\t\tgetNearestApp().cors = this;\n\t}\n\n\t/**\n\t * Adds the CORS headers to an outgoing response.\n\t *\n\t * Called by {@link App.respond} for every response, after the handler chain\n\t * has produced its result.\n\t *\n\t * @param c - The {@link Context} for the request, read for its `Origin` header\n\t * and written to through {@link Context.res}.\n\t */\n\thandler: ContextHandler = (c) => {\n\t\tthis.applyHeaders(c.res.headers, c.req.headers.get(\"origin\") ?? \"\");\n\t};\n\n\t/**\n\t * Preflight handler for OPTIONS requests.\n\t *\n\t * Answers with an empty {@link Status.NO_CONTENT} response carrying the full\n\t * policy, including {@link HeaderKey.AccessControlMaxAge} so the browser can\n\t * cache the result and skip the round trip on subsequent requests.\n\t *\n\t * @param c - The {@link Context} for the preflight request.\n\t * @returns The preflight {@link Res}.\n\t */\n\thandlePreflight: ContextHandler = (c) => {\n\t\tconst res = new Res(undefined, { status: Status.NO_CONTENT });\n\t\tthis.applyHeaders(res.headers, c.req.headers.get(\"origin\") ?? \"\", true);\n\t\treturn res;\n\t};\n\n\t/**\n\t * Applies CORS headers to a Headers object given the request origin.\n\t *\n\t * Origin resolution has three outcomes. A wildcard policy sends `*`. A policy\n\t * listing origins sends the request's own origin when it is listed, and no\n\t * origin header at all when it is not — an unlisted origin is rejected by\n\t * omission rather than by an error status. A wildcard policy combined with\n\t * `credentials` reflects the request origin instead of `*`, because the spec\n\t * forbids the wildcard in credentialed mode. Whenever the origin is reflected,\n\t * {@link HeaderKey.Vary} is appended so caches key on it.\n\t *\n\t * The remaining list headers are written only when their option is a non-empty\n\t * array, so an unset method or header list leaves the browser's defaults\n\t * alone.\n\t *\n\t * @param headers - The response headers to write into.\n\t * @param reqOrigin - The request's `Origin` header, or an empty string when it\n\t * sent none.\n\t * @param includeMaxAge - Whether to send\n\t * {@link HeaderKey.AccessControlMaxAge}. Set for preflights, where it governs\n\t * how long the browser caches the result. Defaults to `false`.\n\t */\n\tprotected applyHeaders(headers: Headers, reqOrigin: string, includeMaxAge = false): void {\n\t\tconst {\n\t\t\tallowedOrigins,\n\t\t\tallowedMethods,\n\t\t\tallowedHeaders,\n\t\t\texposedHeaders,\n\t\t\tcredentials,\n\t\t\tmaxAge = 86400,\n\t\t} = this.opts ?? {};\n\n\t\tconst isWildcard = !allowedOrigins || allowedOrigins.includes(\"*\");\n\t\tconst originAllowed = !isWildcard && allowedOrigins.includes(reqOrigin);\n\t\t// Credentials mode forbids wildcard origin — reflect actual origin instead\n\t\tconst reflectOrigin = originAllowed || (isWildcard && credentials && reqOrigin);\n\n\t\tif (reflectOrigin) {\n\t\t\theaders.set(HeaderKey.AccessControlAllowOrigin, reqOrigin);\n\t\t\theaders.append(HeaderKey.Vary, \"Origin\");\n\t\t} else if (isWildcard) {\n\t\t\theaders.set(HeaderKey.AccessControlAllowOrigin, \"*\");\n\t\t}\n\n\t\tif (isSomeArray(allowedMethods)) {\n\t\t\theaders.set(HeaderKey.AccessControlAllowMethods, allowedMethods.join(\", \"));\n\t\t}\n\n\t\tif (isSomeArray(allowedHeaders)) {\n\t\t\theaders.set(HeaderKey.AccessControlAllowHeaders, allowedHeaders.join(\", \"));\n\t\t}\n\n\t\tif (isSomeArray(exposedHeaders)) {\n\t\t\theaders.set(HeaderKey.AccessControlExposeHeaders, exposedHeaders.join(\", \"));\n\t\t}\n\n\t\tif (includeMaxAge) {\n\t\t\theaders.set(HeaderKey.AccessControlMaxAge, maxAge);\n\t\t}\n\n\t\theaders.set(\n\t\t\tHeaderKey.AccessControlAllowCredentials,\n\t\t\tisPresent(credentials) ? credentials : false,\n\t\t);\n\t}\n}\n\nexport { Cors, type CorsInterface };\n",
40
+ "/**\n * Logic that runs around a route handler — before it, after it, or instead of\n * it.\n *\n * A {@link Middleware} receives the {@link Context} and a `next` function. What\n * runs before `await next()` happens on the way in; what runs after it happens\n * on the way out, with the downstream result in hand. Returning early instead of\n * calling `next()` short-circuits the chain, which is how authentication rejects\n * a request without the route handler ever running.\n *\n * Constructing one registers it on the nearest {@link App}, which folds it into\n * each targeted route's chain at compile time via {@link composeHandlerChain}.\n * Targeting is by route id, but {@link Middleware.useOn} accepts routes and\n * {@link Controller} instances directly and resolves the ids itself.\n *\n * ```ts\n * import { Middleware } from \"@ozanarslan/corpus\";\n *\n * new Middleware({\n * useOn: usersController,\n * handler: async (c, next) => {\n * const started = performance.now();\n * const result = await next();\n * c.res.headers.set(\"X-Response-Time\", performance.now() - started);\n * return result;\n * },\n * });\n * ```\n *\n * @module Middleware\n */\n\nimport type { Context } from \"@/Context\";\nimport type { Controller } from \"@/Controller\";\nimport { getNearestApp } from \"@/Globals/AppsRegistry\";\nimport type { RouteBase } from \"@/RouteBase\";\nimport { assert } from \"@/utils/assert\";\nimport type { MaybePromise, OrString } from \"@/utils/is\";\n\n/**\n * A middleware's handler function. Like a {@link ContextHandler}, but with the\n * rest of the chain passed in.\n *\n * What the return value means is resolved by {@link composeHandlerChain}: a\n * returned value short-circuits the chain, `undefined` after calling `next()`\n * passes the downstream result through, and `undefined` without calling `next()`\n * lets the chain continue anyway.\n *\n * @param context - The {@link Context} for the request, shared with every other\n * handler in the chain. Use {@link Context.data} to pass state downstream.\n * @param next - Runs the rest of the chain and resolves to its result. Calling\n * it more than once throws.\n * @returns The response value, or `undefined` to defer to the chain.\n * @typeParam R - The result type passed along the chain.\n */\ntype MiddlewareHandler<R = unknown> = (\n\tcontext: Context,\n\tnext: () => MaybePromise<R>,\n) => MaybePromise<R>;\n\n/**\n * What a middleware applies to.\n *\n * A {@link RouteBase} targets that route, a {@link Controller} targets every\n * route registered through it, a string targets a route id directly, and an\n * array combines any of these. The literal `\"*\"` targets every route on the app,\n * including requests that match none — global middlewares also run ahead of\n * {@link App.handleNotFound}.\n */\ntype MiddlewareUseOn =\n\t| Array<RouteBase | Controller | string>\n\t| RouteBase\n\t| Controller\n\t| OrString<\"*\">;\n\n/** Construction arguments for a {@link Middleware}. */\ntype MiddlewareDefinition = {\n\t/** What the middleware applies to. Defaults to `\"*\"` — every route. */\n\tuseOn?: MiddlewareUseOn;\n\t/** The {@link MiddlewareHandler} to run. */\n\thandler: MiddlewareHandler;\n};\n\n/**\n * A handler registered to run around some or all of an app's routes.\n *\n * Middlewares execute in the order {@link App.findMiddlewares} returns them:\n * global ones first, then route-specific ones, then the route handler. Since the\n * chain nests, a global middleware wraps everything after it — its post-`next()`\n * code runs last.\n *\n * Targeting a route id that no route claims is not an error, but the middleware\n * will never run; {@link App.warnUnmatchedMiddlewares} logs a warning at startup\n * when that happens.\n */\nclass Middleware {\n\t/**\n\t * Creates a middleware for subclasses, which declare\n\t * {@link Middleware.handler} and {@link Middleware.useOn} as class fields and\n\t * call {@link Middleware.register} themselves.\n\t */\n\tconstructor();\n\t/**\n\t * Creates a middleware and registers it on the nearest {@link App}.\n\t *\n\t * @param definition - The {@link MiddlewareDefinition}: the handler, and\n\t * optionally what it applies to.\n\t */\n\tconstructor(definition: MiddlewareDefinition);\n\tconstructor(definition?: MiddlewareDefinition) {\n\t\tif (new.target !== Middleware) return;\n\t\tassert.present(definition, \"definition is required when Middleware is constructed directly.\");\n\t\tthis.useOn = definition.useOn ?? \"*\";\n\t\tthis.handler = definition.handler;\n\t\tthis.register();\n\t}\n\n\t/**\n\t * Registers this middleware on the nearest {@link App} through\n\t * {@link App.addMiddleware}. Called by the constructor.\n\t *\n\t * Registration order relative to routes does not matter — targets are resolved\n\t * to ids here, and the app only matches them when it compiles routes.\n\t */\n\tregister(): void {\n\t\tgetNearestApp().addMiddleware(this);\n\t}\n\n\t/**\n\t * What this middleware applies to. Defaults to `\"*\"`, meaning every route on\n\t * the app.\n\t */\n\tuseOn: MiddlewareUseOn = \"*\";\n\n\t/** The function that runs when a targeted route is hit. */\n\thandler!: MiddlewareHandler;\n\n\t/**\n\t * The route ids resolved from {@link Middleware.useOn}, which\n\t * {@link App.addMiddleware} indexes the middleware under.\n\t *\n\t * Each target contributes its ids: a route its own, a {@link Controller} all\n\t * of {@link Controller.routeIds}, and a string itself. Duplicates are\n\t * collapsed, so a route listed both directly and through its controller is\n\t * still wrapped once.\n\t *\n\t * @returns The ids, or `[\"*\"]` for a global middleware.\n\t */\n\tget routeIds(): Array<string> {\n\t\tif (this.useOn === \"*\") return [\"*\"];\n\t\tconst targets = Array.isArray(this.useOn) ? this.useOn : [this.useOn];\n\t\tconst routeIds = new Set<string>();\n\t\tfor (const target of targets) {\n\t\t\tif (typeof target === \"string\") {\n\t\t\t\trouteIds.add(target);\n\t\t\t} else if (\"id\" in target) {\n\t\t\t\trouteIds.add(target.id);\n\t\t\t} else {\n\t\t\t\ttarget.routeIds.forEach((id) => routeIds.add(id));\n\t\t\t}\n\t\t}\n\t\treturn Array.from(routeIds);\n\t}\n}\n\nexport { Middleware, type MiddlewareDefinition, type MiddlewareUseOn, type MiddlewareHandler };\n",
41
+ "/**\n * Request rate limiting, as a ready-made {@link Middleware}.\n *\n * {@link RateLimiter} counts requests per caller within a rolling window and\n * rejects anything over the limit with {@link Status.TOO_MANY_REQUESTS}. Who\n * \"the caller\" is depends on what the request proves about itself: an\n * authenticated request is identified by its token, an unauthenticated one by\n * its IP, and one with neither by a fingerprint of its headers. Each tier gets\n * its own limit, since they differ in how accountable and how forgeable they\n * are.\n *\n * Identifiers are hashed with a rotating salt before being stored, so the\n * limiter never holds a token or an IP in memory.\n *\n * ```ts\n * import { RateLimiter } from \"@ozanarslan/corpus\";\n *\n * new RateLimiter({ windowMs: 60_000, limits: { authenticated: 120, ipBased: 60, fingerprint: 20 } });\n * ```\n *\n * Counting is backed by {@link RateLimiterStoreInterface}; the default\n * {@link RateLimiterMemoryStore} is per-process, so a multi-instance deployment\n * wants a shared store instead.\n *\n * @module RateLimiter\n */\n\nimport { hash, randomBytes } from \"node:crypto\";\n\nimport { Exception } from \"@/Exception\";\nimport { getNearestApp } from \"@/Globals/AppsRegistry\";\nimport { HeaderKey } from \"@/Headers\";\nimport { Middleware, type MiddlewareHandler } from \"@/Middleware\";\nimport { Status } from \"@/Res\";\nimport { RouteVariant } from \"@/RouteBase\";\nimport { isEmpty, type Maybe, type MaybePromise, type OrString } from \"@/utils/is\";\nimport { tuple, type Tuple } from \"@/utils/tuple\";\n\n/** One caller's counter for the current window. */\ninterface RateLimiterEntry {\n\t/** Requests seen in this window, including the one being handled. */\n\thits: number;\n\t/** Unix milliseconds at which the window ends and the count resets. */\n\tresetAt: number;\n}\n\n// Storage interface for pluggable backends\n/**\n * The storage contract for rate limit counters.\n *\n * Implement it to back the limiter with Redis or anything else shared across\n * processes — the default {@link RateLimiterMemoryStore} only counts within one.\n * Every method may be synchronous or asynchronous; the limiter awaits either.\n */\ninterface RateLimiterStoreInterface {\n\t/**\n\t * Reads a caller's entry.\n\t *\n\t * @param id - The hashed caller identifier.\n\t * @returns The entry, or `undefined` when the caller has none.\n\t */\n\tget(id: string): MaybePromise<RateLimiterEntry | undefined>;\n\t/**\n\t * Writes a caller's entry, replacing any existing one.\n\t *\n\t * @param id - The hashed caller identifier.\n\t * @param entry - The counter to store.\n\t */\n\tset(id: string, entry: RateLimiterEntry): MaybePromise<void>;\n\t/**\n\t * Removes a caller's entry.\n\t *\n\t * @param id - The hashed caller identifier.\n\t */\n\tdelete(id: string): MaybePromise<void>;\n\t/**\n\t * Removes every entry whose window has ended.\n\t *\n\t * @param now - Unix milliseconds to compare against\n\t * {@link RateLimiterEntry.resetAt}.\n\t */\n\tcleanup(now: number): MaybePromise<void>;\n\t/** Removes every entry, expired or not. */\n\tclear(): MaybePromise<void>;\n\t/**\n\t * Reports how many entries are held.\n\t *\n\t * @returns The entry count, used to decide when a cleanup is forced.\n\t */\n\tsize(): MaybePromise<number>;\n}\n\n/**\n * In-process counter storage, used when no store is configured.\n *\n * Writes are serialised per identifier through a promise lock, so concurrent\n * requests from the same caller cannot interleave their read-modify-write and\n * lose a hit.\n *\n * State lives in one process's memory, so it is lost on restart and not shared\n * between instances — behind a load balancer, each instance enforces the limit\n * separately. Use a shared {@link RateLimiterStoreInterface} when that matters.\n */\nclass RateLimiterMemoryStore implements RateLimiterStoreInterface {\n\t/** The counters, keyed by hashed identifier. */\n\tprotected readonly map = new Map<string, RateLimiterEntry>();\n\n\t/** In-flight write locks, keyed by identifier. Present only while a write is running. */\n\tprotected readonly locks = new Map<string, Promise<void>>();\n\n\t/**\n\t * Reads a caller's entry.\n\t *\n\t * @param id - The hashed caller identifier.\n\t * @returns The entry, or `undefined` when absent. Expired entries are returned\n\t * as-is; the limiter checks {@link RateLimiterEntry.resetAt} itself.\n\t */\n\tget(id: string): RateLimiterEntry | undefined {\n\t\treturn this.map.get(id);\n\t}\n\n\t/**\n\t * Writes a caller's entry, waiting for any write already in progress for the\n\t * same identifier.\n\t *\n\t * @param id - The hashed caller identifier.\n\t * @param entry - The counter to store.\n\t */\n\tasync set(id: string, entry: RateLimiterEntry): Promise<void> {\n\t\twhile (this.locks.has(id)) {\n\t\t\tawait this.locks.get(id);\n\t\t}\n\n\t\tlet resolveLock: () => void;\n\t\tthis.locks.set(\n\t\t\tid,\n\t\t\tnew Promise((resolve) => {\n\t\t\t\tresolveLock = resolve;\n\t\t\t}),\n\t\t);\n\n\t\ttry {\n\t\t\tthis.map.set(id, entry);\n\t\t} finally {\n\t\t\tthis.locks.delete(id);\n\t\t\tresolveLock!();\n\t\t}\n\t}\n\n\t/**\n\t * Removes a caller's entry.\n\t *\n\t * @param id - The hashed caller identifier.\n\t */\n\tdelete(id: string): void {\n\t\tthis.map.delete(id);\n\t}\n\n\t/**\n\t * Removes every entry whose window has already ended.\n\t *\n\t * @param now - Unix milliseconds to compare against.\n\t */\n\tcleanup(now: number): void {\n\t\tfor (const [id, entry] of this.map) {\n\t\t\tif (entry.resetAt <= now) {\n\t\t\t\tthis.delete(id);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Removes every entry, resetting all counters. */\n\tclear(): void {\n\t\tthis.map.clear();\n\t}\n\n\t/**\n\t * @returns How many entries are currently held.\n\t */\n\tsize(): number {\n\t\treturn this.map.size;\n\t}\n}\n\n/**\n * The limiter's settings. {@link RateLimiter} merges what you pass over\n * {@link defaultConfig}, so every field is optional at the call site.\n */\ntype RateLimiterConfig = {\n\t/** Limits based on identifier type: */\n\tlimits: {\n\t\t/** Authenticated users — higher limit, accountable identity (e.g., 120 requests) */\n\t\tauthenticated: number;\n\t\t/** IP-based — moderate, may be shared (NAT, proxies) (e.g., 60 requests) */\n\t\tipBased: number;\n\t\t/** Fingerprint / anonymous — lowest, least trustworthy (e.g., 20 requests) */\n\t\tfingerprint: number;\n\t};\n\n\t/**\n\t * You can pass a different header key to check for authenticated users.\n\t * \"Bearer \" string is only sliced for Authorization.\n\t * */\n\tauthHeader?: OrString<\"Authorization\">;\n\n\t/** Time window in milliseconds during which the rate limit applies (default: 60,000ms = 1 minute) */\n\twindowMs: number;\n\n\t/**\n\t * How often to rotate the salt used for hashing identifiers (default: 24h)\n\t * Prevents long-term tracking and adds an extra layer of privacy\n\t * */\n\tsaltRotateMs: number;\n\n\t/**\n\t * Probability (0-1) of triggering a cleanup of expired entries on each request\n\t * Balances memory usage against performance (default: 0.005 = 0.5%)\n\t * */\n\tcleanProbability: number;\n\n\t/**\n\t * Maximum number of entries before forcing a cleanup\n\t * Prevents unbounded memory growth (default: 50,000)\n\t * */\n\tmaxStoreSize: number;\n\n\t/**\n\t * Bring your own store implementation like redis.\n\t * Uses {@link RateLimiterMemoryStore} by default.\n\t * */\n\tstore?: RateLimiterStoreInterface;\n\n\t/**\n\t * Customizable HTTP header names for rate limit information.\n\t * Allows integration with different API conventions or frontend expectations.\n\t *\n\t * @example\n\t * // Custom header names (e.g., for legacy systems)\n\t * headerNames: {\n\t * limit: \"X-RateLimit-Limit\",\n\t * remaining: \"X-RateLimit-Remaining\",\n\t * reset: \"X-RateLimit-Reset\",\n\t * retryAfter: \"Retry-After\"\n\t * }\n\t *\n\t * @default Uses standard RateLimit-* headers as defined in IETF draft:\n\t * - limit: \"RateLimit-Limit\"\n\t * - remaining: \"RateLimit-Remaining\"\n\t * - reset: \"RateLimit-Reset\"\n\t * - retryAfter: \"Retry-After\"\n\t */\n\theaderNames: {\n\t\t/** Header name for the maximum allowed requests in the current window */\n\t\tlimit: string;\n\t\t/** Header name for the remaining requests in the current window */\n\t\tremaining: string;\n\t\t/** Header name for the timestamp (Unix seconds) when the window resets */\n\t\treset: string;\n\t\t/** Header name for seconds to wait before retrying when rate limited */\n\t\tretryAfter: string;\n\t};\n};\n\n/**\n * Validates an address before it is used as a rate limit identifier.\n *\n * Proxy headers are client-controlled, so an unvalidated value would let a\n * caller mint a fresh bucket per request by sending garbage. IPv4 is checked\n * digit by digit, including a leading-zero check that rejects the octal-looking\n * forms some parsers accept. IPv6 is delegated to the platform's URL parser\n * rather than matched by regex, since the address grammar is too broad to cover\n * reliably by hand.\n *\n * @param ip - The candidate address, possibly absent.\n * @returns `true` when the value is a well-formed IPv4 or IPv6 address.\n */\nfunction isValidIp(ip: Maybe<string>): ip is string {\n\tif (isEmpty(ip) || ip.length === 0) return false;\n\n\t// IPv4\n\tif (ip.includes(\".\")) {\n\t\tconst parts = ip.split(\".\");\n\t\tif (parts.length !== 4) return false;\n\t\treturn parts.every((p) => {\n\t\t\tif (!/^\\d+$/.test(p)) return false;\n\t\t\tconst n = Number(p);\n\t\t\treturn n >= 0 && n <= 255 && p === String(n); // No leading zeros\n\t\t});\n\t}\n\n\t// IPv6 — delegate to the platform; avoids incomplete regex coverage\n\t// Node / V8 will throw on invalid addresses when used in a URL\n\tif (ip.includes(\":\")) {\n\t\ttry {\n\t\t\tnew URL(`http://[${ip}]`);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\treturn false;\n}\n\n/**\n * Hashes an identifier down to a fixed-length key.\n *\n * @param data - The value to hash — a token, or an address combined with the\n * current salt.\n * @param len - How many hex characters to keep.\n * @returns The truncated SHA-256 digest, which is what gets stored instead of\n * the original value.\n */\nfunction hashData(data: string, len: number): string {\n\treturn hash(\"sha256\", data).slice(0, len);\n}\n\n/**\n * @returns 16 cryptographically random bytes as hex, used as the hashing salt.\n */\nfunction getRandomBytes() {\n\treturn randomBytes(16).toString(\"hex\");\n}\n\n/**\n * Settings applied when a {@link RateLimiterConfig} field is not supplied: a\n * one-minute window, daily salt rotation, and the IETF-draft `RateLimit-*`\n * header names.\n */\nconst defaultConfig: RateLimiterConfig = {\n\twindowMs: 60_000,\n\tsaltRotateMs: 24 * 3600 * 1000, // Daily\n\tcleanProbability: 0.005, // ~0.5% chance per request\n\tmaxStoreSize: 50_000, // Trigger forced cleanup above\n\tlimits: { authenticated: 120, ipBased: 60, fingerprint: 20 },\n\theaderNames: {\n\t\tlimit: \"RateLimit-Limit\",\n\t\tremaining: \"RateLimit-Remaining\",\n\t\treset: \"RateLimit-Reset\",\n\t\tretryAfter: \"Retry-After\",\n\t},\n};\n\n/**\n * A {@link Middleware} that limits how often a caller may make requests.\n *\n * Every response carries the limit, the remaining allowance and the reset time,\n * and those header names are added to\n * {@link HeaderKey.AccessControlExposeHeaders} so browser clients can actually\n * read them. Exceeding the limit throws an {@link Exception} carrying the\n * response built so far, so the rejection keeps its rate limit headers.\n */\nclass RateLimiter extends Middleware {\n\t/**\n\t * Creates a limiter and registers it on the nearest {@link App}.\n\t *\n\t * It targets the routes registered so far, minus\n\t * {@link RouteVariant.bundle} ones — a single page load pulls dozens of\n\t * assets, which would exhaust any sensible limit. Construct it after your\n\t * routes; routes registered later are not covered.\n\t *\n\t * @param config - Overrides for {@link defaultConfig}. Merged shallowly, so\n\t * supplying `limits` or `headerNames` replaces that group whole.\n\t */\n\tconstructor(config: Partial<RateLimiterConfig> = {}) {\n\t\tsuper();\n\t\tthis.config = { ...defaultConfig, ...config };\n\t\tthis.store = this.config.store ?? new RateLimiterMemoryStore();\n\t\tthis.storedSalt = getRandomBytes();\n\t\tthis.saltRotatesAt = Date.now() + this.config.saltRotateMs;\n\t\tthis.useOn = getNearestApp()\n\t\t\t.routes.filter((r) => r.variant !== RouteVariant.bundle)\n\t\t\t.map((r) => r.id);\n\t\tthis.register();\n\t}\n\n\t/**\n\t * Counts the request and either continues the chain or rejects it.\n\t *\n\t * @param c - The {@link Context} for the request.\n\t * @param next - Runs the rest of the chain.\n\t * @throws {@link Exception} with {@link Status.TOO_MANY_REQUESTS} when the\n\t * limit is exceeded. The current {@link Res} is passed as the exception data,\n\t * so the rejection carries the rate limit headers already set on it.\n\t */\n\toverride handler: MiddlewareHandler = async (c, next) => {\n\t\tconst success = await this.getResult(c.req.headers, c.res.headers);\n\t\tif (!success) throw new Exception(\"Too many requests\", Status.TOO_MANY_REQUESTS, c.res);\n\t\tawait next();\n\t};\n\n\t/**\n\t * Counts one request against its caller's allowance and writes the rate limit\n\t * headers.\n\t *\n\t * A window is created on the first request and reused until\n\t * {@link RateLimiterEntry.resetAt} passes, at which point the counter starts\n\t * over — a fixed window, not a sliding one. The hit is counted whether or not\n\t * it is allowed, so a caller that keeps hammering a closed window stays closed\n\t * until it resets.\n\t *\n\t * Exposed separately from {@link RateLimiter.handler} so the same accounting\n\t * can be driven from outside a request chain, in tests or a custom handler.\n\t *\n\t * @param reqHeaders - The request headers, used to identify the caller.\n\t * @param resHeaders - The response headers to write the limit, remaining,\n\t * reset and — when rejected — retry-after values into.\n\t * @returns `true` when the request is within the limit.\n\t */\n\tasync getResult(reqHeaders: Headers, resHeaders: Headers): Promise<boolean> {\n\t\tawait this.maybeCleanStore();\n\n\t\tconst [id, limit] = this.getIdAndLimit(reqHeaders);\n\t\tconst now = Date.now();\n\n\t\t// Atomic read-modify-write operation\n\t\tlet entry = await this.store.get(id);\n\n\t\tif (entry && entry.resetAt > now) {\n\t\t\tentry.hits++;\n\t\t} else {\n\t\t\tentry = { hits: 1, resetAt: now + this.config.windowMs };\n\t\t}\n\n\t\tawait this.store.set(id, entry);\n\n\t\tconst success = entry.hits <= limit;\n\t\tconst remaining = Math.max(0, limit - entry.hits);\n\t\tconst reset = Math.ceil(entry.resetAt / 1000);\n\n\t\tconst keys = this.config.headerNames;\n\n\t\tresHeaders.set(keys.limit, limit.toString());\n\t\tresHeaders.set(keys.remaining, remaining.toString());\n\t\tresHeaders.set(keys.reset, reset.toString());\n\t\tfor (const exposed of Object.values(keys)) {\n\t\t\tresHeaders.append(HeaderKey.AccessControlExposeHeaders, exposed);\n\t\t}\n\n\t\t// Add Retry-After header if rate limited\n\t\tif (!success) {\n\t\t\tconst retryAfter = Math.ceil((entry.resetAt - now) / 1000);\n\t\t\tresHeaders.set(keys.retryAfter, retryAfter.toString());\n\t\t}\n\n\t\treturn success;\n\t}\n\n\t/** The merged settings this limiter runs with. */\n\tprotected readonly config: RateLimiterConfig;\n\n\t/** Where counters are kept. The configured store, or a {@link RateLimiterMemoryStore}. */\n\tprotected readonly store: RateLimiterStoreInterface;\n\n\t/** The current hashing salt. Rotated by {@link RateLimiter.salt}. */\n\tprotected storedSalt: string;\n\n\t/** Unix milliseconds at which {@link RateLimiter.storedSalt} is replaced. */\n\tprotected saltRotatesAt: number;\n\n\t/**\n\t * Identifies the caller and picks the limit that applies to it.\n\t *\n\t * Three tiers are tried in order of how much the request proves about itself.\n\t * A bearer token of plausible length identifies an authenticated caller and\n\t * earns the highest limit. Failing that, a valid address from the proxy\n\t * headers earns the IP limit — shared by everyone behind a NAT, hence lower.\n\t * Failing that, a fingerprint of the user agent and accept headers earns the\n\t * lowest limit, since it is trivially forgeable.\n\t *\n\t * Tokens are hashed without the salt so a caller keeps one bucket across\n\t * rotations; addresses and fingerprints are salted, so they cannot be\n\t * correlated across rotation windows.\n\t *\n\t * The prefixes (`u:`, `i:`, `f:`) keep the tiers in separate namespaces, so a\n\t * collision across tiers is impossible.\n\t *\n\t * @param headers - The request headers.\n\t * @returns A {@link Tuple} of the hashed identifier and the applicable limit.\n\t */\n\tprotected getIdAndLimit(headers: Headers): Tuple<string, number> {\n\t\t// --- Authenticated: hash the JWT token ---\n\t\tconst authHeader = headers.get(this.config.authHeader ?? HeaderKey.Authorization);\n\t\tconst token = authHeader?.includes(\"earer \") ? authHeader.slice(7) : authHeader;\n\t\tif (!isEmpty(token) && token.length >= 20 && token.length <= 2048) {\n\t\t\treturn tuple(`u:${hashData(token, 16)}`, this.config.limits.authenticated);\n\t\t}\n\n\t\t// --- IP-based ---\n\t\tconst ip =\n\t\t\theaders.get(\"cf-connecting-ip\") ??\n\t\t\theaders.get(\"x-real-ip\") ??\n\t\t\theaders.get(\"x-forwarded-for\")?.split(\",\")[0]?.trim();\n\t\tif (isValidIp(ip)) {\n\t\t\treturn tuple(`i:${hashData(ip + this.salt(), 16)}`, this.config.limits.ipBased);\n\t\t}\n\n\t\t// --- Fingerprint fallback ---\n\t\tconst parts = [\n\t\t\theaders.get(\"user-agent\") ?? \"no-ua\",\n\t\t\theaders.get(\"accept-language\") ?? \"no-lang\",\n\t\t\theaders.get(\"accept-encoding\") ?? \"no-enc\",\n\t\t];\n\t\treturn tuple(\n\t\t\t`f:${hashData(parts.join(\"|\") + this.salt(), 16)}`,\n\t\t\tthis.config.limits.fingerprint,\n\t\t);\n\t}\n\n\t/**\n\t * Returns the current hashing salt, rotating it when it has expired.\n\t *\n\t * Rotation is lazy rather than scheduled, so no timer is held open. A rotation\n\t * changes every derived identifier at once, which resets the affected\n\t * counters — acceptable at a daily cadence, and the point: it bounds how long\n\t * any caller can be tracked.\n\t *\n\t * @returns The salt to mix into address and fingerprint hashes.\n\t */\n\tprotected salt(): string {\n\t\tif (Date.now() > this.saltRotatesAt) {\n\t\t\tthis.storedSalt = getRandomBytes();\n\t\t\tthis.saltRotatesAt = Date.now() + this.config.saltRotateMs;\n\t\t}\n\t\treturn this.storedSalt;\n\t}\n\n\t/**\n\t * Decides whether to sweep expired entries before handling a request.\n\t *\n\t * Cleanup runs on a small random fraction of requests, so the cost is spread\n\t * out instead of landing on a timer, and unconditionally once the store passes\n\t * {@link RateLimiterConfig.maxStoreSize}, which bounds memory under a flood of\n\t * one-off callers.\n\t */\n\tprotected async maybeCleanStore(): Promise<void> {\n\t\tconst currentSize = await this.store.size();\n\t\tconst shouldClean =\n\t\t\tMath.random() < this.config.cleanProbability || currentSize > this.config.maxStoreSize;\n\n\t\tif (shouldClean) await this.cleanStore();\n\t}\n\n\t/**\n\t * Removes every entry whose window has ended.\n\t *\n\t * @returns How many entries remain.\n\t */\n\tprotected async cleanStore(): Promise<number> {\n\t\tconst now = Date.now();\n\t\tawait this.store.cleanup(now);\n\n\t\treturn this.store.size();\n\t}\n\n\t/**\n\t * Clears every counter, expired or not, resetting all callers to a full\n\t * allowance. Mainly useful between tests.\n\t */\n\tasync clearStore(): Promise<void> {\n\t\tawait this.store.clear();\n\t}\n\n\t/**\n\t * @returns How many counters are currently held, expired ones included until\n\t * the next cleanup.\n\t */\n\tasync getStoreSize(): Promise<number> {\n\t\treturn this.store.size();\n\t}\n}\n\nexport {\n\tRateLimiter,\n\tRateLimiterMemoryStore,\n\ttype RateLimiterStoreInterface,\n\ttype RateLimiterConfig,\n};\n",
42
+ "import { initialize } from \"@/initialize\";\n\n// Global augments and registries need to be initialized once\ninitialize();\n\n// No namespace exports\nexport * from \"./exports\";\n\n// Namespaced exports are also exported by name\nexport * from \"./C.namespace\";\n\n// Namespaced to C\nexport * as C from \"./C.namespace\";\n\n// Namespaced to Corpus\nexport * as Corpus from \"./C.namespace\";\n"
43
+ ],
44
+ "mappings": "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",
45
+ "debugId": "3549DB2BDA93F8FB64756E2164756E21",
46
+ "names": []
47
+ }