@orkestrel/middleware 0.0.9 → 0.0.11
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.
package/dist/src/core/index.js
CHANGED
|
@@ -908,7 +908,7 @@ function createBoundary(options) {
|
|
|
908
908
|
if (options?.report !== void 0 && !isFunction(options.report)) throw new TypeError("BoundaryOptions.report must be a function when provided");
|
|
909
909
|
const expose = options?.expose ?? false;
|
|
910
910
|
const report = options?.report;
|
|
911
|
-
return async (
|
|
911
|
+
return async (_request, _context, next) => {
|
|
912
912
|
try {
|
|
913
913
|
return await next();
|
|
914
914
|
} catch (error) {
|
|
@@ -938,7 +938,7 @@ function createBoundary(options) {
|
|
|
938
938
|
function createTelemetry(options) {
|
|
939
939
|
if (!isFunction(options.record)) throw new TypeError("TelemetryOptions.record must be a function");
|
|
940
940
|
const record = options.record;
|
|
941
|
-
return async (
|
|
941
|
+
return async (_request, context, next) => {
|
|
942
942
|
const start = Date.now();
|
|
943
943
|
let response;
|
|
944
944
|
try {
|
|
@@ -1114,7 +1114,7 @@ function createDeadline(options) {
|
|
|
1114
1114
|
if (options.status !== void 0 && !isFiniteNumber(options.status)) throw new TypeError("DeadlineOptions.status must be a finite number when provided");
|
|
1115
1115
|
const ms = options.ms;
|
|
1116
1116
|
const status = options.status ?? 503;
|
|
1117
|
-
return async (request,
|
|
1117
|
+
return async (request, _context, next) => {
|
|
1118
1118
|
const timeout = createTimeout({ ms });
|
|
1119
1119
|
const linked = linkSignal(timeout.signal, request.signal);
|
|
1120
1120
|
timeout.start();
|
|
@@ -1272,7 +1272,7 @@ function createLimiter(options) {
|
|
|
1272
1272
|
const policy = options.policy ?? false;
|
|
1273
1273
|
const evict = options.evict;
|
|
1274
1274
|
const buckets = /* @__PURE__ */ new Map();
|
|
1275
|
-
return async (
|
|
1275
|
+
return async (_request, context, next) => {
|
|
1276
1276
|
const key = deriveKey === void 0 ? resolveKey(context.state) : deriveKey(context);
|
|
1277
1277
|
const now = clock();
|
|
1278
1278
|
let bucket = buckets.get(key);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#entries","#ttl","#lifetime","#capacity","#evict","#expired","#notify","#reserve","#table","#is","#ttl","#lifetime"],"sources":["../../../src/core/constants.ts","../../../src/core/Session.ts","../../../src/core/helpers.ts","../../../src/core/shapers.ts","../../../src/core/stores/MemorySessionStore.ts","../../../src/core/stores/DatabaseSessionStore.ts","../../../src/core/middlewares.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { Encoding } from '@orkestrel/server'\n\n// ============================================================================\n// @orkestrel/middleware — battery defaults (AGENTS §5 constants file).\n// Every default named in PROPOSAL.md §4 and the salvaged spec sheet.\n// Frozen where the value is a record/array so a consumer can read but never\n// mutate the shared default.\n// ============================================================================\n\n/** Default minimum buffered body size (bytes) `createCompression` will compress. */\nexport const DEFAULT_COMPRESSION_THRESHOLD = 1024\n\n/**\n * Default content-codings `createCompression` offers, in preference order —\n * intersected at construction with what the runtime's `CompressionStream`\n * actually supports.\n *\n * @remarks\n * The shipped `@orkestrel/server` peer's {@link Encoding} union is\n * `'gzip' | 'deflate' | 'identity'` — it does not include `'br'` (see the\n * deviation recorded against this constant in the build report). This\n * default therefore offers every non-`identity` coding the peer's type\n * admits; a node-face brotli variant, if one ships, extends this list there.\n */\nexport const DEFAULT_COMPRESSION_ENCODINGS: readonly Encoding[] = Object.freeze(['gzip', 'deflate'])\n\n/** Default `X-Frame-Options` value `createSecurity` sets. */\nexport const DEFAULT_FRAME_OPTIONS = 'DENY'\n\n/**\n * Default `Content-Security-Policy` value `createSecurity` sets — a custom\n * `csp` option REPLACES this wholesale, never merges.\n */\nexport const DEFAULT_CSP =\n\t\"default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'\"\n\n/** Default `Referrer-Policy` value `createSecurity` sets. */\nexport const DEFAULT_REFERRER_POLICY = 'strict-origin-when-cross-origin'\n\n/** Default `Permissions-Policy` value `createSecurity` sets. */\nexport const DEFAULT_PERMISSIONS_POLICY = 'camera=(), microphone=(), geolocation=()'\n\n/** Default `Cross-Origin-Opener-Policy` value `createSecurity` sets. */\nexport const DEFAULT_COOP = 'same-origin'\n\n/** Default `Cross-Origin-Resource-Policy` value `createSecurity` sets. */\nexport const DEFAULT_CORP = 'same-origin'\n\n/** Default `Origin-Agent-Cluster` value `createSecurity` sets. */\nexport const DEFAULT_CLUSTER = '?1'\n\n/** Value `createSecurity` sets for `Cross-Origin-Embedder-Policy` when `coep: true`. */\nexport const DEFAULT_COEP = 'require-corp'\n\n/** Value `createSecurity` sets for `Strict-Transport-Security` when `hsts: true`. */\nexport const DEFAULT_HSTS = 'max-age=31536000; includeSubDomains'\n\n/** Default header `createSecurity` mints/echoes a request identifier into. */\nexport const DEFAULT_IDENTIFIER_HEADER = 'x-request-id'\n\n/** Default methods `createCors` advertises on a preflight response. */\nexport const DEFAULT_CORS_METHODS: readonly string[] = Object.freeze([\n\t'GET',\n\t'POST',\n\t'PUT',\n\t'PATCH',\n\t'DELETE',\n\t'OPTIONS',\n])\n\n/** Default headers `createCors` advertises on a preflight response. */\nexport const DEFAULT_CORS_HEADERS: readonly string[] = Object.freeze([\n\t'Content-Type',\n\t'Authorization',\n])\n\n/** Default response status `createDeadline` returns when its deadline fires first. */\nexport const DEFAULT_DEADLINE_STATUS = 503\n\n/** Default header `createBearer` reads the token from. */\nexport const DEFAULT_BEARER_HEADER = 'authorization'\n\n/** Default scheme prefix `createBearer` strips before verification. */\nexport const DEFAULT_BEARER_SCHEME = 'Bearer'\n\n/** Default maximum number of distinct rate-limit keys `createLimiter` tracks before LRU eviction. */\nexport const DEFAULT_LIMITER_CAPACITY = 10_000\n\n/** Default maximum number of distinct session ids `createMemorySessionStore` tracks before LRU (by last write) eviction. */\nexport const DEFAULT_SESSION_CAPACITY = 10_000\n\n/** Default 429 body message `createLimiter` sends when a key is over budget. */\nexport const DEFAULT_LIMITER_MESSAGE = 'rate limit exceeded'\n\n/** Default cookie name `createCookieTransport` writes the signed session id under. */\nexport const DEFAULT_SESSION_COOKIE = 'session'\n\n/** Default header `createHeaderTransport` carries the session id in. */\nexport const DEFAULT_SESSION_HEADER = 'session-id'\n\n/** Default signed-cookie name `createCSRF` writes the CSRF token under. */\nexport const DEFAULT_CSRF_COOKIE = 'csrf'\n\n/** Default header `createCSRF` reads a mutating request's submitted token from. */\nexport const DEFAULT_CSRF_HEADER = 'x-csrf-token'\n\n/** Default body field `createCSRF` falls back to reading a mutating request's submitted token from. */\nexport const DEFAULT_CSRF_FIELD = '_csrf'\n\n/** Default methods `createCSRF` treats as safe (mint instead of verify). */\nexport const DEFAULT_CSRF_SAFE_METHODS: readonly string[] = Object.freeze([\n\t'GET',\n\t'HEAD',\n\t'OPTIONS',\n])\n","import type { SessionInterface } from './types.js'\n\n/**\n * A server-managed session's default entity — the `create` option's default\n * value factory for `createSession` (ruling G: `Session` ships WITHOUT a\n * `createSession` factory of its own, since that name belongs to the\n * battery).\n *\n * @remarks\n * `data` is a live, mutable `Map` a handler reads/writes directly;\n * `createSession` persists it to the configured store on the way out.\n *\n * @example\n * ```ts\n * const session = new Session('abc123')\n * session.data.set('userId', 'u_1')\n * ```\n */\nexport class Session implements SessionInterface {\n\treadonly id: string\n\treadonly data: Map<string, unknown>\n\n\tconstructor(id: string) {\n\t\tthis.id = id\n\t\tthis.data = new Map()\n\t}\n}\n","import type {\n\tBearerState,\n\tClientInfo,\n\tClientState,\n\tConnectionState,\n\tMultipartBody,\n\tMultipartFile,\n\tSessionControlInterface,\n\tSessionInterface,\n} from './types.js'\nimport type { Encoding, MiddlewareContext } from '@orkestrel/server'\nimport { isRecord, isString } from '@orkestrel/contract'\nimport { clientRateKey, isCompressibleType, mergeVary, negotiateEncoding } from '@orkestrel/server'\nimport { Session } from './Session.js'\n\n// ============================================================================\n// @orkestrel/middleware — core pure leaves (AGENTS §5 helpers.ts).\n// Every function here is a self-contained, referentially-transparent\n// computation the battery factories in middlewares.ts compose — key\n// derivation, wire-field builders, the forwarded-header hop walk,\n// compression feature detection, buffering-eligibility predicates, the\n// session data-carry, and the total state-slice guards.\n// ============================================================================\n\n/**\n * Derive `createLimiter`'s default rate-limit bucket key from a request's\n * resolved identity facts.\n *\n * @remarks\n * Prefers a verified bearer token ({@link BearerState.token}) as\n * `token:<value>`; else a resolved client IP ({@link ClientState.client.ip},\n * set when `createForwarded` is mounted) or the raw socket peer\n * ({@link ConnectionState.connection.ip}) collapsed via `clientRateKey`\n * (IPv6 to its `/64` network) as `ip:<key>`; else the literal `ip:unknown`.\n * Never reads `X-Forwarded-For` itself — that trust decision belongs solely\n * to `createForwarded`.\n *\n * @param state - The slices `createLimiter`'s default key may read\n * @returns The bucket key for the request\n *\n * @example\n * ```ts\n * resolveKey({ token: 'abc' }) // 'token:abc'\n * resolveKey({ client: { ip: '2001:db8::1' } }) // 'ip:2001:db8:0:0::/64'\n * ```\n */\nexport function resolveKey(state: BearerState & ClientState & ConnectionState): string {\n\tif (state.token !== undefined) return `token:${state.token}`\n\tconst ip = state.client?.ip ?? state.connection?.ip\n\tif (ip !== undefined) return `ip:${clientRateKey(ip)}`\n\treturn 'ip:unknown'\n}\n\n/**\n * Build the `Retry-After` header value — whole seconds until a window reset,\n * floored at a minimum of `1` (ruling I).\n *\n * @param resetAt - The window reset instant (same clock unit as `now`)\n * @param now - The current instant\n * @returns The `Retry-After` value in whole seconds, minimum `1`\n *\n * @example\n * ```ts\n * buildRetryAfter(1_500, 1_000) // '1'\n * ```\n */\nexport function buildRetryAfter(resetAt: number, now: number): string {\n\tconst seconds = Math.ceil((resetAt - now) / 1000)\n\treturn String(Math.max(1, seconds))\n}\n\n/**\n * Build the draft `RateLimit` structured header field (ruling I) — emitted\n * only when `createLimiter`'s `policy` option is `true`.\n *\n * @param remaining - The requests still admitted this window\n * @param resetAt - The window reset instant (same clock unit as `now`)\n * @param now - The current instant\n * @returns The `RateLimit` header value\n *\n * @example\n * ```ts\n * buildRateLimitField(4, 1_500, 1_000) // '\"default\";r=4;t=1'\n * ```\n */\nexport function buildRateLimitField(remaining: number, resetAt: number, now: number): string {\n\tconst seconds = Math.max(1, Math.ceil((resetAt - now) / 1000))\n\treturn `\"default\";r=${remaining};t=${seconds}`\n}\n\n/**\n * Build the draft `RateLimit-Policy` structured header field (ruling I) —\n * emitted only when `createLimiter`'s `policy` option is `true`.\n *\n * @param max - The window's admitted request count\n * @param window - The window length in milliseconds\n * @returns The `RateLimit-Policy` header value\n *\n * @example\n * ```ts\n * buildRateLimitPolicyField(10, 60_000) // '\"default\";q=10;w=60'\n * ```\n */\nexport function buildRateLimitPolicyField(max: number, window: number): string {\n\treturn `\"default\";q=${max};w=${Math.ceil(window / 1000)}`\n}\n\n/**\n * Whether a candidate address is a bare (non-CIDR) trusted-hop match — an\n * exact string match, or a simple prefix-CIDR match for IPv4 (`/8`–`/32`).\n * An IPv6 entry matches by exact string only — there is no IPv6 CIDR\n * support.\n *\n * @remarks\n * Supports exact addresses and dotted-decimal IPv4 CIDR (`10.0.0.0/8`\n * through `/32`) verbatim. An IPv6 entry is matched by exact string only\n * (no CIDR expansion) — documented as the supported subset; `createForwarded`\n * never claims full CIDR generality beyond IPv4.\n *\n * @remarks\n * An IPv6 `trusted` roster entry is compared as an EXACT string — it must be\n * supplied in canonical form (no zero-compression normalization, no case\n * folding) by the caller; this function performs no IPv6 normalization of\n * its own.\n *\n * @param address - The candidate hop address\n * @param entry - One `trusted` roster entry — an exact address or an IPv4 CIDR\n * @returns `true` when `address` is covered by `entry`\n *\n * @example\n * ```ts\n * matchesTrustedEntry('10.1.2.3', '10.0.0.0/8') // true\n * matchesTrustedEntry('192.168.1.1', '10.0.0.0/8') // false\n * ```\n */\nexport function matchesTrustedEntry(address: string, entry: string): boolean {\n\tconst slash = entry.indexOf('/')\n\tif (slash === -1) return address === entry\n\tconst network = entry.slice(0, slash)\n\tconst bits = Number(entry.slice(slash + 1))\n\tconst networkParts = network.split('.')\n\tconst addressParts = address.split('.')\n\tif (networkParts.length !== 4 || addressParts.length !== 4) return false\n\tif (!Number.isInteger(bits) || bits < 8 || bits > 32) return false\n\tconst networkOctets = networkParts.map(Number)\n\tconst addressOctets = addressParts.map(Number)\n\tif (networkOctets.some((value) => !Number.isInteger(value) || value < 0 || value > 255))\n\t\treturn false\n\tif (addressOctets.some((value) => !Number.isInteger(value) || value < 0 || value > 255))\n\t\treturn false\n\tconst networkInt =\n\t\t((networkOctets[0] ?? 0) << 24) |\n\t\t((networkOctets[1] ?? 0) << 16) |\n\t\t((networkOctets[2] ?? 0) << 8) |\n\t\t(networkOctets[3] ?? 0)\n\tconst addressInt =\n\t\t((addressOctets[0] ?? 0) << 24) |\n\t\t((addressOctets[1] ?? 0) << 16) |\n\t\t((addressOctets[2] ?? 0) << 8) |\n\t\t(addressOctets[3] ?? 0)\n\tconst mask = bits === 32 ? -1 : ~((1 << (32 - bits)) - 1)\n\treturn (networkInt & mask) === (addressInt & mask)\n}\n\n/**\n * Walk `X-Forwarded-For` right-to-left and resolve the first UNTRUSTED hop\n * address — `createForwarded`'s core algorithm.\n *\n * @remarks\n * Parses `X-Forwarded-For` only. With `proxies` set, trusts exactly that\n * many hops counted from the right (the closest to this server) and returns\n * the next one left of them; with `trusted` set, trusts every CONSECUTIVE\n * hop from the right that matches one of the roster\n * ({@link matchesTrustedEntry}) and returns the first hop that does not. If\n * the rightmost hop (the immediate sender) does not match the roster, the\n * whole header is untrustworthy and this returns `undefined` rather than\n * returning any client-supplied hop value. When every hop is trusted, or the\n * header is absent/empty, returns `undefined` (the caller falls back to the\n * socket peer).\n *\n * @param header - The raw `X-Forwarded-For` header value (comma-separated hops), if present\n * @param trust - Either a trusted hop COUNT or a `trusted` CIDR/exact roster\n * @returns The first untrusted hop address, or `undefined` when none qualifies\n *\n * @example\n * ```ts\n * resolveForwardedFor('203.0.113.7, 10.0.0.1', { proxies: 1 }) // '203.0.113.7'\n * ```\n */\nexport function resolveForwardedFor(\n\theader: string | undefined,\n\ttrust: { readonly proxies: number } | { readonly trusted: readonly string[] },\n): string | undefined {\n\tif (header === undefined) return undefined\n\tconst hops = header\n\t\t.split(',')\n\t\t.map((hop) => hop.trim())\n\t\t.filter((hop) => hop.length > 0)\n\tif (hops.length === 0) return undefined\n\tif ('proxies' in trust) {\n\t\tconst index = hops.length - trust.proxies - 1\n\t\treturn index >= 0 ? hops[index] : undefined\n\t}\n\tconst rightmost = hops[hops.length - 1]\n\tif (rightmost === undefined) return undefined\n\tconst rightmostTrusted = trust.trusted.some((entry) => matchesTrustedEntry(rightmost, entry))\n\tif (!rightmostTrusted) return undefined\n\tfor (let index = hops.length - 2; index >= 0; index -= 1) {\n\t\tconst hop = hops[index]\n\t\tif (hop === undefined) return undefined\n\t\tconst trusted = trust.trusted.some((entry) => matchesTrustedEntry(hop, entry))\n\t\tif (!trusted) return hop\n\t}\n\treturn undefined\n}\n\n/**\n * Feature-detect which of `candidates` the runtime's `CompressionStream`\n * actually supports — `createCompression`'s construction-time intersection\n * (ruling J).\n *\n * @remarks\n * Probes each candidate with `new CompressionStream(candidate)` inside a\n * `try`/`catch`; a coding the runtime rejects is dropped silently. `identity`\n * is never probed (it has no `CompressionStream` coding and is always\n * implicitly acceptable to negotiation).\n *\n * @param candidates - The codings to probe, in preference order\n * @returns The subset of `candidates` the runtime's `CompressionStream` supports, in order\n *\n * @example\n * ```ts\n * detectEncodings(['gzip', 'deflate']) // ['gzip', 'deflate'] on a runtime with both\n * ```\n */\nexport function detectEncodings(candidates: readonly Encoding[]): readonly Encoding[] {\n\tconst supported: Encoding[] = []\n\tfor (const candidate of candidates) {\n\t\tif (candidate === 'identity') continue\n\t\ttry {\n\t\t\t// Construction throws when the runtime doesn't support this coding;\n\t\t\t// `readable` is checked only to use the instance, never inspected further.\n\t\t\tconst stream = new CompressionStream(candidate)\n\t\t\tif (stream.readable) supported.push(candidate)\n\t\t} catch {\n\t\t\t// Unsupported coding on this runtime — drop it, never throw.\n\t\t}\n\t}\n\treturn supported\n}\n\n/**\n * Compress bytes with the host-independent `CompressionStream` primitive.\n *\n * @param bytes - The uncompressed response bytes\n * @param encoding - The negotiated actionable coding\n * @returns The compressed bytes, or the original bytes when the platform\n * stream unexpectedly has no readable body\n *\n * @example\n * ```ts\n * const bytes = new TextEncoder().encode('compress me')\n * const compressed = await compressBytes(bytes, 'gzip')\n * ```\n */\nexport async function compressBytes(\n\tbytes: Uint8Array<ArrayBuffer>,\n\tencoding: Exclude<Encoding, 'identity'>,\n): Promise<Uint8Array<ArrayBuffer>> {\n\tconst source = new Response(bytes).body\n\tif (source === null) return bytes\n\tconst compressed = await new Response(\n\t\tsource.pipeThrough(new CompressionStream(encoding)),\n\t).arrayBuffer()\n\treturn new Uint8Array(compressed)\n}\n\n/**\n * Whether a response is eligible for the compression/ETag buffering pipeline\n * (ruling J) — the shared cheap-skip predicate both batteries apply before\n * ever touching `response.arrayBuffer()`.\n *\n * @remarks\n * Skips a `HEAD` request, a `204`/`304` or otherwise bodyless response, an\n * `event-stream` response (SSE — buffering would hang the connection), and a\n * response that already carries the header the caller is about to set\n * (`skipHeader`, e.g. `Content-Encoding` for compression, `ETag` for the\n * ETag battery).\n *\n * @param method - The request's HTTP method\n * @param response - The candidate response\n * @param skipHeader - The response header whose presence means \"already handled\"\n * @returns `true` when the response should be left untouched\n *\n * @example\n * ```ts\n * isBufferingIneligible('GET', new Response(null, { status: 204 }), 'content-encoding') // true\n * ```\n */\nexport function isBufferingIneligible(\n\tmethod: string,\n\tresponse: Response,\n\tskipHeader: string,\n): boolean {\n\tif (method === 'HEAD') return true\n\tif (response.status === 204 || response.status === 304) return true\n\tif (response.body === null) return true\n\tconst contentType = response.headers.get('content-type')\n\tif (contentType !== null && contentType.toLowerCase().startsWith('text/event-stream')) return true\n\tif (response.headers.has(skipHeader)) return true\n\treturn false\n}\n\n/**\n * Whether a negotiated `Accept-Encoding` outcome is worth acting on —\n * `createCompression`'s negotiation-eligibility half of ruling J's skip list.\n *\n * @param encoding - The negotiated coding, or `undefined` when negotiation failed\n * @returns `true` when `encoding` names an actionable, non-`identity` coding\n *\n * @example\n * ```ts\n * isCompressionNegotiated('gzip') // true\n * isCompressionNegotiated(undefined) // false\n * ```\n */\nexport function isCompressionNegotiated(\n\tencoding: Encoding | undefined,\n): encoding is Exclude<Encoding, 'identity'> {\n\treturn encoding !== undefined && encoding !== 'identity'\n}\n\n/**\n * Resolve an opt-in, value-bearing security header — `string | boolean`\n * (default OFF, `true` uses the secure default), the shape `createSecurity`'s\n * `coep`/`hsts` options use, distinct from the plain value-or-`false` shape\n * `resolveSecurityHeader` (the peer substrate) handles.\n *\n * @param value - The option value — a `string` override, `true` for the secure default, or `false`/`undefined` to omit\n * @param fallback - The secure-default value used when `value` is `true`\n * @returns The header value to set, or `undefined` to omit the header\n *\n * @example\n * ```ts\n * resolveOptInHeader(true, 'require-corp') // 'require-corp'\n * resolveOptInHeader(undefined, 'require-corp') // undefined — omitted\n * ```\n */\nexport function resolveOptInHeader(\n\tvalue: string | boolean | undefined,\n\tfallback: string,\n): string | undefined {\n\tif (value === true) return fallback\n\tif (value === false || value === undefined) return undefined\n\treturn value\n}\n\n/**\n * Rebuild a `Response` around a replacement body while preserving its\n * status/statusText — the buffered-response reconstruction shared by the\n * compression and ETag batteries after they have consumed\n * `response.arrayBuffer()`.\n *\n * @param body - The replacement body (already-buffered bytes, or `null`)\n * @param response - The original response whose status/statusText/headers are preserved\n * @param headers - The headers to apply; defaults to a fresh copy of `response.headers`\n * @returns A new `Response` carrying `body` with `response`'s status/statusText\n *\n * @example\n * ```ts\n * rebuildResponse(bytes, response) // same status/statusText, response's headers copied\n * rebuildResponse(bytes, response, headers) // explicit replacement headers\n * ```\n */\nexport function rebuildResponse(\n\tbody: ConstructorParameters<typeof Response>[0],\n\tresponse: Response,\n\theaders?: ConstructorParameters<typeof Headers>[0],\n): Response {\n\treturn new Response(body, {\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\theaders: headers ?? new Headers(response.headers),\n\t})\n}\n\n/**\n * The shared negotiate → skip → threshold → compress → header-set skeleton\n * both faces' `createCompression` batteries compose — response-body\n * compression over a caller-supplied set of feature-detected codings.\n *\n * @remarks\n * Decision order: {@link isBufferingIneligible} → `options.filter` → stamp\n * `Vary: Accept-Encoding` (every negotiation-eligible response carries it,\n * even when a later skip declines to compress) → `negotiateEncoding` over\n * `options.encodings` → {@link isCompressionNegotiated} → `isCompressibleType`\n * on `Content-Type` → a fast skip when the response already carries a\n * numeric `Content-Length` BELOW `options.threshold` (avoids buffering a\n * body known too small to be worth compressing) → buffer via\n * `response.arrayBuffer()` → a threshold passthrough when the buffered size\n * is still below `options.threshold` → `options.compress` → set\n * `Content-Encoding` and a fresh `Content-Length` via {@link rebuildResponse}.\n * Returns `response` unchanged (aside from the `Vary` stamp) on any skip.\n *\n * @param request - The inbound `Request` (read for `Accept-Encoding`)\n * @param context - The `MiddlewareContext` (read for `context.method`)\n * @param response - The downstream `Response` to consider compressing\n * @param options - The threshold, optional filter, offered encodings, and the runtime's `compress` primitive\n * @returns The original `response` when skipped, or a new compressed `Response`\n *\n * @example\n * ```ts\n * await compressResponse(request, context, response, {\n * \tthreshold: 1024,\n * \tencodings: ['gzip'],\n * \tcompress: async (bytes, encoding) => gzip(bytes),\n * })\n * ```\n */\nexport async function compressResponse(\n\trequest: Request,\n\tcontext: MiddlewareContext<unknown>,\n\tresponse: Response,\n\toptions: {\n\t\treadonly threshold: number\n\t\treadonly filter?: (request: Request, response: Response) => boolean\n\t\treadonly encodings: readonly Encoding[]\n\t\treadonly compress: (\n\t\t\tbytes: Uint8Array<ArrayBuffer>,\n\t\t\tencoding: Exclude<Encoding, 'identity'>,\n\t\t) => Promise<Uint8Array<ArrayBuffer>>\n\t},\n): Promise<Response> {\n\tif (isBufferingIneligible(context.method, response, 'content-encoding')) return response\n\tif (options.filter !== undefined && !options.filter(request, response)) return response\n\tresponse.headers.set(\n\t\t'vary',\n\t\tmergeVary(response.headers.get('vary') ?? undefined, 'Accept-Encoding'),\n\t)\n\tconst acceptEncoding = request.headers.get('accept-encoding')\n\tif (acceptEncoding === null) return response\n\tconst negotiated = negotiateEncoding(acceptEncoding, options.encodings)\n\tif (!isCompressionNegotiated(negotiated)) return response\n\tconst contentType = response.headers.get('content-type')\n\tif (contentType === null || !isCompressibleType(contentType)) return response\n\tconst declaredLength = response.headers.get('content-length')\n\tif (declaredLength !== null) {\n\t\tconst declared = Number(declaredLength)\n\t\tif (Number.isFinite(declared) && declared < options.threshold) return response\n\t}\n\tconst buffer = await response.arrayBuffer()\n\tif (buffer.byteLength < options.threshold) return rebuildResponse(buffer, response)\n\tconst compressed = await options.compress(new Uint8Array(buffer), negotiated)\n\tconst headers = new Headers(response.headers)\n\theaders.set('content-encoding', negotiated)\n\theaders.set('content-length', String(compressed.byteLength))\n\treturn rebuildResponse(compressed, response, headers)\n}\n\n/**\n * Copy every entry of one session's `data` into another — the regenerate\n * data-carry `createSession`'s `control.regenerate()` applies (ruling D).\n *\n * @param from - The source session whose `data` is copied\n * @param to - The destination session `data` is copied into\n *\n * @example\n * ```ts\n * transferSessionData(oldSession, newSession)\n * ```\n */\nexport function transferSessionData(from: SessionInterface, to: SessionInterface): void {\n\tfor (const [key, value] of from.data) to.data.set(key, value)\n}\n\n/**\n * Determine whether a value implements {@link SessionInterface} — a total\n * structural guard (§14): an `id` string plus a `data` `Map`. Prototype-agnostic\n * — accepts a plain object, a null-prototype object, AND a class instance\n * (a real `Session`), since a restored/stored session is routinely a class\n * instance, not a literal.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link SessionInterface}\n *\n * @example\n * ```ts\n * isSession({ id: 'a', data: new Map() }) // true\n * isSession(new Session('a')) // true\n * ```\n */\nexport function isSession(value: unknown): value is SessionInterface {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst id: unknown = Reflect.get(value, 'id')\n\tconst data: unknown = Reflect.get(value, 'data')\n\treturn isString(id) && data instanceof Map\n}\n\n/**\n * Determine whether a value implements {@link SessionControlInterface} — a\n * total structural guard (§14): callable `regenerate` and `destroy`.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link SessionControlInterface}\n *\n * @example\n * ```ts\n * isSessionControl({ regenerate() {}, destroy() {} }) // true\n * ```\n */\nexport function isSessionControl(value: unknown): value is SessionControlInterface {\n\tif (!isRecord(value)) return false\n\treturn typeof value.regenerate === 'function' && typeof value.destroy === 'function'\n}\n\n/**\n * Determine whether a value is one staged {@link MultipartFile} record — a\n * total structural guard (§14) checking every required field's shape.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link MultipartFile}\n */\nexport function isMultipartFile(value: unknown): value is MultipartFile {\n\tif (!isRecord(value)) return false\n\treturn (\n\t\tisString(value.field) &&\n\t\tisString(value.name) &&\n\t\ttypeof value.size === 'number' &&\n\t\tisString(value.mime) &&\n\t\ttypeof value.validated === 'boolean' &&\n\t\tisString(value.status) &&\n\t\tisString(value.path)\n\t)\n}\n\n/**\n * Determine whether a value implements {@link MultipartBody} — a total\n * structural guard (§14): `files` keyed by field name to arrays of\n * {@link MultipartFile}, and a `fields` string record.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link MultipartBody}\n *\n * @example\n * ```ts\n * isMultipartBody({ files: {}, fields: { name: 'a' } }) // true\n * ```\n */\nexport function isMultipartBody(value: unknown): value is MultipartBody {\n\tif (!isRecord(value)) return false\n\tif (!isRecord(value.files) || !isRecord(value.fields)) return false\n\tfor (const entries of Object.values(value.files)) {\n\t\tif (!Array.isArray(entries)) return false\n\t\tfor (const entry of entries) if (!isMultipartFile(entry)) return false\n\t}\n\tfor (const fieldValue of Object.values(value.fields)) if (!isString(fieldValue)) return false\n\treturn true\n}\n\n/**\n * Determine whether a request is a CORS PREFLIGHT — an `OPTIONS` request\n * carrying an `Access-Control-Request-Method` header.\n *\n * @param method - The request's HTTP method\n * @param headers - The request's `Headers`\n * @returns `true` when the request is a CORS preflight `createCors` must answer\n *\n * @example\n * ```ts\n * isPreflight('OPTIONS', new Headers({ 'access-control-request-method': 'POST' })) // true\n * ```\n */\nexport function isPreflight(method: string, headers: Headers): boolean {\n\treturn method === 'OPTIONS' && headers.has('access-control-request-method')\n}\n\n/**\n * A parsed client-info fact for {@link ClientInfo} — a leaf shaping helper\n * `createForwarded` uses to build its stashed slice.\n *\n * @param ip - The resolved client IP, if any\n * @returns The {@link ClientInfo} slice value\n *\n * @example\n * ```ts\n * buildClientInfo('203.0.113.7') // { ip: '203.0.113.7' }\n * ```\n */\nexport function buildClientInfo(ip: string | undefined): ClientInfo {\n\treturn { ...(ip !== undefined ? { ip } : {}) }\n}\n\n/**\n * Constant-time string equality — `createCSRF`'s double-submit token\n * comparison, avoiding a timing oracle on the submitted-vs-cookie match.\n *\n * @remarks\n * Length-guarded XOR-accumulate over char codes: a length mismatch short\n * circuits (safe — the lengths of two independently-generated tokens are\n * not a useful timing signal), but once lengths match every character is\n * compared with no early return, so a per-character mismatch never affects\n * how long the comparison takes.\n *\n * @param a - The first string\n * @param b - The second string\n * @returns `true` when `a` and `b` are exactly equal\n *\n * @example\n * ```ts\n * equalsConstantTime('abc', 'abc') // true\n * equalsConstantTime('abc', 'abd') // false\n * ```\n */\nexport function equalsConstantTime(a: string, b: string): boolean {\n\tif (a.length !== b.length) return false\n\tlet diff = 0\n\tfor (let index = 0; index < a.length; index += 1)\n\t\tdiff |= a.charCodeAt(index) ^ b.charCodeAt(index)\n\treturn diff === 0\n}\n\n/**\n * Whether a session has aged past its idle timeout or absolute lifetime as\n * of `now` — the pure expiry predicate `MemorySessionStore` delegates to.\n *\n * @param cursors - The session's `lastSeen` (idle) and `createdAt` (absolute) instants\n * @param now - The current instant (same clock unit as `cursors`)\n * @param limits - The optional `ttl` (idle) and `lifetime` (absolute) thresholds\n * @returns `true` when either configured threshold has elapsed\n *\n * @example\n * ```ts\n * sessionExpired({ lastSeen: 0, createdAt: 0 }, 1_000, { ttl: 500 }) // true\n * ```\n */\nexport function sessionExpired(\n\tcursors: { readonly lastSeen: number; readonly createdAt: number },\n\tnow: number,\n\tlimits: { readonly ttl?: number; readonly lifetime?: number },\n): boolean {\n\tif (limits.ttl !== undefined && now - cursors.lastSeen >= limits.ttl) return true\n\tif (limits.lifetime !== undefined && now - cursors.createdAt >= limits.lifetime) return true\n\treturn false\n}\n\n/**\n * Snapshot a session's `data` Map into a plain, serializable record — the\n * projection a durable store's `set` writes to disk.\n *\n * @param session - The session to snapshot\n * @returns A plain-object copy of `session.data`, keyed alongside `session.id`\n *\n * @remarks\n * `data` is built on a null-prototype object (`Object.create(null)`), never\n * a `{}` literal — a session key literally named `__proto__` must round-trip\n * as an OWN enumerable property instead of hitting `Object.prototype`'s\n * `__proto__` accessor (which would silently drop the entry and risk\n * polluting the shared prototype).\n *\n * @example\n * ```ts\n * snapshotSession(session) // { id: 'abc', data: { userId: 'u_1' } }\n * ```\n */\nexport function snapshotSession(session: SessionInterface): {\n\treadonly id: string\n\treadonly data: Record<string, unknown>\n} {\n\tconst data: Record<string, unknown> = Object.create(null)\n\tfor (const [key, value] of session.data) data[key] = value\n\treturn { id: session.id, data }\n}\n\n/**\n * Rebuild a `Session` from an untrusted snapshot value (the inverse of\n * {@link snapshotSession}) — a durable store's `get` deserialization step.\n *\n * @param value - The candidate snapshot, of unknown shape\n * @returns A rebuilt `Session`, or `undefined` when `value` is malformed\n *\n * @example\n * ```ts\n * restoreSession({ id: 'abc', data: { userId: 'u_1' } }) // Session { id: 'abc', data: Map }\n * restoreSession({ id: 1 }) // undefined\n * ```\n */\nexport function restoreSession(value: unknown): Session | undefined {\n\tif (!isRecord(value)) return undefined\n\tif (!isString(value.id) || !isRecord(value.data)) return undefined\n\tconst session = new Session(value.id)\n\tfor (const [key, entry] of Object.entries(value.data)) session.data.set(key, entry)\n\treturn session\n}\n","import { integerShape, jsonShape, stringShape } from '@orkestrel/contract'\n\n/**\n * The `@orkestrel/database` column shape for a\n * {@link import('./types.js').SessionRow} table — pass as-is to\n * `createDatabase({ tables: { sessions: sessionColumns } })` so an app\n * declaring a durable session table never hand-writes the shape.\n *\n * @remarks\n * `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table validates\n * them as integers, so\n * {@link import('./stores/DatabaseSessionStore.js').DatabaseSessionStore}'s\n * `now` clock must yield integer milliseconds (`Date.now()`, the implicit\n * default `createSession` clock). A fractional clock (`performance.now()`)\n * fails the write with a validation error; `MemorySessionStore` carries no\n * such column shape and accepts a fractional clock without complaint.\n *\n * @example\n * ```ts\n * const db = createDatabase({ driver, tables: { sessions: sessionColumns } })\n * ```\n */\nexport const sessionColumns = {\n\tid: stringShape(),\n\tsession: jsonShape(),\n\tlastSeen: integerShape({ min: 0 }),\n\tcreatedAt: integerShape({ min: 0 }),\n}\n","import type { MemorySessionStoreOptions, SessionStoreInterface } from '../types.js'\nimport { DEFAULT_SESSION_CAPACITY } from '../constants.js'\nimport { sessionExpired } from '../helpers.js'\nimport { isFiniteNumber, isFunction } from '@orkestrel/contract'\n\n/**\n * The default in-process {@link SessionStoreInterface} — a `Map`-backed store\n * enforcing both an idle timeout and an absolute lifetime, with lazy\n * (read-time) eviction, a bounded capacity, and no background timers.\n *\n * @typeParam S - The session data payload type\n *\n * @remarks\n * `get` evicts a session whose idle time (`now - lastSeen >= ttl`) or\n * absolute lifetime (`now - createdAt >= lifetime`) has elapsed — the\n * lifetime check fires EVEN IF the session was continuously touched, since\n * `createdAt` is stamped once at the first `set` and preserved across every\n * later re-`set` of the same id. A live read touches `lastSeen`. `delete` of\n * an absent id is a no-op.\n *\n * Capacity is enforced as least-recently-used **by last write**: `set`\n * refreshes an id's recency (deleting then re-inserting so the Map's\n * iteration tail is the most-recently-written id). Inserting a brand-new id\n * once the store is at `capacity` first prunes expired entries; if the store\n * is still full, the least-recently-written id (the Map's head) is evicted.\n * `options.evict` — when provided — is invoked (throw-isolated) with the id\n * on every eviction the store's own policy performs (a capacity eviction or\n * an expired-entry prune), but never for an explicit `delete`.\n *\n * @example\n * ```ts\n * const store = new MemorySessionStore({ ttl: 60_000, lifetime: 3_600_000 })\n * await store.set('abc', { userId: 'u_1' }, Date.now())\n * ```\n */\nexport class MemorySessionStore<S> implements SessionStoreInterface<S> {\n\treadonly #entries: Map<string, { session: S; lastSeen: number; readonly createdAt: number }>\n\treadonly #ttl: number | undefined\n\treadonly #lifetime: number | undefined\n\treadonly #capacity: number\n\treadonly #evict: ((id: string) => void) | undefined\n\n\tconstructor(options?: MemorySessionStoreOptions) {\n\t\tif (options?.ttl !== undefined && !isFiniteNumber(options.ttl))\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.ttl to be a finite number when provided',\n\t\t\t)\n\t\tif (options?.ttl !== undefined && options.ttl <= 0)\n\t\t\tthrow new TypeError('MemorySessionStore requires options.ttl to be positive when provided')\n\t\tif (options?.lifetime !== undefined && !isFiniteNumber(options.lifetime))\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.lifetime to be a finite number when provided',\n\t\t\t)\n\t\tif (options?.lifetime !== undefined && options.lifetime <= 0)\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.lifetime to be positive when provided',\n\t\t\t)\n\t\tif (\n\t\t\toptions?.capacity !== undefined &&\n\t\t\t(!isFiniteNumber(options.capacity) ||\n\t\t\t\t!Number.isInteger(options.capacity) ||\n\t\t\t\toptions.capacity <= 0)\n\t\t)\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.capacity to be a positive integer when provided',\n\t\t\t)\n\t\tif (options?.evict !== undefined && !isFunction(options.evict))\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.evict to be a function when provided',\n\t\t\t)\n\t\tthis.#entries = new Map()\n\t\tthis.#ttl = options?.ttl\n\t\tthis.#lifetime = options?.lifetime\n\t\tthis.#capacity = options?.capacity ?? DEFAULT_SESSION_CAPACITY\n\t\tthis.#evict = options?.evict\n\t}\n\n\tasync get(id: string, now: number): Promise<S | undefined> {\n\t\tconst entry = this.#entries.get(id)\n\t\tif (entry === undefined) return undefined\n\t\tif (this.#expired(entry, now)) {\n\t\t\tthis.#entries.delete(id)\n\t\t\tthis.#notify(id)\n\t\t\treturn undefined\n\t\t}\n\t\tentry.lastSeen = now\n\t\treturn entry.session\n\t}\n\n\tasync set(id: string, session: S, now: number): Promise<void> {\n\t\tconst existing = this.#entries.get(id)\n\t\tif (existing === undefined) this.#reserve(now)\n\t\tconst createdAt = existing?.createdAt ?? now\n\t\tthis.#entries.delete(id)\n\t\tthis.#entries.set(id, { session, lastSeen: now, createdAt })\n\t}\n\n\tasync delete(id: string): Promise<void> {\n\t\tthis.#entries.delete(id)\n\t}\n\n\t// Whether `entry` has aged past its idle timeout or absolute lifetime as of `now`.\n\t#expired(\n\t\tentry: { session: S; lastSeen: number; readonly createdAt: number },\n\t\tnow: number,\n\t): boolean {\n\t\treturn sessionExpired(entry, now, {\n\t\t\t...(this.#ttl !== undefined ? { ttl: this.#ttl } : {}),\n\t\t\t...(this.#lifetime !== undefined ? { lifetime: this.#lifetime } : {}),\n\t\t})\n\t}\n\n\t// Makes room for a brand-new id: prunes expired entries, then evicts the\n\t// least-recently-written id if the store is still at capacity.\n\t#reserve(now: number): void {\n\t\tif (this.#entries.size < this.#capacity) return\n\t\tfor (const [id, entry] of this.#entries) {\n\t\t\tif (this.#expired(entry, now)) {\n\t\t\t\tthis.#entries.delete(id)\n\t\t\t\tthis.#notify(id)\n\t\t\t}\n\t\t}\n\t\tif (this.#entries.size >= this.#capacity) {\n\t\t\tconst oldest = this.#entries.keys().next().value\n\t\t\tif (oldest !== undefined) {\n\t\t\t\tthis.#entries.delete(oldest)\n\t\t\t\tthis.#notify(oldest)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Invokes the configured evict callback, throw-isolated.\n\t#notify(id: string): void {\n\t\tif (this.#evict === undefined) return\n\t\ttry {\n\t\t\tthis.#evict(id)\n\t\t} catch {\n\t\t\t// swallowed — a broken evict callback can never affect the store\n\t\t}\n\t}\n}\n","import type { SessionInterface, SessionRow, SessionStoreInterface } from '../types.js'\nimport type { Guard } from '@orkestrel/contract'\nimport type { TableInterface } from '@orkestrel/database'\nimport { restoreSession, sessionExpired, snapshotSession } from '../helpers.js'\nimport { Session } from '../Session.js'\n\n/**\n * A durable {@link SessionStoreInterface} over an `@orkestrel/database`\n * table — the same idle-timeout + absolute-lifetime contract as\n * {@link MemorySessionStore}, backed by a caller-supplied `TableInterface`\n * instead of an in-process `Map`.\n *\n * @typeParam S - The session data payload type\n *\n * @remarks\n * `get` reads the row, evicts (removes the row) once `sessionExpired`\n * reports either threshold elapsed, then rebuilds the session via\n * {@link restoreSession} — a malformed snapshot or one that fails the\n * caller's `is` guard resolves `undefined` rather than throwing. A live read\n * touches `lastSeen`. `set` preserves an existing row's `createdAt` across a\n * re-`set` of the same id (stamped once at the first `set`), mirroring\n * {@link MemorySessionStore}. `delete` of an absent id is a no-op (the\n * table's `remove` contract).\n *\n * A malformed-snapshot or failed-guard `undefined` LEAVES the row in place —\n * unlike the expired path, which removes it. This is deliberate: a\n * caller-contextual `is` guard may reject a session that is still perfectly\n * valid for another flow reading the same table (a differently-shaped `S`,\n * a stricter guard mid-rollout), so `get` never destroys data on a guard\n * miss. A row that no caller's guard ever accepts again self-heals once its\n * `ttl`/`lifetime` elapses on a later `get`.\n *\n * @example\n * ```ts\n * const store = new DatabaseSessionStore(table, isSession, { ttl: 60_000 })\n * await store.set('abc', new Session('abc'), Date.now())\n * ```\n */\nexport class DatabaseSessionStore<\n\tS extends SessionInterface = Session,\n> implements SessionStoreInterface<S> {\n\treadonly #table: TableInterface<SessionRow>\n\treadonly #is: Guard<S>\n\treadonly #ttl: number | undefined\n\treadonly #lifetime: number | undefined\n\n\tconstructor(\n\t\ttable: TableInterface<SessionRow>,\n\t\tis: Guard<S>,\n\t\toptions?: { readonly ttl?: number; readonly lifetime?: number },\n\t) {\n\t\tthis.#table = table\n\t\tthis.#is = is\n\t\tthis.#ttl = options?.ttl\n\t\tthis.#lifetime = options?.lifetime\n\t}\n\n\tasync get(id: string, now: number): Promise<S | undefined> {\n\t\tconst row = await this.#table.get(id)\n\t\tif (row === undefined) return undefined\n\t\tif (\n\t\t\tsessionExpired(row, now, {\n\t\t\t\t...(this.#ttl !== undefined ? { ttl: this.#ttl } : {}),\n\t\t\t\t...(this.#lifetime !== undefined ? { lifetime: this.#lifetime } : {}),\n\t\t\t})\n\t\t) {\n\t\t\tawait this.#table.remove(id)\n\t\t\treturn undefined\n\t\t}\n\t\tconst session = restoreSession(row.session)\n\t\tif (session === undefined || !this.#is(session)) return undefined\n\t\tawait this.#table.update(id, { lastSeen: now })\n\t\treturn session\n\t}\n\n\tasync set(id: string, session: S, now: number): Promise<void> {\n\t\tconst existing = await this.#table.get(id)\n\t\tconst createdAt = existing?.createdAt ?? now\n\t\tawait this.#table.set({ id, session: snapshotSession(session), lastSeen: now, createdAt })\n\t}\n\n\tasync delete(id: string): Promise<void> {\n\t\tawait this.#table.remove(id)\n\t}\n}\n","import type {\n\tBearerOptions,\n\tBearerState,\n\tBodyState,\n\tBoundaryOptions,\n\tClientState,\n\tCompressionOptions,\n\tConnectionState,\n\tCorsOptions,\n\tCSRFOptions,\n\tCSRFState,\n\tDeadlineOptions,\n\tETagOptions,\n\tForwardedOptions,\n\tIdentifierState,\n\tLimiterOptions,\n\tSecurityOptions,\n\tSessionControlInterface,\n\tSessionInterface,\n\tSessionOptions,\n\tSessionState,\n\tSessionStoreInterface,\n\tTelemetryOptions,\n} from './types.js'\nimport type { BudgetInterface } from '@orkestrel/budget'\nimport type { MiddlewareHandler } from '@orkestrel/server'\nimport { isBoolean, isFiniteNumber, isFunction, isRecord, isString } from '@orkestrel/contract'\nimport { linkSignal } from '@orkestrel/abort'\nimport { createBudget } from '@orkestrel/budget'\nimport {\n\tHTTPError,\n\tisHTTPError,\n\tisValidRequestId,\n\tmergeVary,\n\treadSignedCookie,\n\tresolveOrigin,\n\tresolveSecure,\n\tresolveSecurityHeader,\n\tsignToken,\n\tverifyToken,\n\twriteSignedCookie,\n} from '@orkestrel/server'\nimport { createTimeout } from '@orkestrel/timeout'\nimport {\n\tDEFAULT_BEARER_HEADER,\n\tDEFAULT_BEARER_SCHEME,\n\tDEFAULT_CLUSTER,\n\tDEFAULT_COEP,\n\tDEFAULT_COMPRESSION_ENCODINGS,\n\tDEFAULT_COMPRESSION_THRESHOLD,\n\tDEFAULT_COOP,\n\tDEFAULT_CORP,\n\tDEFAULT_CORS_HEADERS,\n\tDEFAULT_CORS_METHODS,\n\tDEFAULT_CSP,\n\tDEFAULT_CSRF_COOKIE,\n\tDEFAULT_CSRF_FIELD,\n\tDEFAULT_CSRF_HEADER,\n\tDEFAULT_CSRF_SAFE_METHODS,\n\tDEFAULT_DEADLINE_STATUS,\n\tDEFAULT_FRAME_OPTIONS,\n\tDEFAULT_HSTS,\n\tDEFAULT_IDENTIFIER_HEADER,\n\tDEFAULT_LIMITER_CAPACITY,\n\tDEFAULT_LIMITER_MESSAGE,\n\tDEFAULT_PERMISSIONS_POLICY,\n\tDEFAULT_REFERRER_POLICY,\n} from './constants.js'\nimport {\n\tbuildClientInfo,\n\tbuildRateLimitField,\n\tbuildRateLimitPolicyField,\n\tbuildRetryAfter,\n\tcompressBytes,\n\tcompressResponse,\n\tdetectEncodings,\n\tequalsConstantTime,\n\tisBufferingIneligible,\n\tisPreflight,\n\trebuildResponse,\n\tresolveForwardedFor,\n\tresolveKey,\n\tresolveOptInHeader,\n\ttransferSessionData,\n} from './helpers.js'\nimport { MemorySessionStore } from './stores/MemorySessionStore.js'\nimport { Session } from './Session.js'\nimport { computeBodyETag, matchesETag } from '@orkestrel/server'\n\n// ============================================================================\n// @orkestrel/middleware — the thirteen pure battery factories\n// (AGENTS §5 middlewares.ts). Each `create{Noun}` closes over its guarded\n// option bag and returns a `MiddlewareHandler<TState>` — a behavior, never a\n// class. See PROPOSAL.md §4 and the orchestrator's seam rulings A–K for the\n// exact semantics each battery pins.\n// ============================================================================\n\n/**\n * The outermost error-rendering battery — catches a downstream throw and\n * renders it as a `Response`.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link BoundaryOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.expose` or `options.report` is malformed\n *\n * @example\n * ```ts\n * const boundary = createBoundary({ expose: false })\n * ```\n */\nexport function createBoundary<TState>(options?: BoundaryOptions): MiddlewareHandler<TState> {\n\tif (options?.expose !== undefined && !isBoolean(options.expose))\n\t\tthrow new TypeError('BoundaryOptions.expose must be a boolean when provided')\n\tif (options?.report !== undefined && !isFunction(options.report))\n\t\tthrow new TypeError('BoundaryOptions.report must be a function when provided')\n\tconst expose = options?.expose ?? false\n\tconst report = options?.report\n\treturn async (request, context, next) => {\n\t\ttry {\n\t\t\treturn await next()\n\t\t} catch (error) {\n\t\t\tif (report !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\treport(error)\n\t\t\t\t} catch {\n\t\t\t\t\t// report is fire-and-forget; its own throw can never alter the response.\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isHTTPError(error)) return new Response(error.message, { status: error.status })\n\t\t\tconst message = expose\n\t\t\t\t? error instanceof Error\n\t\t\t\t\t? error.message\n\t\t\t\t\t: String(error)\n\t\t\t\t: 'internal server error'\n\t\t\treturn new Response(message, { status: 500 })\n\t\t}\n\t}\n}\n\n/**\n * The access-log/timing seam — records one {@link TelemetryEntry} per request\n * after the response settles.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link TelemetryOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.record` is not a function\n *\n * @example\n * ```ts\n * const telemetry = createTelemetry({ record: (entry) => console.log(entry) })\n * ```\n */\nexport function createTelemetry<TState>(options: TelemetryOptions): MiddlewareHandler<TState> {\n\tif (!isFunction(options.record)) throw new TypeError('TelemetryOptions.record must be a function')\n\tconst record = options.record\n\treturn async (request, context, next) => {\n\t\tconst start = Date.now()\n\t\tlet response: Response | undefined\n\t\ttry {\n\t\t\tresponse = await next()\n\t\t\treturn response\n\t\t} finally {\n\t\t\tconst duration = Date.now() - start\n\t\t\tconst status = response?.status ?? 500\n\t\t\ttry {\n\t\t\t\trecord({ method: context.method, pathname: context.url.pathname, status, duration })\n\t\t\t} catch {\n\t\t\t\t// record's own throw is swallowed — a broken sink can never fail the response.\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Response-body compression — negotiates and compresses a buffered response\n * body over the runtime's feature-detected `CompressionStream` codings.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link CompressionOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.threshold` or `options.filter` is malformed\n *\n * @example\n * ```ts\n * const compression = createCompression({ threshold: 1024 })\n * ```\n */\nexport function createCompression<TState>(options?: CompressionOptions): MiddlewareHandler<TState> {\n\tif (\n\t\toptions?.threshold !== undefined &&\n\t\t(!isFiniteNumber(options.threshold) || options.threshold < 0)\n\t)\n\t\tthrow new TypeError('CompressionOptions.threshold must be a finite number when provided')\n\tif (options?.filter !== undefined && !isFunction(options.filter))\n\t\tthrow new TypeError('CompressionOptions.filter must be a function when provided')\n\tif (options?.encodings !== undefined && !Array.isArray(options.encodings))\n\t\tthrow new TypeError('CompressionOptions.encodings must be an array when provided')\n\tconst threshold = options?.threshold ?? DEFAULT_COMPRESSION_THRESHOLD\n\tconst encodings = detectEncodings(options?.encodings ?? DEFAULT_COMPRESSION_ENCODINGS)\n\tconst filter = options?.filter\n\treturn async (request, context, next) => {\n\t\tconst response = await next()\n\t\treturn compressResponse(request, context, response, {\n\t\t\tthreshold,\n\t\t\t...(filter !== undefined ? { filter } : {}),\n\t\t\tencodings,\n\t\t\tcompress: compressBytes,\n\t\t})\n\t}\n}\n\n/**\n * Security-headers + request-identifier battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link IdentifierState}\n * @param options - See {@link SecurityOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n *\n * @example\n * ```ts\n * const security = createSecurity({ hsts: true })\n * ```\n */\nexport function createSecurity<TState extends IdentifierState>(\n\toptions?: SecurityOptions,\n): MiddlewareHandler<TState> {\n\tif (\n\t\toptions?.frame !== undefined &&\n\t\toptions.frame !== false &&\n\t\toptions.frame !== 'DENY' &&\n\t\toptions.frame !== 'SAMEORIGIN'\n\t)\n\t\tthrow new TypeError(\n\t\t\t\"SecurityOptions.frame must be 'DENY', 'SAMEORIGIN', or false when provided\",\n\t\t)\n\tif (options?.csp !== undefined && options.csp !== false && !isString(options.csp))\n\t\tthrow new TypeError('SecurityOptions.csp must be a string or false when provided')\n\tif (options?.referrer !== undefined && options.referrer !== false && !isString(options.referrer))\n\t\tthrow new TypeError('SecurityOptions.referrer must be a string or false when provided')\n\tif (\n\t\toptions?.permissions !== undefined &&\n\t\toptions.permissions !== false &&\n\t\t!isString(options.permissions)\n\t)\n\t\tthrow new TypeError('SecurityOptions.permissions must be a string or false when provided')\n\tif (options?.coop !== undefined && options.coop !== false && !isString(options.coop))\n\t\tthrow new TypeError('SecurityOptions.coop must be a string or false when provided')\n\tif (options?.corp !== undefined && options.corp !== false && !isString(options.corp))\n\t\tthrow new TypeError('SecurityOptions.corp must be a string or false when provided')\n\tif (options?.cluster !== undefined && options.cluster !== false && !isString(options.cluster))\n\t\tthrow new TypeError('SecurityOptions.cluster must be a string or false when provided')\n\tif (options?.coep !== undefined && !isBoolean(options.coep) && !isString(options.coep))\n\t\tthrow new TypeError('SecurityOptions.coep must be a string or boolean when provided')\n\tif (options?.hsts !== undefined && !isBoolean(options.hsts) && !isString(options.hsts))\n\t\tthrow new TypeError('SecurityOptions.hsts must be a string or boolean when provided')\n\tif (\n\t\toptions?.identifier !== undefined &&\n\t\toptions.identifier !== false &&\n\t\t!isRecord(options.identifier)\n\t)\n\t\tthrow new TypeError('SecurityOptions.identifier must be an options bag or false when provided')\n\tif (\n\t\tisRecord(options?.identifier) &&\n\t\toptions.identifier.trust !== undefined &&\n\t\t!isBoolean(options.identifier.trust)\n\t)\n\t\tthrow new TypeError('SecurityOptions.identifier.trust must be a boolean when provided')\n\n\tconst frame = options?.frame\n\tconst csp = options?.csp\n\tconst referrer = options?.referrer\n\tconst permissions = options?.permissions\n\tconst coop = options?.coop\n\tconst corp = options?.corp\n\tconst cluster = options?.cluster\n\tconst coep = options?.coep\n\tconst hsts = options?.hsts\n\tconst identifierOption = options?.identifier\n\tconst identifierEnabled = identifierOption !== false\n\tconst trust = isRecord(identifierOption) ? identifierOption.trust === true : false\n\n\treturn async (request, context, next) => {\n\t\tlet identifier: string | undefined\n\t\tif (identifierEnabled) {\n\t\t\tconst incoming = request.headers.get(DEFAULT_IDENTIFIER_HEADER)\n\t\t\tidentifier =\n\t\t\t\ttrust && incoming !== null && isValidRequestId(incoming) ? incoming : crypto.randomUUID()\n\t\t\tObject.assign(context.state, { identifier })\n\t\t}\n\t\tconst response = await next()\n\t\tresponse.headers.set('x-content-type-options', 'nosniff')\n\t\tconst headerTable: readonly (readonly [string, string | undefined])[] = [\n\t\t\t['x-frame-options', resolveSecurityHeader(frame, DEFAULT_FRAME_OPTIONS)],\n\t\t\t['content-security-policy', resolveSecurityHeader(csp, DEFAULT_CSP)],\n\t\t\t['referrer-policy', resolveSecurityHeader(referrer, DEFAULT_REFERRER_POLICY)],\n\t\t\t['permissions-policy', resolveSecurityHeader(permissions, DEFAULT_PERMISSIONS_POLICY)],\n\t\t\t['cross-origin-opener-policy', resolveSecurityHeader(coop, DEFAULT_COOP)],\n\t\t\t['cross-origin-resource-policy', resolveSecurityHeader(corp, DEFAULT_CORP)],\n\t\t\t['origin-agent-cluster', resolveSecurityHeader(cluster, DEFAULT_CLUSTER)],\n\t\t\t['cross-origin-embedder-policy', resolveOptInHeader(coep, DEFAULT_COEP)],\n\t\t\t['strict-transport-security', resolveOptInHeader(hsts, DEFAULT_HSTS)],\n\t\t]\n\t\tfor (const [name, value] of headerTable)\n\t\t\tif (value !== undefined) response.headers.set(name, value)\n\t\tif (identifierEnabled && identifier !== undefined)\n\t\t\tresponse.headers.set(DEFAULT_IDENTIFIER_HEADER, identifier)\n\t\treturn response\n\t}\n}\n\n/**\n * Cross-Origin Resource Sharing battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link CorsOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n *\n * @example\n * ```ts\n * const cors = createCors({ origin: ['https://app.example'] })\n * ```\n */\nexport function createCors<TState>(options?: CorsOptions): MiddlewareHandler<TState> {\n\tif (options?.origin !== undefined && !isString(options.origin) && !Array.isArray(options.origin))\n\t\tthrow new TypeError('CorsOptions.origin must be a string or string array when provided')\n\tif (options?.methods !== undefined && !Array.isArray(options.methods))\n\t\tthrow new TypeError('CorsOptions.methods must be an array when provided')\n\tif (options?.headers !== undefined && !Array.isArray(options.headers))\n\t\tthrow new TypeError('CorsOptions.headers must be an array when provided')\n\tconst origin = options?.origin ?? '*'\n\tconst methods = options?.methods ?? DEFAULT_CORS_METHODS\n\tconst headers = options?.headers ?? DEFAULT_CORS_HEADERS\n\tconst reflecting = Array.isArray(origin)\n\treturn async (request, context, next) => {\n\t\tconst requestOrigin = request.headers.get('origin') ?? undefined\n\t\tconst allowOrigin = resolveOrigin(origin, requestOrigin)\n\t\tif (isPreflight(context.method, request.headers)) {\n\t\t\tconst preflightHeaders = new Headers()\n\t\t\tif (allowOrigin !== undefined)\n\t\t\t\tpreflightHeaders.set('access-control-allow-origin', allowOrigin)\n\t\t\tpreflightHeaders.set('access-control-allow-methods', methods.join(', '))\n\t\t\tpreflightHeaders.set('access-control-allow-headers', headers.join(', '))\n\t\t\tif (reflecting && allowOrigin !== undefined)\n\t\t\t\tpreflightHeaders.set('vary', mergeVary(undefined, 'Origin'))\n\t\t\treturn new Response(null, { status: 204, headers: preflightHeaders })\n\t\t}\n\t\tconst response = await next()\n\t\tif (allowOrigin !== undefined) response.headers.set('access-control-allow-origin', allowOrigin)\n\t\tif (reflecting && allowOrigin !== undefined)\n\t\t\tresponse.headers.set('vary', mergeVary(response.headers.get('vary') ?? undefined, 'Origin'))\n\t\treturn response\n\t}\n}\n\n/**\n * The application-level per-request deadline battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link DeadlineOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.ms` or `options.status` is malformed\n *\n * @remarks\n * MUST sit OUTSIDE `createBody` in the chain — it reconstructs the inbound\n * `Request` (to link its `signal` to the deadline `signal`), which throws if\n * the body was already consumed upstream (e.g. by `createBody`'s cached read).\n *\n * @example\n * ```ts\n * const deadline = createDeadline({ ms: 5_000 })\n * ```\n */\nexport function createDeadline<TState>(options: DeadlineOptions): MiddlewareHandler<TState> {\n\tif (!isFiniteNumber(options.ms) || options.ms <= 0)\n\t\tthrow new TypeError('DeadlineOptions.ms must be a finite number')\n\tif (options.status !== undefined && !isFiniteNumber(options.status))\n\t\tthrow new TypeError('DeadlineOptions.status must be a finite number when provided')\n\tconst ms = options.ms\n\tconst status = options.status ?? DEFAULT_DEADLINE_STATUS\n\treturn async (request, context, next) => {\n\t\tconst timeout = createTimeout({ ms })\n\t\tconst linked = linkSignal(timeout.signal, request.signal)\n\t\ttimeout.start()\n\t\tconst downstream = next(new Request(request, { signal: linked }))\n\t\tconst deadline = new Promise<Response>((resolve) => {\n\t\t\ttimeout.signal.addEventListener('abort', () => resolve(new Response(null, { status })), {\n\t\t\t\tonce: true,\n\t\t\t})\n\t\t})\n\t\tlet deadlineWon = false\n\t\ttry {\n\t\t\tconst winner = await Promise.race([\n\t\t\t\tdownstream,\n\t\t\t\tdeadline.then((response) => {\n\t\t\t\t\tdeadlineWon = true\n\t\t\t\t\treturn response\n\t\t\t\t}),\n\t\t\t])\n\t\t\treturn winner\n\t\t} finally {\n\t\t\ttimeout.clear()\n\t\t\t// The losing `next()` promise is settled-and-discarded — a late throw\n\t\t\t// from it can never surface as an unhandled rejection; when the\n\t\t\t// deadline won the race, best-effort cancel its late body so the\n\t\t\t// runtime can reclaim the underlying stream.\n\t\t\tdownstream\n\t\t\t\t.then((response) => {\n\t\t\t\t\tif (deadlineWon) response.body?.cancel().catch(() => {})\n\t\t\t\t})\n\t\t\t\t.catch(() => {})\n\t\t}\n\t}\n}\n\n/**\n * The trusted-proxy client-IP resolver battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link ClientState} and {@link ConnectionState}\n * @param options - See {@link ForwardedOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When neither or both of `proxies`/`trusted` are provided, or either is malformed\n *\n * @example\n * ```ts\n * const forwarded = createForwarded({ proxies: 1 })\n * ```\n */\nexport function createForwarded<TState extends ClientState & ConnectionState>(\n\toptions: ForwardedOptions,\n): MiddlewareHandler<TState> {\n\tconst hasProxies = isRecord(options) && 'proxies' in options\n\tconst hasTrusted = isRecord(options) && 'trusted' in options\n\tif (!isRecord(options) || hasProxies === hasTrusted)\n\t\tthrow new TypeError('ForwardedOptions requires exactly one of proxies or trusted')\n\tif (hasProxies) {\n\t\tconst proxies = options.proxies\n\t\tif (!isFiniteNumber(proxies) || !Number.isInteger(proxies) || proxies < 1)\n\t\t\tthrow new TypeError('ForwardedOptions.proxies must be a positive integer')\n\t} else {\n\t\tconst trusted = options.trusted\n\t\tif (!Array.isArray(trusted))\n\t\t\tthrow new TypeError('ForwardedOptions.trusted must be an array when provided')\n\t}\n\tconst trust = hasProxies ? { proxies: options.proxies } : { trusted: options.trusted }\n\treturn async (request, context, next) => {\n\t\tconst header = request.headers.get('x-forwarded-for') ?? undefined\n\t\tconst resolved = resolveForwardedFor(header, trust)\n\t\tconst ip = resolved ?? context.state.connection?.ip\n\t\tObject.assign(context.state, { client: buildClientInfo(ip) })\n\t\treturn next()\n\t}\n}\n\n/**\n * Dynamic response `ETag` + conditional GET battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link ETagOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.weak` is not a boolean\n *\n * @example\n * ```ts\n * const etag = createETag({ weak: true })\n * ```\n */\nexport function createETag<TState>(options?: ETagOptions): MiddlewareHandler<TState> {\n\tif (options?.weak !== undefined && !isBoolean(options.weak))\n\t\tthrow new TypeError('ETagOptions.weak must be a boolean when provided')\n\tconst weak = options?.weak ?? true\n\treturn async (request, context, next) => {\n\t\tconst response = await next()\n\t\tif (context.method !== 'GET' || response.status !== 200) return response\n\t\tif (isBufferingIneligible(context.method, response, 'etag')) return response\n\t\tconst buffer = await response.arrayBuffer()\n\t\tconst bytes = new Uint8Array(buffer)\n\t\tconst etag = await computeBodyETag(bytes, weak)\n\t\tconst ifNoneMatch = request.headers.get('if-none-match')\n\t\tif (ifNoneMatch !== null && matchesETag(ifNoneMatch, etag)) {\n\t\t\tconst headers = new Headers(response.headers)\n\t\t\theaders.set('etag', etag)\n\t\t\theaders.delete('content-length')\n\t\t\treturn new Response(null, { status: 304, headers })\n\t\t}\n\t\tconst headers = new Headers(response.headers)\n\t\theaders.set('etag', etag)\n\t\treturn rebuildResponse(bytes, response, headers)\n\t}\n}\n\n/**\n * Bearer-token authentication battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link BearerState}\n * @param options - See {@link BearerOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n * @throws {HTTPError} `401` when the token is missing, invalid, or expired\n *\n * @example\n * ```ts\n * const bearer = createBearer({ secret: 'shh' })\n * ```\n */\nexport function createBearer<TState extends BearerState>(\n\toptions: BearerOptions,\n): MiddlewareHandler<TState> {\n\tif (\n\t\t!isString(options.secret) &&\n\t\t(!Array.isArray(options.secret) || !options.secret.every(isString))\n\t)\n\t\tthrow new TypeError('BearerOptions.secret must be a string or string array')\n\tif (options.header !== undefined && !isString(options.header))\n\t\tthrow new TypeError('BearerOptions.header must be a string when provided')\n\tif (options.scheme !== undefined && !isString(options.scheme))\n\t\tthrow new TypeError('BearerOptions.scheme must be a string when provided')\n\tconst secret = options.secret\n\tconst header = (options.header ?? DEFAULT_BEARER_HEADER).toLowerCase()\n\tconst scheme = options.scheme ?? DEFAULT_BEARER_SCHEME\n\treturn async (request, context, next) => {\n\t\tconst raw = request.headers.get(header)\n\t\tif (raw === null) throw new HTTPError(401, 'missing token')\n\t\tlet candidate = raw\n\t\tif (scheme !== '') {\n\t\t\tconst prefix = `${scheme.toLowerCase()} `\n\t\t\tif (!raw.toLowerCase().startsWith(prefix)) throw new HTTPError(401, 'missing token')\n\t\t\tcandidate = raw.slice(prefix.length)\n\t\t}\n\t\tconst verified = await verifyToken(candidate, secret)\n\t\tif (verified === undefined) throw new HTTPError(401, 'invalid token')\n\t\tObject.assign(context.state, { token: verified })\n\t\treturn next()\n\t}\n}\n\n/**\n * Fixed-window rate-limiting battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link BearerState}, {@link ClientState}, and {@link ConnectionState}\n * @param options - See {@link LimiterOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n *\n * @example\n * ```ts\n * const limiter = createLimiter({ max: 100, window: 60_000 })\n * ```\n */\nexport function createLimiter<TState extends BearerState & ClientState & ConnectionState>(\n\toptions: LimiterOptions<TState>,\n): MiddlewareHandler<TState> {\n\tif (!isFiniteNumber(options.max) || !Number.isInteger(options.max) || options.max <= 0)\n\t\tthrow new TypeError('LimiterOptions.max must be a positive integer')\n\tif (!isFiniteNumber(options.window) || options.window <= 0)\n\t\tthrow new TypeError('LimiterOptions.window must be a positive finite number')\n\tif (\n\t\toptions.capacity !== undefined &&\n\t\t(!isFiniteNumber(options.capacity) ||\n\t\t\t!Number.isInteger(options.capacity) ||\n\t\t\toptions.capacity <= 0)\n\t)\n\t\tthrow new TypeError('LimiterOptions.capacity must be a positive integer when provided')\n\tif (options.key !== undefined && !isFunction(options.key))\n\t\tthrow new TypeError('LimiterOptions.key must be a function when provided')\n\tif (options.message !== undefined && !isString(options.message))\n\t\tthrow new TypeError('LimiterOptions.message must be a string when provided')\n\tif (options.clock !== undefined && !isFunction(options.clock))\n\t\tthrow new TypeError('LimiterOptions.clock must be a function when provided')\n\tif (options.policy !== undefined && !isBoolean(options.policy))\n\t\tthrow new TypeError('LimiterOptions.policy must be a boolean when provided')\n\tif (options.evict !== undefined && !isFunction(options.evict))\n\t\tthrow new TypeError('LimiterOptions.evict must be a function when provided')\n\tconst max = options.max\n\tconst window = options.window\n\tconst capacity = options.capacity ?? DEFAULT_LIMITER_CAPACITY\n\tconst deriveKey = options.key\n\tconst message = options.message ?? DEFAULT_LIMITER_MESSAGE\n\tconst clock = options.clock ?? Date.now\n\tconst policy = options.policy ?? false\n\tconst evict = options.evict\n\tconst buckets = new Map<string, { budget: BudgetInterface<number>; resetAt: number }>()\n\treturn async (request, context, next) => {\n\t\tconst key = deriveKey === undefined ? resolveKey(context.state) : deriveKey(context)\n\t\tconst now = clock()\n\t\tlet bucket = buckets.get(key)\n\t\tif (bucket === undefined) {\n\t\t\tif (buckets.size >= capacity) {\n\t\t\t\tconst oldest = buckets.keys().next().value\n\t\t\t\tif (oldest !== undefined) {\n\t\t\t\t\tbuckets.delete(oldest)\n\t\t\t\t\tif (evict !== undefined) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tevict(oldest)\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// swallowed — a broken evict callback can never fail the request\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbucket = {\n\t\t\t\tbudget: createBudget<number>({ max, consume: Number }),\n\t\t\t\tresetAt: now + window,\n\t\t\t}\n\t\t\tbuckets.set(key, bucket)\n\t\t} else {\n\t\t\tbuckets.delete(key)\n\t\t\tbuckets.set(key, bucket)\n\t\t\tif (now >= bucket.resetAt) {\n\t\t\t\tbucket.budget.clear()\n\t\t\t\tbucket.resetAt = now + window\n\t\t\t}\n\t\t}\n\t\tif (bucket.budget.exhausted) {\n\t\t\tconst headers = new Headers()\n\t\t\theaders.set('retry-after', buildRetryAfter(bucket.resetAt, now))\n\t\t\tif (policy) {\n\t\t\t\theaders.set('ratelimit', buildRateLimitField(0, bucket.resetAt, now))\n\t\t\t\theaders.set('ratelimit-policy', buildRateLimitPolicyField(max, window))\n\t\t\t}\n\t\t\treturn new Response(message, { status: 429, headers })\n\t\t}\n\t\tbucket.budget.consume(1)\n\t\treturn next()\n\t}\n}\n\n/**\n * The body-driving battery — eagerly awaits the cached `context.body()` so\n * its throws (or a malformed-JSON `undefined`) surface before the handler\n * runs, and stashes the resolved value onto {@link BodyState.body}.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link BodyState}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {HTTPError} `400` when the request declares `application/json` and the body resolves `undefined`\n *\n * @remarks\n * The shipped `MiddlewareContext.body()` is a parameterless, server-owned\n * cache (`ServerOptions.limit` governs its size cap) — this battery carries\n * no `limit`/`decompression` options (a deliberate break from the deleted old\n * `createBodyParser` surface, which configured them itself). `state.body` is\n * stashed from the SAME awaited call the 400 check reads — `context.body()`\n * is never invoked twice.\n *\n * @example\n * ```ts\n * const body = createBody()\n * ```\n */\nexport function createBody<TState extends BodyState = BodyState>(): MiddlewareHandler<TState> {\n\treturn async (request, context, next) => {\n\t\tconst body = await context.body()\n\t\tif (body !== undefined) Object.assign(context.state, { body })\n\t\tconst contentType = request.headers.get('content-type')\n\t\tif (\n\t\t\tcontentType !== null &&\n\t\t\tcontentType.toLowerCase().startsWith('application/json') &&\n\t\t\tbody === undefined\n\t\t)\n\t\t\tthrow new HTTPError(400, 'invalid json')\n\t\treturn next()\n\t}\n}\n\n/**\n * The generic session battery — resolves, mints, and persists a session\n * across the request, with a mid-handler `regenerate`/`destroy` control handle.\n *\n * @typeParam S - The session entity type the store persists (must implement {@link SessionInterface})\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link SessionState} and {@link ConnectionState}\n * @param options - See {@link SessionOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n * @throws {HTTPError} `404` when `require` is set and no session resolves or mints\n *\n * @example\n * ```ts\n * const session = createSession({ transport: createHeaderTransport() })\n * ```\n */\nexport function createSession<\n\tS extends SessionInterface = SessionInterface,\n\tTState extends SessionState & ConnectionState = SessionState & ConnectionState,\n>(options: SessionOptions<S, TState>): MiddlewareHandler<TState> {\n\tif (\n\t\t!isFunction(options.transport.read) ||\n\t\t!isFunction(options.transport.write) ||\n\t\t!isFunction(options.transport.clear)\n\t)\n\t\tthrow new TypeError('SessionOptions.transport must implement SessionTransport')\n\tif (\n\t\toptions.store !== undefined &&\n\t\t(!isFunction(options.store.get) ||\n\t\t\t!isFunction(options.store.set) ||\n\t\t\t!isFunction(options.store.delete))\n\t)\n\t\tthrow new TypeError('SessionOptions.store must implement SessionStoreInterface when provided')\n\tif (options.ttl !== undefined && !isFiniteNumber(options.ttl))\n\t\tthrow new TypeError('SessionOptions.ttl must be a finite number when provided')\n\tif (options.lifetime !== undefined && !isFiniteNumber(options.lifetime))\n\t\tthrow new TypeError('SessionOptions.lifetime must be a finite number when provided')\n\tif (options.create !== undefined && !isFunction(options.create))\n\t\tthrow new TypeError('SessionOptions.create must be a function when provided')\n\tif (options.mint !== undefined && !isFunction(options.mint))\n\t\tthrow new TypeError('SessionOptions.mint must be a function when provided')\n\tif (options.require !== undefined && !isBoolean(options.require))\n\t\tthrow new TypeError('SessionOptions.require must be a boolean when provided')\n\tif (options.ends !== undefined && !isBoolean(options.ends))\n\t\tthrow new TypeError('SessionOptions.ends must be a boolean when provided')\n\tif (options.clock !== undefined && !isFunction(options.clock))\n\t\tthrow new TypeError('SessionOptions.clock must be a function when provided')\n\n\tconst transport = options.transport\n\tconst clock = options.clock ?? Date.now\n\tconst store: SessionStoreInterface<SessionInterface> =\n\t\toptions.store ??\n\t\tnew MemorySessionStore<SessionInterface>({\n\t\t\t...(options.ttl !== undefined ? { ttl: options.ttl } : {}),\n\t\t\t...(options.lifetime !== undefined ? { lifetime: options.lifetime } : {}),\n\t\t\t...(options.capacity !== undefined ? { capacity: options.capacity } : {}),\n\t\t\t...(options.evict !== undefined ? { evict: options.evict } : {}),\n\t\t})\n\tconst create = options.create\n\tconst mint = options.mint\n\tconst requireSession = options.require ?? false\n\tconst ends = options.ends ?? false\n\n\treturn async (request, context, next) => {\n\t\tconst now = clock()\n\t\tconst encrypted = context.state.connection?.encrypted ?? false\n\t\tconst incomingId = await transport.read(request)\n\t\tlet session = incomingId !== undefined ? await store.get(incomingId, now) : undefined\n\n\t\tif (ends && context.method === 'DELETE' && session !== undefined) {\n\t\t\tawait store.delete(session.id)\n\t\t\treturn new Response(null, { status: 204 })\n\t\t}\n\n\t\tlet minted = false\n\t\tif (session === undefined) {\n\t\t\tconst shouldMint = mint !== undefined ? await mint(context) : true\n\t\t\tif (shouldMint) {\n\t\t\t\tconst id = crypto.randomUUID()\n\t\t\t\tsession = create === undefined ? new Session(id) : create(id)\n\t\t\t\tminted = true\n\t\t\t} else if (requireSession) {\n\t\t\t\tthrow new HTTPError(404, 'session required')\n\t\t\t}\n\t\t}\n\n\t\tlet destroyed = false\n\t\tlet regenerated: SessionInterface | undefined\n\t\tconst activeSession = session\n\n\t\tif (activeSession !== undefined) {\n\t\t\tconst control: SessionControlInterface = {\n\t\t\t\tregenerate() {\n\t\t\t\t\tif (destroyed) return\n\t\t\t\t\tconst newSession =\n\t\t\t\t\t\tcreate === undefined ? new Session(crypto.randomUUID()) : create(crypto.randomUUID())\n\t\t\t\t\ttransferSessionData(activeSession, newSession)\n\t\t\t\t\tregenerated = newSession\n\t\t\t\t},\n\t\t\t\tdestroy() {\n\t\t\t\t\tdestroyed = true\n\t\t\t\t\tregenerated = undefined\n\t\t\t\t},\n\t\t\t}\n\t\t\tObject.assign(context.state, { session: activeSession, control })\n\t\t}\n\n\t\tconst response = await next()\n\n\t\tif (activeSession !== undefined) {\n\t\t\tif (destroyed) {\n\t\t\t\tawait store.delete(activeSession.id)\n\t\t\t\tawait transport.clear(response)\n\t\t\t} else if (regenerated !== undefined) {\n\t\t\t\tawait store.set(regenerated.id, regenerated, clock())\n\t\t\t\tawait store.delete(activeSession.id)\n\t\t\t\tawait transport.write(response, regenerated.id, encrypted)\n\t\t\t} else {\n\t\t\t\tawait store.set(activeSession.id, activeSession, clock())\n\t\t\t\tif (minted) await transport.write(response, activeSession.id, encrypted)\n\t\t\t}\n\t\t}\n\n\t\treturn response\n\t}\n}\n\n/**\n * Session-bound double-submit CSRF protection battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link CSRFState}, {@link SessionState}, and {@link ConnectionState}\n * @param options - See {@link CSRFOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n * @throws {HTTPError} `403` when the submitted token is missing, mismatched, or bound to a different session\n *\n * @example\n * ```ts\n * const csrf = createCSRF({ secret: 'shh' })\n * ```\n */\nexport function createCSRF<TState extends CSRFState & SessionState & ConnectionState>(\n\toptions: CSRFOptions,\n): MiddlewareHandler<TState> {\n\tif (\n\t\t!isString(options.secret) &&\n\t\t(!Array.isArray(options.secret) || !options.secret.every(isString))\n\t)\n\t\tthrow new TypeError('CSRFOptions.secret must be a string or string array')\n\tif (options.cookie !== undefined && !isString(options.cookie))\n\t\tthrow new TypeError('CSRFOptions.cookie must be a string when provided')\n\tif (options.header !== undefined && !isString(options.header))\n\t\tthrow new TypeError('CSRFOptions.header must be a string when provided')\n\tif (options.field !== undefined && !isString(options.field))\n\t\tthrow new TypeError('CSRFOptions.field must be a string when provided')\n\tif (options.safe !== undefined && !Array.isArray(options.safe))\n\t\tthrow new TypeError('CSRFOptions.safe must be an array when provided')\n\n\tconst secret = options.secret\n\tconst cookieName = options.cookie ?? DEFAULT_CSRF_COOKIE\n\tconst header = options.header ?? DEFAULT_CSRF_HEADER\n\tconst field = options.field ?? DEFAULT_CSRF_FIELD\n\tconst safe = options.safe ?? DEFAULT_CSRF_SAFE_METHODS\n\n\treturn async (request, context, next) => {\n\t\tif (safe.includes(context.method)) {\n\t\t\tconst bound = context.state.session?.id ?? crypto.randomUUID()\n\t\t\tconst token = await signToken(bound, { secret })\n\t\t\tObject.assign(context.state, { csrf: token })\n\t\t\tconst response = await next()\n\t\t\tconst secure = resolveSecure(undefined, context.state.connection?.encrypted ?? false)\n\t\t\tawait writeSignedCookie(response.headers, cookieName, token, secret, {\n\t\t\t\tsameSite: 'Strict',\n\t\t\t\thttpOnly: false,\n\t\t\t\tpath: '/',\n\t\t\t\tsecure,\n\t\t\t})\n\t\t\treturn response\n\t\t}\n\n\t\tconst headerToken = request.headers.get(header)\n\t\tlet submitted: string | undefined\n\t\tif (headerToken !== null) {\n\t\t\tsubmitted = headerToken\n\t\t} else {\n\t\t\tconst body = await context.body()\n\t\t\tif (isRecord(body) && isString(body[field])) submitted = body[field]\n\t\t}\n\t\tif (submitted === undefined) throw new HTTPError(403, 'invalid csrf token')\n\t\tconst cookieToken = await readSignedCookie(request, cookieName, secret)\n\t\tif (cookieToken === undefined || !equalsConstantTime(cookieToken, submitted))\n\t\t\tthrow new HTTPError(403, 'invalid csrf token')\n\t\tconst sessionId = context.state.session?.id\n\t\tif (sessionId !== undefined) {\n\t\t\tconst boundId = await verifyToken(cookieToken, secret)\n\t\t\tif (boundId !== sessionId) throw new HTTPError(403, 'invalid csrf token')\n\t\t}\n\t\treturn next()\n\t}\n}\n\n/**\n * Scope a battery to run ONLY on a set of exact pathnames — elsewhere it\n * steps aside via `next()`.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param paths - One pathname, or a set of pathnames, matched exactly against `context.url.pathname`\n * @param handler - The battery to run when `paths` matches\n * @returns A `MiddlewareHandler<TState>`\n *\n * @example\n * ```ts\n * const scoped = only('/admin', createBearer({ secret }))\n * ```\n */\nexport function only<TState>(\n\tpaths: string | readonly string[],\n\thandler: MiddlewareHandler<TState>,\n): MiddlewareHandler<TState> {\n\tconst matches = new Set(typeof paths === 'string' ? [paths] : paths)\n\treturn async (request, context, next) => {\n\t\tif (matches.has(context.url.pathname)) return handler(request, context, next)\n\t\treturn next()\n\t}\n}\n\n/**\n * Scope a battery to run everywhere EXCEPT a set of exact pathnames — there\n * it steps aside via `next()`.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param paths - One pathname, or a set of pathnames, matched exactly against `context.url.pathname`\n * @param handler - The battery to run when `paths` does not match\n * @returns A `MiddlewareHandler<TState>`\n *\n * @example\n * ```ts\n * const scoped = except('/health', createTelemetry({ record }))\n * ```\n */\nexport function except<TState>(\n\tpaths: string | readonly string[],\n\thandler: MiddlewareHandler<TState>,\n): MiddlewareHandler<TState> {\n\tconst matches = new Set(typeof paths === 'string' ? [paths] : paths)\n\treturn async (request, context, next) => {\n\t\tif (matches.has(context.url.pathname)) return next()\n\t\treturn handler(request, context, next)\n\t}\n}\n","import type {\n\tCookieTransportOptions,\n\tHeaderTransportOptions,\n\tMemorySessionStoreOptions,\n\tSessionInterface,\n\tSessionRow,\n\tSessionStoreInterface,\n\tSessionTransport,\n} from './types.js'\nimport type { Session } from './Session.js'\nimport type { Guard } from '@orkestrel/contract'\nimport type { TableInterface } from '@orkestrel/database'\nimport { isString } from '@orkestrel/contract'\nimport { clearCookie, readSignedCookie, resolveSecure, writeSignedCookie } from '@orkestrel/server'\nimport { DEFAULT_SESSION_COOKIE, DEFAULT_SESSION_HEADER } from './constants.js'\nimport { DatabaseSessionStore } from './stores/DatabaseSessionStore.js'\nimport { MemorySessionStore } from './stores/MemorySessionStore.js'\n\n// ============================================================================\n// @orkestrel/middleware — entity / transport / store factories\n// (AGENTS §5 factories.ts, kind-pure counterpart to middlewares.ts). Every\n// companion `createSession`'s option bag composes: the two `SessionTransport`\n// implementations, and the default `SessionStoreInterface`.\n// ============================================================================\n\n/**\n * Create a signed-cookie {@link SessionTransport} — the session id travels as\n * a `signToken`-signed cookie value.\n *\n * @param options - See {@link CookieTransportOptions}\n * @returns A {@link SessionTransport}\n * @throws {TypeError} When `options.secret` or `options.name` is malformed\n *\n * @example\n * ```ts\n * const transport = createCookieTransport({ secret: 'shh' })\n * ```\n */\nexport function createCookieTransport(options: CookieTransportOptions): SessionTransport {\n\tif (\n\t\t!isString(options.secret) &&\n\t\t(!Array.isArray(options.secret) || !options.secret.every(isString))\n\t)\n\t\tthrow new TypeError('CookieTransportOptions.secret must be a string or string array')\n\tif (options.name !== undefined && !isString(options.name))\n\t\tthrow new TypeError('CookieTransportOptions.name must be a string when provided')\n\tconst name = options.name ?? DEFAULT_SESSION_COOKIE\n\tconst secret = options.secret\n\tconst cookie = options.cookie\n\treturn {\n\t\tread(request) {\n\t\t\treturn readSignedCookie(request, name, secret)\n\t\t},\n\t\tasync write(response, id, encrypted) {\n\t\t\tconst secure = resolveSecure(cookie?.secure, encrypted)\n\t\t\tawait writeSignedCookie(response.headers, name, id, secret, { ...cookie, secure })\n\t\t},\n\t\tclear(response) {\n\t\t\tclearCookie(response.headers, name, cookie)\n\t\t},\n\t}\n}\n\n/**\n * Create a bare-header {@link SessionTransport} — the session id travels\n * verbatim in a request/response header.\n *\n * @param options - See {@link HeaderTransportOptions}\n * @returns A {@link SessionTransport}\n * @throws {TypeError} When `options.header` is malformed\n *\n * @example\n * ```ts\n * const transport = createHeaderTransport()\n * ```\n */\nexport function createHeaderTransport(options?: HeaderTransportOptions): SessionTransport {\n\tif (options?.header !== undefined && !isString(options.header))\n\t\tthrow new TypeError('HeaderTransportOptions.header must be a string when provided')\n\tconst header = options?.header ?? DEFAULT_SESSION_HEADER\n\treturn {\n\t\tread(request) {\n\t\t\treturn request.headers.get(header) ?? undefined\n\t\t},\n\t\twrite(response, id) {\n\t\t\tresponse.headers.set(header, id)\n\t\t},\n\t\tclear(response) {\n\t\t\tresponse.headers.delete(header)\n\t\t},\n\t}\n}\n\n/**\n * Create the default in-process {@link SessionStoreInterface} — a `Map`-backed\n * store enforcing an idle timeout and an absolute lifetime.\n *\n * @typeParam S - The session data payload type\n * @param options - See {@link MemorySessionStoreOptions}\n * @returns A {@link SessionStoreInterface}\n * @throws {TypeError} When `options.ttl` or `options.lifetime` is malformed\n *\n * @remarks\n * The {@link Session} entity is the `create` option's default value factory\n * for `createSession` and deliberately ships WITHOUT its own `create*`\n * factory — the name `createSession` belongs to the battery, not this class.\n *\n * @example\n * ```ts\n * const store = createMemorySessionStore({ ttl: 60_000 })\n * ```\n */\nexport function createMemorySessionStore<S>(\n\toptions?: MemorySessionStoreOptions,\n): SessionStoreInterface<S> {\n\treturn new MemorySessionStore<S>(options)\n}\n\n/**\n * Create a {@link DatabaseSessionStore} as a {@link SessionStoreInterface} —\n * the durable counterpart to `createMemorySessionStore`, over a caller-opened\n * `@orkestrel/database` table (declare it with {@link sessionColumns}).\n *\n * @typeParam S - The session data payload type\n * @param table - The backing `TableInterface<SessionRow>`\n * @param is - A {@link Guard} narrowing a restored snapshot to `S`\n * @param options - The idle `ttl` / absolute `lifetime` thresholds\n * @returns A {@link SessionStoreInterface}\n *\n * @remarks\n * This factory only wraps `new DatabaseSessionStore(...)` — it never opens a\n * database or driver itself; the caller owns that lifecycle and passes in an\n * already-open table.\n *\n * @example\n * ```ts\n * const store = createDatabaseSessionStore(db.table('sessions'), isSession, { ttl: 60_000 })\n * ```\n */\nexport function createDatabaseSessionStore<S extends SessionInterface = Session>(\n\ttable: TableInterface<SessionRow>,\n\tis: Guard<S>,\n\toptions?: { readonly ttl?: number; readonly lifetime?: number },\n): SessionStoreInterface<S> {\n\treturn new DatabaseSessionStore<S>(table, is, options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#entries","#ttl","#lifetime","#capacity","#evict","#expired","#notify","#reserve","#table","#is","#ttl","#lifetime"],"sources":["../../../src/core/constants.ts","../../../src/core/Session.ts","../../../src/core/helpers.ts","../../../src/core/shapers.ts","../../../src/core/stores/MemorySessionStore.ts","../../../src/core/stores/DatabaseSessionStore.ts","../../../src/core/middlewares.ts","../../../src/core/factories.ts"],"sourcesContent":["import type { Encoding } from '@orkestrel/server'\n\n// ============================================================================\n// @orkestrel/middleware — battery defaults (AGENTS §5 constants file).\n// Every default named in PROPOSAL.md §4 and the salvaged spec sheet.\n// Frozen where the value is a record/array so a consumer can read but never\n// mutate the shared default.\n// ============================================================================\n\n/** Default minimum buffered body size (bytes) `createCompression` will compress. */\nexport const DEFAULT_COMPRESSION_THRESHOLD = 1024\n\n/**\n * Default content-codings `createCompression` offers, in preference order —\n * intersected at construction with what the runtime's `CompressionStream`\n * actually supports.\n *\n * @remarks\n * The shipped `@orkestrel/server` peer's {@link Encoding} union is\n * `'gzip' | 'deflate' | 'identity'` — it does not include `'br'` (see the\n * deviation recorded against this constant in the build report). This\n * default therefore offers every non-`identity` coding the peer's type\n * admits; a node-face brotli variant, if one ships, extends this list there.\n */\nexport const DEFAULT_COMPRESSION_ENCODINGS: readonly Encoding[] = Object.freeze(['gzip', 'deflate'])\n\n/** Default `X-Frame-Options` value `createSecurity` sets. */\nexport const DEFAULT_FRAME_OPTIONS = 'DENY'\n\n/**\n * Default `Content-Security-Policy` value `createSecurity` sets — a custom\n * `csp` option REPLACES this wholesale, never merges.\n */\nexport const DEFAULT_CSP =\n\t\"default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'\"\n\n/** Default `Referrer-Policy` value `createSecurity` sets. */\nexport const DEFAULT_REFERRER_POLICY = 'strict-origin-when-cross-origin'\n\n/** Default `Permissions-Policy` value `createSecurity` sets. */\nexport const DEFAULT_PERMISSIONS_POLICY = 'camera=(), microphone=(), geolocation=()'\n\n/** Default `Cross-Origin-Opener-Policy` value `createSecurity` sets. */\nexport const DEFAULT_COOP = 'same-origin'\n\n/** Default `Cross-Origin-Resource-Policy` value `createSecurity` sets. */\nexport const DEFAULT_CORP = 'same-origin'\n\n/** Default `Origin-Agent-Cluster` value `createSecurity` sets. */\nexport const DEFAULT_CLUSTER = '?1'\n\n/** Value `createSecurity` sets for `Cross-Origin-Embedder-Policy` when `coep: true`. */\nexport const DEFAULT_COEP = 'require-corp'\n\n/** Value `createSecurity` sets for `Strict-Transport-Security` when `hsts: true`. */\nexport const DEFAULT_HSTS = 'max-age=31536000; includeSubDomains'\n\n/** Default header `createSecurity` mints/echoes a request identifier into. */\nexport const DEFAULT_IDENTIFIER_HEADER = 'x-request-id'\n\n/** Default methods `createCors` advertises on a preflight response. */\nexport const DEFAULT_CORS_METHODS: readonly string[] = Object.freeze([\n\t'GET',\n\t'POST',\n\t'PUT',\n\t'PATCH',\n\t'DELETE',\n\t'OPTIONS',\n])\n\n/** Default headers `createCors` advertises on a preflight response. */\nexport const DEFAULT_CORS_HEADERS: readonly string[] = Object.freeze([\n\t'Content-Type',\n\t'Authorization',\n])\n\n/** Default response status `createDeadline` returns when its deadline fires first. */\nexport const DEFAULT_DEADLINE_STATUS = 503\n\n/** Default header `createBearer` reads the token from. */\nexport const DEFAULT_BEARER_HEADER = 'authorization'\n\n/** Default scheme prefix `createBearer` strips before verification. */\nexport const DEFAULT_BEARER_SCHEME = 'Bearer'\n\n/** Default maximum number of distinct rate-limit keys `createLimiter` tracks before LRU eviction. */\nexport const DEFAULT_LIMITER_CAPACITY = 10_000\n\n/** Default maximum number of distinct session ids `createMemorySessionStore` tracks before LRU (by last write) eviction. */\nexport const DEFAULT_SESSION_CAPACITY = 10_000\n\n/** Default 429 body message `createLimiter` sends when a key is over budget. */\nexport const DEFAULT_LIMITER_MESSAGE = 'rate limit exceeded'\n\n/** Default cookie name `createCookieTransport` writes the signed session id under. */\nexport const DEFAULT_SESSION_COOKIE = 'session'\n\n/** Default header `createHeaderTransport` carries the session id in. */\nexport const DEFAULT_SESSION_HEADER = 'session-id'\n\n/** Default signed-cookie name `createCSRF` writes the CSRF token under. */\nexport const DEFAULT_CSRF_COOKIE = 'csrf'\n\n/** Default header `createCSRF` reads a mutating request's submitted token from. */\nexport const DEFAULT_CSRF_HEADER = 'x-csrf-token'\n\n/** Default body field `createCSRF` falls back to reading a mutating request's submitted token from. */\nexport const DEFAULT_CSRF_FIELD = '_csrf'\n\n/** Default methods `createCSRF` treats as safe (mint instead of verify). */\nexport const DEFAULT_CSRF_SAFE_METHODS: readonly string[] = Object.freeze([\n\t'GET',\n\t'HEAD',\n\t'OPTIONS',\n])\n","import type { SessionInterface } from './types.js'\n\n/**\n * A server-managed session's default entity — the `create` option's default\n * value factory for `createSession` (ruling G: `Session` ships WITHOUT a\n * `createSession` factory of its own, since that name belongs to the\n * battery).\n *\n * @remarks\n * `data` is a live, mutable `Map` a handler reads/writes directly;\n * `createSession` persists it to the configured store on the way out.\n *\n * @example\n * ```ts\n * const session = new Session('abc123')\n * session.data.set('userId', 'u_1')\n * ```\n */\nexport class Session implements SessionInterface {\n\treadonly id: string\n\treadonly data: Map<string, unknown>\n\n\tconstructor(id: string) {\n\t\tthis.id = id\n\t\tthis.data = new Map()\n\t}\n}\n","import type {\n\tBearerState,\n\tClientInfo,\n\tClientState,\n\tConnectionState,\n\tMultipartBody,\n\tMultipartFile,\n\tSessionControlInterface,\n\tSessionInterface,\n} from './types.js'\nimport type { Encoding, MiddlewareContext } from '@orkestrel/server'\nimport { isRecord, isString } from '@orkestrel/contract'\nimport { clientRateKey, isCompressibleType, mergeVary, negotiateEncoding } from '@orkestrel/server'\nimport { Session } from './Session.js'\n\n// ============================================================================\n// @orkestrel/middleware — core pure leaves (AGENTS §5 helpers.ts).\n// Every function here is a self-contained, referentially-transparent\n// computation the battery factories in middlewares.ts compose — key\n// derivation, wire-field builders, the forwarded-header hop walk,\n// compression feature detection, buffering-eligibility predicates, the\n// session data-carry, and the total state-slice guards.\n// ============================================================================\n\n/**\n * Derive `createLimiter`'s default rate-limit bucket key from a request's\n * resolved identity facts.\n *\n * @remarks\n * Prefers a verified bearer token ({@link BearerState.token}) as\n * `token:<value>`; else a resolved client IP ({@link ClientState.client.ip},\n * set when `createForwarded` is mounted) or the raw socket peer\n * ({@link ConnectionState.connection.ip}) collapsed via `clientRateKey`\n * (IPv6 to its `/64` network) as `ip:<key>`; else the literal `ip:unknown`.\n * Never reads `X-Forwarded-For` itself — that trust decision belongs solely\n * to `createForwarded`.\n *\n * @param state - The slices `createLimiter`'s default key may read\n * @returns The bucket key for the request\n *\n * @example\n * ```ts\n * resolveKey({ token: 'abc' }) // 'token:abc'\n * resolveKey({ client: { ip: '2001:db8::1' } }) // 'ip:2001:db8:0:0::/64'\n * ```\n */\nexport function resolveKey(state: BearerState & ClientState & ConnectionState): string {\n\tif (state.token !== undefined) return `token:${state.token}`\n\tconst ip = state.client?.ip ?? state.connection?.ip\n\tif (ip !== undefined) return `ip:${clientRateKey(ip)}`\n\treturn 'ip:unknown'\n}\n\n/**\n * Build the `Retry-After` header value — whole seconds until a window reset,\n * floored at a minimum of `1` (ruling I).\n *\n * @param resetAt - The window reset instant (same clock unit as `now`)\n * @param now - The current instant\n * @returns The `Retry-After` value in whole seconds, minimum `1`\n *\n * @example\n * ```ts\n * buildRetryAfter(1_500, 1_000) // '1'\n * ```\n */\nexport function buildRetryAfter(resetAt: number, now: number): string {\n\tconst seconds = Math.ceil((resetAt - now) / 1000)\n\treturn String(Math.max(1, seconds))\n}\n\n/**\n * Build the draft `RateLimit` structured header field (ruling I) — emitted\n * only when `createLimiter`'s `policy` option is `true`.\n *\n * @param remaining - The requests still admitted this window\n * @param resetAt - The window reset instant (same clock unit as `now`)\n * @param now - The current instant\n * @returns The `RateLimit` header value\n *\n * @example\n * ```ts\n * buildRateLimitField(4, 1_500, 1_000) // '\"default\";r=4;t=1'\n * ```\n */\nexport function buildRateLimitField(remaining: number, resetAt: number, now: number): string {\n\tconst seconds = Math.max(1, Math.ceil((resetAt - now) / 1000))\n\treturn `\"default\";r=${remaining};t=${seconds}`\n}\n\n/**\n * Build the draft `RateLimit-Policy` structured header field (ruling I) —\n * emitted only when `createLimiter`'s `policy` option is `true`.\n *\n * @param max - The window's admitted request count\n * @param window - The window length in milliseconds\n * @returns The `RateLimit-Policy` header value\n *\n * @example\n * ```ts\n * buildRateLimitPolicyField(10, 60_000) // '\"default\";q=10;w=60'\n * ```\n */\nexport function buildRateLimitPolicyField(max: number, window: number): string {\n\treturn `\"default\";q=${max};w=${Math.ceil(window / 1000)}`\n}\n\n/**\n * Whether a candidate address is a bare (non-CIDR) trusted-hop match — an\n * exact string match, or a simple prefix-CIDR match for IPv4 (`/8`–`/32`).\n * An IPv6 entry matches by exact string only — there is no IPv6 CIDR\n * support.\n *\n * @remarks\n * Supports exact addresses and dotted-decimal IPv4 CIDR (`10.0.0.0/8`\n * through `/32`) verbatim. An IPv6 entry is matched by exact string only\n * (no CIDR expansion) — documented as the supported subset; `createForwarded`\n * never claims full CIDR generality beyond IPv4.\n *\n * @remarks\n * An IPv6 `trusted` roster entry is compared as an EXACT string — it must be\n * supplied in canonical form (no zero-compression normalization, no case\n * folding) by the caller; this function performs no IPv6 normalization of\n * its own.\n *\n * @param address - The candidate hop address\n * @param entry - One `trusted` roster entry — an exact address or an IPv4 CIDR\n * @returns `true` when `address` is covered by `entry`\n *\n * @example\n * ```ts\n * matchesTrustedEntry('10.1.2.3', '10.0.0.0/8') // true\n * matchesTrustedEntry('192.168.1.1', '10.0.0.0/8') // false\n * ```\n */\nexport function matchesTrustedEntry(address: string, entry: string): boolean {\n\tconst slash = entry.indexOf('/')\n\tif (slash === -1) return address === entry\n\tconst network = entry.slice(0, slash)\n\tconst bits = Number(entry.slice(slash + 1))\n\tconst networkParts = network.split('.')\n\tconst addressParts = address.split('.')\n\tif (networkParts.length !== 4 || addressParts.length !== 4) return false\n\tif (!Number.isInteger(bits) || bits < 8 || bits > 32) return false\n\tconst networkOctets = networkParts.map(Number)\n\tconst addressOctets = addressParts.map(Number)\n\tif (networkOctets.some((value) => !Number.isInteger(value) || value < 0 || value > 255))\n\t\treturn false\n\tif (addressOctets.some((value) => !Number.isInteger(value) || value < 0 || value > 255))\n\t\treturn false\n\tconst networkInt =\n\t\t((networkOctets[0] ?? 0) << 24) |\n\t\t((networkOctets[1] ?? 0) << 16) |\n\t\t((networkOctets[2] ?? 0) << 8) |\n\t\t(networkOctets[3] ?? 0)\n\tconst addressInt =\n\t\t((addressOctets[0] ?? 0) << 24) |\n\t\t((addressOctets[1] ?? 0) << 16) |\n\t\t((addressOctets[2] ?? 0) << 8) |\n\t\t(addressOctets[3] ?? 0)\n\tconst mask = bits === 32 ? -1 : ~((1 << (32 - bits)) - 1)\n\treturn (networkInt & mask) === (addressInt & mask)\n}\n\n/**\n * Walk `X-Forwarded-For` right-to-left and resolve the first UNTRUSTED hop\n * address — `createForwarded`'s core algorithm.\n *\n * @remarks\n * Parses `X-Forwarded-For` only. With `proxies` set, trusts exactly that\n * many hops counted from the right (the closest to this server) and returns\n * the next one left of them; with `trusted` set, trusts every CONSECUTIVE\n * hop from the right that matches one of the roster\n * ({@link matchesTrustedEntry}) and returns the first hop that does not. If\n * the rightmost hop (the immediate sender) does not match the roster, the\n * whole header is untrustworthy and this returns `undefined` rather than\n * returning any client-supplied hop value. When every hop is trusted, or the\n * header is absent/empty, returns `undefined` (the caller falls back to the\n * socket peer).\n *\n * @param header - The raw `X-Forwarded-For` header value (comma-separated hops), if present\n * @param trust - Either a trusted hop COUNT or a `trusted` CIDR/exact roster\n * @returns The first untrusted hop address, or `undefined` when none qualifies\n *\n * @example\n * ```ts\n * resolveForwardedFor('203.0.113.7, 10.0.0.1', { proxies: 1 }) // '203.0.113.7'\n * ```\n */\nexport function resolveForwardedFor(\n\theader: string | undefined,\n\ttrust: { readonly proxies: number } | { readonly trusted: readonly string[] },\n): string | undefined {\n\tif (header === undefined) return undefined\n\tconst hops = header\n\t\t.split(',')\n\t\t.map((hop) => hop.trim())\n\t\t.filter((hop) => hop.length > 0)\n\tif (hops.length === 0) return undefined\n\tif ('proxies' in trust) {\n\t\tconst index = hops.length - trust.proxies - 1\n\t\treturn index >= 0 ? hops[index] : undefined\n\t}\n\tconst rightmost = hops[hops.length - 1]\n\tif (rightmost === undefined) return undefined\n\tconst rightmostTrusted = trust.trusted.some((entry) => matchesTrustedEntry(rightmost, entry))\n\tif (!rightmostTrusted) return undefined\n\tfor (let index = hops.length - 2; index >= 0; index -= 1) {\n\t\tconst hop = hops[index]\n\t\tif (hop === undefined) return undefined\n\t\tconst trusted = trust.trusted.some((entry) => matchesTrustedEntry(hop, entry))\n\t\tif (!trusted) return hop\n\t}\n\treturn undefined\n}\n\n/**\n * Feature-detect which of `candidates` the runtime's `CompressionStream`\n * actually supports — `createCompression`'s construction-time intersection\n * (ruling J).\n *\n * @remarks\n * Probes each candidate with `new CompressionStream(candidate)` inside a\n * `try`/`catch`; a coding the runtime rejects is dropped silently. `identity`\n * is never probed (it has no `CompressionStream` coding and is always\n * implicitly acceptable to negotiation).\n *\n * @param candidates - The codings to probe, in preference order\n * @returns The subset of `candidates` the runtime's `CompressionStream` supports, in order\n *\n * @example\n * ```ts\n * detectEncodings(['gzip', 'deflate']) // ['gzip', 'deflate'] on a runtime with both\n * ```\n */\nexport function detectEncodings(candidates: readonly Encoding[]): readonly Encoding[] {\n\tconst supported: Encoding[] = []\n\tfor (const candidate of candidates) {\n\t\tif (candidate === 'identity') continue\n\t\ttry {\n\t\t\t// Construction throws when the runtime doesn't support this coding;\n\t\t\t// `readable` is checked only to use the instance, never inspected further.\n\t\t\tconst stream = new CompressionStream(candidate)\n\t\t\tif (stream.readable) supported.push(candidate)\n\t\t} catch {\n\t\t\t// Unsupported coding on this runtime — drop it, never throw.\n\t\t}\n\t}\n\treturn supported\n}\n\n/**\n * Compress bytes with the host-independent `CompressionStream` primitive.\n *\n * @param bytes - The uncompressed response bytes\n * @param encoding - The negotiated actionable coding\n * @returns The compressed bytes, or the original bytes when the platform\n * stream unexpectedly has no readable body\n *\n * @example\n * ```ts\n * const bytes = new TextEncoder().encode('compress me')\n * const compressed = await compressBytes(bytes, 'gzip')\n * ```\n */\nexport async function compressBytes(\n\tbytes: Uint8Array<ArrayBuffer>,\n\tencoding: Exclude<Encoding, 'identity'>,\n): Promise<Uint8Array<ArrayBuffer>> {\n\tconst source = new Response(bytes).body\n\tif (source === null) return bytes\n\tconst compressed = await new Response(\n\t\tsource.pipeThrough(new CompressionStream(encoding)),\n\t).arrayBuffer()\n\treturn new Uint8Array(compressed)\n}\n\n/**\n * Whether a response is eligible for the compression/ETag buffering pipeline\n * (ruling J) — the shared cheap-skip predicate both batteries apply before\n * ever touching `response.arrayBuffer()`.\n *\n * @remarks\n * Skips a `HEAD` request, a `204`/`304` or otherwise bodyless response, an\n * `event-stream` response (SSE — buffering would hang the connection), and a\n * response that already carries the header the caller is about to set\n * (`skipHeader`, e.g. `Content-Encoding` for compression, `ETag` for the\n * ETag battery).\n *\n * @param method - The request's HTTP method\n * @param response - The candidate response\n * @param skipHeader - The response header whose presence means \"already handled\"\n * @returns `true` when the response should be left untouched\n *\n * @example\n * ```ts\n * isBufferingIneligible('GET', new Response(null, { status: 204 }), 'content-encoding') // true\n * ```\n */\nexport function isBufferingIneligible(\n\tmethod: string,\n\tresponse: Response,\n\tskipHeader: string,\n): boolean {\n\tif (method === 'HEAD') return true\n\tif (response.status === 204 || response.status === 304) return true\n\tif (response.body === null) return true\n\tconst contentType = response.headers.get('content-type')\n\tif (contentType !== null && contentType.toLowerCase().startsWith('text/event-stream')) return true\n\tif (response.headers.has(skipHeader)) return true\n\treturn false\n}\n\n/**\n * Whether a negotiated `Accept-Encoding` outcome is worth acting on —\n * `createCompression`'s negotiation-eligibility half of ruling J's skip list.\n *\n * @param encoding - The negotiated coding, or `undefined` when negotiation failed\n * @returns `true` when `encoding` names an actionable, non-`identity` coding\n *\n * @example\n * ```ts\n * isCompressionNegotiated('gzip') // true\n * isCompressionNegotiated(undefined) // false\n * ```\n */\nexport function isCompressionNegotiated(\n\tencoding: Encoding | undefined,\n): encoding is Exclude<Encoding, 'identity'> {\n\treturn encoding !== undefined && encoding !== 'identity'\n}\n\n/**\n * Resolve an opt-in, value-bearing security header — `string | boolean`\n * (default OFF, `true` uses the secure default), the shape `createSecurity`'s\n * `coep`/`hsts` options use, distinct from the plain value-or-`false` shape\n * `resolveSecurityHeader` (the peer substrate) handles.\n *\n * @param value - The option value — a `string` override, `true` for the secure default, or `false`/`undefined` to omit\n * @param fallback - The secure-default value used when `value` is `true`\n * @returns The header value to set, or `undefined` to omit the header\n *\n * @example\n * ```ts\n * resolveOptInHeader(true, 'require-corp') // 'require-corp'\n * resolveOptInHeader(undefined, 'require-corp') // undefined — omitted\n * ```\n */\nexport function resolveOptInHeader(\n\tvalue: string | boolean | undefined,\n\tfallback: string,\n): string | undefined {\n\tif (value === true) return fallback\n\tif (value === false || value === undefined) return undefined\n\treturn value\n}\n\n/**\n * Rebuild a `Response` around a replacement body while preserving its\n * status/statusText — the buffered-response reconstruction shared by the\n * compression and ETag batteries after they have consumed\n * `response.arrayBuffer()`.\n *\n * @param body - The replacement body (already-buffered bytes, or `null`)\n * @param response - The original response whose status/statusText/headers are preserved\n * @param headers - The headers to apply; defaults to a fresh copy of `response.headers`\n * @returns A new `Response` carrying `body` with `response`'s status/statusText\n *\n * @example\n * ```ts\n * rebuildResponse(bytes, response) // same status/statusText, response's headers copied\n * rebuildResponse(bytes, response, headers) // explicit replacement headers\n * ```\n */\nexport function rebuildResponse(\n\tbody: ConstructorParameters<typeof Response>[0],\n\tresponse: Response,\n\theaders?: ConstructorParameters<typeof Headers>[0],\n): Response {\n\treturn new Response(body, {\n\t\tstatus: response.status,\n\t\tstatusText: response.statusText,\n\t\theaders: headers ?? new Headers(response.headers),\n\t})\n}\n\n/**\n * The shared negotiate → skip → threshold → compress → header-set skeleton\n * both faces' `createCompression` batteries compose — response-body\n * compression over a caller-supplied set of feature-detected codings.\n *\n * @remarks\n * Decision order: {@link isBufferingIneligible} → `options.filter` → stamp\n * `Vary: Accept-Encoding` (every negotiation-eligible response carries it,\n * even when a later skip declines to compress) → `negotiateEncoding` over\n * `options.encodings` → {@link isCompressionNegotiated} → `isCompressibleType`\n * on `Content-Type` → a fast skip when the response already carries a\n * numeric `Content-Length` BELOW `options.threshold` (avoids buffering a\n * body known too small to be worth compressing) → buffer via\n * `response.arrayBuffer()` → a threshold passthrough when the buffered size\n * is still below `options.threshold` → `options.compress` → set\n * `Content-Encoding` and a fresh `Content-Length` via {@link rebuildResponse}.\n * Returns `response` unchanged (aside from the `Vary` stamp) on any skip.\n *\n * @param request - The inbound `Request` (read for `Accept-Encoding`)\n * @param context - The `MiddlewareContext` (read for `context.method`)\n * @param response - The downstream `Response` to consider compressing\n * @param options - The threshold, optional filter, offered encodings, and the runtime's `compress` primitive\n * @returns The original `response` when skipped, or a new compressed `Response`\n *\n * @example\n * ```ts\n * await compressResponse(request, context, response, {\n * \tthreshold: 1024,\n * \tencodings: ['gzip'],\n * \tcompress: async (bytes, encoding) => gzip(bytes),\n * })\n * ```\n */\nexport async function compressResponse(\n\trequest: Request,\n\tcontext: MiddlewareContext<unknown>,\n\tresponse: Response,\n\toptions: {\n\t\treadonly threshold: number\n\t\treadonly filter?: (request: Request, response: Response) => boolean\n\t\treadonly encodings: readonly Encoding[]\n\t\treadonly compress: (\n\t\t\tbytes: Uint8Array<ArrayBuffer>,\n\t\t\tencoding: Exclude<Encoding, 'identity'>,\n\t\t) => Promise<Uint8Array<ArrayBuffer>>\n\t},\n): Promise<Response> {\n\tif (isBufferingIneligible(context.method, response, 'content-encoding')) return response\n\tif (options.filter !== undefined && !options.filter(request, response)) return response\n\tresponse.headers.set(\n\t\t'vary',\n\t\tmergeVary(response.headers.get('vary') ?? undefined, 'Accept-Encoding'),\n\t)\n\tconst acceptEncoding = request.headers.get('accept-encoding')\n\tif (acceptEncoding === null) return response\n\tconst negotiated = negotiateEncoding(acceptEncoding, options.encodings)\n\tif (!isCompressionNegotiated(negotiated)) return response\n\tconst contentType = response.headers.get('content-type')\n\tif (contentType === null || !isCompressibleType(contentType)) return response\n\tconst declaredLength = response.headers.get('content-length')\n\tif (declaredLength !== null) {\n\t\tconst declared = Number(declaredLength)\n\t\tif (Number.isFinite(declared) && declared < options.threshold) return response\n\t}\n\tconst buffer = await response.arrayBuffer()\n\tif (buffer.byteLength < options.threshold) return rebuildResponse(buffer, response)\n\tconst compressed = await options.compress(new Uint8Array(buffer), negotiated)\n\tconst headers = new Headers(response.headers)\n\theaders.set('content-encoding', negotiated)\n\theaders.set('content-length', String(compressed.byteLength))\n\treturn rebuildResponse(compressed, response, headers)\n}\n\n/**\n * Copy every entry of one session's `data` into another — the regenerate\n * data-carry `createSession`'s `control.regenerate()` applies (ruling D).\n *\n * @param from - The source session whose `data` is copied\n * @param to - The destination session `data` is copied into\n *\n * @example\n * ```ts\n * transferSessionData(oldSession, newSession)\n * ```\n */\nexport function transferSessionData(from: SessionInterface, to: SessionInterface): void {\n\tfor (const [key, value] of from.data) to.data.set(key, value)\n}\n\n/**\n * Determine whether a value implements {@link SessionInterface} — a total\n * structural guard (§14): an `id` string plus a `data` `Map`. Prototype-agnostic\n * — accepts a plain object, a null-prototype object, AND a class instance\n * (a real `Session`), since a restored/stored session is routinely a class\n * instance, not a literal.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link SessionInterface}\n *\n * @example\n * ```ts\n * isSession({ id: 'a', data: new Map() }) // true\n * isSession(new Session('a')) // true\n * ```\n */\nexport function isSession(value: unknown): value is SessionInterface {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst id: unknown = Reflect.get(value, 'id')\n\tconst data: unknown = Reflect.get(value, 'data')\n\treturn isString(id) && data instanceof Map\n}\n\n/**\n * Determine whether a value implements {@link SessionControlInterface} — a\n * total structural guard (§14): callable `regenerate` and `destroy`.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link SessionControlInterface}\n *\n * @example\n * ```ts\n * isSessionControl({ regenerate() {}, destroy() {} }) // true\n * ```\n */\nexport function isSessionControl(value: unknown): value is SessionControlInterface {\n\tif (!isRecord(value)) return false\n\treturn typeof value.regenerate === 'function' && typeof value.destroy === 'function'\n}\n\n/**\n * Determine whether a value is one staged {@link MultipartFile} record — a\n * total structural guard (§14) checking every required field's shape.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link MultipartFile}\n */\nexport function isMultipartFile(value: unknown): value is MultipartFile {\n\tif (!isRecord(value)) return false\n\treturn (\n\t\tisString(value.field) &&\n\t\tisString(value.name) &&\n\t\ttypeof value.size === 'number' &&\n\t\tisString(value.mime) &&\n\t\ttypeof value.validated === 'boolean' &&\n\t\tisString(value.status) &&\n\t\tisString(value.path)\n\t)\n}\n\n/**\n * Determine whether a value implements {@link MultipartBody} — a total\n * structural guard (§14): `files` keyed by field name to arrays of\n * {@link MultipartFile}, and a `fields` string record.\n *\n * @param value - The candidate value\n * @returns `true` when `value` is shaped like a {@link MultipartBody}\n *\n * @example\n * ```ts\n * isMultipartBody({ files: {}, fields: { name: 'a' } }) // true\n * ```\n */\nexport function isMultipartBody(value: unknown): value is MultipartBody {\n\tif (!isRecord(value)) return false\n\tif (!isRecord(value.files) || !isRecord(value.fields)) return false\n\tfor (const entries of Object.values(value.files)) {\n\t\tif (!Array.isArray(entries)) return false\n\t\tfor (const entry of entries) if (!isMultipartFile(entry)) return false\n\t}\n\tfor (const fieldValue of Object.values(value.fields)) if (!isString(fieldValue)) return false\n\treturn true\n}\n\n/**\n * Determine whether a request is a CORS PREFLIGHT — an `OPTIONS` request\n * carrying an `Access-Control-Request-Method` header.\n *\n * @param method - The request's HTTP method\n * @param headers - The request's `Headers`\n * @returns `true` when the request is a CORS preflight `createCors` must answer\n *\n * @example\n * ```ts\n * isPreflight('OPTIONS', new Headers({ 'access-control-request-method': 'POST' })) // true\n * ```\n */\nexport function isPreflight(method: string, headers: Headers): boolean {\n\treturn method === 'OPTIONS' && headers.has('access-control-request-method')\n}\n\n/**\n * A parsed client-info fact for {@link ClientInfo} — a leaf shaping helper\n * `createForwarded` uses to build its stashed slice.\n *\n * @param ip - The resolved client IP, if any\n * @returns The {@link ClientInfo} slice value\n *\n * @example\n * ```ts\n * buildClientInfo('203.0.113.7') // { ip: '203.0.113.7' }\n * ```\n */\nexport function buildClientInfo(ip: string | undefined): ClientInfo {\n\treturn { ...(ip !== undefined ? { ip } : {}) }\n}\n\n/**\n * Constant-time string equality — `createCSRF`'s double-submit token\n * comparison, avoiding a timing oracle on the submitted-vs-cookie match.\n *\n * @remarks\n * Length-guarded XOR-accumulate over char codes: a length mismatch short\n * circuits (safe — the lengths of two independently-generated tokens are\n * not a useful timing signal), but once lengths match every character is\n * compared with no early return, so a per-character mismatch never affects\n * how long the comparison takes.\n *\n * @param a - The first string\n * @param b - The second string\n * @returns `true` when `a` and `b` are exactly equal\n *\n * @example\n * ```ts\n * equalsConstantTime('abc', 'abc') // true\n * equalsConstantTime('abc', 'abd') // false\n * ```\n */\nexport function equalsConstantTime(a: string, b: string): boolean {\n\tif (a.length !== b.length) return false\n\tlet diff = 0\n\tfor (let index = 0; index < a.length; index += 1)\n\t\tdiff |= a.charCodeAt(index) ^ b.charCodeAt(index)\n\treturn diff === 0\n}\n\n/**\n * Whether a session has aged past its idle timeout or absolute lifetime as\n * of `now` — the pure expiry predicate `MemorySessionStore` delegates to.\n *\n * @param cursors - The session's `lastSeen` (idle) and `createdAt` (absolute) instants\n * @param now - The current instant (same clock unit as `cursors`)\n * @param limits - The optional `ttl` (idle) and `lifetime` (absolute) thresholds\n * @returns `true` when either configured threshold has elapsed\n *\n * @example\n * ```ts\n * sessionExpired({ lastSeen: 0, createdAt: 0 }, 1_000, { ttl: 500 }) // true\n * ```\n */\nexport function sessionExpired(\n\tcursors: { readonly lastSeen: number; readonly createdAt: number },\n\tnow: number,\n\tlimits: { readonly ttl?: number; readonly lifetime?: number },\n): boolean {\n\tif (limits.ttl !== undefined && now - cursors.lastSeen >= limits.ttl) return true\n\tif (limits.lifetime !== undefined && now - cursors.createdAt >= limits.lifetime) return true\n\treturn false\n}\n\n/**\n * Snapshot a session's `data` Map into a plain, serializable record — the\n * projection a durable store's `set` writes to disk.\n *\n * @param session - The session to snapshot\n * @returns A plain-object copy of `session.data`, keyed alongside `session.id`\n *\n * @remarks\n * `data` is built on a null-prototype object (`Object.create(null)`), never\n * a `{}` literal — a session key literally named `__proto__` must round-trip\n * as an OWN enumerable property instead of hitting `Object.prototype`'s\n * `__proto__` accessor (which would silently drop the entry and risk\n * polluting the shared prototype).\n *\n * @example\n * ```ts\n * snapshotSession(session) // { id: 'abc', data: { userId: 'u_1' } }\n * ```\n */\nexport function snapshotSession(session: SessionInterface): {\n\treadonly id: string\n\treadonly data: Record<string, unknown>\n} {\n\tconst data: Record<string, unknown> = Object.create(null)\n\tfor (const [key, value] of session.data) data[key] = value\n\treturn { id: session.id, data }\n}\n\n/**\n * Rebuild a `Session` from an untrusted snapshot value (the inverse of\n * {@link snapshotSession}) — a durable store's `get` deserialization step.\n *\n * @param value - The candidate snapshot, of unknown shape\n * @returns A rebuilt `Session`, or `undefined` when `value` is malformed\n *\n * @example\n * ```ts\n * restoreSession({ id: 'abc', data: { userId: 'u_1' } }) // Session { id: 'abc', data: Map }\n * restoreSession({ id: 1 }) // undefined\n * ```\n */\nexport function restoreSession(value: unknown): Session | undefined {\n\tif (!isRecord(value)) return undefined\n\tif (!isString(value.id) || !isRecord(value.data)) return undefined\n\tconst session = new Session(value.id)\n\tfor (const [key, entry] of Object.entries(value.data)) session.data.set(key, entry)\n\treturn session\n}\n","import { integerShape, jsonShape, stringShape } from '@orkestrel/contract'\n\n/**\n * The `@orkestrel/database` column shape for a\n * {@link import('./types.js').SessionRow} table — pass as-is to\n * `createDatabase({ tables: { sessions: sessionColumns } })` so an app\n * declaring a durable session table never hand-writes the shape.\n *\n * @remarks\n * `lastSeen`/`createdAt` are `integerShape({ min: 0 })` — the table validates\n * them as integers, so\n * {@link import('./stores/DatabaseSessionStore.js').DatabaseSessionStore}'s\n * `now` clock must yield integer milliseconds (`Date.now()`, the implicit\n * default `createSession` clock). A fractional clock (`performance.now()`)\n * fails the write with a validation error; `MemorySessionStore` carries no\n * such column shape and accepts a fractional clock without complaint.\n *\n * @example\n * ```ts\n * const db = createDatabase({ driver, tables: { sessions: sessionColumns } })\n * ```\n */\nexport const sessionColumns = {\n\tid: stringShape(),\n\tsession: jsonShape(),\n\tlastSeen: integerShape({ min: 0 }),\n\tcreatedAt: integerShape({ min: 0 }),\n}\n","import type { MemorySessionStoreOptions, SessionStoreInterface } from '../types.js'\nimport { DEFAULT_SESSION_CAPACITY } from '../constants.js'\nimport { sessionExpired } from '../helpers.js'\nimport { isFiniteNumber, isFunction } from '@orkestrel/contract'\n\n/**\n * The default in-process {@link SessionStoreInterface} — a `Map`-backed store\n * enforcing both an idle timeout and an absolute lifetime, with lazy\n * (read-time) eviction, a bounded capacity, and no background timers.\n *\n * @typeParam S - The session data payload type\n *\n * @remarks\n * `get` evicts a session whose idle time (`now - lastSeen >= ttl`) or\n * absolute lifetime (`now - createdAt >= lifetime`) has elapsed — the\n * lifetime check fires EVEN IF the session was continuously touched, since\n * `createdAt` is stamped once at the first `set` and preserved across every\n * later re-`set` of the same id. A live read touches `lastSeen`. `delete` of\n * an absent id is a no-op.\n *\n * Capacity is enforced as least-recently-used **by last write**: `set`\n * refreshes an id's recency (deleting then re-inserting so the Map's\n * iteration tail is the most-recently-written id). Inserting a brand-new id\n * once the store is at `capacity` first prunes expired entries; if the store\n * is still full, the least-recently-written id (the Map's head) is evicted.\n * `options.evict` — when provided — is invoked (throw-isolated) with the id\n * on every eviction the store's own policy performs (a capacity eviction or\n * an expired-entry prune), but never for an explicit `delete`.\n *\n * @example\n * ```ts\n * const store = new MemorySessionStore({ ttl: 60_000, lifetime: 3_600_000 })\n * await store.set('abc', { userId: 'u_1' }, Date.now())\n * ```\n */\nexport class MemorySessionStore<S> implements SessionStoreInterface<S> {\n\treadonly #entries: Map<string, { session: S; lastSeen: number; readonly createdAt: number }>\n\treadonly #ttl: number | undefined\n\treadonly #lifetime: number | undefined\n\treadonly #capacity: number\n\treadonly #evict: ((id: string) => void) | undefined\n\n\tconstructor(options?: MemorySessionStoreOptions) {\n\t\tif (options?.ttl !== undefined && !isFiniteNumber(options.ttl))\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.ttl to be a finite number when provided',\n\t\t\t)\n\t\tif (options?.ttl !== undefined && options.ttl <= 0)\n\t\t\tthrow new TypeError('MemorySessionStore requires options.ttl to be positive when provided')\n\t\tif (options?.lifetime !== undefined && !isFiniteNumber(options.lifetime))\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.lifetime to be a finite number when provided',\n\t\t\t)\n\t\tif (options?.lifetime !== undefined && options.lifetime <= 0)\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.lifetime to be positive when provided',\n\t\t\t)\n\t\tif (\n\t\t\toptions?.capacity !== undefined &&\n\t\t\t(!isFiniteNumber(options.capacity) ||\n\t\t\t\t!Number.isInteger(options.capacity) ||\n\t\t\t\toptions.capacity <= 0)\n\t\t)\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.capacity to be a positive integer when provided',\n\t\t\t)\n\t\tif (options?.evict !== undefined && !isFunction(options.evict))\n\t\t\tthrow new TypeError(\n\t\t\t\t'MemorySessionStore requires options.evict to be a function when provided',\n\t\t\t)\n\t\tthis.#entries = new Map()\n\t\tthis.#ttl = options?.ttl\n\t\tthis.#lifetime = options?.lifetime\n\t\tthis.#capacity = options?.capacity ?? DEFAULT_SESSION_CAPACITY\n\t\tthis.#evict = options?.evict\n\t}\n\n\tasync get(id: string, now: number): Promise<S | undefined> {\n\t\tconst entry = this.#entries.get(id)\n\t\tif (entry === undefined) return undefined\n\t\tif (this.#expired(entry, now)) {\n\t\t\tthis.#entries.delete(id)\n\t\t\tthis.#notify(id)\n\t\t\treturn undefined\n\t\t}\n\t\tentry.lastSeen = now\n\t\treturn entry.session\n\t}\n\n\tasync set(id: string, session: S, now: number): Promise<void> {\n\t\tconst existing = this.#entries.get(id)\n\t\tif (existing === undefined) this.#reserve(now)\n\t\tconst createdAt = existing?.createdAt ?? now\n\t\tthis.#entries.delete(id)\n\t\tthis.#entries.set(id, { session, lastSeen: now, createdAt })\n\t}\n\n\tasync delete(id: string): Promise<void> {\n\t\tthis.#entries.delete(id)\n\t}\n\n\t// Whether `entry` has aged past its idle timeout or absolute lifetime as of `now`.\n\t#expired(\n\t\tentry: { session: S; lastSeen: number; readonly createdAt: number },\n\t\tnow: number,\n\t): boolean {\n\t\treturn sessionExpired(entry, now, {\n\t\t\t...(this.#ttl !== undefined ? { ttl: this.#ttl } : {}),\n\t\t\t...(this.#lifetime !== undefined ? { lifetime: this.#lifetime } : {}),\n\t\t})\n\t}\n\n\t// Makes room for a brand-new id: prunes expired entries, then evicts the\n\t// least-recently-written id if the store is still at capacity.\n\t#reserve(now: number): void {\n\t\tif (this.#entries.size < this.#capacity) return\n\t\tfor (const [id, entry] of this.#entries) {\n\t\t\tif (this.#expired(entry, now)) {\n\t\t\t\tthis.#entries.delete(id)\n\t\t\t\tthis.#notify(id)\n\t\t\t}\n\t\t}\n\t\tif (this.#entries.size >= this.#capacity) {\n\t\t\tconst oldest = this.#entries.keys().next().value\n\t\t\tif (oldest !== undefined) {\n\t\t\t\tthis.#entries.delete(oldest)\n\t\t\t\tthis.#notify(oldest)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Invokes the configured evict callback, throw-isolated.\n\t#notify(id: string): void {\n\t\tif (this.#evict === undefined) return\n\t\ttry {\n\t\t\tthis.#evict(id)\n\t\t} catch {\n\t\t\t// swallowed — a broken evict callback can never affect the store\n\t\t}\n\t}\n}\n","import type { SessionInterface, SessionRow, SessionStoreInterface } from '../types.js'\nimport type { Guard } from '@orkestrel/contract'\nimport type { TableInterface } from '@orkestrel/database'\nimport { restoreSession, sessionExpired, snapshotSession } from '../helpers.js'\nimport type { Session } from '../Session.js'\n\n/**\n * A durable {@link SessionStoreInterface} over an `@orkestrel/database`\n * table — the same idle-timeout + absolute-lifetime contract as\n * {@link MemorySessionStore}, backed by a caller-supplied `TableInterface`\n * instead of an in-process `Map`.\n *\n * @typeParam S - The session data payload type\n *\n * @remarks\n * `get` reads the row, evicts (removes the row) once `sessionExpired`\n * reports either threshold elapsed, then rebuilds the session via\n * {@link restoreSession} — a malformed snapshot or one that fails the\n * caller's `is` guard resolves `undefined` rather than throwing. A live read\n * touches `lastSeen`. `set` preserves an existing row's `createdAt` across a\n * re-`set` of the same id (stamped once at the first `set`), mirroring\n * {@link MemorySessionStore}. `delete` of an absent id is a no-op (the\n * table's `remove` contract).\n *\n * A malformed-snapshot or failed-guard `undefined` LEAVES the row in place —\n * unlike the expired path, which removes it. This is deliberate: a\n * caller-contextual `is` guard may reject a session that is still perfectly\n * valid for another flow reading the same table (a differently-shaped `S`,\n * a stricter guard mid-rollout), so `get` never destroys data on a guard\n * miss. A row that no caller's guard ever accepts again self-heals once its\n * `ttl`/`lifetime` elapses on a later `get`.\n *\n * @example\n * ```ts\n * const store = new DatabaseSessionStore(table, isSession, { ttl: 60_000 })\n * await store.set('abc', new Session('abc'), Date.now())\n * ```\n */\nexport class DatabaseSessionStore<\n\tS extends SessionInterface = Session,\n> implements SessionStoreInterface<S> {\n\treadonly #table: TableInterface<SessionRow>\n\treadonly #is: Guard<S>\n\treadonly #ttl: number | undefined\n\treadonly #lifetime: number | undefined\n\n\tconstructor(\n\t\ttable: TableInterface<SessionRow>,\n\t\tis: Guard<S>,\n\t\toptions?: { readonly ttl?: number; readonly lifetime?: number },\n\t) {\n\t\tthis.#table = table\n\t\tthis.#is = is\n\t\tthis.#ttl = options?.ttl\n\t\tthis.#lifetime = options?.lifetime\n\t}\n\n\tasync get(id: string, now: number): Promise<S | undefined> {\n\t\tconst row = await this.#table.get(id)\n\t\tif (row === undefined) return undefined\n\t\tif (\n\t\t\tsessionExpired(row, now, {\n\t\t\t\t...(this.#ttl !== undefined ? { ttl: this.#ttl } : {}),\n\t\t\t\t...(this.#lifetime !== undefined ? { lifetime: this.#lifetime } : {}),\n\t\t\t})\n\t\t) {\n\t\t\tawait this.#table.remove(id)\n\t\t\treturn undefined\n\t\t}\n\t\tconst session = restoreSession(row.session)\n\t\tif (session === undefined || !this.#is(session)) return undefined\n\t\tawait this.#table.update(id, { lastSeen: now })\n\t\treturn session\n\t}\n\n\tasync set(id: string, session: S, now: number): Promise<void> {\n\t\tconst existing = await this.#table.get(id)\n\t\tconst createdAt = existing?.createdAt ?? now\n\t\tawait this.#table.set({ id, session: snapshotSession(session), lastSeen: now, createdAt })\n\t}\n\n\tasync delete(id: string): Promise<void> {\n\t\tawait this.#table.remove(id)\n\t}\n}\n","import type {\n\tBearerOptions,\n\tBearerState,\n\tBodyState,\n\tBoundaryOptions,\n\tClientState,\n\tCompressionOptions,\n\tConnectionState,\n\tCorsOptions,\n\tCSRFOptions,\n\tCSRFState,\n\tDeadlineOptions,\n\tETagOptions,\n\tForwardedOptions,\n\tIdentifierState,\n\tLimiterOptions,\n\tSecurityOptions,\n\tSessionControlInterface,\n\tSessionInterface,\n\tSessionOptions,\n\tSessionState,\n\tSessionStoreInterface,\n\tTelemetryOptions,\n} from './types.js'\nimport type { BudgetInterface } from '@orkestrel/budget'\nimport type { MiddlewareHandler } from '@orkestrel/server'\nimport { isBoolean, isFiniteNumber, isFunction, isRecord, isString } from '@orkestrel/contract'\nimport { linkSignal } from '@orkestrel/abort'\nimport { createBudget } from '@orkestrel/budget'\nimport {\n\tHTTPError,\n\tisHTTPError,\n\tisValidRequestId,\n\tmergeVary,\n\treadSignedCookie,\n\tresolveOrigin,\n\tresolveSecure,\n\tresolveSecurityHeader,\n\tsignToken,\n\tverifyToken,\n\twriteSignedCookie,\n} from '@orkestrel/server'\nimport { createTimeout } from '@orkestrel/timeout'\nimport {\n\tDEFAULT_BEARER_HEADER,\n\tDEFAULT_BEARER_SCHEME,\n\tDEFAULT_CLUSTER,\n\tDEFAULT_COEP,\n\tDEFAULT_COMPRESSION_ENCODINGS,\n\tDEFAULT_COMPRESSION_THRESHOLD,\n\tDEFAULT_COOP,\n\tDEFAULT_CORP,\n\tDEFAULT_CORS_HEADERS,\n\tDEFAULT_CORS_METHODS,\n\tDEFAULT_CSP,\n\tDEFAULT_CSRF_COOKIE,\n\tDEFAULT_CSRF_FIELD,\n\tDEFAULT_CSRF_HEADER,\n\tDEFAULT_CSRF_SAFE_METHODS,\n\tDEFAULT_DEADLINE_STATUS,\n\tDEFAULT_FRAME_OPTIONS,\n\tDEFAULT_HSTS,\n\tDEFAULT_IDENTIFIER_HEADER,\n\tDEFAULT_LIMITER_CAPACITY,\n\tDEFAULT_LIMITER_MESSAGE,\n\tDEFAULT_PERMISSIONS_POLICY,\n\tDEFAULT_REFERRER_POLICY,\n} from './constants.js'\nimport {\n\tbuildClientInfo,\n\tbuildRateLimitField,\n\tbuildRateLimitPolicyField,\n\tbuildRetryAfter,\n\tcompressBytes,\n\tcompressResponse,\n\tdetectEncodings,\n\tequalsConstantTime,\n\tisBufferingIneligible,\n\tisPreflight,\n\trebuildResponse,\n\tresolveForwardedFor,\n\tresolveKey,\n\tresolveOptInHeader,\n\ttransferSessionData,\n} from './helpers.js'\nimport { MemorySessionStore } from './stores/MemorySessionStore.js'\nimport { Session } from './Session.js'\nimport { computeBodyETag, matchesETag } from '@orkestrel/server'\n\n// ============================================================================\n// @orkestrel/middleware — the thirteen pure battery factories\n// (AGENTS §5 middlewares.ts). Each `create{Noun}` closes over its guarded\n// option bag and returns a `MiddlewareHandler<TState>` — a behavior, never a\n// class. See PROPOSAL.md §4 and the orchestrator's seam rulings A–K for the\n// exact semantics each battery pins.\n// ============================================================================\n\n/**\n * The outermost error-rendering battery — catches a downstream throw and\n * renders it as a `Response`.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link BoundaryOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.expose` or `options.report` is malformed\n *\n * @example\n * ```ts\n * const boundary = createBoundary({ expose: false })\n * ```\n */\nexport function createBoundary<TState>(options?: BoundaryOptions): MiddlewareHandler<TState> {\n\tif (options?.expose !== undefined && !isBoolean(options.expose))\n\t\tthrow new TypeError('BoundaryOptions.expose must be a boolean when provided')\n\tif (options?.report !== undefined && !isFunction(options.report))\n\t\tthrow new TypeError('BoundaryOptions.report must be a function when provided')\n\tconst expose = options?.expose ?? false\n\tconst report = options?.report\n\t// The boundary reads only what `next` throws, so the leading two positions of the\n\t// handler signature are conformance-only here.\n\treturn async (_request, _context, next) => {\n\t\ttry {\n\t\t\treturn await next()\n\t\t} catch (error) {\n\t\t\tif (report !== undefined) {\n\t\t\t\ttry {\n\t\t\t\t\treport(error)\n\t\t\t\t} catch {\n\t\t\t\t\t// report is fire-and-forget; its own throw can never alter the response.\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (isHTTPError(error)) return new Response(error.message, { status: error.status })\n\t\t\tconst message = expose\n\t\t\t\t? error instanceof Error\n\t\t\t\t\t? error.message\n\t\t\t\t\t: String(error)\n\t\t\t\t: 'internal server error'\n\t\t\treturn new Response(message, { status: 500 })\n\t\t}\n\t}\n}\n\n/**\n * The access-log/timing seam — records one {@link TelemetryEntry} per request\n * after the response settles.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link TelemetryOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.record` is not a function\n *\n * @example\n * ```ts\n * const telemetry = createTelemetry({ record: (entry) => console.log(entry) })\n * ```\n */\nexport function createTelemetry<TState>(options: TelemetryOptions): MiddlewareHandler<TState> {\n\tif (!isFunction(options.record)) throw new TypeError('TelemetryOptions.record must be a function')\n\tconst record = options.record\n\t// The entry is built from `context` alone, so the first position is conformance-only.\n\treturn async (_request, context, next) => {\n\t\tconst start = Date.now()\n\t\tlet response: Response | undefined\n\t\ttry {\n\t\t\tresponse = await next()\n\t\t\treturn response\n\t\t} finally {\n\t\t\tconst duration = Date.now() - start\n\t\t\tconst status = response?.status ?? 500\n\t\t\ttry {\n\t\t\t\trecord({ method: context.method, pathname: context.url.pathname, status, duration })\n\t\t\t} catch {\n\t\t\t\t// record's own throw is swallowed — a broken sink can never fail the response.\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Response-body compression — negotiates and compresses a buffered response\n * body over the runtime's feature-detected `CompressionStream` codings.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link CompressionOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.threshold` or `options.filter` is malformed\n *\n * @example\n * ```ts\n * const compression = createCompression({ threshold: 1024 })\n * ```\n */\nexport function createCompression<TState>(options?: CompressionOptions): MiddlewareHandler<TState> {\n\tif (\n\t\toptions?.threshold !== undefined &&\n\t\t(!isFiniteNumber(options.threshold) || options.threshold < 0)\n\t)\n\t\tthrow new TypeError('CompressionOptions.threshold must be a finite number when provided')\n\tif (options?.filter !== undefined && !isFunction(options.filter))\n\t\tthrow new TypeError('CompressionOptions.filter must be a function when provided')\n\tif (options?.encodings !== undefined && !Array.isArray(options.encodings))\n\t\tthrow new TypeError('CompressionOptions.encodings must be an array when provided')\n\tconst threshold = options?.threshold ?? DEFAULT_COMPRESSION_THRESHOLD\n\tconst encodings = detectEncodings(options?.encodings ?? DEFAULT_COMPRESSION_ENCODINGS)\n\tconst filter = options?.filter\n\treturn async (request, context, next) => {\n\t\tconst response = await next()\n\t\treturn compressResponse(request, context, response, {\n\t\t\tthreshold,\n\t\t\t...(filter !== undefined ? { filter } : {}),\n\t\t\tencodings,\n\t\t\tcompress: compressBytes,\n\t\t})\n\t}\n}\n\n/**\n * Security-headers + request-identifier battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link IdentifierState}\n * @param options - See {@link SecurityOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n *\n * @example\n * ```ts\n * const security = createSecurity({ hsts: true })\n * ```\n */\nexport function createSecurity<TState extends IdentifierState>(\n\toptions?: SecurityOptions,\n): MiddlewareHandler<TState> {\n\tif (\n\t\toptions?.frame !== undefined &&\n\t\toptions.frame !== false &&\n\t\toptions.frame !== 'DENY' &&\n\t\toptions.frame !== 'SAMEORIGIN'\n\t)\n\t\tthrow new TypeError(\n\t\t\t\"SecurityOptions.frame must be 'DENY', 'SAMEORIGIN', or false when provided\",\n\t\t)\n\tif (options?.csp !== undefined && options.csp !== false && !isString(options.csp))\n\t\tthrow new TypeError('SecurityOptions.csp must be a string or false when provided')\n\tif (options?.referrer !== undefined && options.referrer !== false && !isString(options.referrer))\n\t\tthrow new TypeError('SecurityOptions.referrer must be a string or false when provided')\n\tif (\n\t\toptions?.permissions !== undefined &&\n\t\toptions.permissions !== false &&\n\t\t!isString(options.permissions)\n\t)\n\t\tthrow new TypeError('SecurityOptions.permissions must be a string or false when provided')\n\tif (options?.coop !== undefined && options.coop !== false && !isString(options.coop))\n\t\tthrow new TypeError('SecurityOptions.coop must be a string or false when provided')\n\tif (options?.corp !== undefined && options.corp !== false && !isString(options.corp))\n\t\tthrow new TypeError('SecurityOptions.corp must be a string or false when provided')\n\tif (options?.cluster !== undefined && options.cluster !== false && !isString(options.cluster))\n\t\tthrow new TypeError('SecurityOptions.cluster must be a string or false when provided')\n\tif (options?.coep !== undefined && !isBoolean(options.coep) && !isString(options.coep))\n\t\tthrow new TypeError('SecurityOptions.coep must be a string or boolean when provided')\n\tif (options?.hsts !== undefined && !isBoolean(options.hsts) && !isString(options.hsts))\n\t\tthrow new TypeError('SecurityOptions.hsts must be a string or boolean when provided')\n\tif (\n\t\toptions?.identifier !== undefined &&\n\t\toptions.identifier !== false &&\n\t\t!isRecord(options.identifier)\n\t)\n\t\tthrow new TypeError('SecurityOptions.identifier must be an options bag or false when provided')\n\tif (\n\t\tisRecord(options?.identifier) &&\n\t\toptions.identifier.trust !== undefined &&\n\t\t!isBoolean(options.identifier.trust)\n\t)\n\t\tthrow new TypeError('SecurityOptions.identifier.trust must be a boolean when provided')\n\n\tconst frame = options?.frame\n\tconst csp = options?.csp\n\tconst referrer = options?.referrer\n\tconst permissions = options?.permissions\n\tconst coop = options?.coop\n\tconst corp = options?.corp\n\tconst cluster = options?.cluster\n\tconst coep = options?.coep\n\tconst hsts = options?.hsts\n\tconst identifierOption = options?.identifier\n\tconst identifierEnabled = identifierOption !== false\n\tconst trust = isRecord(identifierOption) ? identifierOption.trust === true : false\n\n\treturn async (request, context, next) => {\n\t\tlet identifier: string | undefined\n\t\tif (identifierEnabled) {\n\t\t\tconst incoming = request.headers.get(DEFAULT_IDENTIFIER_HEADER)\n\t\t\tidentifier =\n\t\t\t\ttrust && incoming !== null && isValidRequestId(incoming) ? incoming : crypto.randomUUID()\n\t\t\tObject.assign(context.state, { identifier })\n\t\t}\n\t\tconst response = await next()\n\t\tresponse.headers.set('x-content-type-options', 'nosniff')\n\t\tconst headerTable: ReadonlyArray<readonly [string, string | undefined]> = [\n\t\t\t['x-frame-options', resolveSecurityHeader(frame, DEFAULT_FRAME_OPTIONS)],\n\t\t\t['content-security-policy', resolveSecurityHeader(csp, DEFAULT_CSP)],\n\t\t\t['referrer-policy', resolveSecurityHeader(referrer, DEFAULT_REFERRER_POLICY)],\n\t\t\t['permissions-policy', resolveSecurityHeader(permissions, DEFAULT_PERMISSIONS_POLICY)],\n\t\t\t['cross-origin-opener-policy', resolveSecurityHeader(coop, DEFAULT_COOP)],\n\t\t\t['cross-origin-resource-policy', resolveSecurityHeader(corp, DEFAULT_CORP)],\n\t\t\t['origin-agent-cluster', resolveSecurityHeader(cluster, DEFAULT_CLUSTER)],\n\t\t\t['cross-origin-embedder-policy', resolveOptInHeader(coep, DEFAULT_COEP)],\n\t\t\t['strict-transport-security', resolveOptInHeader(hsts, DEFAULT_HSTS)],\n\t\t]\n\t\tfor (const [name, value] of headerTable)\n\t\t\tif (value !== undefined) response.headers.set(name, value)\n\t\tif (identifierEnabled && identifier !== undefined)\n\t\t\tresponse.headers.set(DEFAULT_IDENTIFIER_HEADER, identifier)\n\t\treturn response\n\t}\n}\n\n/**\n * Cross-Origin Resource Sharing battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link CorsOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n *\n * @example\n * ```ts\n * const cors = createCors({ origin: ['https://app.example'] })\n * ```\n */\nexport function createCors<TState>(options?: CorsOptions): MiddlewareHandler<TState> {\n\tif (options?.origin !== undefined && !isString(options.origin) && !Array.isArray(options.origin))\n\t\tthrow new TypeError('CorsOptions.origin must be a string or string array when provided')\n\tif (options?.methods !== undefined && !Array.isArray(options.methods))\n\t\tthrow new TypeError('CorsOptions.methods must be an array when provided')\n\tif (options?.headers !== undefined && !Array.isArray(options.headers))\n\t\tthrow new TypeError('CorsOptions.headers must be an array when provided')\n\tconst origin = options?.origin ?? '*'\n\tconst methods = options?.methods ?? DEFAULT_CORS_METHODS\n\tconst headers = options?.headers ?? DEFAULT_CORS_HEADERS\n\tconst reflecting = Array.isArray(origin)\n\treturn async (request, context, next) => {\n\t\tconst requestOrigin = request.headers.get('origin') ?? undefined\n\t\tconst allowOrigin = resolveOrigin(origin, requestOrigin)\n\t\tif (isPreflight(context.method, request.headers)) {\n\t\t\tconst preflightHeaders = new Headers()\n\t\t\tif (allowOrigin !== undefined)\n\t\t\t\tpreflightHeaders.set('access-control-allow-origin', allowOrigin)\n\t\t\tpreflightHeaders.set('access-control-allow-methods', methods.join(', '))\n\t\t\tpreflightHeaders.set('access-control-allow-headers', headers.join(', '))\n\t\t\tif (reflecting && allowOrigin !== undefined)\n\t\t\t\tpreflightHeaders.set('vary', mergeVary(undefined, 'Origin'))\n\t\t\treturn new Response(null, { status: 204, headers: preflightHeaders })\n\t\t}\n\t\tconst response = await next()\n\t\tif (allowOrigin !== undefined) response.headers.set('access-control-allow-origin', allowOrigin)\n\t\tif (reflecting && allowOrigin !== undefined)\n\t\t\tresponse.headers.set('vary', mergeVary(response.headers.get('vary') ?? undefined, 'Origin'))\n\t\treturn response\n\t}\n}\n\n/**\n * The application-level per-request deadline battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link DeadlineOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.ms` or `options.status` is malformed\n *\n * @remarks\n * MUST sit OUTSIDE `createBody` in the chain — it reconstructs the inbound\n * `Request` (to link its `signal` to the deadline `signal`), which throws if\n * the body was already consumed upstream (e.g. by `createBody`'s cached read).\n *\n * @example\n * ```ts\n * const deadline = createDeadline({ ms: 5_000 })\n * ```\n */\nexport function createDeadline<TState>(options: DeadlineOptions): MiddlewareHandler<TState> {\n\tif (!isFiniteNumber(options.ms) || options.ms <= 0)\n\t\tthrow new TypeError('DeadlineOptions.ms must be a finite number')\n\tif (options.status !== undefined && !isFiniteNumber(options.status))\n\t\tthrow new TypeError('DeadlineOptions.status must be a finite number when provided')\n\tconst ms = options.ms\n\tconst status = options.status ?? DEFAULT_DEADLINE_STATUS\n\t// The deadline links signals off `request` alone, so the second position is\n\t// conformance-only.\n\treturn async (request, _context, next) => {\n\t\tconst timeout = createTimeout({ ms })\n\t\tconst linked = linkSignal(timeout.signal, request.signal)\n\t\ttimeout.start()\n\t\tconst downstream = next(new Request(request, { signal: linked }))\n\t\tconst deadline = new Promise<Response>((resolve) => {\n\t\t\ttimeout.signal.addEventListener('abort', () => resolve(new Response(null, { status })), {\n\t\t\t\tonce: true,\n\t\t\t})\n\t\t})\n\t\tlet deadlineWon = false\n\t\ttry {\n\t\t\tconst winner = await Promise.race([\n\t\t\t\tdownstream,\n\t\t\t\tdeadline.then((response) => {\n\t\t\t\t\tdeadlineWon = true\n\t\t\t\t\treturn response\n\t\t\t\t}),\n\t\t\t])\n\t\t\treturn winner\n\t\t} finally {\n\t\t\ttimeout.clear()\n\t\t\t// The losing `next()` promise is settled-and-discarded — a late throw\n\t\t\t// from it can never surface as an unhandled rejection; when the\n\t\t\t// deadline won the race, best-effort cancel its late body so the\n\t\t\t// runtime can reclaim the underlying stream.\n\t\t\tdownstream\n\t\t\t\t.then((response) => {\n\t\t\t\t\tif (deadlineWon) response.body?.cancel().catch(() => {})\n\t\t\t\t})\n\t\t\t\t.catch(() => {})\n\t\t}\n\t}\n}\n\n/**\n * The trusted-proxy client-IP resolver battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link ClientState} and {@link ConnectionState}\n * @param options - See {@link ForwardedOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When neither or both of `proxies`/`trusted` are provided, or either is malformed\n *\n * @example\n * ```ts\n * const forwarded = createForwarded({ proxies: 1 })\n * ```\n */\nexport function createForwarded<TState extends ClientState & ConnectionState>(\n\toptions: ForwardedOptions,\n): MiddlewareHandler<TState> {\n\tconst hasProxies = isRecord(options) && 'proxies' in options\n\tconst hasTrusted = isRecord(options) && 'trusted' in options\n\tif (!isRecord(options) || hasProxies === hasTrusted)\n\t\tthrow new TypeError('ForwardedOptions requires exactly one of proxies or trusted')\n\tif (hasProxies) {\n\t\tconst proxies = options.proxies\n\t\tif (!isFiniteNumber(proxies) || !Number.isInteger(proxies) || proxies < 1)\n\t\t\tthrow new TypeError('ForwardedOptions.proxies must be a positive integer')\n\t} else {\n\t\tconst trusted = options.trusted\n\t\tif (!Array.isArray(trusted))\n\t\t\tthrow new TypeError('ForwardedOptions.trusted must be an array when provided')\n\t}\n\tconst trust = hasProxies ? { proxies: options.proxies } : { trusted: options.trusted }\n\treturn async (request, context, next) => {\n\t\tconst header = request.headers.get('x-forwarded-for') ?? undefined\n\t\tconst resolved = resolveForwardedFor(header, trust)\n\t\tconst ip = resolved ?? context.state.connection?.ip\n\t\tObject.assign(context.state, { client: buildClientInfo(ip) })\n\t\treturn next()\n\t}\n}\n\n/**\n * Dynamic response `ETag` + conditional GET battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param options - See {@link ETagOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When `options.weak` is not a boolean\n *\n * @example\n * ```ts\n * const etag = createETag({ weak: true })\n * ```\n */\nexport function createETag<TState>(options?: ETagOptions): MiddlewareHandler<TState> {\n\tif (options?.weak !== undefined && !isBoolean(options.weak))\n\t\tthrow new TypeError('ETagOptions.weak must be a boolean when provided')\n\tconst weak = options?.weak ?? true\n\treturn async (request, context, next) => {\n\t\tconst response = await next()\n\t\tif (context.method !== 'GET' || response.status !== 200) return response\n\t\tif (isBufferingIneligible(context.method, response, 'etag')) return response\n\t\tconst buffer = await response.arrayBuffer()\n\t\tconst bytes = new Uint8Array(buffer)\n\t\tconst etag = await computeBodyETag(bytes, weak)\n\t\tconst ifNoneMatch = request.headers.get('if-none-match')\n\t\tif (ifNoneMatch !== null && matchesETag(ifNoneMatch, etag)) {\n\t\t\tconst headers = new Headers(response.headers)\n\t\t\theaders.set('etag', etag)\n\t\t\theaders.delete('content-length')\n\t\t\treturn new Response(null, { status: 304, headers })\n\t\t}\n\t\tconst headers = new Headers(response.headers)\n\t\theaders.set('etag', etag)\n\t\treturn rebuildResponse(bytes, response, headers)\n\t}\n}\n\n/**\n * Bearer-token authentication battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link BearerState}\n * @param options - See {@link BearerOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n * @throws {HTTPError} `401` when the token is missing, invalid, or expired\n *\n * @example\n * ```ts\n * const bearer = createBearer({ secret: 'shh' })\n * ```\n */\nexport function createBearer<TState extends BearerState>(\n\toptions: BearerOptions,\n): MiddlewareHandler<TState> {\n\tif (\n\t\t!isString(options.secret) &&\n\t\t(!Array.isArray(options.secret) || !options.secret.every(isString))\n\t)\n\t\tthrow new TypeError('BearerOptions.secret must be a string or string array')\n\tif (options.header !== undefined && !isString(options.header))\n\t\tthrow new TypeError('BearerOptions.header must be a string when provided')\n\tif (options.scheme !== undefined && !isString(options.scheme))\n\t\tthrow new TypeError('BearerOptions.scheme must be a string when provided')\n\tconst secret = options.secret\n\tconst header = (options.header ?? DEFAULT_BEARER_HEADER).toLowerCase()\n\tconst scheme = options.scheme ?? DEFAULT_BEARER_SCHEME\n\treturn async (request, context, next) => {\n\t\tconst raw = request.headers.get(header)\n\t\tif (raw === null) throw new HTTPError(401, 'missing token')\n\t\tlet candidate = raw\n\t\tif (scheme !== '') {\n\t\t\tconst prefix = `${scheme.toLowerCase()} `\n\t\t\tif (!raw.toLowerCase().startsWith(prefix)) throw new HTTPError(401, 'missing token')\n\t\t\tcandidate = raw.slice(prefix.length)\n\t\t}\n\t\tconst verified = await verifyToken(candidate, secret)\n\t\tif (verified === undefined) throw new HTTPError(401, 'invalid token')\n\t\tObject.assign(context.state, { token: verified })\n\t\treturn next()\n\t}\n}\n\n/**\n * Fixed-window rate-limiting battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link BearerState}, {@link ClientState}, and {@link ConnectionState}\n * @param options - See {@link LimiterOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n *\n * @example\n * ```ts\n * const limiter = createLimiter({ max: 100, window: 60_000 })\n * ```\n */\nexport function createLimiter<TState extends BearerState & ClientState & ConnectionState>(\n\toptions: LimiterOptions<TState>,\n): MiddlewareHandler<TState> {\n\tif (!isFiniteNumber(options.max) || !Number.isInteger(options.max) || options.max <= 0)\n\t\tthrow new TypeError('LimiterOptions.max must be a positive integer')\n\tif (!isFiniteNumber(options.window) || options.window <= 0)\n\t\tthrow new TypeError('LimiterOptions.window must be a positive finite number')\n\tif (\n\t\toptions.capacity !== undefined &&\n\t\t(!isFiniteNumber(options.capacity) ||\n\t\t\t!Number.isInteger(options.capacity) ||\n\t\t\toptions.capacity <= 0)\n\t)\n\t\tthrow new TypeError('LimiterOptions.capacity must be a positive integer when provided')\n\tif (options.key !== undefined && !isFunction(options.key))\n\t\tthrow new TypeError('LimiterOptions.key must be a function when provided')\n\tif (options.message !== undefined && !isString(options.message))\n\t\tthrow new TypeError('LimiterOptions.message must be a string when provided')\n\tif (options.clock !== undefined && !isFunction(options.clock))\n\t\tthrow new TypeError('LimiterOptions.clock must be a function when provided')\n\tif (options.policy !== undefined && !isBoolean(options.policy))\n\t\tthrow new TypeError('LimiterOptions.policy must be a boolean when provided')\n\tif (options.evict !== undefined && !isFunction(options.evict))\n\t\tthrow new TypeError('LimiterOptions.evict must be a function when provided')\n\tconst max = options.max\n\tconst window = options.window\n\tconst capacity = options.capacity ?? DEFAULT_LIMITER_CAPACITY\n\tconst deriveKey = options.key\n\tconst message = options.message ?? DEFAULT_LIMITER_MESSAGE\n\tconst clock = options.clock ?? Date.now\n\tconst policy = options.policy ?? false\n\tconst evict = options.evict\n\tconst buckets = new Map<string, { budget: BudgetInterface<number>; resetAt: number }>()\n\t// The key is derived from `context` alone, so the first position is conformance-only.\n\treturn async (_request, context, next) => {\n\t\tconst key = deriveKey === undefined ? resolveKey(context.state) : deriveKey(context)\n\t\tconst now = clock()\n\t\tlet bucket = buckets.get(key)\n\t\tif (bucket === undefined) {\n\t\t\tif (buckets.size >= capacity) {\n\t\t\t\tconst oldest = buckets.keys().next().value\n\t\t\t\tif (oldest !== undefined) {\n\t\t\t\t\tbuckets.delete(oldest)\n\t\t\t\t\tif (evict !== undefined) {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tevict(oldest)\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// swallowed — a broken evict callback can never fail the request\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tbucket = {\n\t\t\t\tbudget: createBudget<number>({ max, consume: Number }),\n\t\t\t\tresetAt: now + window,\n\t\t\t}\n\t\t\tbuckets.set(key, bucket)\n\t\t} else {\n\t\t\tbuckets.delete(key)\n\t\t\tbuckets.set(key, bucket)\n\t\t\tif (now >= bucket.resetAt) {\n\t\t\t\tbucket.budget.clear()\n\t\t\t\tbucket.resetAt = now + window\n\t\t\t}\n\t\t}\n\t\tif (bucket.budget.exhausted) {\n\t\t\tconst headers = new Headers()\n\t\t\theaders.set('retry-after', buildRetryAfter(bucket.resetAt, now))\n\t\t\tif (policy) {\n\t\t\t\theaders.set('ratelimit', buildRateLimitField(0, bucket.resetAt, now))\n\t\t\t\theaders.set('ratelimit-policy', buildRateLimitPolicyField(max, window))\n\t\t\t}\n\t\t\treturn new Response(message, { status: 429, headers })\n\t\t}\n\t\tbucket.budget.consume(1)\n\t\treturn next()\n\t}\n}\n\n/**\n * The body-driving battery — eagerly awaits the cached `context.body()` so\n * its throws (or a malformed-JSON `undefined`) surface before the handler\n * runs, and stashes the resolved value onto {@link BodyState.body}.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link BodyState}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {HTTPError} `400` when the request declares `application/json` and the body resolves `undefined`\n *\n * @remarks\n * The shipped `MiddlewareContext.body()` is a parameterless, server-owned\n * cache (`ServerOptions.limit` governs its size cap) — this battery carries\n * no `limit`/`decompression` options (a deliberate break from the deleted old\n * `createBodyParser` surface, which configured them itself). `state.body` is\n * stashed from the SAME awaited call the 400 check reads — `context.body()`\n * is never invoked twice.\n *\n * @example\n * ```ts\n * const body = createBody()\n * ```\n */\nexport function createBody<TState extends BodyState = BodyState>(): MiddlewareHandler<TState> {\n\treturn async (request, context, next) => {\n\t\tconst body = await context.body()\n\t\tif (body !== undefined) Object.assign(context.state, { body })\n\t\tconst contentType = request.headers.get('content-type')\n\t\tif (\n\t\t\tcontentType !== null &&\n\t\t\tcontentType.toLowerCase().startsWith('application/json') &&\n\t\t\tbody === undefined\n\t\t)\n\t\t\tthrow new HTTPError(400, 'invalid json')\n\t\treturn next()\n\t}\n}\n\n/**\n * The generic session battery — resolves, mints, and persists a session\n * across the request, with a mid-handler `regenerate`/`destroy` control handle.\n *\n * @typeParam S - The session entity type the store persists (must implement {@link SessionInterface})\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link SessionState} and {@link ConnectionState}\n * @param options - See {@link SessionOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n * @throws {HTTPError} `404` when `require` is set and no session resolves or mints\n *\n * @example\n * ```ts\n * const session = createSession({ transport: createHeaderTransport() })\n * ```\n */\nexport function createSession<\n\tS extends SessionInterface = SessionInterface,\n\tTState extends SessionState & ConnectionState = SessionState & ConnectionState,\n>(options: SessionOptions<S, TState>): MiddlewareHandler<TState> {\n\tif (\n\t\t!isFunction(options.transport.read) ||\n\t\t!isFunction(options.transport.write) ||\n\t\t!isFunction(options.transport.clear)\n\t)\n\t\tthrow new TypeError('SessionOptions.transport must implement SessionTransport')\n\tif (\n\t\toptions.store !== undefined &&\n\t\t(!isFunction(options.store.get) ||\n\t\t\t!isFunction(options.store.set) ||\n\t\t\t!isFunction(options.store.delete))\n\t)\n\t\tthrow new TypeError('SessionOptions.store must implement SessionStoreInterface when provided')\n\tif (options.ttl !== undefined && !isFiniteNumber(options.ttl))\n\t\tthrow new TypeError('SessionOptions.ttl must be a finite number when provided')\n\tif (options.lifetime !== undefined && !isFiniteNumber(options.lifetime))\n\t\tthrow new TypeError('SessionOptions.lifetime must be a finite number when provided')\n\tif (options.create !== undefined && !isFunction(options.create))\n\t\tthrow new TypeError('SessionOptions.create must be a function when provided')\n\tif (options.mint !== undefined && !isFunction(options.mint))\n\t\tthrow new TypeError('SessionOptions.mint must be a function when provided')\n\tif (options.require !== undefined && !isBoolean(options.require))\n\t\tthrow new TypeError('SessionOptions.require must be a boolean when provided')\n\tif (options.ends !== undefined && !isBoolean(options.ends))\n\t\tthrow new TypeError('SessionOptions.ends must be a boolean when provided')\n\tif (options.clock !== undefined && !isFunction(options.clock))\n\t\tthrow new TypeError('SessionOptions.clock must be a function when provided')\n\n\tconst transport = options.transport\n\tconst clock = options.clock ?? Date.now\n\tconst store: SessionStoreInterface<SessionInterface> =\n\t\toptions.store ??\n\t\tnew MemorySessionStore<SessionInterface>({\n\t\t\t...(options.ttl !== undefined ? { ttl: options.ttl } : {}),\n\t\t\t...(options.lifetime !== undefined ? { lifetime: options.lifetime } : {}),\n\t\t\t...(options.capacity !== undefined ? { capacity: options.capacity } : {}),\n\t\t\t...(options.evict !== undefined ? { evict: options.evict } : {}),\n\t\t})\n\tconst create = options.create\n\tconst mint = options.mint\n\tconst requireSession = options.require ?? false\n\tconst ends = options.ends ?? false\n\n\treturn async (request, context, next) => {\n\t\tconst now = clock()\n\t\tconst encrypted = context.state.connection?.encrypted ?? false\n\t\tconst incomingId = await transport.read(request)\n\t\tlet session = incomingId !== undefined ? await store.get(incomingId, now) : undefined\n\n\t\tif (ends && context.method === 'DELETE' && session !== undefined) {\n\t\t\tawait store.delete(session.id)\n\t\t\treturn new Response(null, { status: 204 })\n\t\t}\n\n\t\tlet minted = false\n\t\tif (session === undefined) {\n\t\t\tconst shouldMint = mint !== undefined ? await mint(context) : true\n\t\t\tif (shouldMint) {\n\t\t\t\tconst id = crypto.randomUUID()\n\t\t\t\tsession = create === undefined ? new Session(id) : create(id)\n\t\t\t\tminted = true\n\t\t\t} else if (requireSession) {\n\t\t\t\tthrow new HTTPError(404, 'session required')\n\t\t\t}\n\t\t}\n\n\t\tlet destroyed = false\n\t\tlet regenerated: SessionInterface | undefined\n\t\tconst activeSession = session\n\n\t\tif (activeSession !== undefined) {\n\t\t\tconst control: SessionControlInterface = {\n\t\t\t\tregenerate() {\n\t\t\t\t\tif (destroyed) return\n\t\t\t\t\tconst newSession =\n\t\t\t\t\t\tcreate === undefined ? new Session(crypto.randomUUID()) : create(crypto.randomUUID())\n\t\t\t\t\ttransferSessionData(activeSession, newSession)\n\t\t\t\t\tregenerated = newSession\n\t\t\t\t},\n\t\t\t\tdestroy() {\n\t\t\t\t\tdestroyed = true\n\t\t\t\t\tregenerated = undefined\n\t\t\t\t},\n\t\t\t}\n\t\t\tObject.assign(context.state, { session: activeSession, control })\n\t\t}\n\n\t\tconst response = await next()\n\n\t\tif (activeSession !== undefined) {\n\t\t\tif (destroyed) {\n\t\t\t\tawait store.delete(activeSession.id)\n\t\t\t\tawait transport.clear(response)\n\t\t\t} else if (regenerated !== undefined) {\n\t\t\t\tawait store.set(regenerated.id, regenerated, clock())\n\t\t\t\tawait store.delete(activeSession.id)\n\t\t\t\tawait transport.write(response, regenerated.id, encrypted)\n\t\t\t} else {\n\t\t\t\tawait store.set(activeSession.id, activeSession, clock())\n\t\t\t\tif (minted) await transport.write(response, activeSession.id, encrypted)\n\t\t\t}\n\t\t}\n\n\t\treturn response\n\t}\n}\n\n/**\n * Session-bound double-submit CSRF protection battery.\n *\n * @typeParam TState - The consumer's opaque per-request state type, must carry {@link CSRFState}, {@link SessionState}, and {@link ConnectionState}\n * @param options - See {@link CSRFOptions}\n * @returns A `MiddlewareHandler<TState>`\n * @throws {TypeError} When any option is malformed\n * @throws {HTTPError} `403` when the submitted token is missing, mismatched, or bound to a different session\n *\n * @example\n * ```ts\n * const csrf = createCSRF({ secret: 'shh' })\n * ```\n */\nexport function createCSRF<TState extends CSRFState & SessionState & ConnectionState>(\n\toptions: CSRFOptions,\n): MiddlewareHandler<TState> {\n\tif (\n\t\t!isString(options.secret) &&\n\t\t(!Array.isArray(options.secret) || !options.secret.every(isString))\n\t)\n\t\tthrow new TypeError('CSRFOptions.secret must be a string or string array')\n\tif (options.cookie !== undefined && !isString(options.cookie))\n\t\tthrow new TypeError('CSRFOptions.cookie must be a string when provided')\n\tif (options.header !== undefined && !isString(options.header))\n\t\tthrow new TypeError('CSRFOptions.header must be a string when provided')\n\tif (options.field !== undefined && !isString(options.field))\n\t\tthrow new TypeError('CSRFOptions.field must be a string when provided')\n\tif (options.safe !== undefined && !Array.isArray(options.safe))\n\t\tthrow new TypeError('CSRFOptions.safe must be an array when provided')\n\n\tconst secret = options.secret\n\tconst cookieName = options.cookie ?? DEFAULT_CSRF_COOKIE\n\tconst header = options.header ?? DEFAULT_CSRF_HEADER\n\tconst field = options.field ?? DEFAULT_CSRF_FIELD\n\tconst safe = options.safe ?? DEFAULT_CSRF_SAFE_METHODS\n\n\treturn async (request, context, next) => {\n\t\tif (safe.includes(context.method)) {\n\t\t\tconst bound = context.state.session?.id ?? crypto.randomUUID()\n\t\t\tconst token = await signToken(bound, { secret })\n\t\t\tObject.assign(context.state, { csrf: token })\n\t\t\tconst response = await next()\n\t\t\tconst secure = resolveSecure(undefined, context.state.connection?.encrypted ?? false)\n\t\t\tawait writeSignedCookie(response.headers, cookieName, token, secret, {\n\t\t\t\tsameSite: 'Strict',\n\t\t\t\thttpOnly: false,\n\t\t\t\tpath: '/',\n\t\t\t\tsecure,\n\t\t\t})\n\t\t\treturn response\n\t\t}\n\n\t\tconst headerToken = request.headers.get(header)\n\t\tlet submitted: string | undefined\n\t\tif (headerToken !== null) {\n\t\t\tsubmitted = headerToken\n\t\t} else {\n\t\t\tconst body = await context.body()\n\t\t\tif (isRecord(body) && isString(body[field])) submitted = body[field]\n\t\t}\n\t\tif (submitted === undefined) throw new HTTPError(403, 'invalid csrf token')\n\t\tconst cookieToken = await readSignedCookie(request, cookieName, secret)\n\t\tif (cookieToken === undefined || !equalsConstantTime(cookieToken, submitted))\n\t\t\tthrow new HTTPError(403, 'invalid csrf token')\n\t\tconst sessionId = context.state.session?.id\n\t\tif (sessionId !== undefined) {\n\t\t\tconst boundId = await verifyToken(cookieToken, secret)\n\t\t\tif (boundId !== sessionId) throw new HTTPError(403, 'invalid csrf token')\n\t\t}\n\t\treturn next()\n\t}\n}\n\n/**\n * Scope a battery to run ONLY on a set of exact pathnames — elsewhere it\n * steps aside via `next()`.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param paths - One pathname, or a set of pathnames, matched exactly against `context.url.pathname`\n * @param handler - The battery to run when `paths` matches\n * @returns A `MiddlewareHandler<TState>`\n *\n * @example\n * ```ts\n * const scoped = only('/admin', createBearer({ secret }))\n * ```\n */\nexport function only<TState>(\n\tpaths: string | readonly string[],\n\thandler: MiddlewareHandler<TState>,\n): MiddlewareHandler<TState> {\n\tconst matches = new Set(typeof paths === 'string' ? [paths] : paths)\n\treturn async (request, context, next) => {\n\t\tif (matches.has(context.url.pathname)) return handler(request, context, next)\n\t\treturn next()\n\t}\n}\n\n/**\n * Scope a battery to run everywhere EXCEPT a set of exact pathnames — there\n * it steps aside via `next()`.\n *\n * @typeParam TState - The consumer's opaque per-request state type\n * @param paths - One pathname, or a set of pathnames, matched exactly against `context.url.pathname`\n * @param handler - The battery to run when `paths` does not match\n * @returns A `MiddlewareHandler<TState>`\n *\n * @example\n * ```ts\n * const scoped = except('/health', createTelemetry({ record }))\n * ```\n */\nexport function except<TState>(\n\tpaths: string | readonly string[],\n\thandler: MiddlewareHandler<TState>,\n): MiddlewareHandler<TState> {\n\tconst matches = new Set(typeof paths === 'string' ? [paths] : paths)\n\treturn async (request, context, next) => {\n\t\tif (matches.has(context.url.pathname)) return next()\n\t\treturn handler(request, context, next)\n\t}\n}\n","import type {\n\tCookieTransportOptions,\n\tHeaderTransportOptions,\n\tMemorySessionStoreOptions,\n\tSessionInterface,\n\tSessionRow,\n\tSessionStoreInterface,\n\tSessionTransport,\n} from './types.js'\nimport type { Session } from './Session.js'\nimport type { Guard } from '@orkestrel/contract'\nimport type { TableInterface } from '@orkestrel/database'\nimport { isString } from '@orkestrel/contract'\nimport { clearCookie, readSignedCookie, resolveSecure, writeSignedCookie } from '@orkestrel/server'\nimport { DEFAULT_SESSION_COOKIE, DEFAULT_SESSION_HEADER } from './constants.js'\nimport { DatabaseSessionStore } from './stores/DatabaseSessionStore.js'\nimport { MemorySessionStore } from './stores/MemorySessionStore.js'\n\n// ============================================================================\n// @orkestrel/middleware — entity / transport / store factories\n// (AGENTS §5 factories.ts, kind-pure counterpart to middlewares.ts). Every\n// companion `createSession`'s option bag composes: the two `SessionTransport`\n// implementations, and the default `SessionStoreInterface`.\n// ============================================================================\n\n/**\n * Create a signed-cookie {@link SessionTransport} — the session id travels as\n * a `signToken`-signed cookie value.\n *\n * @param options - See {@link CookieTransportOptions}\n * @returns A {@link SessionTransport}\n * @throws {TypeError} When `options.secret` or `options.name` is malformed\n *\n * @example\n * ```ts\n * const transport = createCookieTransport({ secret: 'shh' })\n * ```\n */\nexport function createCookieTransport(options: CookieTransportOptions): SessionTransport {\n\tif (\n\t\t!isString(options.secret) &&\n\t\t(!Array.isArray(options.secret) || !options.secret.every(isString))\n\t)\n\t\tthrow new TypeError('CookieTransportOptions.secret must be a string or string array')\n\tif (options.name !== undefined && !isString(options.name))\n\t\tthrow new TypeError('CookieTransportOptions.name must be a string when provided')\n\tconst name = options.name ?? DEFAULT_SESSION_COOKIE\n\tconst secret = options.secret\n\tconst cookie = options.cookie\n\treturn {\n\t\tread(request) {\n\t\t\treturn readSignedCookie(request, name, secret)\n\t\t},\n\t\tasync write(response, id, encrypted) {\n\t\t\tconst secure = resolveSecure(cookie?.secure, encrypted)\n\t\t\tawait writeSignedCookie(response.headers, name, id, secret, { ...cookie, secure })\n\t\t},\n\t\tclear(response) {\n\t\t\tclearCookie(response.headers, name, cookie)\n\t\t},\n\t}\n}\n\n/**\n * Create a bare-header {@link SessionTransport} — the session id travels\n * verbatim in a request/response header.\n *\n * @param options - See {@link HeaderTransportOptions}\n * @returns A {@link SessionTransport}\n * @throws {TypeError} When `options.header` is malformed\n *\n * @example\n * ```ts\n * const transport = createHeaderTransport()\n * ```\n */\nexport function createHeaderTransport(options?: HeaderTransportOptions): SessionTransport {\n\tif (options?.header !== undefined && !isString(options.header))\n\t\tthrow new TypeError('HeaderTransportOptions.header must be a string when provided')\n\tconst header = options?.header ?? DEFAULT_SESSION_HEADER\n\treturn {\n\t\tread(request) {\n\t\t\treturn request.headers.get(header) ?? undefined\n\t\t},\n\t\twrite(response, id) {\n\t\t\tresponse.headers.set(header, id)\n\t\t},\n\t\tclear(response) {\n\t\t\tresponse.headers.delete(header)\n\t\t},\n\t}\n}\n\n/**\n * Create the default in-process {@link SessionStoreInterface} — a `Map`-backed\n * store enforcing an idle timeout and an absolute lifetime.\n *\n * @typeParam S - The session data payload type\n * @param options - See {@link MemorySessionStoreOptions}\n * @returns A {@link SessionStoreInterface}\n * @throws {TypeError} When `options.ttl` or `options.lifetime` is malformed\n *\n * @remarks\n * The {@link Session} entity is the `create` option's default value factory\n * for `createSession` and deliberately ships WITHOUT its own `create*`\n * factory — the name `createSession` belongs to the battery, not this class.\n *\n * @example\n * ```ts\n * const store = createMemorySessionStore({ ttl: 60_000 })\n * ```\n */\nexport function createMemorySessionStore<S>(\n\toptions?: MemorySessionStoreOptions,\n): SessionStoreInterface<S> {\n\treturn new MemorySessionStore<S>(options)\n}\n\n/**\n * Create a {@link DatabaseSessionStore} as a {@link SessionStoreInterface} —\n * the durable counterpart to `createMemorySessionStore`, over a caller-opened\n * `@orkestrel/database` table (declare it with {@link sessionColumns}).\n *\n * @typeParam S - The session data payload type\n * @param table - The backing `TableInterface<SessionRow>`\n * @param is - A {@link Guard} narrowing a restored snapshot to `S`\n * @param options - The idle `ttl` / absolute `lifetime` thresholds\n * @returns A {@link SessionStoreInterface}\n *\n * @remarks\n * This factory only wraps `new DatabaseSessionStore(...)` — it never opens a\n * database or driver itself; the caller owns that lifecycle and passes in an\n * already-open table.\n *\n * @example\n * ```ts\n * const store = createDatabaseSessionStore(db.table('sessions'), isSession, { ttl: 60_000 })\n * ```\n */\nexport function createDatabaseSessionStore<S extends SessionInterface = Session>(\n\ttable: TableInterface<SessionRow>,\n\tis: Guard<S>,\n\toptions?: { readonly ttl?: number; readonly lifetime?: number },\n): SessionStoreInterface<S> {\n\treturn new DatabaseSessionStore<S>(table, is, options)\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Encoding } from '@orkestrel/server';
|
|
2
2
|
import { FileHandle } from 'node:fs/promises';
|
|
3
3
|
import { MiddlewareHandler } from '@orkestrel/server';
|
|
4
|
-
import { MultipartBody } from '
|
|
5
|
-
import { MultipartFile } from '
|
|
6
|
-
import { MultipartState } from '
|
|
4
|
+
import { MultipartBody } from '@orkestrel/middleware';
|
|
5
|
+
import { MultipartFile } from '@orkestrel/middleware';
|
|
6
|
+
import { MultipartState } from '@orkestrel/middleware';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Compress response bytes with Node's guaranteed zlib gzip/deflate codecs.
|