theokit 0.12.1 → 0.13.0
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/{actions-virtual-module-SQDY3V5X.js → actions-virtual-module-3CDQTWOC.js} +6 -6
- package/dist/{actions-virtual-module-PNPRCEOS.js → actions-virtual-module-EIPXX4ZB.js} +3 -3
- package/dist/agents-typed-client-SAWAAH7K.js +142 -0
- package/dist/agents-typed-client-SAWAAH7K.js.map +1 -0
- package/dist/agents-typed-client-UTEQUA63.js +143 -0
- package/dist/agents-typed-client-UTEQUA63.js.map +1 -0
- package/dist/{app-typed-client-5GYEOYP3.js → app-typed-client-7PBFWZUE.js} +3 -3
- package/dist/{app-typed-client-QG7BVZYW.js → app-typed-client-CSOK7NPC.js} +6 -6
- package/dist/body-parser-web-FV5HWCY3.js +71 -0
- package/dist/body-parser-web-FV5HWCY3.js.map +1 -0
- package/dist/{build-QFRLSEZ4.js → build-HXND27XG.js} +11 -11
- package/dist/{chunk-223EFY5X.js → chunk-2J7XU3PW.js} +68 -27
- package/dist/chunk-2J7XU3PW.js.map +1 -0
- package/dist/{chunk-RESN62GB.js → chunk-2KZQPDYR.js} +5 -48
- package/dist/chunk-2KZQPDYR.js.map +1 -0
- package/dist/chunk-3S3BNW5K.js +445 -0
- package/dist/chunk-3S3BNW5K.js.map +1 -0
- package/dist/{chunk-6FYD34NX.js → chunk-BQDGES7C.js} +28 -28
- package/dist/{chunk-6FYD34NX.js.map → chunk-BQDGES7C.js.map} +1 -1
- package/dist/chunk-EXP56GFQ.js +52 -0
- package/dist/chunk-EXP56GFQ.js.map +1 -0
- package/dist/chunk-F4YUPDJ2.js +115 -0
- package/dist/chunk-F4YUPDJ2.js.map +1 -0
- package/dist/{chunk-NAZ4E2GT.js → chunk-KXA37ONC.js} +2 -2
- package/dist/chunk-NHJMZCAS.js +32 -0
- package/dist/chunk-NHJMZCAS.js.map +1 -0
- package/dist/{chunk-43D6XNDR.js → chunk-O62MW4MT.js} +91 -18
- package/dist/chunk-O62MW4MT.js.map +1 -0
- package/dist/chunk-RSVN727G.js +1 -0
- package/dist/{chunk-7CBRKNQA.js → chunk-RYTZYFSD.js} +198 -6
- package/dist/chunk-RYTZYFSD.js.map +1 -0
- package/dist/chunk-UNLA45FY.js +235 -0
- package/dist/chunk-UNLA45FY.js.map +1 -0
- package/dist/{chunk-GFMQJHXX.js → chunk-WR4F4EEZ.js} +1082 -1074
- package/dist/chunk-WR4F4EEZ.js.map +1 -0
- package/dist/{chunk-AD74EAK3.js → chunk-ZSTZXR2D.js} +1 -30
- package/dist/chunk-ZSTZXR2D.js.map +1 -0
- package/dist/cli/index.js +5 -5
- package/dist/client/index.d.ts +57 -1
- package/dist/client/index.js +84 -3
- package/dist/client/index.js.map +1 -1
- package/dist/{dev-GBXOTXUP.js → dev-OWW4XVIH.js} +10 -10
- package/dist/{dev-emit-FEFEDLZF.js → dev-emit-5MDSBP5D.js} +3 -3
- package/dist/{dev-emit-O4EGOSNV.js → dev-emit-QH2YGZXN.js} +2 -2
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/internal-api-4YTJDITC.js +83 -0
- package/dist/internal-api-EFKZWIYZ.js +66 -0
- package/dist/internal-api-EFKZWIYZ.js.map +1 -0
- package/dist/{openapi-VR6AFBLJ.js → openapi-FHY6HC6I.js} +7 -7
- package/dist/{registry-Q2TZQLUH.js → registry-34LL7NF4.js} +1 -1
- package/dist/{routes-LRYOIIAI.js → routes-EW7TP7NJ.js} +2 -2
- package/dist/server/agent/index.js +2 -1
- package/dist/server/define/index.js +4 -2
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.js +9 -294
- package/dist/server/index.js.map +1 -1
- package/dist/server/scan/index.d.ts +22 -2
- package/dist/server/scan/index.js +1 -1
- package/dist/{start-3ZHAXSJE.js → start-KIQ5TTLR.js} +76 -13
- package/dist/start-KIQ5TTLR.js.map +1 -0
- package/dist/vite-plugin/index.js +6 -4
- package/dist/{vite-plugin-WO72VLYR.js → vite-plugin-RK66K26Z.js} +7 -7
- package/dist/vite-plugin-RK66K26Z.js.map +1 -0
- package/package.json +3 -3
- package/dist/chunk-223EFY5X.js.map +0 -1
- package/dist/chunk-3LVRAGAZ.js +0 -73
- package/dist/chunk-3LVRAGAZ.js.map +0 -1
- package/dist/chunk-43D6XNDR.js.map +0 -1
- package/dist/chunk-7CBRKNQA.js.map +0 -1
- package/dist/chunk-AD74EAK3.js.map +0 -1
- package/dist/chunk-GFMQJHXX.js.map +0 -1
- package/dist/chunk-PBEH6NXR.js +0 -44
- package/dist/chunk-PBEH6NXR.js.map +0 -1
- package/dist/chunk-PIVX3DYW.js +0 -142
- package/dist/chunk-PIVX3DYW.js.map +0 -1
- package/dist/chunk-PPPR5DGR.js +0 -1
- package/dist/chunk-RESN62GB.js.map +0 -1
- package/dist/start-3ZHAXSJE.js.map +0 -1
- /package/dist/{actions-virtual-module-SQDY3V5X.js.map → actions-virtual-module-3CDQTWOC.js.map} +0 -0
- /package/dist/{actions-virtual-module-PNPRCEOS.js.map → actions-virtual-module-EIPXX4ZB.js.map} +0 -0
- /package/dist/{app-typed-client-5GYEOYP3.js.map → app-typed-client-7PBFWZUE.js.map} +0 -0
- /package/dist/{app-typed-client-QG7BVZYW.js.map → app-typed-client-CSOK7NPC.js.map} +0 -0
- /package/dist/{build-QFRLSEZ4.js.map → build-HXND27XG.js.map} +0 -0
- /package/dist/{chunk-NAZ4E2GT.js.map → chunk-KXA37ONC.js.map} +0 -0
- /package/dist/{chunk-PPPR5DGR.js.map → chunk-RSVN727G.js.map} +0 -0
- /package/dist/{dev-GBXOTXUP.js.map → dev-OWW4XVIH.js.map} +0 -0
- /package/dist/{dev-emit-FEFEDLZF.js.map → dev-emit-5MDSBP5D.js.map} +0 -0
- /package/dist/{dev-emit-O4EGOSNV.js.map → dev-emit-QH2YGZXN.js.map} +0 -0
- /package/dist/{vite-plugin-WO72VLYR.js.map → internal-api-4YTJDITC.js.map} +0 -0
- /package/dist/{openapi-VR6AFBLJ.js.map → openapi-FHY6HC6I.js.map} +0 -0
- /package/dist/{registry-Q2TZQLUH.js.map → registry-34LL7NF4.js.map} +0 -0
- /package/dist/{routes-LRYOIIAI.js.map → routes-EW7TP7NJ.js.map} +0 -0
package/dist/server/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/server/openapi/serve-docs.ts","../../src/cache/constants.ts","../../src/cache/validation.ts","../../src/cache/define-cached-function.ts","../../src/cache/cache-control-header.ts","../../src/cache/key-derivation.ts","../../src/cache/define-cached-route.ts","../../src/cache/cache-engine.ts","../../src/cache/in-memory-adapter.ts","../../src/cache/engine-singleton.ts","../../src/cache/revalidate.ts","../../src/cache/route-rules.ts","../../src/server/serialization.ts","../../src/server/http/web-middleware-runner.ts","../../src/server/web-handler.ts","../../src/server/plugin-types.ts","../../src/server/index.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename -- paths derived from build-time cwd, not user input */\n/**\n * Built-in OpenAPI docs — serves Scalar UI at /api/docs and the JSON spec\n * at /api/docs/openapi.json. Zero npm deps (Scalar loaded via CDN).\n *\n * Absorbed from @theokit/plugin-openapi (theokit-plugins sibling repo).\n * Core already emits the spec via vite-plugin/openapi-emit/ — this module\n * adds the runtime serving layer.\n *\n * Security: XSS-safe HTML escaping, CSP header for CDN host, GET-only,\n * 10MB filesize cap, path-traversal defense on specFilePath.\n */\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nexport interface OpenApiDocsOptions {\n /** Path to serve Scalar UI (default: '/api/docs'). */\n docsPath?: string\n /** Path to serve the JSON spec (default: '/api/docs/openapi.json'). */\n openapiJsonPath?: string\n /** Path to the spec file on disk (default: '.theokit/openapi.json'). */\n specFilePath?: string\n /** Page title (default: 'API Reference'). */\n pageTitle?: string\n /** Scalar CDN URL (default: jsdelivr). Must be HTTPS. */\n cdnUrl?: string\n}\n\nconst MAX_SPEC_BYTES = 10 * 1024 * 1024 // 10MB cap (DoS defense)\nconst DEFAULT_CDN = 'https://cdn.jsdelivr.net/npm/@scalar/api-reference'\n\n/**\n * Creates a request handler that serves OpenAPI docs.\n * Returns `null` for non-matching requests (passthrough).\n */\nexport function createOpenApiHandler(opts: OpenApiDocsOptions = {}) {\n const docsPath = opts.docsPath ?? '/api/docs'\n const jsonPath = opts.openapiJsonPath ?? '/api/docs/openapi.json'\n const rawSpecFile = opts.specFilePath ?? '.theokit/openapi.json'\n const title = opts.pageTitle ?? 'API Reference'\n const cdn = opts.cdnUrl ?? DEFAULT_CDN\n\n // Path-traversal defense — validate the RAW input, BEFORE resolve().\n // resolve() normalizes `..` away (e.g. '../../../etc/passwd' becomes an\n // absolute path with no '..' left), so checking the resolved string is a\n // dead guard. Reject any `..` path segment in the input instead; absolute\n // paths without traversal segments stay allowed.\n if (hasDotDotSegment(rawSpecFile)) {\n throw new Error(`[theokit:openapi] specFilePath must not contain \"..\" segments: ${rawSpecFile}`)\n }\n const specFile = resolve(rawSpecFile)\n\n return (request: Request): Response | null => {\n if (request.method !== 'GET') return null\n\n const url = new URL(request.url)\n\n if (url.pathname === docsPath) {\n const cdnHost = new URL(cdn).host\n return new Response(renderScalarHtml(title, jsonPath, cdn), {\n status: 200,\n headers: {\n 'content-type': 'text/html; charset=utf-8',\n 'content-security-policy': `script-src 'self' 'unsafe-eval' ${cdnHost}; style-src 'self' 'unsafe-inline'; img-src 'self' data: ${cdnHost}; font-src 'self' data: ${cdnHost}; connect-src 'self' ${cdnHost}`,\n },\n })\n }\n\n if (url.pathname === jsonPath) {\n return serveSpecFile(specFile)\n }\n\n return null\n }\n}\n\n/** True if the raw path contains a `..` segment (POSIX `/` or Windows `\\` separator). */\nfunction hasDotDotSegment(p: string): boolean {\n return p.split(/[/\\\\]/).includes('..')\n}\n\n// ── HTML renderer ──\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nfunction renderScalarHtml(title: string, specUrl: string, cdnUrl: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>${escapeHtml(title)}</title>\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n</head>\n<body>\n <script id=\"api-reference\" data-url=\"${escapeHtml(specUrl)}\"></script>\n <script src=\"${escapeHtml(cdnUrl)}\"></script>\n</body>\n</html>`\n}\n\n// ── Spec file server ──\n\nfunction serveSpecFile(filePath: string): Response {\n if (!existsSync(filePath)) {\n return jsonResponse(503, {\n error: {\n code: 'OPENAPI_NOT_EMITTED',\n message: 'OpenAPI spec not generated yet. Start the dev server and visit a route first.',\n },\n })\n }\n\n try {\n const stat = statSync(filePath)\n if (stat.size > MAX_SPEC_BYTES) {\n return jsonResponse(413, {\n error: {\n code: 'OPENAPI_TOO_LARGE',\n message: `Spec file exceeds ${MAX_SPEC_BYTES / 1024 / 1024}MB limit`,\n },\n })\n }\n\n const content = readFileSync(filePath, 'utf-8')\n return new Response(content, {\n status: 200,\n headers: { 'content-type': 'application/json', 'cache-control': 'no-cache' },\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to read OpenAPI spec file'\n return jsonResponse(500, {\n error: { code: 'OPENAPI_READ_FAILED', message },\n })\n }\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n","/**\n * Single source of truth for cache subsystem constants.\n * Limits mirror Next.js (NEXT_CACHE_TAG_MAX_LENGTH / NEXT_CACHE_TAG_MAX_ITEMS)\n * — see reference doc §3.2 and ADR D6 of caching-and-revalidation-plan.md.\n */\n\nexport const CACHE_TAG_MAX_LENGTH = 256\nexport const CACHE_TAG_MAX_ITEMS = 128\nexport const THEO_T_PREFIX = '_THEO_T_'\n\nexport const DEFAULT_MAX_AGE = 1\nexport const DEFAULT_SWR_MULTIPLIER = 60\n","import {\n CACHE_TAG_MAX_ITEMS,\n CACHE_TAG_MAX_LENGTH,\n DEFAULT_MAX_AGE,\n THEO_T_PREFIX,\n} from './constants.js'\n\nexport interface ValidationResult<T> {\n valid: T[]\n dropped: { value: unknown; reason: string }[]\n}\n\n/**\n * Validate an array of cache tags.\n * Drops invalid entries (type / length / reserved-prefix / overflow) with warn log.\n * NEVER throws on runtime input — caller-facing safety.\n *\n * EC-1: defensive guard for non-array input (e.g., undefined from optional chain).\n */\nexport function validateTags(tags: unknown, description: string): ValidationResult<string> {\n // EC-1 guard\n if (!Array.isArray(tags)) {\n const result: ValidationResult<string> = {\n valid: [],\n dropped: [{ value: tags, reason: `expected array, got ${typeof tags}` }],\n }\n warnDropped(result, description)\n return result\n }\n\n // Array.isArray narrows to `any[]` (TS limitation). Re-type as unknown[]\n // so each element flows through explicit type guards below.\n const tagArr: unknown[] = tags as unknown[]\n const valid: string[] = []\n const dropped: { value: unknown; reason: string }[] = []\n\n for (let i = 0; i < tagArr.length; i++) {\n if (valid.length >= CACHE_TAG_MAX_ITEMS) {\n for (let j = i; j < tagArr.length; j++) {\n dropped.push({ value: tagArr[j], reason: 'overflow (max 128 tags)' })\n }\n break\n }\n const tag: unknown = tagArr[i]\n if (typeof tag !== 'string') {\n dropped.push({ value: tag, reason: 'invalid type, must be a string' })\n continue\n }\n if (tag.length > CACHE_TAG_MAX_LENGTH) {\n dropped.push({\n value: tag,\n reason: `exceeded max length of ${CACHE_TAG_MAX_LENGTH}`,\n })\n continue\n }\n if (tag.startsWith(THEO_T_PREFIX)) {\n dropped.push({\n value: tag,\n reason: `reserved prefix \"${THEO_T_PREFIX}\"`,\n })\n continue\n }\n valid.push(tag)\n }\n\n warnDropped({ valid, dropped }, description)\n return { valid, dropped }\n}\n\n/**\n * Validate a `maxAge` value in seconds.\n * Throws on invalid input (config-time validation).\n * Returns DEFAULT_MAX_AGE when undefined.\n */\nexport function validateMaxAge(maxAge: unknown, description: string): number {\n if (maxAge === undefined) return DEFAULT_MAX_AGE\n if (typeof maxAge === 'number' && Number.isFinite(maxAge) && maxAge >= 0) {\n return maxAge\n }\n throw new Error(\n `Invalid maxAge \"${JSON.stringify(maxAge)}\" in ${description}, must be a non-negative finite number`,\n )\n}\n\n/**\n * Validate an `expire` value in seconds, optionally cross-checked against `revalidate`.\n * Throws on invalid input (config-time validation).\n */\nexport function validateExpire(\n expire: unknown,\n revalidate: number | undefined,\n description: string,\n): number | undefined {\n if (expire === undefined) return undefined\n if (typeof expire !== 'number' || !Number.isFinite(expire) || expire < 0) {\n throw new Error(\n `Invalid expire \"${JSON.stringify(expire)}\" in ${description}, must be a non-negative finite number`,\n )\n }\n if (revalidate !== undefined && expire < revalidate) {\n throw new Error(\n `Invalid expire ${expire} in ${description}, must be greater than or equal to revalidate ${revalidate}`,\n )\n }\n return expire\n}\n\nfunction warnDropped(result: ValidationResult<string>, description: string): void {\n if (result.dropped.length === 0) return\n console.warn(`[theokit:cache] ${description}: dropped ${result.dropped.length} invalid tag(s):`)\n for (const { value, reason } of result.dropped) {\n console.warn(` - ${JSON.stringify(value)}: ${reason}`)\n }\n}\n","import type { CacheEngine } from './cache-engine.js'\nimport { validateExpire, validateMaxAge, validateTags } from './validation.js'\n\nexport interface DefineCachedFunctionOptions<TArgs extends unknown[], TReturn> {\n /** Required cache namespace; appears in keys as \"fn:${name}:...\" */\n name: string\n /** seconds; defaults to DEFAULT_MAX_AGE (1) */\n maxAge?: number\n /** seconds; stale-while-revalidate window */\n swr?: number\n /** Custom key derivation from args. Default: JSON.stringify(args). */\n getKey?: (...args: TArgs) => string\n /** Static or dynamic tags. */\n tags?: string[] | ((...args: TArgs) => string[])\n /** Version stamp; bump to invalidate all entries under this name. */\n cacheVersion?: string\n /** Transform returned value before caching. */\n transform?: (raw: TReturn) => TReturn\n /** Skip cache if validate returns false (treats existing entry as miss). */\n validate?: (raw: TReturn) => boolean\n /** Called on any error in the cache pipeline. */\n onError?: (err: unknown, ctx: { args: TArgs }) => void\n}\n\nexport type CachedFunction<TArgs extends unknown[], TReturn> = ((\n ...args: TArgs\n) => Promise<TReturn>) & {\n /** Bust the cache entry for these specific args. */\n invalidate: (...args: TArgs) => Promise<void>\n}\n\n/**\n * Wrap an async function with cache semantics.\n * Returns a callable that memoizes by `(name + args)` and exposes `.invalidate(args)`.\n *\n * The engine is supplied by the caller (avoids module-level singleton coupling\n * during testing; framework wiring provides it in production).\n */\nexport function defineCachedFunction<TArgs extends unknown[], TReturn>(\n engine: CacheEngine,\n fn: (...args: TArgs) => TReturn | Promise<TReturn>,\n opts: DefineCachedFunctionOptions<TArgs, TReturn>,\n): CachedFunction<TArgs, TReturn> {\n if (typeof opts.name !== 'string' || opts.name.length === 0) {\n throw new Error('defineCachedFunction: opts.name is required (non-empty string)')\n }\n const maxAge = validateMaxAge(opts.maxAge, `defineCachedFunction(${opts.name})`)\n const swr = validateExpire(opts.swr, maxAge, `defineCachedFunction(${opts.name})`)\n\n const prefix = `fn:${opts.name}`\n\n function deriveCacheKey(args: TArgs): string {\n const tail = opts.getKey ? opts.getKey(...args) : JSON.stringify(args)\n return `${prefix}:${tail}`\n }\n\n function resolveTags(args: TArgs): string[] {\n const raw = typeof opts.tags === 'function' ? opts.tags(...args) : (opts.tags ?? [])\n const { valid } = validateTags(raw, `defineCachedFunction(${opts.name})`)\n return valid\n }\n\n const wrapped = (async (...args: TArgs) => {\n const key = deriveCacheKey(args)\n const tags = resolveTags(args)\n try {\n const { value } = await engine.getOrCompute<TReturn>(key, async () => fn(...args), {\n maxAge,\n swr: swr ?? maxAge * 60,\n tags,\n cacheVersion: opts.cacheVersion,\n transform: opts.transform,\n validate: opts.validate,\n })\n return value\n } catch (err) {\n opts.onError?.(err, { args })\n throw err\n }\n }) as CachedFunction<TArgs, TReturn>\n\n wrapped.invalidate = async (...args: TArgs) => {\n const key = deriveCacheKey(args)\n await engine.invalidate(key)\n }\n\n return wrapped\n}\n","export interface CacheControlInput {\n /** seconds; 0 forces no-cache */\n maxAge: number\n /** stale-while-revalidate window in seconds; 0 or undefined omits directive */\n swr?: number\n /** emit `private,` prefix (skips shared CDN caching) */\n isPrivate?: boolean\n}\n\nconst NO_CACHE_HEADER = 'private, no-cache, no-store, max-age=0, must-revalidate'\n\n/**\n * Build a canonical Cache-Control header value.\n *\n * `maxAge === 0` always yields the strict no-cache directive regardless\n * of `swr` or `isPrivate` — defensive default.\n *\n * EC-13: pure function intentional — caller is responsible for input\n * validation (see validateMaxAge / validateExpire in validation.ts).\n */\nexport function getCacheControlHeader(input: CacheControlInput): string {\n if (input.maxAge === 0) return NO_CACHE_HEADER\n const parts: string[] = []\n if (input.isPrivate) parts.push('private')\n parts.push(`s-maxage=${input.maxAge}`)\n if (input.swr !== undefined && input.swr > 0) {\n parts.push(`stale-while-revalidate=${input.swr}`)\n }\n return parts.join(', ')\n}\n","/**\n * Default tracking/analytics query parameters excluded from cache keys.\n * Mirrors Astro's DEFAULT_EXCLUDED_PARAMS list (memory-provider.ts:117).\n * Set as exact-match (not glob) for KISS + zero-dep.\n */\nexport const DEFAULT_EXCLUDED_QUERY_PARAMS = [\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n 'fbclid',\n 'gclid',\n 'gbraid',\n 'wbraid',\n 'dclid',\n 'msclkid',\n 'twclid',\n 'li_fat_id',\n 'mc_cid',\n 'mc_eid',\n '_ga',\n '_gl',\n '_hsenc',\n '_hsmi',\n '_ke',\n 'oly_anon_id',\n 'oly_enc_id',\n 'rb_clickid',\n 's_cid',\n 'vero_id',\n 'wickedid',\n 'yclid',\n '__s',\n 'ref',\n]\n\nexport interface KeyDerivationOptions {\n /** Total override — when provided, bypasses all internal logic. */\n getKey?: (req: Request) => string | Promise<string>\n /** Exact-match query param names to drop. Defaults to DEFAULT_EXCLUDED_QUERY_PARAMS. */\n excludeQuery?: string[]\n /** Whether to sort query params alphabetically. Defaults to true. */\n sortQuery?: boolean\n /** Header names whose values are appended as `\\0name=value` suffix. */\n varies?: string[]\n /** Namespace prefix (e.g., route name). */\n prefix?: string\n}\n\n/**\n * Derive a deterministic cache key from a Request.\n *\n * Default behaviour: `${prefix?}${protocol}//${lower(host)}${path}${?sortedFilteredQuery}` + Vary suffix.\n * `\\0` separator chosen because it cannot appear in URLs or HTTP header values.\n *\n * EC-6: malformed URL on getKey path is caller's responsibility.\n * EC-7: enforces getKey returns string.\n */\nexport async function deriveKey(req: Request, opts: KeyDerivationOptions = {}): Promise<string> {\n if (opts.getKey) {\n const k = await opts.getKey(req)\n if (typeof k !== 'string') {\n throw new Error(`getKey must return a string, got ${typeof k}`)\n }\n return k\n }\n\n const url = new URL(req.url)\n const queryString = buildQueryString(url, opts)\n const base = `${opts.prefix ? opts.prefix + ':' : ''}${url.protocol}//${url.hostname.toLowerCase()}${url.pathname}${queryString ? '?' + queryString : ''}`\n\n if (!opts.varies || opts.varies.length === 0) return base\n const parts: string[] = []\n for (const header of opts.varies) {\n parts.push(`${header}=${req.headers.get(header) ?? ''}`)\n }\n return base + '\\0' + parts.join('\\0')\n}\n\nfunction buildQueryString(url: URL, opts: KeyDerivationOptions): string {\n const params = new URLSearchParams(url.searchParams)\n const exclude = opts.excludeQuery ?? DEFAULT_EXCLUDED_QUERY_PARAMS\n const excludeSet = new Set(exclude)\n for (const key of [...params.keys()]) {\n if (excludeSet.has(key)) params.delete(key)\n }\n if (opts.sortQuery !== false) params.sort()\n return params.toString()\n}\n","import type { z } from 'zod'\n\nimport type { RouteConfig } from '../core/contracts/route-config.js'\n\nimport { getCacheControlHeader } from './cache-control-header.js'\nimport type { CacheEngine, CacheStatus } from './cache-engine.js'\nimport { THEO_T_PREFIX } from './constants.js'\nimport { deriveKey } from './key-derivation.js'\nimport type { CacheEntry } from './storage-adapter.js'\nimport { validateExpire, validateMaxAge, validateTags } from './validation.js'\n\n/** Default 10 MB cap per cached route entry (EC-3). */\nexport const DEFAULT_MAX_ENTRY_SIZE = 10 * 1024 * 1024\nconst VARIES_IGNORED = new Set(['cookie', 'set-cookie'])\n\nexport interface RouteCacheOptions {\n maxAge?: number\n swr?: number\n tags?: string[]\n varies?: string[]\n getKey?: (req: Request) => string | Promise<string>\n bypassWhen?: (req: Request) => boolean | Promise<boolean>\n cacheVersion?: string\n cacheErrors?: boolean\n methods?: string[]\n cacheable?: (response: Response) => boolean\n /** Max body bytes (EC-3); default DEFAULT_MAX_ENTRY_SIZE. */\n maxEntrySize?: number\n}\n\nexport interface CachedRouteConfig<\n TQuery extends z.ZodType = z.ZodUndefined,\n TBody extends z.ZodType = z.ZodUndefined,\n TParams extends z.ZodType = z.ZodUndefined,\n TCtx = unknown,\n> extends Omit<RouteConfig<TQuery, TBody, TParams, TCtx>, 'handler'> {\n cache: RouteCacheOptions\n handler: (ctx: {\n query: z.infer<TQuery>\n body: z.infer<TBody>\n params: z.infer<TParams>\n request: Request\n ctx: TCtx\n }) => unknown\n}\n\ninterface RouteCacheValue {\n body: string\n status: number\n headers: [string, string][]\n}\n\nconst setCookieWarnedRoutes = new WeakSet()\nconst variesCookieWarnedRoutes = new WeakSet()\nconst oversizedWarnedRoutes = new WeakSet()\n\n/**\n * Wrap a `RouteConfig` with cache-aware handler logic.\n *\n * Architecture: wraps the user `handler` so cache lookup happens AT\n * handler-invocation time, AFTER router middleware (auth/csrf/etc) ran.\n * This structurally satisfies EC-4 (cache-after-auth) without modifying\n * the router internals.\n *\n * Algorithm per request:\n * 1. Method check + bypassWhen + maxAge=0 → call handler raw\n * 2. Derive key (path + sortedQuery + varies, prefix by method)\n * 3. Cache lookup → HIT/STALE return cached Response\n * 4. Miss → run handler, check cacheability (Set-Cookie / status / size / SSE / streaming)\n * 5. Cacheable → write entry + return Response with X-Theo-Cache: MISS (dev)\n * 6. Not cacheable → return original Response unchanged\n *\n * Concurrent dedupe is INTENTIONALLY NOT used for routes — Response objects\n * cannot be safely shared across concurrent callers (body is single-use stream).\n * Each request that misses runs the handler independently.\n */\nexport function defineCachedRoute<\n TQuery extends z.ZodType = z.ZodUndefined,\n TBody extends z.ZodType = z.ZodUndefined,\n TParams extends z.ZodType = z.ZodUndefined,\n TCtx = unknown,\n>(\n engine: CacheEngine,\n config: CachedRouteConfig<TQuery, TBody, TParams, TCtx>,\n): RouteConfig<TQuery, TBody, TParams, TCtx, Response> {\n const { cache, handler, ...rest } = config\n\n const maxAge = validateMaxAge(cache.maxAge, 'defineCachedRoute')\n const swr = validateExpire(cache.swr, maxAge, 'defineCachedRoute')\n if (cache.cacheVersion !== undefined && cache.cacheVersion === '') {\n throw new Error('defineCachedRoute: cacheVersion must be non-empty if provided')\n }\n // EC-19: maxEntrySize validation\n const maxEntrySize = cache.maxEntrySize ?? DEFAULT_MAX_ENTRY_SIZE\n if (!Number.isFinite(maxEntrySize) || maxEntrySize < 0) {\n throw new Error(\n `Invalid maxEntrySize \"${String(cache.maxEntrySize)}\" in defineCachedRoute, must be a non-negative finite number`,\n )\n }\n const methods = new Set((cache.methods ?? ['GET', 'HEAD']).map((m) => m.toUpperCase()))\n const baseTags = validateTags(cache.tags ?? [], 'defineCachedRoute').valid\n\n // EC-2: filter cookie/set-cookie from varies + warn-once per route\n const variesRaw = cache.varies ?? []\n const variesLower = variesRaw.map((v) => v.toLowerCase())\n const hadCookieVary = variesLower.some((v) => VARIES_IGNORED.has(v))\n const safeVaries = variesLower.filter((v) => !VARIES_IGNORED.has(v))\n if (hadCookieVary && !variesCookieWarnedRoutes.has(config)) {\n variesCookieWarnedRoutes.add(config)\n console.warn(\n `[theokit:cache] defineCachedRoute: 'cookie'/'set-cookie' removed from varies — they fragment cache to zero hit rate (EC-2)`,\n )\n }\n\n const wrappedHandler = async (ctx: {\n query: z.infer<TQuery>\n body: z.infer<TBody>\n params: z.infer<TParams>\n request: Request\n ctx: TCtx\n }): Promise<Response> => {\n // TheoKit's dispatcher may pass a Node IncomingMessage (not a Web Request).\n // Normalize to a Web Request so deriveKey / bypassWhen / URL parsing work uniformly.\n const webRequest = toWebRequest(ctx.request)\n\n if (!methods.has(webRequest.method.toUpperCase())) {\n return invokeHandlerAsResponse(handler, ctx)\n }\n if (cache.bypassWhen && (await cache.bypassWhen(webRequest))) {\n return invokeHandlerAsResponse(handler, ctx)\n }\n if (maxAge === 0) {\n return invokeHandlerAsResponse(handler, ctx)\n }\n\n const key = await deriveKey(webRequest, {\n prefix: 'route:' + webRequest.method.toUpperCase(),\n varies: safeVaries,\n getKey: cache.getKey,\n })\n\n // ---- Cache lookup (T4.2 DRY — delegates to engine canonical) ----\n const cached = await engine.tryReadCached<RouteCacheValue>(key, {\n cacheVersion: cache.cacheVersion,\n })\n\n // T4.3 — build options bag once; pass to all helpers (≤ 4 params each).\n const routeCacheCtx: RouteCacheCtx = {\n engine,\n key,\n cache,\n routeConfig: config,\n maxEntrySize,\n maxAge,\n swr,\n baseTags,\n webRequest,\n }\n\n if (cached) {\n if (cached.status === 'hit') {\n return buildResponseFromCache(cached.value, 'hit', maxAge, swr)\n }\n // Only 'stale' remains (engine never returns 'miss' from tryReadCached).\n // Stale: schedule background refresh + return stale immediately.\n scheduleRouteRevalidate(routeCacheCtx, handler, ctx)\n return buildResponseFromCache(cached.value, 'stale', maxAge, swr)\n }\n\n // ---- Miss: run handler ----\n const response = await invokeHandlerAsResponse(handler, ctx)\n return persistAndReturn(routeCacheCtx, response)\n }\n\n return {\n ...rest,\n handler: wrappedHandler,\n }\n}\n\n// T4.2 (PV-5 DRY): tryReadCacheEntry was removed — duplicated the engine's\n// canonical tryReadCached (staleness check, version check, JSON parse,\n// clock-skew clamp). Route wrapper now delegates to `engine.tryReadCached`.\n\n/**\n * T4.3 options-bag context (PV-6 — Clean Code consensus ≤ 4 params).\n * Collapses 10/11 positional params of `persistAndReturn` and\n * `scheduleRouteRevalidate` to 2 params each (ctx + payload).\n *\n * Some fields are derived from `cache` config (EC-22 documented redundancy) —\n * the trade-off is O(1) construction per request for vastly simpler call\n * sites and safer additions of new fields without reordering args.\n */\ninterface RouteCacheCtx {\n engine: CacheEngine\n key: string\n cache: RouteCacheOptions\n routeConfig: object\n maxEntrySize: number\n maxAge: number\n swr: number | undefined\n baseTags: string[]\n webRequest: Request\n}\n\nfunction buildRouteCacheEntry(value: RouteCacheValue, ctx: RouteCacheCtx): CacheEntry {\n const pathTag = THEO_T_PREFIX + new URL(ctx.webRequest.url).pathname\n return {\n body: JSON.stringify(value),\n status: 200,\n headers: [],\n storedAt: Date.now(),\n maxAge: ctx.maxAge,\n swr: ctx.swr ?? ctx.maxAge * 60,\n tags: [...ctx.baseTags, pathTag],\n cacheVersion: ctx.cache.cacheVersion,\n }\n}\n\nasync function persistAndReturn(ctx: RouteCacheCtx, response: Response): Promise<Response> {\n const cacheableResult = await tryCacheResponse(\n response,\n ctx.cache,\n ctx.routeConfig,\n ctx.maxEntrySize,\n )\n if (!cacheableResult) {\n // Not cached — return original response unchanged\n return response\n }\n await ctx.engine.set(ctx.key, buildRouteCacheEntry(cacheableResult, ctx))\n return buildResponseFromCache(cacheableResult, 'miss', ctx.maxAge, ctx.swr)\n}\n\nfunction scheduleRouteRevalidate<THandlerCtx>(\n ctx: RouteCacheCtx,\n handler: (handlerCtx: THandlerCtx) => unknown,\n handlerCtx: THandlerCtx,\n): void {\n void (async () => {\n try {\n const response = await invokeHandlerAsResponse(handler, handlerCtx)\n const result = await tryCacheResponse(response, ctx.cache, ctx.routeConfig, ctx.maxEntrySize)\n if (!result) return\n await ctx.engine.set(ctx.key, buildRouteCacheEntry(result, ctx))\n } catch {\n // Stale entry remains; future request retries on its own stale-check\n }\n })()\n}\n\n/**\n * Type for Node.js IncomingMessage (subset we read).\n * Avoids a direct `node:http` import which would block edge runtimes.\n */\ninterface NodeLikeRequest {\n url?: string\n method?: string\n headers: Record<string, string | string[] | undefined>\n socket?: { encrypted?: boolean }\n}\n\n/**\n * Adapt either a Web Request or a Node IncomingMessage to a Web Request.\n * TheoKit's runtime dispatch may pass either depending on the adapter.\n */\n\n// Node IncomingMessage) inside one function so the call sites stay shape-blind.\nfunction toWebRequest(req: Request | NodeLikeRequest): Request {\n // Web Request fast-path\n if (typeof (req as Request).clone === 'function' && (req as Request).headers instanceof Headers) {\n return req as Request\n }\n const node = req as NodeLikeRequest\n const host = (typeof node.headers.host === 'string' ? node.headers.host : null) ?? 'localhost'\n const protocol = node.socket?.encrypted ? 'https' : 'http'\n const path = node.url ?? '/'\n const url = path.startsWith('http') ? path : `${protocol}://${host}${path}`\n const headers = new Headers()\n for (const [k, v] of Object.entries(node.headers)) {\n if (Array.isArray(v)) {\n for (const item of v) headers.append(k, item)\n } else if (typeof v === 'string') {\n headers.set(k, v)\n }\n }\n return new Request(url, {\n method: (node.method ?? 'GET').toUpperCase(),\n headers,\n })\n}\n\nasync function invokeHandlerAsResponse<TCtx>(\n handler: (ctx: TCtx) => unknown,\n ctx: TCtx,\n): Promise<Response> {\n const raw = await handler(ctx)\n if (raw instanceof Response) return raw\n return Response.json(raw)\n}\n\n/**\n * Decide whether `response` is cacheable. Returns serialized form on yes; undefined on no.\n * Order matters: cheap checks first, body read last (only for cacheable candidates).\n */\nasync function tryCacheResponse(\n response: Response,\n cache: RouteCacheOptions,\n routeConfig: object,\n maxEntrySize: number,\n): Promise<RouteCacheValue | undefined> {\n // D7 / EC-2: Set-Cookie auto-bypass\n if (response.headers.has('set-cookie')) {\n if (!setCookieWarnedRoutes.has(routeConfig)) {\n setCookieWarnedRoutes.add(routeConfig)\n console.warn('[theokit:cache] response has Set-Cookie — skipping cache write (D7)')\n }\n return undefined\n }\n // SSE\n const contentType = response.headers.get('content-type') ?? ''\n if (contentType.includes('text/event-stream')) return undefined\n // EC-11: chunked streaming (transfer-encoding: chunked OR no content-length on a\n // user-constructed Response — i.e., a Response built from a ReadableStream that\n // wasn't via Response.json/text helpers). We detect via the explicit\n // transfer-encoding header since Response.json() also uses a ReadableStream\n // body internally and we don't want to refuse those.\n if (response.headers.get('transfer-encoding')?.toLowerCase() === 'chunked') {\n return undefined\n }\n // D9: status >= 400 not cached unless opt-in\n if (response.status >= 400 && !cache.cacheErrors) return undefined\n // Custom predicate (overrides built-ins)\n if (cache.cacheable && !cache.cacheable(response)) return undefined\n\n // Read body (clone to preserve the response for downstream)\n const text = await response.clone().text()\n\n // EC-3: oversized bypass\n if (text.length > maxEntrySize) {\n if (!oversizedWarnedRoutes.has(routeConfig)) {\n oversizedWarnedRoutes.add(routeConfig)\n console.warn(\n `[theokit:cache] response body ${text.length} bytes exceeds maxEntrySize ${maxEntrySize}; skipping cache (EC-3)`,\n )\n }\n return undefined\n }\n\n const headers: [string, string][] = []\n response.headers.forEach((v, k) => {\n if (k.toLowerCase() === 'set-cookie') return // defense-in-depth\n headers.push([k, v])\n })\n return { body: text, status: response.status, headers }\n}\n\nfunction buildResponseFromCache(\n value: RouteCacheValue,\n status: CacheStatus,\n maxAge: number,\n swr: number | undefined,\n): Response {\n const headers = new Headers(value.headers)\n if (!headers.has('cache-control')) {\n headers.set('cache-control', getCacheControlHeader({ maxAge, swr: swr ?? maxAge * 60 }))\n }\n if (process.env.NODE_ENV !== 'production') {\n let cacheStatusHeader: 'HIT' | 'STALE' | 'MISS' = 'MISS'\n if (status === 'hit') cacheStatusHeader = 'HIT'\n else if (status === 'stale') cacheStatusHeader = 'STALE'\n headers.set('X-Theo-Cache', cacheStatusHeader)\n }\n return new Response(value.body, { status: value.status, headers })\n}\n","import { THEO_T_PREFIX } from './constants.js'\nimport type { CacheEntry, CacheStorageAdapter } from './storage-adapter.js'\n\nexport type CacheStatus = 'hit' | 'stale' | 'miss'\n\nexport interface CacheEngineOptions {\n storage: CacheStorageAdapter\n defaults?: {\n maxAge?: number\n swr?: number\n cacheVersion?: string\n }\n onError?: (err: unknown, ctx: { phase: 'get' | 'set' | 'revalidate'; key: string }) => void\n}\n\nexport interface GetOrComputeOptions<T> {\n maxAge: number\n swr?: number\n tags?: string[]\n cacheVersion?: string\n transform?: (raw: T) => T\n validate?: (raw: T) => boolean\n /**\n * When `true`, the value is returned but NOT written to cache.\n * Used by route middleware to bypass cache for uncacheable responses\n * (Set-Cookie, oversized body, status >= 400 with cacheErrors=false).\n */\n skipCacheWhen?: (raw: T) => boolean\n}\n\nexport interface CacheEngine {\n getOrCompute<T>(\n key: string,\n fn: () => Promise<T>,\n opts: GetOrComputeOptions<T>,\n ): Promise<{ value: T; status: CacheStatus }>\n\n /**\n * Public canonical cache read (T4.2 of architecture-review-remediation-plan,\n * PV-5 DRY consolidation). Returns the parsed value + status (`hit` | `stale`)\n * for callers that DON'T want to bind a loader function (e.g., HTTP route\n * middleware that may want to bypass on miss instead of running a loader).\n *\n * Returns undefined when:\n * - Entry not present in storage\n * - `opts.cacheVersion` mismatch with entry\n * - Body is not a JSON string (parse failed)\n * - `opts.validate` returns false (or throws — caller's onError invoked)\n * - Entry is fully expired (past maxAge + swr)\n */\n tryReadCached<T>(\n key: string,\n opts: { cacheVersion?: string; validate?: (v: T) => boolean },\n ): Promise<{ value: T; status: 'hit' | 'stale' } | undefined>\n\n set(key: string, entry: CacheEntry): Promise<void>\n invalidate(key: string): Promise<boolean>\n invalidateTag(tag: string): Promise<number>\n revalidatePath(path: string, type?: 'layout' | 'page'): Promise<number>\n\n /** Storage adapter passthrough (read-only access for diagnostics). */\n readonly storage: CacheStorageAdapter\n}\n\n/**\n * Build a cache engine wrapping a storage adapter.\n *\n * Implements:\n * - SWR (fresh / stale / expired branching) — Astro `memory-provider.ts:423`-style.\n * - In-flight dedupe via `Map<key, Promise>` — Next.js `pendingRevalidates` pattern.\n * - Tag-based invalidation via adapter's deleteByTag.\n * - Path-as-tag encoding (revalidatePath sugar) — Next.js `revalidate.ts:105`.\n *\n * EC-8: Math.max(0, age) guards clock skew.\n * EC-9: validate callback wrapped in try/catch.\n * EC-10: loader returning undefined skips cache write + warns once.\n *\n * NOTE on max-lines-per-function disable below: createCacheEngine is a factory\n * closure that owns the in-flight/bg/warned maps. Splitting would force the\n * helpers across modules and re-introduce the shared mutable state through\n * parameter lists.\n */\n// eslint-disable-next-line max-lines-per-function\nexport function createCacheEngine(opts: CacheEngineOptions): CacheEngine {\n const { storage, onError } = opts\n const inFlight = new Map<string, Promise<unknown>>()\n const bgInFlight = new Set<string>()\n const undefinedLoaderWarned = new Set<string>()\n\n async function getOrCompute<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n ): Promise<{ value: T; status: CacheStatus }> {\n // Dedupe: concurrent first-miss shares the loader promise\n const pending = inFlight.get(key)\n if (pending) {\n const value = (await pending) as T\n return { value, status: 'miss' }\n }\n\n // maxAge=0 → always miss, never cache\n if (options.maxAge === 0) {\n return claimAndRun(key, fn, options, /* skipWrite */ true)\n }\n\n // Atomically claim the in-flight slot BEFORE any await (dedupe race fix).\n return claimAndRun(key, fn, options, false)\n }\n\n /**\n * Claims the in-flight slot synchronously, then performs the get/loader work\n * inside. Concurrent callers awaiting the same key share this slot.\n */\n function claimAndRun<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n skipWrite: boolean,\n ): Promise<{ value: T; status: CacheStatus }> {\n let resolveOuter!: (v: T) => void\n let rejectOuter!: (e: unknown) => void\n const outerPromise = new Promise<T>((resolve, reject) => {\n resolveOuter = resolve\n rejectOuter = reject\n })\n // Prevent unhandled-rejection when no concurrent awaiter exists\n void outerPromise.catch(() => {\n /* swallow — the leader handles via work promise */\n })\n inFlight.set(key, outerPromise)\n\n const work = (async (): Promise<{ value: T; status: CacheStatus }> => {\n try {\n if (!skipWrite) {\n const cached = await tryReadCached<T>(key, options)\n if (cached) {\n // HIT or STALE — value already resolved\n resolveOuter(cached.value)\n if (cached.status === 'stale') {\n scheduleBackgroundRevalidate(key, fn, options)\n }\n return cached\n }\n }\n // Miss path: run loader\n const value = await runLoader(key, fn, options, skipWrite)\n resolveOuter(value)\n return { value, status: 'miss' as const }\n } catch (err) {\n rejectOuter(err)\n throw err\n } finally {\n inFlight.delete(key)\n }\n })()\n\n return work\n }\n\n // One inline staleness machine; each guard (version check, parse, validate,\n // age, swr window) is one short branch. Extracting per-step would dilute,\n // not clarify.\n // eslint-disable-next-line complexity\n async function tryReadCached<T>(\n key: string,\n options: GetOrComputeOptions<T>,\n ): Promise<{ value: T; status: CacheStatus } | undefined> {\n let entry: CacheEntry | undefined\n try {\n entry = await storage.get(key)\n } catch (err) {\n onError?.(err, { phase: 'get', key })\n return undefined\n }\n if (!entry) return undefined\n if (options.cacheVersion !== undefined && entry.cacheVersion !== options.cacheVersion) {\n return undefined\n }\n if (typeof entry.body !== 'string') return undefined\n let parsed: T\n try {\n parsed = JSON.parse(entry.body) as T\n } catch (err) {\n onError?.(err, { phase: 'get', key })\n return undefined\n }\n // EC-9: validate wrapped in try/catch\n if (options.validate) {\n try {\n if (!options.validate(parsed)) return undefined\n } catch (err) {\n onError?.(err, { phase: 'get', key })\n return undefined\n }\n }\n // EC-8: clamp age to non-negative (clock skew)\n const age = Math.max(0, (Date.now() - entry.storedAt) / 1000)\n if (age <= entry.maxAge) {\n const value = options.transform ? options.transform(parsed) : parsed\n return { value, status: 'hit' }\n }\n if (age <= entry.maxAge + entry.swr) {\n const value = options.transform ? options.transform(parsed) : parsed\n return { value, status: 'stale' }\n }\n return undefined\n }\n\n // runLoader always returns `value` by design: loader output is the caller's\n // contract; every branch short-circuits a *write*, never the return path.\n // eslint-disable-next-line sonarjs/no-invariant-returns, complexity\n async function runLoader<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n skipWrite = false,\n ): Promise<T> {\n const raw = await fn()\n const value = options.transform ? options.transform(raw) : raw\n\n if (skipWrite) return value\n\n // skipCacheWhen sentinel — caller-controlled skip-write\n if (options.skipCacheWhen?.(value)) return value\n\n // EC-9: validate during write\n if (options.validate) {\n let isValid = true\n try {\n isValid = options.validate(value)\n } catch (err) {\n onError?.(err, { phase: 'set', key })\n return value\n }\n if (!isValid) return value\n }\n\n // EC-10: undefined return → warn-once + skip cache\n if (value === undefined) {\n if (!undefinedLoaderWarned.has(key)) {\n undefinedLoaderWarned.add(key)\n console.warn(`[theokit:cache] loader returned undefined for key \"${key}\"; entry not cached`)\n }\n return value\n }\n\n try {\n const entry: CacheEntry = {\n body: JSON.stringify(value),\n status: 200,\n headers: [],\n storedAt: Date.now(),\n maxAge: options.maxAge,\n swr: options.swr ?? 0,\n tags: options.tags ?? [],\n cacheVersion: options.cacheVersion,\n }\n await storage.set(key, entry)\n } catch (err) {\n onError?.(err, { phase: 'set', key })\n }\n return value\n }\n\n function scheduleBackgroundRevalidate<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n ): void {\n if (bgInFlight.has(key)) return\n bgInFlight.add(key)\n void runLoader(key, fn, options)\n .catch((err: unknown) => {\n onError?.(err, { phase: 'revalidate', key })\n })\n .finally(() => {\n bgInFlight.delete(key)\n })\n }\n\n return {\n storage,\n\n getOrCompute,\n\n async tryReadCached<T>(\n key: string,\n opts: { cacheVersion?: string; validate?: (v: T) => boolean },\n ): Promise<{ value: T; status: 'hit' | 'stale' } | undefined> {\n // Delegate to internal impl (which uses the broader GetOrComputeOptions\n // type). tryReadCached never returns status: 'miss' — that codepath\n // returns undefined. Narrow the type via assertion.\n const result = await tryReadCached<T>(key, {\n ...opts,\n maxAge: 0, // unused by tryReadCached\n })\n if (!result) return undefined\n return result as { value: T; status: 'hit' | 'stale' }\n },\n\n async set(key, entry) {\n await storage.set(key, entry)\n },\n\n async invalidate(key) {\n // Best-effort: clear in-flight too so next request starts fresh\n inFlight.delete(key)\n return storage.delete(key)\n },\n\n async invalidateTag(tag) {\n return storage.deleteByTag(tag)\n },\n\n async revalidatePath(path, type) {\n const tag = THEO_T_PREFIX + path + (type ? '/' + type : '')\n return storage.deleteByTag(tag)\n },\n }\n}\n","import type { CacheEntry, CacheStorageAdapter } from './storage-adapter.js'\n\nexport interface InMemoryCacheAdapterOptions {\n /** Maximum number of entries before LRU eviction. Default 1000. */\n maxEntries?: number\n}\n\n/**\n * In-memory cache adapter with LRU eviction + reverse tag index.\n *\n * Uses Map insertion-order for O(1) LRU (Astro LRUMap pattern).\n * Reverse index `tagIndex: Map<tag, Set<key>>` makes deleteByTag O(matched-keys).\n *\n * Invariants:\n * - `entries.size <= maxEntries` post-set.\n * - `tagIndex[tag].has(key)` ↔ `entries.get(key)?.tags.includes(tag)`.\n *\n * eslint-disable @typescript-eslint/require-await — the CacheStorageAdapter\n * interface is intentionally async so Redis/file/external adapters fit. The\n * in-memory variant returns immediately but keeps the async signature to\n * satisfy the contract.\n */\n/* eslint-disable @typescript-eslint/require-await */\nexport class InMemoryCacheAdapter implements CacheStorageAdapter {\n readonly name = 'memory'\n readonly #entries = new Map<string, CacheEntry>()\n readonly #tagIndex = new Map<string, Set<string>>()\n readonly #maxEntries: number\n\n constructor(opts: InMemoryCacheAdapterOptions = {}) {\n this.#maxEntries = opts.maxEntries ?? 1000\n }\n\n async get(key: string): Promise<CacheEntry | undefined> {\n const entry = this.#entries.get(key)\n if (entry === undefined) return undefined\n // LRU bump\n this.#entries.delete(key)\n this.#entries.set(key, entry)\n return entry\n }\n\n async set(key: string, entry: CacheEntry): Promise<void> {\n // Overwrite case: clean old tags first\n const existing = this.#entries.get(key)\n if (existing) {\n this.#removeKeyFromTagIndex(key, existing.tags)\n this.#entries.delete(key)\n } else if (this.#entries.size >= this.#maxEntries) {\n // LRU eviction\n const oldestIter = this.#entries.keys().next()\n if (!oldestIter.done) {\n const oldestKey = oldestIter.value\n const oldestEntry = this.#entries.get(oldestKey)\n if (oldestEntry) {\n this.#removeKeyFromTagIndex(oldestKey, oldestEntry.tags)\n }\n this.#entries.delete(oldestKey)\n }\n }\n this.#entries.set(key, entry)\n for (const tag of entry.tags) {\n let bucket = this.#tagIndex.get(tag)\n if (!bucket) {\n bucket = new Set()\n this.#tagIndex.set(tag, bucket)\n }\n bucket.add(key)\n }\n }\n\n async delete(key: string): Promise<boolean> {\n const entry = this.#entries.get(key)\n if (!entry) return false\n this.#removeKeyFromTagIndex(key, entry.tags)\n this.#entries.delete(key)\n return true\n }\n\n async deleteByTag(tag: string): Promise<number> {\n const bucket = this.#tagIndex.get(tag)\n if (!bucket || bucket.size === 0) return 0\n const keys = [...bucket]\n for (const key of keys) {\n const entry = this.#entries.get(key)\n if (entry) {\n this.#unindexFromOtherTags(key, entry.tags, tag)\n }\n this.#entries.delete(key)\n }\n this.#tagIndex.delete(tag)\n return keys.length\n }\n\n async size(): Promise<number> {\n return this.#entries.size\n }\n\n async clear(): Promise<void> {\n this.#entries.clear()\n this.#tagIndex.clear()\n }\n\n async *keys(prefix?: string): AsyncIterableIterator<string> {\n for (const key of this.#entries.keys()) {\n if (prefix && !key.startsWith(prefix)) continue\n yield key\n }\n }\n\n #removeKeyFromTagIndex(key: string, tags: string[]): void {\n for (const tag of tags) {\n const bucket = this.#tagIndex.get(tag)\n if (!bucket) continue\n bucket.delete(key)\n if (bucket.size === 0) this.#tagIndex.delete(tag)\n }\n }\n\n #unindexFromOtherTags(key: string, tags: string[], skipTag: string): void {\n for (const otherTag of tags) {\n if (otherTag === skipTag) continue\n const otherBucket = this.#tagIndex.get(otherTag)\n if (!otherBucket) continue\n otherBucket.delete(key)\n if (otherBucket.size === 0) this.#tagIndex.delete(otherTag)\n }\n }\n}\n","import type { CacheEngine, CacheEngineOptions } from './cache-engine.js'\nimport { createCacheEngine } from './cache-engine.js'\nimport { InMemoryCacheAdapter } from './in-memory-adapter.js'\nimport type { CacheStorageAdapter } from './storage-adapter.js'\n\nexport interface NormalizedCacheConfig {\n enabled: boolean\n storage: 'memory' | CacheStorageAdapter\n maxEntries: number\n defaults: {\n maxAge: number\n swr?: number\n cacheErrors: boolean\n }\n}\n\nlet _engine: CacheEngine | undefined\n\n/**\n * Initialize the singleton cache engine for this process.\n * Throws if called twice; tests should call `_resetCacheEngine()` between.\n */\nexport function initCacheEngine(\n config: NormalizedCacheConfig,\n hooks: Pick<CacheEngineOptions, 'onError'> = {},\n): CacheEngine {\n if (_engine) {\n throw new Error(\n 'Cache engine already initialized — call _resetCacheEngine() in tests, or check init order in production.',\n )\n }\n if (!config.enabled) {\n throw new Error(\n 'initCacheEngine: config.enabled is false. Skip this call entirely when cache is disabled.',\n )\n }\n const adapter =\n config.storage === 'memory'\n ? new InMemoryCacheAdapter({ maxEntries: config.maxEntries })\n : config.storage\n _engine = createCacheEngine({\n storage: adapter,\n defaults: config.defaults,\n onError: hooks.onError,\n })\n return _engine\n}\n\n/**\n * Resolve the singleton cache engine.\n * Throws a clear error if not initialized — usually means the framework\n * bootstrap missed calling initCacheEngine, or cache.enabled is false.\n */\nexport function getCacheEngine(): CacheEngine {\n if (!_engine) {\n throw new Error(\n 'Cache engine not initialized. Ensure theo.config.ts has `cache.enabled: true` and the framework bootstrap called initCacheEngine.',\n )\n }\n return _engine\n}\n\n/**\n * Test-only: clear the singleton so the next test can re-init.\n * Production code MUST NOT call this.\n */\nexport function _resetCacheEngine(): void {\n _engine = undefined\n}\n","import { getCacheEngine } from './engine-singleton.js'\nimport { validateTags } from './validation.js'\n\nexport interface RevalidateResult {\n deleted: number\n}\n\n/**\n * Invalidate all cache entries carrying the given tag.\n * Safe to call from any context (route handler, action, webhook).\n *\n * `opts.expire` accepted for API compatibility with Next.js; at MVP we delete\n * immediately (no SWR-with-expire semantics). A non-zero value emits one warn.\n */\nexport async function revalidateTag(\n tag: string,\n opts?: { expire?: number },\n): Promise<RevalidateResult> {\n if (opts?.expire !== undefined && opts.expire > 0) {\n warnOnce(\n 'revalidateTag-expire',\n '[theokit:cache] revalidateTag opts.expire is accepted but not honored at MVP (entries are deleted immediately).',\n )\n }\n const { valid } = validateTags([tag], 'revalidateTag')\n if (valid.length === 0) return { deleted: 0 }\n const engine = getCacheEngine()\n const deleted = await engine.invalidateTag(valid[0])\n return { deleted }\n}\n\n/**\n * Immediate invalidation (no SWR), Server-Action-safe.\n * Same semantics as revalidateTag at MVP; kept as separate name for\n * call-site clarity (\"I want fresh data NOW\").\n */\nexport async function updateTag(tag: string): Promise<RevalidateResult> {\n const { valid } = validateTags([tag], 'updateTag')\n if (valid.length === 0) return { deleted: 0 }\n const engine = getCacheEngine()\n const deleted = await engine.invalidateTag(valid[0])\n return { deleted }\n}\n\n/**\n * Invalidate cached entries for a route path.\n * Sugar over `revalidateTag('_THEO_T_/${path}/${type?}')`.\n */\nexport async function revalidatePath(\n path: string,\n opts?: { type?: 'layout' | 'page'; expire?: number },\n): Promise<RevalidateResult> {\n if (opts?.expire !== undefined && opts.expire > 0) {\n warnOnce(\n 'revalidatePath-expire',\n '[theokit:cache] revalidatePath opts.expire is accepted but not honored at MVP.',\n )\n }\n const engine = getCacheEngine()\n const deleted = await engine.revalidatePath(path, opts?.type)\n return { deleted }\n}\n\nconst warnedKeys = new Set<string>()\nfunction warnOnce(key: string, message: string): void {\n if (warnedKeys.has(key)) return\n warnedKeys.add(key)\n console.warn(message)\n}\n","import picomatch from 'picomatch'\n\nexport interface RouteRule {\n maxAge?: number\n swr?: number\n tags?: string[]\n}\n\nexport type RouteRules = Record<string, RouteRule>\n\nexport interface CompiledRouteRule {\n matcher: (path: string) => boolean\n rule: RouteRule\n /** Original glob pattern (for debugging). */\n pattern: string\n}\n\n/**\n * Compile route-rule glob patterns into matcher functions.\n * First-match-wins semantics (preserves insertion order).\n *\n * EC-5: picomatch is a direct production dependency (see plan T7.2).\n */\nexport function compileRouteRules(rules: RouteRules): CompiledRouteRule[] {\n return Object.entries(rules).map(([pattern, rule]) => ({\n matcher: picomatch(pattern, { dot: true }),\n rule,\n pattern,\n }))\n}\n\n/**\n * Resolve the first matching rule for `path`, or `undefined` if none match.\n */\nexport function resolveRouteRule(\n path: string,\n compiled: CompiledRouteRule[],\n): RouteRule | undefined {\n for (const c of compiled) {\n if (c.matcher(path)) return c.rule\n }\n return undefined\n}\n","import superjson from 'superjson'\n\nexport interface SerializedResponse {\n json: unknown\n meta?: unknown\n}\n\n/**\n * Serialize data using superjson for rich type support (Date, Map, Set, BigInt, etc.)\n */\nexport function serializeResponse(data: unknown): SerializedResponse {\n return superjson.serialize(data)\n}\n\n/**\n * Deserialize data that was serialized with superjson.\n */\nexport function deserializeResponse(serialized: SerializedResponse): unknown {\n return superjson.deserialize(serialized as Parameters<typeof superjson.deserialize>[0])\n}\n","/**\n * T3.2 — Web-Standards middleware runner for the `executeWebRequest` path.\n *\n * The Node request path (`http/execute.ts`) runs middleware via\n * `runMiddlewareAndContext`, which is coupled to Node `req`/`res`. Per G8\n * (Web Standards over Node APIs) the Web path needs a `Request`-shaped runner;\n * this is it. Extracted to a sibling so `web-handler.ts` stays under its LoC\n * budget (G6).\n *\n * Ordering invariant (EC-3): the caller runs the CSRF gate BEFORE invoking this\n * runner, mirroring the Node path (CSRF stage precedes user middleware).\n *\n * NOTE (D4 / EC-12): this is its own middleware contract — `(request, context)`.\n * Full semantic parity with the Node `defineMiddleware` contract is part of the\n * deferred Node→Web pipeline convergence, not this slice.\n */\n\n/** A Web-path middleware: mutate `context`, or return a `Response` to short-circuit. */\nexport type WebMiddleware = (\n request: Request,\n context: Record<string, unknown>,\n // A middleware may mutate `context` and return nothing (void) OR return a\n // `Response` to short-circuit (Express/Koa-style). `void` in this union is\n // intentional — `runWebMiddleware` only inspects `instanceof Response`, so a\n // void return means \"continue to the next middleware / handler\".\n // eslint-disable-next-line @typescript-eslint/no-invalid-void-type\n) => Promise<Response | undefined> | Response | undefined | void\n\n/**\n * Run middleware in order against a shared mutable `context`. The first\n * middleware that returns a `Response` short-circuits the chain (the handler\n * is not reached); its `Response` is returned verbatim so headers like\n * `Set-Cookie` are preserved (EC-11). Returns `undefined` when every\n * middleware passes (the handler should run next).\n */\nexport async function runWebMiddleware(\n request: Request,\n middleware: readonly WebMiddleware[],\n context: Record<string, unknown>,\n): Promise<Response | undefined> {\n for (const mw of middleware) {\n const result = await mw(request, context)\n if (result instanceof Response) return result\n }\n return undefined\n}\n","/**\n * T5a.2 Phase A — Foundation: Web-Standards request handler entry-point.\n *\n * Per `docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md`\n * v1.0 § Phase A. Bridges the gap between Web `Request`/`Response` and the\n * existing `defineRoute` config shape (which is consumed today by the Node-\n * shaped `executeRoute`).\n *\n * This is the entry-point T1.2 RED tests expect:\n * `executeWebRequest(request: Request, routeModule: { GET?, POST?, ... }): Promise<Response>`\n *\n * Architecture per ADR-0028 (R3a) — this function accepts a native Web\n * `Request` and returns a native Web `Response`. No `node:*` is required in\n * the implementation (Zod + native Headers/URL/Response cover the surface).\n *\n * **Scope (intentionally narrow):**\n * - Method dispatch (GET/POST/PUT/PATCH/DELETE).\n * - Zod validation for `query` (from URL.searchParams), `body` (from\n * request.json() OR request.text()), `params` (consumer-supplied OR\n * parsed from path).\n * - Handler invocation.\n * - Result → JSON Response (200 default, 204 for void return).\n * - Error → envelope-shaped JSON Response (400 for validation, 500 for\n * handler throws) per G5 boundary translation (serverErrorToEnvelope).\n *\n * **NOT included (deferred to T5a.2 Phase B-G):**\n * - Plugin runner integration (onRequest/preHandler/onResponse hooks).\n * - CSRF / CORS / security headers / rate limiting / cookies / auth.\n * - Middleware chain. SSR rendering. WebSocket upgrade. File upload.\n * - File-system routing scan; consumer provides the route module\n * explicitly (Web Request entry doesn't yet know about scan results).\n *\n * The narrow scope makes this a viable Phase A landing zone — turns the\n * 7 T1.2 RED tests GREEN without prematurely refactoring the full\n * IncomingMessage→Request boundary across server/http/ files.\n */\nimport type { z } from 'zod'\n\nimport { envelopeCodeToStatus } from '../core/contracts/envelope-code-to-status.js'\nimport type { TheoErrorEnvelope } from '../core/contracts/error-envelope.js'\nimport { serverErrorToEnvelope } from '../core/contracts/server-error-to-envelope.js'\nimport { TheoError } from '../core/contracts/theo-error.js'\n\nimport { isZodLike } from './http/execute-stages.js'\nimport { runWebMiddleware, type WebMiddleware } from './http/web-middleware-runner.js'\nimport type {\n WebOnErrorHook,\n WebOnRequestHook,\n WebOnResponseHook,\n WebPluginContext,\n WebPluginErrorContext,\n WebPreHandlerHook,\n} from './plugin-types.js'\nimport { validateCsrfRequest } from './security/csrf.js'\n\nexport type { WebMiddleware }\n\n/**\n * Minimal structural type for a `defineRoute` config. Mirrors\n * `core/contracts/route-config.ts` shape but only the fields this entry\n * point needs at runtime (Zod schemas + handler). Kept structural to\n * avoid a hard dep on the full RouteConfig generic chain.\n */\ninterface WebRouteHandlerConfig {\n query?: z.ZodType\n body?: z.ZodType\n params?: z.ZodType\n /** Optional Zod schema validating a plain-object handler return (D1/D2). */\n response?: z.ZodType\n /** Honored for plain-object returns to match the Node runner (D3). */\n status?: number\n handler: (ctx: {\n query: unknown\n body: unknown\n params: unknown\n request: Request\n /** Mutable per-request context populated by the Web middleware chain (T3.2). */\n context: Record<string, unknown>\n }) => unknown\n}\n\n/**\n * The route module exported by a `server/routes/*.ts` file (after T2.6 G6\n * router lockdown). Method-named exports map to `defineRoute` results.\n */\ntype WebRouteModule = Partial<Record<'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', unknown>>\n\n/**\n * Coerce URL.searchParams into a plain `{ key: value }` object so Zod\n * `z.object({...})` schemas work without per-route adapters. Repeated keys\n * become arrays (most browsers serialize multi-select that way).\n */\nfunction searchParamsToObject(params: URLSearchParams): Record<string, string | string[]> {\n const out: Record<string, string | string[]> = {}\n for (const [key, value] of params.entries()) {\n if (!Object.hasOwn(out, key)) {\n out[key] = value\n continue\n }\n const existing = out[key]\n if (Array.isArray(existing)) {\n existing.push(value)\n } else {\n out[key] = [existing, value]\n }\n }\n return out\n}\n\n/**\n * Parse request body based on Content-Type. JSON is the canonical path;\n * empty body returns `undefined` (the handler treats as \"no body\").\n *\n * **Inline-only mode (default):** handles `application/json` and `text/*`.\n * Other content-types return `undefined`. Multipart/form-data requires\n * the opt-in `bodyParser: 'full'` mode (T5a.2 Phase E).\n */\nasync function parseBodyInline(request: Request): Promise<unknown> {\n // GET/HEAD have no body by spec; even if the consumer sets one, ignore it\n // (matches Web Request semantics — `body` is null for GET/HEAD anyway).\n if (request.method === 'GET' || request.method === 'HEAD') return undefined\n const contentType = request.headers.get('content-type') ?? ''\n if (contentType.includes('application/json')) {\n const text = await request.text()\n if (text.length === 0) return undefined\n try {\n return JSON.parse(text)\n } catch {\n // Malformed JSON — return undefined; Zod schema (if any) will fail with\n // a more useful \"required\" message than a raw SyntaxError.\n return undefined\n }\n }\n // text/plain or no body: return raw text or undefined\n if (contentType.startsWith('text/')) {\n const text = await request.text()\n return text.length === 0 ? undefined : text\n }\n return undefined\n}\n\n/**\n * T5a.2 Phase E — \"full\" body parser delegating to `parseWebRequestBody`.\n * Returns a `ParsedWebBody` (`{ json?, fields, files }`) for JSON OR\n * multipart, or `undefined` when the body is empty / unparseable.\n *\n * Handler downstream pattern:\n * `body?.json` (JSON requests)\n * `body?.fields` + `body?.files` (multipart upload requests)\n *\n * Limits enforced by parseWebRequestBody:\n * - declared Content-Length cap (default 10MB × maxFiles + 1MB margin)\n * - per-file size cap (default 10MB)\n * - max files (default 10)\n */\nasync function parseBodyFull(request: Request): Promise<unknown> {\n if (request.method === 'GET' || request.method === 'HEAD') return undefined\n const { parseWebRequestBody } = await import('./body-parser-web.js')\n try {\n const parsed = await parseWebRequestBody(request)\n // If nothing parsed (empty body OR unhandled content-type), return undefined\n // so Zod schemas treat it as missing — same semantics as inline parser.\n if (\n parsed.json === undefined &&\n Object.keys(parsed.fields).length === 0 &&\n parsed.files.length === 0\n ) {\n return undefined\n }\n return parsed\n } catch {\n return undefined\n }\n}\n\n/**\n * Build an envelope-shaped 400 Response for a Zod validation failure. Uses\n * the canonical `TheoErrorEnvelope` shape per G5 D3 (boundary translation).\n */\nfunction validationErrorResponse(zodError: z.ZodError, field: string): Response {\n const envelope: TheoErrorEnvelope = {\n code: 'BAD_REQUEST',\n message: `Validation failed: ${field}`,\n ext: {\n fields: zodError.issues.map((issue) => ({\n path: issue.path.join('.'),\n message: issue.message,\n })),\n },\n }\n return new Response(JSON.stringify(envelope), {\n status: 400,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/**\n * Run a `defineRoute` config against a parsed Web Request. Returns the raw\n * handler result; serialization to Response happens in the caller.\n *\n * Throws if Zod validation fails (caller catches and emits 400).\n */\nasync function runHandler(\n config: WebRouteHandlerConfig,\n request: Request,\n bodyParser: 'inline' | 'full' = 'inline',\n paramsInput: Record<string, string> = {},\n context: Record<string, unknown> = {},\n): Promise<{ ok: true; result: unknown } | { ok: false; response: Response }> {\n const url = new URL(request.url)\n const queryRaw = searchParamsToObject(url.searchParams)\n const bodyRaw =\n bodyParser === 'full' ? await parseBodyFull(request) : await parseBodyInline(request)\n // T3.1 — route params resolved upstream (matchRoute) and threaded via\n // opts.params. Defaults to `{}` so callers that don't supply params keep the\n // prior behavior (a route declaring config.params then fails validation).\n const paramsRaw = paramsInput\n\n // Validate input via Zod schemas when present.\n let query: unknown = queryRaw\n if (config.query !== undefined) {\n const parsed = config.query.safeParse(queryRaw)\n if (!parsed.success)\n return { ok: false, response: validationErrorResponse(parsed.error, 'query') }\n query = parsed.data\n }\n let body: unknown = bodyRaw\n if (config.body !== undefined) {\n const parsed = config.body.safeParse(bodyRaw)\n if (!parsed.success)\n return { ok: false, response: validationErrorResponse(parsed.error, 'body') }\n body = parsed.data\n }\n let params: unknown = paramsRaw\n if (config.params !== undefined) {\n const parsed = config.params.safeParse(paramsRaw)\n if (!parsed.success)\n return { ok: false, response: validationErrorResponse(parsed.error, 'params') }\n params = parsed.data\n }\n\n const result = await config.handler({ query, body, params, request, context })\n return validateResponseOutput(config.response, result) ?? { ok: true, result }\n}\n\n/**\n * Validate a plain-object handler return against `config.response` (D1/D2). A\n * mismatch is a SERVER fault → 500 envelope. `Response`-instance returns and\n * `undefined`/`null` (→ 204) skip validation — parity with the Node runner,\n * which validates only in its plain-object branch. Returns `undefined` when no\n * validation applies (caller forwards the raw result).\n */\nfunction validateResponseOutput(\n response: unknown,\n result: unknown,\n): { ok: true; result: unknown } | { ok: false; response: Response } | undefined {\n const validatable = result !== undefined && result !== null && !(result instanceof Response)\n if (!validatable || !isZodLike(response)) return undefined\n const parsed = response.safeParse(result)\n if (parsed.success) return { ok: true, result: parsed.data }\n const err = new TheoError({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'response validation failed',\n ext: { issues: parsed.error?.issues },\n })\n return { ok: false, response: handlerErrorResponse(err) }\n}\n\n/**\n * Serialize a handler return value into a native Web `Response`. Conventions:\n * - `undefined` / `void` → 204 No Content.\n * - existing `Response` instance → pass through unchanged.\n * - everything else → 200 JSON.\n */\nfunction toResponse(result: unknown, status?: number): Response {\n if (result === undefined) {\n return new Response(null, { status: 204 })\n }\n if (result instanceof Response) {\n return result\n }\n return new Response(JSON.stringify(result), {\n status: status ?? 200,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/**\n * Build an envelope-shaped 500 Response when a handler throws. Uses\n * `serverErrorToEnvelope` so existing ad-hoc Error classes (AuthRequired,\n * FileTooLarge, etc.) get mapped to canonical envelope codes.\n */\nfunction handlerErrorResponse(err: unknown): Response {\n const envelope = serverErrorToEnvelope(err)\n // Map envelope code → HTTP status when possible. Default to 500 for\n // INTERNAL_SERVER_ERROR. Other codes map per HTTP semantics.\n const status = envelopeCodeToStatus(envelope.code)\n return new Response(JSON.stringify(envelope), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n// envelopeCodeToStatus extracted to core/contracts/envelope-code-to-status.ts\n// (architecture-remediation T1.2, 2026-06-12)\n\n/**\n * Optional behavior knobs for `executeWebRequest`. Each knob defaults to\n * the safe \"no-op\" stance so existing Phase A consumers (T1.2 fixture\n * tests) keep working unchanged.\n *\n * **`csrfMode`** (T5a.2 Phase B slice 1/6) — when set to `'strict'`, the\n * Web-Standards request gate runs `validateCsrfRequest` BEFORE method\n * dispatch on state-changing methods (POST/PUT/PATCH/DELETE) and emits a\n * `403 FORBIDDEN` envelope when the check fails. Default: `'off'` (no\n * CSRF enforcement) to preserve Phase A backward compat. Production\n * consumers SHOULD pass `csrfMode: 'strict'`.\n *\n * Per the T5a.2 plan v1.0 § Phase B header-only leaves: csrf.ts is the\n * first leaf to be migrated (slice 1/6); 5 more sibling leaves remain\n * (csrf-multi-header, csrf-readiness-endpoint, csp-report, cors, cookies).\n */\nexport interface ExecuteWebRequestOptions {\n csrfMode?: 'off' | 'strict'\n /**\n * T3.1 — route params resolved upstream by `matchRoute` (e.g. `{ id: '42' }`\n * for `/users/:id`). Threaded to the handler's `params` input and validated\n * against `config.params` (Zod) when declared. Defaults to `{}` — callers\n * that don't supply params keep the prior behavior (additive, backward-compat).\n */\n params?: Record<string, string>\n /**\n * T3.2 — Web-Standards middleware chain. Runs in order AFTER the CSRF gate\n * (EC-3) and BEFORE the handler. A middleware returning a `Response`\n * short-circuits (handler not reached); mutating `context` passes data to\n * the handler. Omitted → zero overhead (handler runs directly).\n */\n middleware?: readonly WebMiddleware[]\n /**\n * T5a.2 Phase E — body parser strategy.\n *\n * - `'inline'` (default): handle `application/json` + `text/*` only.\n * Returns the parsed value (object for JSON, string for text). Other\n * content-types (e.g., multipart) return `undefined`.\n * - `'full'`: delegate to `parseWebRequestBody` for multipart support\n * + per-file size caps + max-files cap (Web Standards `request.formData()`\n * under the hood). Returns a `ParsedWebBody` object:\n * `{ json?, fields, files }`. Multipart consumers MUST opt into this\n * mode; JSON-only routes pay zero cost staying on `'inline'`.\n */\n bodyParser?: 'inline' | 'full'\n /**\n * T5a.2 Phase G slice 1/N — plugin lifecycle hooks.\n *\n * Adapters (Node, CF Workers, Bun, Deno) wire `WebPluginContext`-shaped\n * hooks at the executeWebRequest lifecycle. Lifecycle order mirrors the\n * Fastify / Hono convention:\n *\n * 1. CSRF check (when opts.csrfMode === 'strict')\n * 2. onRequest — earliest, before body parsing. Plugins can short-circuit\n * by setting `ctx.response` (handler skipped; subsequent hooks see\n * the short-circuit response).\n * 3. body parse (inline OR full per opts.bodyParser)\n * 4. preHandler — after body parsed, before handler runs. Same\n * short-circuit semantic.\n * 5. handler invocation\n * 6. onResponse — after handler returns OR after a hook short-circuit.\n * `ctx.response` is populated.\n * 7. onError — fires if any of (handler, onRequest, preHandler) throws.\n * `ctx.response` is the envelope-shaped error response built via\n * serverErrorToEnvelope.\n *\n * `responseHeaders` is shared across hooks; the final Response merges\n * them with the handler's Response headers (handler headers win on\n * conflict; hook headers add new ones). Decorations made via\n * `ctx.ctx[key] = value` persist across hooks (request-scoped state).\n *\n * `hooks: undefined` (default) → no plugin lifecycle, Phase A behavior\n * preserved. Production consumers wire via the WebPluginRunner facade\n * (a future Phase G slice).\n */\n hooks?: {\n onRequest?: readonly WebOnRequestHook[]\n preHandler?: readonly WebPreHandlerHook[]\n onResponse?: readonly WebOnResponseHook[]\n onError?: readonly WebOnErrorHook[]\n }\n /**\n * Stable request identifier propagated into hook contexts. Adapters\n * resolve via traceparent / x-request-id / generated UUID (see\n * `extractTraceIdFromRequest` from Phase C slice 1/2). Default:\n * `globalThis.crypto.randomUUID()`.\n */\n requestId?: string\n}\n\n/**\n * Methods that require CSRF enforcement when `csrfMode: 'strict'`. GET\n * and HEAD are read-only per HTTP semantics and bypass CSRF (the threat\n * model is state-changing requests forged via cross-origin POSTs); OPTIONS\n * is the CORS preflight and also bypasses.\n */\nexport const CSRF_PROTECTED_METHODS = new Set(['POST', 'PUT', 'PATCH', 'DELETE'])\n\n/**\n * Web-Standards entry-point for executing a route module against a native\n * Web `Request`. Returns a native Web `Response`.\n *\n * Method dispatch: looks up the handler keyed by `request.method`\n * (uppercase). Returns 405 if the method isn't exported.\n *\n * @example\n * import * as users from './app/users/route.js'\n * import { executeWebRequest } from 'theokit/server'\n *\n * const response = await executeWebRequest(\n * new Request('http://localhost/api/users', { method: 'GET' }),\n * users,\n * { csrfMode: 'strict' },\n * )\n */\n/**\n * T5a.2 Phase G slice 1/N — merge hook-mutated response headers into the\n * handler's Response. Handler-set headers win on conflict (the handler\n * has the most context about its own response); hook headers add new\n * entries (e.g., CORS, Set-Cookie). Returns a new Response with the\n * merged headers + same body/status as the source.\n *\n * Set-Cookie is multi-value at the Web spec layer — `getSetCookie()` is\n * the only way to retrieve multiple values. The merge appends ALL hook-\n * set Set-Cookie values to the response (multiple Set-Cookie headers is\n * spec-correct and how browsers expect cookie issuance).\n */\nfunction mergeHookHeaders(response: Response, hookHeaders: Headers): Response {\n if ([...hookHeaders].length === 0) return response\n const merged = new Headers(response.headers)\n for (const [k, v] of hookHeaders.entries()) {\n if (k.toLowerCase() === 'set-cookie') continue // handled separately\n if (!merged.has(k)) merged.set(k, v)\n }\n for (const sc of hookHeaders.getSetCookie()) {\n merged.append('Set-Cookie', sc)\n }\n return new Response(response.body, {\n status: response.status,\n statusText: response.statusText,\n headers: merged,\n })\n}\n\n/** Build a 405 envelope Response. */\nfunction methodNotAllowedResponse(method: string): Response {\n const envelope: TheoErrorEnvelope = {\n code: 'METHOD_NOT_ALLOWED',\n message: `Method ${method} not allowed`,\n }\n return new Response(JSON.stringify(envelope), {\n status: 405,\n headers: { 'content-type': 'application/json' },\n })\n}\n\n/** Build a 403 CSRF envelope Response. */\nfunction csrfFailedResponse(reason: string): Response {\n const envelope: TheoErrorEnvelope = {\n code: 'FORBIDDEN',\n message: `CSRF check failed: ${reason}`,\n }\n return new Response(JSON.stringify(envelope), {\n status: 403,\n headers: { 'content-type': 'application/json' },\n })\n}\n\nexport async function executeWebRequest(\n request: Request,\n routeModule: WebRouteModule,\n opts: ExecuteWebRequestOptions = {},\n): Promise<Response> {\n const method = request.method.toUpperCase() as keyof WebRouteModule\n const config = routeModule[method] as WebRouteHandlerConfig | undefined\n\n // T5a.2 Phase G slice 1/N — plugin lifecycle hooks.\n // When opts.hooks is undefined, skip the entire hook orchestration to\n // preserve Phase A backward compat (zero overhead for consumers not\n // wiring hooks). In the no-hooks branch, dispatch + CSRF gates run\n // FIRST and 405 short-circuits before the handler runs.\n if (opts.hooks === undefined) {\n if (config === undefined || typeof config.handler !== 'function') {\n return methodNotAllowedResponse(method)\n }\n if (opts.csrfMode === 'strict' && CSRF_PROTECTED_METHODS.has(method)) {\n const csrfCheck = validateCsrfRequest(request)\n if (!csrfCheck.valid) return csrfFailedResponse(csrfCheck.reason)\n }\n try {\n // T3.2 — middleware runs AFTER the CSRF gate (EC-3), BEFORE the handler.\n const context: Record<string, unknown> = {}\n if (opts.middleware?.length) {\n const shortCircuit = await runWebMiddleware(request, opts.middleware, context)\n if (shortCircuit) return shortCircuit\n }\n const outcome = await runHandler(\n config,\n request,\n opts.bodyParser ?? 'inline',\n opts.params ?? {},\n context,\n )\n if (!outcome.ok) return outcome.response\n return toResponse(outcome.result, config.status)\n } catch (err) {\n return handlerErrorResponse(err)\n }\n }\n\n // With hooks: onRequest hooks run BEFORE method dispatch (Hono /\n // Fastify convention) so CORS preflight plugins can intercept OPTIONS\n // requests regardless of route shape. If no hook short-circuits, then\n // 405 fires when the method isn't exported by the route module.\n return runWithHooks(request, config, opts, opts.hooks)\n}\n\n/**\n * Run the request through the full plugin lifecycle. Extracted to keep\n * `executeWebRequest`'s cyclomatic + cognitive complexity under the lint\n * caps (15/20). Pure side-effect surface — same return type as the\n * no-hooks branch.\n */\n/**\n * Pre-handler pipeline: onRequest hooks → CSRF gate → preHandler hooks →\n * method dispatch + handler. Mutates `hookCtx.response` on short-circuit\n * OR on handler completion. Extracted from `runWithHooks` to keep the\n * latter's cyclomatic complexity under the lint cap (15).\n */\nasync function runPreHandlerPipeline(\n hookCtx: WebPluginContext,\n request: Request,\n config: WebRouteHandlerConfig | undefined,\n opts: ExecuteWebRequestOptions,\n hooks: NonNullable<ExecuteWebRequestOptions['hooks']>,\n): Promise<void> {\n const method = request.method.toUpperCase()\n const runList = async (list: readonly WebOnRequestHook[]): Promise<void> => {\n for (const hook of list) {\n if (hookCtx.response !== undefined) return\n await hook(hookCtx)\n }\n }\n // onRequest first (CORS preflight + auth gate intercept before dispatch).\n if (hooks.onRequest) await runList(hooks.onRequest)\n // CSRF gate AFTER onRequest (auth-short-circuit avoids CSRF cost) but\n // BEFORE the handler.\n if (\n hookCtx.response === undefined &&\n opts.csrfMode === 'strict' &&\n CSRF_PROTECTED_METHODS.has(method)\n ) {\n const csrfCheck = validateCsrfRequest(request)\n if (!csrfCheck.valid) hookCtx.response = csrfFailedResponse(csrfCheck.reason)\n }\n if (hookCtx.response === undefined && hooks.preHandler) {\n await runList(hooks.preHandler)\n }\n if (hookCtx.response === undefined) {\n await runHandlerStage(hookCtx, request, config, opts, method)\n }\n}\n\n/**\n * T3.2 — the middleware + handler stage of the hook pipeline. Extracted from\n * `runPreHandlerPipeline` to keep its cyclomatic complexity under the lint cap.\n * Order: 405 check → user middleware (after CSRF + preHandler) → handler.\n */\nasync function runHandlerStage(\n hookCtx: WebPluginContext,\n request: Request,\n config: WebRouteHandlerConfig | undefined,\n opts: ExecuteWebRequestOptions,\n method: string,\n): Promise<void> {\n // 405 if route module doesn't export this method.\n if (config === undefined || typeof config.handler !== 'function') {\n hookCtx.response = methodNotAllowedResponse(method)\n return\n }\n // hookCtx.ctx is the shared per-request context the middleware mutates.\n if (opts.middleware?.length) {\n const shortCircuit = await runWebMiddleware(request, opts.middleware, hookCtx.ctx)\n if (shortCircuit) {\n hookCtx.response = shortCircuit\n return\n }\n }\n const outcome = await runHandler(\n config,\n request,\n opts.bodyParser ?? 'inline',\n opts.params ?? {},\n hookCtx.ctx,\n )\n hookCtx.response = outcome.ok ? toResponse(outcome.result, config.status) : outcome.response\n}\n\nasync function runWithHooks(\n request: Request,\n config: WebRouteHandlerConfig | undefined,\n opts: ExecuteWebRequestOptions,\n hooks: NonNullable<ExecuteWebRequestOptions['hooks']>,\n): Promise<Response> {\n const hookCtx: WebPluginContext = {\n request,\n responseHeaders: new Headers(),\n ctx: {},\n requestId: opts.requestId ?? globalThis.crypto.randomUUID(),\n }\n try {\n await runPreHandlerPipeline(hookCtx, request, config, opts, hooks)\n if (hooks.onResponse) {\n for (const hook of hooks.onResponse) {\n await hook(hookCtx)\n }\n }\n // runPreHandlerPipeline guarantees hookCtx.response is set (via either\n // a hook short-circuit OR handler outcome OR 405). TS doesn't infer this\n // post-mutation; the assertion is safe per the function's contract.\n const finalResponse =\n hookCtx.response ??\n new Response(\n JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'No response built' }),\n {\n status: 500,\n headers: { 'content-type': 'application/json' },\n },\n )\n return mergeHookHeaders(finalResponse, hookCtx.responseHeaders)\n } catch (err) {\n return runErrorHooks(err, hookCtx, hooks.onError)\n }\n}\n\n/**\n * Run onError hooks against an envelope-shaped error response. EC-9 —\n * each hook throw is swallowed to avoid error-in-error-handler recursion.\n * Returns the merged final Response.\n */\nasync function runErrorHooks(\n err: unknown,\n hookCtx: WebPluginContext,\n onError: readonly WebOnErrorHook[] | undefined,\n): Promise<Response> {\n const errorResponse = handlerErrorResponse(err)\n if (onError !== undefined) {\n const errorCtx: WebPluginErrorContext = {\n ...hookCtx,\n response: errorResponse,\n error: err,\n }\n for (const hook of onError) {\n try {\n await hook(errorCtx)\n } catch {\n // EC-9: error in error handler — swallow to avoid recursion.\n }\n }\n }\n return mergeHookHeaders(errorResponse, hookCtx.responseHeaders)\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport interface PluginContext {\n request: IncomingMessage\n response: ServerResponse\n ctx: Record<string, unknown>\n requestId: string\n}\n\nexport interface PluginErrorContext extends PluginContext {\n error: unknown\n}\n\nexport interface RunHookOptions {\n inErrorPath?: boolean\n}\n\nexport interface HookResult {\n shortCircuited: boolean\n}\n\nexport type OnRequestHook = (ctx: PluginContext) => void | Promise<void>\nexport type PreHandlerHook = (ctx: PluginContext) => void | Promise<void>\nexport type OnResponseHook = (ctx: PluginContext) => void | Promise<void>\nexport type OnErrorHook = (ctx: PluginErrorContext) => void | Promise<void>\n\nexport type HookName = 'onRequest' | 'preHandler' | 'onResponse' | 'onError'\n\nexport type HookByName<K extends HookName> = K extends 'onError'\n ? OnErrorHook\n : K extends 'onRequest'\n ? OnRequestHook\n : K extends 'preHandler'\n ? PreHandlerHook\n : K extends 'onResponse'\n ? OnResponseHook\n : never\n\nexport interface TheoApp {\n addHook<K extends HookName>(name: K, fn: HookByName<K>): void\n // `T` lets plugin authors document the per-key shape of decorations.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T documents the value type for plugin authors\n decorateRequest<T>(key: string, value: T): void\n}\n\nexport interface TheoPlugin {\n name: string\n register(app: TheoApp): void | Promise<void>\n}\n\n/**\n * Identity function for plugin authors. Provides auto-completion + type\n * inference at the call site (TanStack/Vite/Astro pattern). Pure runtime\n * no-op — returns the input unchanged.\n *\n * @example\n * import { definePlugin } from 'theokit/server'\n * export default definePlugin({\n * name: 'my-plugin',\n * register(app) {\n * app.addHook('onRequest', (req) => { ... })\n * },\n * })\n *\n * Equivalent to `const p: TheoPlugin = {...}` but more ergonomic. See\n * ADR-0008 (D1 + D6) for the rationale.\n */\nexport function definePlugin(plugin: TheoPlugin): TheoPlugin {\n return plugin\n}\n\n// ===== T5a.2 Phase F slice 1/3 — Web-Standards plugin context types =====\n//\n// Mirror of the IncomingMessage/ServerResponse-shaped `PluginContext` for\n// the Web `Request`/`Headers` shape. Per `docs/plans/t5a2-incoming-message-\n// to-request-shape-refactor-plan.md` v1.0 § Phase F.\n//\n// **Key difference vs IncomingMessage path:** the Web path has no\n// `ServerResponse` to mutate. Plugins instead get a `responseHeaders: Headers`\n// instance they can append to (e.g., add Set-Cookie, CORS headers) and the\n// runtime composes the final `Response` after the hook chain runs. The\n// `response` object (if any) is the in-flight Response constructed by the\n// handler — present only during `onResponse` / `onError` hooks AFTER the\n// handler returned, NOT during `onRequest` / `preHandler` (which fire BEFORE\n// the handler runs).\n//\n// This split mirrors Hono's `c.res` + Fastify's `reply.headers` semantics\n// — plugins mutate headers freely; the body is the handler's responsibility.\n\n/**\n * Web-Standards plugin context. Available during all 4 hook lifecycle\n * stages (onRequest, preHandler, onResponse, onError).\n *\n * - `request` — the incoming Web Request (read-only at the runtime level;\n * plugins can call `request.headers.get()`, `request.clone()`, etc.).\n * - `responseHeaders` — a mutable `Headers` instance the runtime threads\n * through the hook chain. Plugins append (e.g., CORS, Set-Cookie); the\n * final Response composes these.\n * - `response` — set to the handler's Response AFTER the handler returns.\n * Available during `onResponse` / `onError`. `undefined` during\n * `onRequest` / `preHandler` (which fire before the handler runs).\n * - `ctx` / `requestId` — same semantics as the IncomingMessage path.\n */\nexport interface WebPluginContext {\n request: Request\n responseHeaders: Headers\n response?: Response\n ctx: Record<string, unknown>\n requestId: string\n}\n\nexport interface WebPluginErrorContext extends WebPluginContext {\n error: unknown\n}\n\nexport type WebOnRequestHook = (ctx: WebPluginContext) => void | Promise<void>\nexport type WebPreHandlerHook = (ctx: WebPluginContext) => void | Promise<void>\nexport type WebOnResponseHook = (ctx: WebPluginContext) => void | Promise<void>\nexport type WebOnErrorHook = (ctx: WebPluginErrorContext) => void | Promise<void>\n\nexport type WebHookByName<K extends HookName> = K extends 'onError'\n ? WebOnErrorHook\n : K extends 'onRequest'\n ? WebOnRequestHook\n : K extends 'preHandler'\n ? WebPreHandlerHook\n : K extends 'onResponse'\n ? WebOnResponseHook\n : never\n\n/**\n * Web-Standards `TheoApp` facade. Same `addHook` + `decorateRequest`\n * surface as the IncomingMessage path; only the hook function signatures\n * differ (they receive `WebPluginContext` instead of `PluginContext`).\n *\n * Plugin authors who target both shapes can branch on the context type\n * via a type guard (`'responseHeaders' in ctx`) OR ship two separate\n * `register()` exports — one for each runtime adapter. Most plugins\n * register hooks at the IncomingMessage path today (legacy); future\n * Web-native plugins will register against `WebTheoApp`.\n */\nexport interface WebTheoApp {\n addHook<K extends HookName>(name: K, fn: WebHookByName<K>): void\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T documents the value type for plugin authors\n decorateRequest<T>(key: string, value: T): void\n}\n\n/**\n * Web-Standards plugin shape. Identical to `TheoPlugin` except the\n * `register(app)` argument is `WebTheoApp` instead of `TheoApp`.\n *\n * Cross-runtime plugins ship BOTH `TheoPlugin` + `WebTheoPlugin` exports;\n * the adapter (Node, CF Workers, Bun, Deno) picks the matching one based\n * on which runtime executes the plugin chain. This is the canonical\n * Hono/Nitro pattern.\n */\nexport interface WebTheoPlugin {\n name: string\n register(app: WebTheoApp): void | Promise<void>\n}\n\n/**\n * Identity function for Web plugin authors — same auto-completion +\n * type-inference DX as `definePlugin`, but for the Web-shaped `WebTheoApp`.\n */\nexport function defineWebPlugin(plugin: WebTheoPlugin): WebTheoPlugin {\n return plugin\n}\n","/**\n * theokit/server — DEPRECATED umbrella barrel.\n *\n * **Per plan theokit-arch-gaps-implementation T2.5 (M1) + EC-2:**\n *\n * Importing from `theokit/server` is DEPRECATED as of this release. The\n * 16 sub-domain exports (auth, jobs, http, security, observability, etc.)\n * are now individually addressable via `package.json#exports`:\n *\n * import { defineAuth } from 'theokit/server/auth'\n * import { defineJob } from 'theokit/server/jobs'\n * import { defineRoute, defineAction } from 'theokit/server/define'\n * // ...etc per `packages/theo/package.json` exports field\n *\n * This umbrella barrel will continue to work for ONE minor cycle\n * (0.x → 0.x+1) with a single runtime warning printed on first import.\n * Final removal in 0.x+2 per CHANGELOG.\n *\n * Why: `export *` wildcards across 16 sub-domains violate ISP at the\n * package-surface level (consumers pay for 376 transitive exports when\n * they wanted 6). See `architecture-output/consolidated_final_report.md`\n * M1 finding + blueprint Q4 D4 (Hono-shape exports field adoption).\n *\n * Migration codemod (provided in this release):\n * pnpm exec theokit migrate server-umbrella-to-subpaths\n *\n * For body-parser, serialization, transformer, webhook helpers, trace context\n * propagation, error pages, plugin types, and config/env helpers — re-exported\n * inline because they don't fit a single sub-barrel cleanly. Those will land\n * in a `theokit/server/runtime` sub-path in the 0.x+2 cleanup.\n */\n\n// One-time deprecation warning. Fires on first import in the process and\n// is silenced afterwards via a module-scoped flag. Tree-shake-safe: the\n// IIFE body executes on module load (no DCE), but the cost is one\n// console.warn at startup — negligible. Apps that grep for this string\n// see exactly which entry point logged it.\nif (typeof globalThis !== 'undefined') {\n const WARN_KEY = '__theokit_server_umbrella_warn_emitted__'\n const g = globalThis as Record<string, unknown>\n if (g[WARN_KEY] !== true) {\n g[WARN_KEY] = true\n\n console.warn(\n '[theokit] umbrella import \"theokit/server\" is DEPRECATED. ' +\n 'Use sub-paths (theokit/server/<domain>): auth, jobs, http, security, ' +\n 'observability, etc. See packages/theo/package.json#exports for the ' +\n 'full list. Removal scheduled for 0.x+2.',\n )\n }\n}\n\n// Core defines + low-level pipeline primitives (used by adapter templates)\nexport * from './define/index.js'\nexport * from './http/index.js'\nexport * from './scan/index.js'\n\n// G3 action protocol: ActionError + ActionInputError + helpers, re-exported\n// from core/contracts for consumer ergonomics (`from 'theokit/server'`).\nexport {\n ActionError,\n ActionInputError,\n extractUniversalIssues,\n isActionError,\n isInputError,\n type ActionErrorCode,\n type ActionManifestEntry,\n type ActionResult,\n type SerializedActionResult,\n type UniversalZodIssue,\n} from '../core/contracts/action-protocol.js'\n\n// Subdomain sub-barrels (consumers can also import direct: theokit/server/<sub>)\nexport * from './agent/index.js'\nexport * from './auth/index.js'\nexport * from './cost/index.js'\nexport * from './cron/index.js'\nexport * from './jobs/index.js'\nexport * from './observability/index.js'\nexport * from './plugins/index.js'\nexport * from './rate-limit/index.js'\nexport * from './realtime/index.js'\nexport * from './security/index.js'\nexport * from './storage/index.js'\nexport * from './webhook/index.js'\nexport * from './openapi/index.js'\n\n// Cross-module: cache lives at packages/theo/src/cache/ (not server/cache)\nexport * from '../cache/index.js'\n\n// Inline re-exports — items that don't belong to a single sub-barrel\nexport { parseRequestBody, FileTooLargeError } from './body-parser.js'\nexport type { UploadedFile, ParsedBody, BodyParserOptions } from './body-parser.js'\n\nexport { serializeResponse, deserializeResponse } from './serialization.js'\nexport type { SerializedResponse } from './serialization.js'\n\nexport { superjsonTransformer, jsonTransformer, resolveTransformer } from './transformer.js'\nexport type { TheoTransformer } from './transformer.js'\n\n// T5a.2 Phase A — Web-Standards entry-point (R3a per ADR-0028). Accepts\n// a native Web Request and returns a native Web Response. Narrow scope:\n// method dispatch + Zod validation + envelope-shaped errors. Plugin\n// runner / CSRF / CORS / middleware integration deferred to Phase B-G\n// per docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md.\nexport { executeWebRequest } from './web-handler.js'\n\n// Plugin types (used by definePlugin + extension authors)\nexport type {\n TheoPlugin,\n TheoApp,\n PluginContext,\n PluginErrorContext,\n HookName,\n HookResult,\n OnRequestHook,\n PreHandlerHook,\n OnResponseHook,\n OnErrorHook,\n RunHookOptions,\n} from './plugin-types.js'\nexport { definePlugin } from './plugin-types.js'\n\n// Config helpers — auto-load .env for standalone server scripts (Telegram bots,\n// queue consumers, cron jobs) that bypass the CLI.\nexport { loadEnv, _resetEnvCache } from '../config/load-env.js'\nexport type { LoadEnvOptions, LoadEnvResult } from '../config/load-env-types.js'\n\n// Wave 2 — Polyglot services orchestration types (types only — runtime in services/)\nexport type {\n ServiceDefinition,\n ServicesConfig,\n ServicesConfigInput,\n ServicesConfigOutput,\n} from '../services/index.js'\n"],"mappings":"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","names":["deriveKey","deriveKey","resolve","opts","warnOnce"]}
|
|
1
|
+
{"version":3,"sources":["../../src/server/openapi/serve-docs.ts","../../src/cache/constants.ts","../../src/cache/validation.ts","../../src/cache/define-cached-function.ts","../../src/cache/cache-control-header.ts","../../src/cache/key-derivation.ts","../../src/cache/define-cached-route.ts","../../src/cache/cache-engine.ts","../../src/cache/in-memory-adapter.ts","../../src/cache/engine-singleton.ts","../../src/cache/revalidate.ts","../../src/cache/route-rules.ts","../../src/server/serialization.ts","../../src/server/plugin-types.ts","../../src/server/index.ts"],"sourcesContent":["/* eslint-disable security/detect-non-literal-fs-filename -- paths derived from build-time cwd, not user input */\n/**\n * Built-in OpenAPI docs — serves Scalar UI at /api/docs and the JSON spec\n * at /api/docs/openapi.json. Zero npm deps (Scalar loaded via CDN).\n *\n * Absorbed from @theokit/plugin-openapi (theokit-plugins sibling repo).\n * Core already emits the spec via vite-plugin/openapi-emit/ — this module\n * adds the runtime serving layer.\n *\n * Security: XSS-safe HTML escaping, CSP header for CDN host, GET-only,\n * 10MB filesize cap, path-traversal defense on specFilePath.\n */\nimport { existsSync, readFileSync, statSync } from 'node:fs'\nimport { resolve } from 'node:path'\n\nexport interface OpenApiDocsOptions {\n /** Path to serve Scalar UI (default: '/api/docs'). */\n docsPath?: string\n /** Path to serve the JSON spec (default: '/api/docs/openapi.json'). */\n openapiJsonPath?: string\n /** Path to the spec file on disk (default: '.theokit/openapi.json'). */\n specFilePath?: string\n /** Page title (default: 'API Reference'). */\n pageTitle?: string\n /** Scalar CDN URL (default: jsdelivr). Must be HTTPS. */\n cdnUrl?: string\n}\n\nconst MAX_SPEC_BYTES = 10 * 1024 * 1024 // 10MB cap (DoS defense)\nconst DEFAULT_CDN = 'https://cdn.jsdelivr.net/npm/@scalar/api-reference'\n\n/**\n * Creates a request handler that serves OpenAPI docs.\n * Returns `null` for non-matching requests (passthrough).\n */\nexport function createOpenApiHandler(opts: OpenApiDocsOptions = {}) {\n const docsPath = opts.docsPath ?? '/api/docs'\n const jsonPath = opts.openapiJsonPath ?? '/api/docs/openapi.json'\n const rawSpecFile = opts.specFilePath ?? '.theokit/openapi.json'\n const title = opts.pageTitle ?? 'API Reference'\n const cdn = opts.cdnUrl ?? DEFAULT_CDN\n\n // Path-traversal defense — validate the RAW input, BEFORE resolve().\n // resolve() normalizes `..` away (e.g. '../../../etc/passwd' becomes an\n // absolute path with no '..' left), so checking the resolved string is a\n // dead guard. Reject any `..` path segment in the input instead; absolute\n // paths without traversal segments stay allowed.\n if (hasDotDotSegment(rawSpecFile)) {\n throw new Error(`[theokit:openapi] specFilePath must not contain \"..\" segments: ${rawSpecFile}`)\n }\n const specFile = resolve(rawSpecFile)\n\n return (request: Request): Response | null => {\n if (request.method !== 'GET') return null\n\n const url = new URL(request.url)\n\n if (url.pathname === docsPath) {\n const cdnHost = new URL(cdn).host\n return new Response(renderScalarHtml(title, jsonPath, cdn), {\n status: 200,\n headers: {\n 'content-type': 'text/html; charset=utf-8',\n 'content-security-policy': `script-src 'self' 'unsafe-eval' ${cdnHost}; style-src 'self' 'unsafe-inline'; img-src 'self' data: ${cdnHost}; font-src 'self' data: ${cdnHost}; connect-src 'self' ${cdnHost}`,\n },\n })\n }\n\n if (url.pathname === jsonPath) {\n return serveSpecFile(specFile)\n }\n\n return null\n }\n}\n\n/** True if the raw path contains a `..` segment (POSIX `/` or Windows `\\` separator). */\nfunction hasDotDotSegment(p: string): boolean {\n return p.split(/[/\\\\]/).includes('..')\n}\n\n// ── HTML renderer ──\n\nfunction escapeHtml(s: string): string {\n return s\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n}\n\nfunction renderScalarHtml(title: string, specUrl: string, cdnUrl: string): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <title>${escapeHtml(title)}</title>\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n</head>\n<body>\n <script id=\"api-reference\" data-url=\"${escapeHtml(specUrl)}\"></script>\n <script src=\"${escapeHtml(cdnUrl)}\"></script>\n</body>\n</html>`\n}\n\n// ── Spec file server ──\n\nfunction serveSpecFile(filePath: string): Response {\n if (!existsSync(filePath)) {\n return jsonResponse(503, {\n error: {\n code: 'OPENAPI_NOT_EMITTED',\n message: 'OpenAPI spec not generated yet. Start the dev server and visit a route first.',\n },\n })\n }\n\n try {\n const stat = statSync(filePath)\n if (stat.size > MAX_SPEC_BYTES) {\n return jsonResponse(413, {\n error: {\n code: 'OPENAPI_TOO_LARGE',\n message: `Spec file exceeds ${MAX_SPEC_BYTES / 1024 / 1024}MB limit`,\n },\n })\n }\n\n const content = readFileSync(filePath, 'utf-8')\n return new Response(content, {\n status: 200,\n headers: { 'content-type': 'application/json', 'cache-control': 'no-cache' },\n })\n } catch (err) {\n const message = err instanceof Error ? err.message : 'Failed to read OpenAPI spec file'\n return jsonResponse(500, {\n error: { code: 'OPENAPI_READ_FAILED', message },\n })\n }\n}\n\nfunction jsonResponse(status: number, body: unknown): Response {\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'content-type': 'application/json' },\n })\n}\n","/**\n * Single source of truth for cache subsystem constants.\n * Limits mirror Next.js (NEXT_CACHE_TAG_MAX_LENGTH / NEXT_CACHE_TAG_MAX_ITEMS)\n * — see reference doc §3.2 and ADR D6 of caching-and-revalidation-plan.md.\n */\n\nexport const CACHE_TAG_MAX_LENGTH = 256\nexport const CACHE_TAG_MAX_ITEMS = 128\nexport const THEO_T_PREFIX = '_THEO_T_'\n\nexport const DEFAULT_MAX_AGE = 1\nexport const DEFAULT_SWR_MULTIPLIER = 60\n","import {\n CACHE_TAG_MAX_ITEMS,\n CACHE_TAG_MAX_LENGTH,\n DEFAULT_MAX_AGE,\n THEO_T_PREFIX,\n} from './constants.js'\n\nexport interface ValidationResult<T> {\n valid: T[]\n dropped: { value: unknown; reason: string }[]\n}\n\n/**\n * Validate an array of cache tags.\n * Drops invalid entries (type / length / reserved-prefix / overflow) with warn log.\n * NEVER throws on runtime input — caller-facing safety.\n *\n * EC-1: defensive guard for non-array input (e.g., undefined from optional chain).\n */\nexport function validateTags(tags: unknown, description: string): ValidationResult<string> {\n // EC-1 guard\n if (!Array.isArray(tags)) {\n const result: ValidationResult<string> = {\n valid: [],\n dropped: [{ value: tags, reason: `expected array, got ${typeof tags}` }],\n }\n warnDropped(result, description)\n return result\n }\n\n // Array.isArray narrows to `any[]` (TS limitation). Re-type as unknown[]\n // so each element flows through explicit type guards below.\n const tagArr: unknown[] = tags as unknown[]\n const valid: string[] = []\n const dropped: { value: unknown; reason: string }[] = []\n\n for (let i = 0; i < tagArr.length; i++) {\n if (valid.length >= CACHE_TAG_MAX_ITEMS) {\n for (let j = i; j < tagArr.length; j++) {\n dropped.push({ value: tagArr[j], reason: 'overflow (max 128 tags)' })\n }\n break\n }\n const tag: unknown = tagArr[i]\n if (typeof tag !== 'string') {\n dropped.push({ value: tag, reason: 'invalid type, must be a string' })\n continue\n }\n if (tag.length > CACHE_TAG_MAX_LENGTH) {\n dropped.push({\n value: tag,\n reason: `exceeded max length of ${CACHE_TAG_MAX_LENGTH}`,\n })\n continue\n }\n if (tag.startsWith(THEO_T_PREFIX)) {\n dropped.push({\n value: tag,\n reason: `reserved prefix \"${THEO_T_PREFIX}\"`,\n })\n continue\n }\n valid.push(tag)\n }\n\n warnDropped({ valid, dropped }, description)\n return { valid, dropped }\n}\n\n/**\n * Validate a `maxAge` value in seconds.\n * Throws on invalid input (config-time validation).\n * Returns DEFAULT_MAX_AGE when undefined.\n */\nexport function validateMaxAge(maxAge: unknown, description: string): number {\n if (maxAge === undefined) return DEFAULT_MAX_AGE\n if (typeof maxAge === 'number' && Number.isFinite(maxAge) && maxAge >= 0) {\n return maxAge\n }\n throw new Error(\n `Invalid maxAge \"${JSON.stringify(maxAge)}\" in ${description}, must be a non-negative finite number`,\n )\n}\n\n/**\n * Validate an `expire` value in seconds, optionally cross-checked against `revalidate`.\n * Throws on invalid input (config-time validation).\n */\nexport function validateExpire(\n expire: unknown,\n revalidate: number | undefined,\n description: string,\n): number | undefined {\n if (expire === undefined) return undefined\n if (typeof expire !== 'number' || !Number.isFinite(expire) || expire < 0) {\n throw new Error(\n `Invalid expire \"${JSON.stringify(expire)}\" in ${description}, must be a non-negative finite number`,\n )\n }\n if (revalidate !== undefined && expire < revalidate) {\n throw new Error(\n `Invalid expire ${expire} in ${description}, must be greater than or equal to revalidate ${revalidate}`,\n )\n }\n return expire\n}\n\nfunction warnDropped(result: ValidationResult<string>, description: string): void {\n if (result.dropped.length === 0) return\n console.warn(`[theokit:cache] ${description}: dropped ${result.dropped.length} invalid tag(s):`)\n for (const { value, reason } of result.dropped) {\n console.warn(` - ${JSON.stringify(value)}: ${reason}`)\n }\n}\n","import type { CacheEngine } from './cache-engine.js'\nimport { validateExpire, validateMaxAge, validateTags } from './validation.js'\n\nexport interface DefineCachedFunctionOptions<TArgs extends unknown[], TReturn> {\n /** Required cache namespace; appears in keys as \"fn:${name}:...\" */\n name: string\n /** seconds; defaults to DEFAULT_MAX_AGE (1) */\n maxAge?: number\n /** seconds; stale-while-revalidate window */\n swr?: number\n /** Custom key derivation from args. Default: JSON.stringify(args). */\n getKey?: (...args: TArgs) => string\n /** Static or dynamic tags. */\n tags?: string[] | ((...args: TArgs) => string[])\n /** Version stamp; bump to invalidate all entries under this name. */\n cacheVersion?: string\n /** Transform returned value before caching. */\n transform?: (raw: TReturn) => TReturn\n /** Skip cache if validate returns false (treats existing entry as miss). */\n validate?: (raw: TReturn) => boolean\n /** Called on any error in the cache pipeline. */\n onError?: (err: unknown, ctx: { args: TArgs }) => void\n}\n\nexport type CachedFunction<TArgs extends unknown[], TReturn> = ((\n ...args: TArgs\n) => Promise<TReturn>) & {\n /** Bust the cache entry for these specific args. */\n invalidate: (...args: TArgs) => Promise<void>\n}\n\n/**\n * Wrap an async function with cache semantics.\n * Returns a callable that memoizes by `(name + args)` and exposes `.invalidate(args)`.\n *\n * The engine is supplied by the caller (avoids module-level singleton coupling\n * during testing; framework wiring provides it in production).\n */\nexport function defineCachedFunction<TArgs extends unknown[], TReturn>(\n engine: CacheEngine,\n fn: (...args: TArgs) => TReturn | Promise<TReturn>,\n opts: DefineCachedFunctionOptions<TArgs, TReturn>,\n): CachedFunction<TArgs, TReturn> {\n if (typeof opts.name !== 'string' || opts.name.length === 0) {\n throw new Error('defineCachedFunction: opts.name is required (non-empty string)')\n }\n const maxAge = validateMaxAge(opts.maxAge, `defineCachedFunction(${opts.name})`)\n const swr = validateExpire(opts.swr, maxAge, `defineCachedFunction(${opts.name})`)\n\n const prefix = `fn:${opts.name}`\n\n function deriveCacheKey(args: TArgs): string {\n const tail = opts.getKey ? opts.getKey(...args) : JSON.stringify(args)\n return `${prefix}:${tail}`\n }\n\n function resolveTags(args: TArgs): string[] {\n const raw = typeof opts.tags === 'function' ? opts.tags(...args) : (opts.tags ?? [])\n const { valid } = validateTags(raw, `defineCachedFunction(${opts.name})`)\n return valid\n }\n\n const wrapped = (async (...args: TArgs) => {\n const key = deriveCacheKey(args)\n const tags = resolveTags(args)\n try {\n const { value } = await engine.getOrCompute<TReturn>(key, async () => fn(...args), {\n maxAge,\n swr: swr ?? maxAge * 60,\n tags,\n cacheVersion: opts.cacheVersion,\n transform: opts.transform,\n validate: opts.validate,\n })\n return value\n } catch (err) {\n opts.onError?.(err, { args })\n throw err\n }\n }) as CachedFunction<TArgs, TReturn>\n\n wrapped.invalidate = async (...args: TArgs) => {\n const key = deriveCacheKey(args)\n await engine.invalidate(key)\n }\n\n return wrapped\n}\n","export interface CacheControlInput {\n /** seconds; 0 forces no-cache */\n maxAge: number\n /** stale-while-revalidate window in seconds; 0 or undefined omits directive */\n swr?: number\n /** emit `private,` prefix (skips shared CDN caching) */\n isPrivate?: boolean\n}\n\nconst NO_CACHE_HEADER = 'private, no-cache, no-store, max-age=0, must-revalidate'\n\n/**\n * Build a canonical Cache-Control header value.\n *\n * `maxAge === 0` always yields the strict no-cache directive regardless\n * of `swr` or `isPrivate` — defensive default.\n *\n * EC-13: pure function intentional — caller is responsible for input\n * validation (see validateMaxAge / validateExpire in validation.ts).\n */\nexport function getCacheControlHeader(input: CacheControlInput): string {\n if (input.maxAge === 0) return NO_CACHE_HEADER\n const parts: string[] = []\n if (input.isPrivate) parts.push('private')\n parts.push(`s-maxage=${input.maxAge}`)\n if (input.swr !== undefined && input.swr > 0) {\n parts.push(`stale-while-revalidate=${input.swr}`)\n }\n return parts.join(', ')\n}\n","/**\n * Default tracking/analytics query parameters excluded from cache keys.\n * Mirrors Astro's DEFAULT_EXCLUDED_PARAMS list (memory-provider.ts:117).\n * Set as exact-match (not glob) for KISS + zero-dep.\n */\nexport const DEFAULT_EXCLUDED_QUERY_PARAMS = [\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_term',\n 'utm_content',\n 'fbclid',\n 'gclid',\n 'gbraid',\n 'wbraid',\n 'dclid',\n 'msclkid',\n 'twclid',\n 'li_fat_id',\n 'mc_cid',\n 'mc_eid',\n '_ga',\n '_gl',\n '_hsenc',\n '_hsmi',\n '_ke',\n 'oly_anon_id',\n 'oly_enc_id',\n 'rb_clickid',\n 's_cid',\n 'vero_id',\n 'wickedid',\n 'yclid',\n '__s',\n 'ref',\n]\n\nexport interface KeyDerivationOptions {\n /** Total override — when provided, bypasses all internal logic. */\n getKey?: (req: Request) => string | Promise<string>\n /** Exact-match query param names to drop. Defaults to DEFAULT_EXCLUDED_QUERY_PARAMS. */\n excludeQuery?: string[]\n /** Whether to sort query params alphabetically. Defaults to true. */\n sortQuery?: boolean\n /** Header names whose values are appended as `\\0name=value` suffix. */\n varies?: string[]\n /** Namespace prefix (e.g., route name). */\n prefix?: string\n}\n\n/**\n * Derive a deterministic cache key from a Request.\n *\n * Default behaviour: `${prefix?}${protocol}//${lower(host)}${path}${?sortedFilteredQuery}` + Vary suffix.\n * `\\0` separator chosen because it cannot appear in URLs or HTTP header values.\n *\n * EC-6: malformed URL on getKey path is caller's responsibility.\n * EC-7: enforces getKey returns string.\n */\nexport async function deriveKey(req: Request, opts: KeyDerivationOptions = {}): Promise<string> {\n if (opts.getKey) {\n const k = await opts.getKey(req)\n if (typeof k !== 'string') {\n throw new Error(`getKey must return a string, got ${typeof k}`)\n }\n return k\n }\n\n const url = new URL(req.url)\n const queryString = buildQueryString(url, opts)\n const base = `${opts.prefix ? opts.prefix + ':' : ''}${url.protocol}//${url.hostname.toLowerCase()}${url.pathname}${queryString ? '?' + queryString : ''}`\n\n if (!opts.varies || opts.varies.length === 0) return base\n const parts: string[] = []\n for (const header of opts.varies) {\n parts.push(`${header}=${req.headers.get(header) ?? ''}`)\n }\n return base + '\\0' + parts.join('\\0')\n}\n\nfunction buildQueryString(url: URL, opts: KeyDerivationOptions): string {\n const params = new URLSearchParams(url.searchParams)\n const exclude = opts.excludeQuery ?? DEFAULT_EXCLUDED_QUERY_PARAMS\n const excludeSet = new Set(exclude)\n for (const key of [...params.keys()]) {\n if (excludeSet.has(key)) params.delete(key)\n }\n if (opts.sortQuery !== false) params.sort()\n return params.toString()\n}\n","import type { z } from 'zod'\n\nimport type { RouteConfig } from '../core/contracts/route-config.js'\n\nimport { getCacheControlHeader } from './cache-control-header.js'\nimport type { CacheEngine, CacheStatus } from './cache-engine.js'\nimport { THEO_T_PREFIX } from './constants.js'\nimport { deriveKey } from './key-derivation.js'\nimport type { CacheEntry } from './storage-adapter.js'\nimport { validateExpire, validateMaxAge, validateTags } from './validation.js'\n\n/** Default 10 MB cap per cached route entry (EC-3). */\nexport const DEFAULT_MAX_ENTRY_SIZE = 10 * 1024 * 1024\nconst VARIES_IGNORED = new Set(['cookie', 'set-cookie'])\n\nexport interface RouteCacheOptions {\n maxAge?: number\n swr?: number\n tags?: string[]\n varies?: string[]\n getKey?: (req: Request) => string | Promise<string>\n bypassWhen?: (req: Request) => boolean | Promise<boolean>\n cacheVersion?: string\n cacheErrors?: boolean\n methods?: string[]\n cacheable?: (response: Response) => boolean\n /** Max body bytes (EC-3); default DEFAULT_MAX_ENTRY_SIZE. */\n maxEntrySize?: number\n}\n\nexport interface CachedRouteConfig<\n TQuery extends z.ZodType = z.ZodUndefined,\n TBody extends z.ZodType = z.ZodUndefined,\n TParams extends z.ZodType = z.ZodUndefined,\n TCtx = unknown,\n> extends Omit<RouteConfig<TQuery, TBody, TParams, TCtx>, 'handler'> {\n cache: RouteCacheOptions\n handler: (ctx: {\n query: z.infer<TQuery>\n body: z.infer<TBody>\n params: z.infer<TParams>\n request: Request\n ctx: TCtx\n }) => unknown\n}\n\ninterface RouteCacheValue {\n body: string\n status: number\n headers: [string, string][]\n}\n\nconst setCookieWarnedRoutes = new WeakSet()\nconst variesCookieWarnedRoutes = new WeakSet()\nconst oversizedWarnedRoutes = new WeakSet()\n\n/**\n * Wrap a `RouteConfig` with cache-aware handler logic.\n *\n * Architecture: wraps the user `handler` so cache lookup happens AT\n * handler-invocation time, AFTER router middleware (auth/csrf/etc) ran.\n * This structurally satisfies EC-4 (cache-after-auth) without modifying\n * the router internals.\n *\n * Algorithm per request:\n * 1. Method check + bypassWhen + maxAge=0 → call handler raw\n * 2. Derive key (path + sortedQuery + varies, prefix by method)\n * 3. Cache lookup → HIT/STALE return cached Response\n * 4. Miss → run handler, check cacheability (Set-Cookie / status / size / SSE / streaming)\n * 5. Cacheable → write entry + return Response with X-Theo-Cache: MISS (dev)\n * 6. Not cacheable → return original Response unchanged\n *\n * Concurrent dedupe is INTENTIONALLY NOT used for routes — Response objects\n * cannot be safely shared across concurrent callers (body is single-use stream).\n * Each request that misses runs the handler independently.\n */\nexport function defineCachedRoute<\n TQuery extends z.ZodType = z.ZodUndefined,\n TBody extends z.ZodType = z.ZodUndefined,\n TParams extends z.ZodType = z.ZodUndefined,\n TCtx = unknown,\n>(\n engine: CacheEngine,\n config: CachedRouteConfig<TQuery, TBody, TParams, TCtx>,\n): RouteConfig<TQuery, TBody, TParams, TCtx, Response> {\n const { cache, handler, ...rest } = config\n\n const maxAge = validateMaxAge(cache.maxAge, 'defineCachedRoute')\n const swr = validateExpire(cache.swr, maxAge, 'defineCachedRoute')\n if (cache.cacheVersion !== undefined && cache.cacheVersion === '') {\n throw new Error('defineCachedRoute: cacheVersion must be non-empty if provided')\n }\n // EC-19: maxEntrySize validation\n const maxEntrySize = cache.maxEntrySize ?? DEFAULT_MAX_ENTRY_SIZE\n if (!Number.isFinite(maxEntrySize) || maxEntrySize < 0) {\n throw new Error(\n `Invalid maxEntrySize \"${String(cache.maxEntrySize)}\" in defineCachedRoute, must be a non-negative finite number`,\n )\n }\n const methods = new Set((cache.methods ?? ['GET', 'HEAD']).map((m) => m.toUpperCase()))\n const baseTags = validateTags(cache.tags ?? [], 'defineCachedRoute').valid\n\n // EC-2: filter cookie/set-cookie from varies + warn-once per route\n const variesRaw = cache.varies ?? []\n const variesLower = variesRaw.map((v) => v.toLowerCase())\n const hadCookieVary = variesLower.some((v) => VARIES_IGNORED.has(v))\n const safeVaries = variesLower.filter((v) => !VARIES_IGNORED.has(v))\n if (hadCookieVary && !variesCookieWarnedRoutes.has(config)) {\n variesCookieWarnedRoutes.add(config)\n console.warn(\n `[theokit:cache] defineCachedRoute: 'cookie'/'set-cookie' removed from varies — they fragment cache to zero hit rate (EC-2)`,\n )\n }\n\n const wrappedHandler = async (ctx: {\n query: z.infer<TQuery>\n body: z.infer<TBody>\n params: z.infer<TParams>\n request: Request\n ctx: TCtx\n }): Promise<Response> => {\n // TheoKit's dispatcher may pass a Node IncomingMessage (not a Web Request).\n // Normalize to a Web Request so deriveKey / bypassWhen / URL parsing work uniformly.\n const webRequest = toWebRequest(ctx.request)\n\n if (!methods.has(webRequest.method.toUpperCase())) {\n return invokeHandlerAsResponse(handler, ctx)\n }\n if (cache.bypassWhen && (await cache.bypassWhen(webRequest))) {\n return invokeHandlerAsResponse(handler, ctx)\n }\n if (maxAge === 0) {\n return invokeHandlerAsResponse(handler, ctx)\n }\n\n const key = await deriveKey(webRequest, {\n prefix: 'route:' + webRequest.method.toUpperCase(),\n varies: safeVaries,\n getKey: cache.getKey,\n })\n\n // ---- Cache lookup (T4.2 DRY — delegates to engine canonical) ----\n const cached = await engine.tryReadCached<RouteCacheValue>(key, {\n cacheVersion: cache.cacheVersion,\n })\n\n // T4.3 — build options bag once; pass to all helpers (≤ 4 params each).\n const routeCacheCtx: RouteCacheCtx = {\n engine,\n key,\n cache,\n routeConfig: config,\n maxEntrySize,\n maxAge,\n swr,\n baseTags,\n webRequest,\n }\n\n if (cached) {\n if (cached.status === 'hit') {\n return buildResponseFromCache(cached.value, 'hit', maxAge, swr)\n }\n // Only 'stale' remains (engine never returns 'miss' from tryReadCached).\n // Stale: schedule background refresh + return stale immediately.\n scheduleRouteRevalidate(routeCacheCtx, handler, ctx)\n return buildResponseFromCache(cached.value, 'stale', maxAge, swr)\n }\n\n // ---- Miss: run handler ----\n const response = await invokeHandlerAsResponse(handler, ctx)\n return persistAndReturn(routeCacheCtx, response)\n }\n\n return {\n ...rest,\n handler: wrappedHandler,\n }\n}\n\n// T4.2 (PV-5 DRY): tryReadCacheEntry was removed — duplicated the engine's\n// canonical tryReadCached (staleness check, version check, JSON parse,\n// clock-skew clamp). Route wrapper now delegates to `engine.tryReadCached`.\n\n/**\n * T4.3 options-bag context (PV-6 — Clean Code consensus ≤ 4 params).\n * Collapses 10/11 positional params of `persistAndReturn` and\n * `scheduleRouteRevalidate` to 2 params each (ctx + payload).\n *\n * Some fields are derived from `cache` config (EC-22 documented redundancy) —\n * the trade-off is O(1) construction per request for vastly simpler call\n * sites and safer additions of new fields without reordering args.\n */\ninterface RouteCacheCtx {\n engine: CacheEngine\n key: string\n cache: RouteCacheOptions\n routeConfig: object\n maxEntrySize: number\n maxAge: number\n swr: number | undefined\n baseTags: string[]\n webRequest: Request\n}\n\nfunction buildRouteCacheEntry(value: RouteCacheValue, ctx: RouteCacheCtx): CacheEntry {\n const pathTag = THEO_T_PREFIX + new URL(ctx.webRequest.url).pathname\n return {\n body: JSON.stringify(value),\n status: 200,\n headers: [],\n storedAt: Date.now(),\n maxAge: ctx.maxAge,\n swr: ctx.swr ?? ctx.maxAge * 60,\n tags: [...ctx.baseTags, pathTag],\n cacheVersion: ctx.cache.cacheVersion,\n }\n}\n\nasync function persistAndReturn(ctx: RouteCacheCtx, response: Response): Promise<Response> {\n const cacheableResult = await tryCacheResponse(\n response,\n ctx.cache,\n ctx.routeConfig,\n ctx.maxEntrySize,\n )\n if (!cacheableResult) {\n // Not cached — return original response unchanged\n return response\n }\n await ctx.engine.set(ctx.key, buildRouteCacheEntry(cacheableResult, ctx))\n return buildResponseFromCache(cacheableResult, 'miss', ctx.maxAge, ctx.swr)\n}\n\nfunction scheduleRouteRevalidate<THandlerCtx>(\n ctx: RouteCacheCtx,\n handler: (handlerCtx: THandlerCtx) => unknown,\n handlerCtx: THandlerCtx,\n): void {\n void (async () => {\n try {\n const response = await invokeHandlerAsResponse(handler, handlerCtx)\n const result = await tryCacheResponse(response, ctx.cache, ctx.routeConfig, ctx.maxEntrySize)\n if (!result) return\n await ctx.engine.set(ctx.key, buildRouteCacheEntry(result, ctx))\n } catch {\n // Stale entry remains; future request retries on its own stale-check\n }\n })()\n}\n\n/**\n * Type for Node.js IncomingMessage (subset we read).\n * Avoids a direct `node:http` import which would block edge runtimes.\n */\ninterface NodeLikeRequest {\n url?: string\n method?: string\n headers: Record<string, string | string[] | undefined>\n socket?: { encrypted?: boolean }\n}\n\n/**\n * Adapt either a Web Request or a Node IncomingMessage to a Web Request.\n * TheoKit's runtime dispatch may pass either depending on the adapter.\n */\n\n// Node IncomingMessage) inside one function so the call sites stay shape-blind.\nfunction toWebRequest(req: Request | NodeLikeRequest): Request {\n // Web Request fast-path\n if (typeof (req as Request).clone === 'function' && (req as Request).headers instanceof Headers) {\n return req as Request\n }\n const node = req as NodeLikeRequest\n const host = (typeof node.headers.host === 'string' ? node.headers.host : null) ?? 'localhost'\n const protocol = node.socket?.encrypted ? 'https' : 'http'\n const path = node.url ?? '/'\n const url = path.startsWith('http') ? path : `${protocol}://${host}${path}`\n const headers = new Headers()\n for (const [k, v] of Object.entries(node.headers)) {\n if (Array.isArray(v)) {\n for (const item of v) headers.append(k, item)\n } else if (typeof v === 'string') {\n headers.set(k, v)\n }\n }\n return new Request(url, {\n method: (node.method ?? 'GET').toUpperCase(),\n headers,\n })\n}\n\nasync function invokeHandlerAsResponse<TCtx>(\n handler: (ctx: TCtx) => unknown,\n ctx: TCtx,\n): Promise<Response> {\n const raw = await handler(ctx)\n if (raw instanceof Response) return raw\n return Response.json(raw)\n}\n\n/**\n * Decide whether `response` is cacheable. Returns serialized form on yes; undefined on no.\n * Order matters: cheap checks first, body read last (only for cacheable candidates).\n */\nasync function tryCacheResponse(\n response: Response,\n cache: RouteCacheOptions,\n routeConfig: object,\n maxEntrySize: number,\n): Promise<RouteCacheValue | undefined> {\n // D7 / EC-2: Set-Cookie auto-bypass\n if (response.headers.has('set-cookie')) {\n if (!setCookieWarnedRoutes.has(routeConfig)) {\n setCookieWarnedRoutes.add(routeConfig)\n console.warn('[theokit:cache] response has Set-Cookie — skipping cache write (D7)')\n }\n return undefined\n }\n // SSE\n const contentType = response.headers.get('content-type') ?? ''\n if (contentType.includes('text/event-stream')) return undefined\n // EC-11: chunked streaming (transfer-encoding: chunked OR no content-length on a\n // user-constructed Response — i.e., a Response built from a ReadableStream that\n // wasn't via Response.json/text helpers). We detect via the explicit\n // transfer-encoding header since Response.json() also uses a ReadableStream\n // body internally and we don't want to refuse those.\n if (response.headers.get('transfer-encoding')?.toLowerCase() === 'chunked') {\n return undefined\n }\n // D9: status >= 400 not cached unless opt-in\n if (response.status >= 400 && !cache.cacheErrors) return undefined\n // Custom predicate (overrides built-ins)\n if (cache.cacheable && !cache.cacheable(response)) return undefined\n\n // Read body (clone to preserve the response for downstream)\n const text = await response.clone().text()\n\n // EC-3: oversized bypass\n if (text.length > maxEntrySize) {\n if (!oversizedWarnedRoutes.has(routeConfig)) {\n oversizedWarnedRoutes.add(routeConfig)\n console.warn(\n `[theokit:cache] response body ${text.length} bytes exceeds maxEntrySize ${maxEntrySize}; skipping cache (EC-3)`,\n )\n }\n return undefined\n }\n\n const headers: [string, string][] = []\n response.headers.forEach((v, k) => {\n if (k.toLowerCase() === 'set-cookie') return // defense-in-depth\n headers.push([k, v])\n })\n return { body: text, status: response.status, headers }\n}\n\nfunction buildResponseFromCache(\n value: RouteCacheValue,\n status: CacheStatus,\n maxAge: number,\n swr: number | undefined,\n): Response {\n const headers = new Headers(value.headers)\n if (!headers.has('cache-control')) {\n headers.set('cache-control', getCacheControlHeader({ maxAge, swr: swr ?? maxAge * 60 }))\n }\n if (process.env.NODE_ENV !== 'production') {\n let cacheStatusHeader: 'HIT' | 'STALE' | 'MISS' = 'MISS'\n if (status === 'hit') cacheStatusHeader = 'HIT'\n else if (status === 'stale') cacheStatusHeader = 'STALE'\n headers.set('X-Theo-Cache', cacheStatusHeader)\n }\n return new Response(value.body, { status: value.status, headers })\n}\n","import { THEO_T_PREFIX } from './constants.js'\nimport type { CacheEntry, CacheStorageAdapter } from './storage-adapter.js'\n\nexport type CacheStatus = 'hit' | 'stale' | 'miss'\n\nexport interface CacheEngineOptions {\n storage: CacheStorageAdapter\n defaults?: {\n maxAge?: number\n swr?: number\n cacheVersion?: string\n }\n onError?: (err: unknown, ctx: { phase: 'get' | 'set' | 'revalidate'; key: string }) => void\n}\n\nexport interface GetOrComputeOptions<T> {\n maxAge: number\n swr?: number\n tags?: string[]\n cacheVersion?: string\n transform?: (raw: T) => T\n validate?: (raw: T) => boolean\n /**\n * When `true`, the value is returned but NOT written to cache.\n * Used by route middleware to bypass cache for uncacheable responses\n * (Set-Cookie, oversized body, status >= 400 with cacheErrors=false).\n */\n skipCacheWhen?: (raw: T) => boolean\n}\n\nexport interface CacheEngine {\n getOrCompute<T>(\n key: string,\n fn: () => Promise<T>,\n opts: GetOrComputeOptions<T>,\n ): Promise<{ value: T; status: CacheStatus }>\n\n /**\n * Public canonical cache read (T4.2 of architecture-review-remediation-plan,\n * PV-5 DRY consolidation). Returns the parsed value + status (`hit` | `stale`)\n * for callers that DON'T want to bind a loader function (e.g., HTTP route\n * middleware that may want to bypass on miss instead of running a loader).\n *\n * Returns undefined when:\n * - Entry not present in storage\n * - `opts.cacheVersion` mismatch with entry\n * - Body is not a JSON string (parse failed)\n * - `opts.validate` returns false (or throws — caller's onError invoked)\n * - Entry is fully expired (past maxAge + swr)\n */\n tryReadCached<T>(\n key: string,\n opts: { cacheVersion?: string; validate?: (v: T) => boolean },\n ): Promise<{ value: T; status: 'hit' | 'stale' } | undefined>\n\n set(key: string, entry: CacheEntry): Promise<void>\n invalidate(key: string): Promise<boolean>\n invalidateTag(tag: string): Promise<number>\n revalidatePath(path: string, type?: 'layout' | 'page'): Promise<number>\n\n /** Storage adapter passthrough (read-only access for diagnostics). */\n readonly storage: CacheStorageAdapter\n}\n\n/**\n * Build a cache engine wrapping a storage adapter.\n *\n * Implements:\n * - SWR (fresh / stale / expired branching) — Astro `memory-provider.ts:423`-style.\n * - In-flight dedupe via `Map<key, Promise>` — Next.js `pendingRevalidates` pattern.\n * - Tag-based invalidation via adapter's deleteByTag.\n * - Path-as-tag encoding (revalidatePath sugar) — Next.js `revalidate.ts:105`.\n *\n * EC-8: Math.max(0, age) guards clock skew.\n * EC-9: validate callback wrapped in try/catch.\n * EC-10: loader returning undefined skips cache write + warns once.\n *\n * NOTE on max-lines-per-function disable below: createCacheEngine is a factory\n * closure that owns the in-flight/bg/warned maps. Splitting would force the\n * helpers across modules and re-introduce the shared mutable state through\n * parameter lists.\n */\n// eslint-disable-next-line max-lines-per-function\nexport function createCacheEngine(opts: CacheEngineOptions): CacheEngine {\n const { storage, onError } = opts\n const inFlight = new Map<string, Promise<unknown>>()\n const bgInFlight = new Set<string>()\n const undefinedLoaderWarned = new Set<string>()\n\n async function getOrCompute<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n ): Promise<{ value: T; status: CacheStatus }> {\n // Dedupe: concurrent first-miss shares the loader promise\n const pending = inFlight.get(key)\n if (pending) {\n const value = (await pending) as T\n return { value, status: 'miss' }\n }\n\n // maxAge=0 → always miss, never cache\n if (options.maxAge === 0) {\n return claimAndRun(key, fn, options, /* skipWrite */ true)\n }\n\n // Atomically claim the in-flight slot BEFORE any await (dedupe race fix).\n return claimAndRun(key, fn, options, false)\n }\n\n /**\n * Claims the in-flight slot synchronously, then performs the get/loader work\n * inside. Concurrent callers awaiting the same key share this slot.\n */\n function claimAndRun<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n skipWrite: boolean,\n ): Promise<{ value: T; status: CacheStatus }> {\n let resolveOuter!: (v: T) => void\n let rejectOuter!: (e: unknown) => void\n const outerPromise = new Promise<T>((resolve, reject) => {\n resolveOuter = resolve\n rejectOuter = reject\n })\n // Prevent unhandled-rejection when no concurrent awaiter exists\n void outerPromise.catch(() => {\n /* swallow — the leader handles via work promise */\n })\n inFlight.set(key, outerPromise)\n\n const work = (async (): Promise<{ value: T; status: CacheStatus }> => {\n try {\n if (!skipWrite) {\n const cached = await tryReadCached<T>(key, options)\n if (cached) {\n // HIT or STALE — value already resolved\n resolveOuter(cached.value)\n if (cached.status === 'stale') {\n scheduleBackgroundRevalidate(key, fn, options)\n }\n return cached\n }\n }\n // Miss path: run loader\n const value = await runLoader(key, fn, options, skipWrite)\n resolveOuter(value)\n return { value, status: 'miss' as const }\n } catch (err) {\n rejectOuter(err)\n throw err\n } finally {\n inFlight.delete(key)\n }\n })()\n\n return work\n }\n\n // One inline staleness machine; each guard (version check, parse, validate,\n // age, swr window) is one short branch. Extracting per-step would dilute,\n // not clarify.\n // eslint-disable-next-line complexity\n async function tryReadCached<T>(\n key: string,\n options: GetOrComputeOptions<T>,\n ): Promise<{ value: T; status: CacheStatus } | undefined> {\n let entry: CacheEntry | undefined\n try {\n entry = await storage.get(key)\n } catch (err) {\n onError?.(err, { phase: 'get', key })\n return undefined\n }\n if (!entry) return undefined\n if (options.cacheVersion !== undefined && entry.cacheVersion !== options.cacheVersion) {\n return undefined\n }\n if (typeof entry.body !== 'string') return undefined\n let parsed: T\n try {\n parsed = JSON.parse(entry.body) as T\n } catch (err) {\n onError?.(err, { phase: 'get', key })\n return undefined\n }\n // EC-9: validate wrapped in try/catch\n if (options.validate) {\n try {\n if (!options.validate(parsed)) return undefined\n } catch (err) {\n onError?.(err, { phase: 'get', key })\n return undefined\n }\n }\n // EC-8: clamp age to non-negative (clock skew)\n const age = Math.max(0, (Date.now() - entry.storedAt) / 1000)\n if (age <= entry.maxAge) {\n const value = options.transform ? options.transform(parsed) : parsed\n return { value, status: 'hit' }\n }\n if (age <= entry.maxAge + entry.swr) {\n const value = options.transform ? options.transform(parsed) : parsed\n return { value, status: 'stale' }\n }\n return undefined\n }\n\n // runLoader always returns `value` by design: loader output is the caller's\n // contract; every branch short-circuits a *write*, never the return path.\n // eslint-disable-next-line sonarjs/no-invariant-returns, complexity\n async function runLoader<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n skipWrite = false,\n ): Promise<T> {\n const raw = await fn()\n const value = options.transform ? options.transform(raw) : raw\n\n if (skipWrite) return value\n\n // skipCacheWhen sentinel — caller-controlled skip-write\n if (options.skipCacheWhen?.(value)) return value\n\n // EC-9: validate during write\n if (options.validate) {\n let isValid = true\n try {\n isValid = options.validate(value)\n } catch (err) {\n onError?.(err, { phase: 'set', key })\n return value\n }\n if (!isValid) return value\n }\n\n // EC-10: undefined return → warn-once + skip cache\n if (value === undefined) {\n if (!undefinedLoaderWarned.has(key)) {\n undefinedLoaderWarned.add(key)\n console.warn(`[theokit:cache] loader returned undefined for key \"${key}\"; entry not cached`)\n }\n return value\n }\n\n try {\n const entry: CacheEntry = {\n body: JSON.stringify(value),\n status: 200,\n headers: [],\n storedAt: Date.now(),\n maxAge: options.maxAge,\n swr: options.swr ?? 0,\n tags: options.tags ?? [],\n cacheVersion: options.cacheVersion,\n }\n await storage.set(key, entry)\n } catch (err) {\n onError?.(err, { phase: 'set', key })\n }\n return value\n }\n\n function scheduleBackgroundRevalidate<T>(\n key: string,\n fn: () => Promise<T>,\n options: GetOrComputeOptions<T>,\n ): void {\n if (bgInFlight.has(key)) return\n bgInFlight.add(key)\n void runLoader(key, fn, options)\n .catch((err: unknown) => {\n onError?.(err, { phase: 'revalidate', key })\n })\n .finally(() => {\n bgInFlight.delete(key)\n })\n }\n\n return {\n storage,\n\n getOrCompute,\n\n async tryReadCached<T>(\n key: string,\n opts: { cacheVersion?: string; validate?: (v: T) => boolean },\n ): Promise<{ value: T; status: 'hit' | 'stale' } | undefined> {\n // Delegate to internal impl (which uses the broader GetOrComputeOptions\n // type). tryReadCached never returns status: 'miss' — that codepath\n // returns undefined. Narrow the type via assertion.\n const result = await tryReadCached<T>(key, {\n ...opts,\n maxAge: 0, // unused by tryReadCached\n })\n if (!result) return undefined\n return result as { value: T; status: 'hit' | 'stale' }\n },\n\n async set(key, entry) {\n await storage.set(key, entry)\n },\n\n async invalidate(key) {\n // Best-effort: clear in-flight too so next request starts fresh\n inFlight.delete(key)\n return storage.delete(key)\n },\n\n async invalidateTag(tag) {\n return storage.deleteByTag(tag)\n },\n\n async revalidatePath(path, type) {\n const tag = THEO_T_PREFIX + path + (type ? '/' + type : '')\n return storage.deleteByTag(tag)\n },\n }\n}\n","import type { CacheEntry, CacheStorageAdapter } from './storage-adapter.js'\n\nexport interface InMemoryCacheAdapterOptions {\n /** Maximum number of entries before LRU eviction. Default 1000. */\n maxEntries?: number\n}\n\n/**\n * In-memory cache adapter with LRU eviction + reverse tag index.\n *\n * Uses Map insertion-order for O(1) LRU (Astro LRUMap pattern).\n * Reverse index `tagIndex: Map<tag, Set<key>>` makes deleteByTag O(matched-keys).\n *\n * Invariants:\n * - `entries.size <= maxEntries` post-set.\n * - `tagIndex[tag].has(key)` ↔ `entries.get(key)?.tags.includes(tag)`.\n *\n * eslint-disable @typescript-eslint/require-await — the CacheStorageAdapter\n * interface is intentionally async so Redis/file/external adapters fit. The\n * in-memory variant returns immediately but keeps the async signature to\n * satisfy the contract.\n */\n/* eslint-disable @typescript-eslint/require-await */\nexport class InMemoryCacheAdapter implements CacheStorageAdapter {\n readonly name = 'memory'\n readonly #entries = new Map<string, CacheEntry>()\n readonly #tagIndex = new Map<string, Set<string>>()\n readonly #maxEntries: number\n\n constructor(opts: InMemoryCacheAdapterOptions = {}) {\n this.#maxEntries = opts.maxEntries ?? 1000\n }\n\n async get(key: string): Promise<CacheEntry | undefined> {\n const entry = this.#entries.get(key)\n if (entry === undefined) return undefined\n // LRU bump\n this.#entries.delete(key)\n this.#entries.set(key, entry)\n return entry\n }\n\n async set(key: string, entry: CacheEntry): Promise<void> {\n // Overwrite case: clean old tags first\n const existing = this.#entries.get(key)\n if (existing) {\n this.#removeKeyFromTagIndex(key, existing.tags)\n this.#entries.delete(key)\n } else if (this.#entries.size >= this.#maxEntries) {\n // LRU eviction\n const oldestIter = this.#entries.keys().next()\n if (!oldestIter.done) {\n const oldestKey = oldestIter.value\n const oldestEntry = this.#entries.get(oldestKey)\n if (oldestEntry) {\n this.#removeKeyFromTagIndex(oldestKey, oldestEntry.tags)\n }\n this.#entries.delete(oldestKey)\n }\n }\n this.#entries.set(key, entry)\n for (const tag of entry.tags) {\n let bucket = this.#tagIndex.get(tag)\n if (!bucket) {\n bucket = new Set()\n this.#tagIndex.set(tag, bucket)\n }\n bucket.add(key)\n }\n }\n\n async delete(key: string): Promise<boolean> {\n const entry = this.#entries.get(key)\n if (!entry) return false\n this.#removeKeyFromTagIndex(key, entry.tags)\n this.#entries.delete(key)\n return true\n }\n\n async deleteByTag(tag: string): Promise<number> {\n const bucket = this.#tagIndex.get(tag)\n if (!bucket || bucket.size === 0) return 0\n const keys = [...bucket]\n for (const key of keys) {\n const entry = this.#entries.get(key)\n if (entry) {\n this.#unindexFromOtherTags(key, entry.tags, tag)\n }\n this.#entries.delete(key)\n }\n this.#tagIndex.delete(tag)\n return keys.length\n }\n\n async size(): Promise<number> {\n return this.#entries.size\n }\n\n async clear(): Promise<void> {\n this.#entries.clear()\n this.#tagIndex.clear()\n }\n\n async *keys(prefix?: string): AsyncIterableIterator<string> {\n for (const key of this.#entries.keys()) {\n if (prefix && !key.startsWith(prefix)) continue\n yield key\n }\n }\n\n #removeKeyFromTagIndex(key: string, tags: string[]): void {\n for (const tag of tags) {\n const bucket = this.#tagIndex.get(tag)\n if (!bucket) continue\n bucket.delete(key)\n if (bucket.size === 0) this.#tagIndex.delete(tag)\n }\n }\n\n #unindexFromOtherTags(key: string, tags: string[], skipTag: string): void {\n for (const otherTag of tags) {\n if (otherTag === skipTag) continue\n const otherBucket = this.#tagIndex.get(otherTag)\n if (!otherBucket) continue\n otherBucket.delete(key)\n if (otherBucket.size === 0) this.#tagIndex.delete(otherTag)\n }\n }\n}\n","import type { CacheEngine, CacheEngineOptions } from './cache-engine.js'\nimport { createCacheEngine } from './cache-engine.js'\nimport { InMemoryCacheAdapter } from './in-memory-adapter.js'\nimport type { CacheStorageAdapter } from './storage-adapter.js'\n\nexport interface NormalizedCacheConfig {\n enabled: boolean\n storage: 'memory' | CacheStorageAdapter\n maxEntries: number\n defaults: {\n maxAge: number\n swr?: number\n cacheErrors: boolean\n }\n}\n\nlet _engine: CacheEngine | undefined\n\n/**\n * Initialize the singleton cache engine for this process.\n * Throws if called twice; tests should call `_resetCacheEngine()` between.\n */\nexport function initCacheEngine(\n config: NormalizedCacheConfig,\n hooks: Pick<CacheEngineOptions, 'onError'> = {},\n): CacheEngine {\n if (_engine) {\n throw new Error(\n 'Cache engine already initialized — call _resetCacheEngine() in tests, or check init order in production.',\n )\n }\n if (!config.enabled) {\n throw new Error(\n 'initCacheEngine: config.enabled is false. Skip this call entirely when cache is disabled.',\n )\n }\n const adapter =\n config.storage === 'memory'\n ? new InMemoryCacheAdapter({ maxEntries: config.maxEntries })\n : config.storage\n _engine = createCacheEngine({\n storage: adapter,\n defaults: config.defaults,\n onError: hooks.onError,\n })\n return _engine\n}\n\n/**\n * Resolve the singleton cache engine.\n * Throws a clear error if not initialized — usually means the framework\n * bootstrap missed calling initCacheEngine, or cache.enabled is false.\n */\nexport function getCacheEngine(): CacheEngine {\n if (!_engine) {\n throw new Error(\n 'Cache engine not initialized. Ensure theo.config.ts has `cache.enabled: true` and the framework bootstrap called initCacheEngine.',\n )\n }\n return _engine\n}\n\n/**\n * Test-only: clear the singleton so the next test can re-init.\n * Production code MUST NOT call this.\n */\nexport function _resetCacheEngine(): void {\n _engine = undefined\n}\n","import { getCacheEngine } from './engine-singleton.js'\nimport { validateTags } from './validation.js'\n\nexport interface RevalidateResult {\n deleted: number\n}\n\n/**\n * Invalidate all cache entries carrying the given tag.\n * Safe to call from any context (route handler, action, webhook).\n *\n * `opts.expire` accepted for API compatibility with Next.js; at MVP we delete\n * immediately (no SWR-with-expire semantics). A non-zero value emits one warn.\n */\nexport async function revalidateTag(\n tag: string,\n opts?: { expire?: number },\n): Promise<RevalidateResult> {\n if (opts?.expire !== undefined && opts.expire > 0) {\n warnOnce(\n 'revalidateTag-expire',\n '[theokit:cache] revalidateTag opts.expire is accepted but not honored at MVP (entries are deleted immediately).',\n )\n }\n const { valid } = validateTags([tag], 'revalidateTag')\n if (valid.length === 0) return { deleted: 0 }\n const engine = getCacheEngine()\n const deleted = await engine.invalidateTag(valid[0])\n return { deleted }\n}\n\n/**\n * Immediate invalidation (no SWR), Server-Action-safe.\n * Same semantics as revalidateTag at MVP; kept as separate name for\n * call-site clarity (\"I want fresh data NOW\").\n */\nexport async function updateTag(tag: string): Promise<RevalidateResult> {\n const { valid } = validateTags([tag], 'updateTag')\n if (valid.length === 0) return { deleted: 0 }\n const engine = getCacheEngine()\n const deleted = await engine.invalidateTag(valid[0])\n return { deleted }\n}\n\n/**\n * Invalidate cached entries for a route path.\n * Sugar over `revalidateTag('_THEO_T_/${path}/${type?}')`.\n */\nexport async function revalidatePath(\n path: string,\n opts?: { type?: 'layout' | 'page'; expire?: number },\n): Promise<RevalidateResult> {\n if (opts?.expire !== undefined && opts.expire > 0) {\n warnOnce(\n 'revalidatePath-expire',\n '[theokit:cache] revalidatePath opts.expire is accepted but not honored at MVP.',\n )\n }\n const engine = getCacheEngine()\n const deleted = await engine.revalidatePath(path, opts?.type)\n return { deleted }\n}\n\nconst warnedKeys = new Set<string>()\nfunction warnOnce(key: string, message: string): void {\n if (warnedKeys.has(key)) return\n warnedKeys.add(key)\n console.warn(message)\n}\n","import picomatch from 'picomatch'\n\nexport interface RouteRule {\n maxAge?: number\n swr?: number\n tags?: string[]\n}\n\nexport type RouteRules = Record<string, RouteRule>\n\nexport interface CompiledRouteRule {\n matcher: (path: string) => boolean\n rule: RouteRule\n /** Original glob pattern (for debugging). */\n pattern: string\n}\n\n/**\n * Compile route-rule glob patterns into matcher functions.\n * First-match-wins semantics (preserves insertion order).\n *\n * EC-5: picomatch is a direct production dependency (see plan T7.2).\n */\nexport function compileRouteRules(rules: RouteRules): CompiledRouteRule[] {\n return Object.entries(rules).map(([pattern, rule]) => ({\n matcher: picomatch(pattern, { dot: true }),\n rule,\n pattern,\n }))\n}\n\n/**\n * Resolve the first matching rule for `path`, or `undefined` if none match.\n */\nexport function resolveRouteRule(\n path: string,\n compiled: CompiledRouteRule[],\n): RouteRule | undefined {\n for (const c of compiled) {\n if (c.matcher(path)) return c.rule\n }\n return undefined\n}\n","import superjson from 'superjson'\n\nexport interface SerializedResponse {\n json: unknown\n meta?: unknown\n}\n\n/**\n * Serialize data using superjson for rich type support (Date, Map, Set, BigInt, etc.)\n */\nexport function serializeResponse(data: unknown): SerializedResponse {\n return superjson.serialize(data)\n}\n\n/**\n * Deserialize data that was serialized with superjson.\n */\nexport function deserializeResponse(serialized: SerializedResponse): unknown {\n return superjson.deserialize(serialized as Parameters<typeof superjson.deserialize>[0])\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\n\nexport interface PluginContext {\n request: IncomingMessage\n response: ServerResponse\n ctx: Record<string, unknown>\n requestId: string\n}\n\nexport interface PluginErrorContext extends PluginContext {\n error: unknown\n}\n\nexport interface RunHookOptions {\n inErrorPath?: boolean\n}\n\nexport interface HookResult {\n shortCircuited: boolean\n}\n\nexport type OnRequestHook = (ctx: PluginContext) => void | Promise<void>\nexport type PreHandlerHook = (ctx: PluginContext) => void | Promise<void>\nexport type OnResponseHook = (ctx: PluginContext) => void | Promise<void>\nexport type OnErrorHook = (ctx: PluginErrorContext) => void | Promise<void>\n\nexport type HookName = 'onRequest' | 'preHandler' | 'onResponse' | 'onError'\n\nexport type HookByName<K extends HookName> = K extends 'onError'\n ? OnErrorHook\n : K extends 'onRequest'\n ? OnRequestHook\n : K extends 'preHandler'\n ? PreHandlerHook\n : K extends 'onResponse'\n ? OnResponseHook\n : never\n\nexport interface TheoApp {\n addHook<K extends HookName>(name: K, fn: HookByName<K>): void\n // `T` lets plugin authors document the per-key shape of decorations.\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T documents the value type for plugin authors\n decorateRequest<T>(key: string, value: T): void\n}\n\nexport interface TheoPlugin {\n name: string\n register(app: TheoApp): void | Promise<void>\n}\n\n/**\n * Identity function for plugin authors. Provides auto-completion + type\n * inference at the call site (TanStack/Vite/Astro pattern). Pure runtime\n * no-op — returns the input unchanged.\n *\n * @example\n * import { definePlugin } from 'theokit/server'\n * export default definePlugin({\n * name: 'my-plugin',\n * register(app) {\n * app.addHook('onRequest', (req) => { ... })\n * },\n * })\n *\n * Equivalent to `const p: TheoPlugin = {...}` but more ergonomic. See\n * ADR-0008 (D1 + D6) for the rationale.\n */\nexport function definePlugin(plugin: TheoPlugin): TheoPlugin {\n return plugin\n}\n\n// ===== T5a.2 Phase F slice 1/3 — Web-Standards plugin context types =====\n//\n// Mirror of the IncomingMessage/ServerResponse-shaped `PluginContext` for\n// the Web `Request`/`Headers` shape. Per `docs/plans/t5a2-incoming-message-\n// to-request-shape-refactor-plan.md` v1.0 § Phase F.\n//\n// **Key difference vs IncomingMessage path:** the Web path has no\n// `ServerResponse` to mutate. Plugins instead get a `responseHeaders: Headers`\n// instance they can append to (e.g., add Set-Cookie, CORS headers) and the\n// runtime composes the final `Response` after the hook chain runs. The\n// `response` object (if any) is the in-flight Response constructed by the\n// handler — present only during `onResponse` / `onError` hooks AFTER the\n// handler returned, NOT during `onRequest` / `preHandler` (which fire BEFORE\n// the handler runs).\n//\n// This split mirrors Hono's `c.res` + Fastify's `reply.headers` semantics\n// — plugins mutate headers freely; the body is the handler's responsibility.\n\n/**\n * Web-Standards plugin context. Available during all 4 hook lifecycle\n * stages (onRequest, preHandler, onResponse, onError).\n *\n * - `request` — the incoming Web Request (read-only at the runtime level;\n * plugins can call `request.headers.get()`, `request.clone()`, etc.).\n * - `responseHeaders` — a mutable `Headers` instance the runtime threads\n * through the hook chain. Plugins append (e.g., CORS, Set-Cookie); the\n * final Response composes these.\n * - `response` — set to the handler's Response AFTER the handler returns.\n * Available during `onResponse` / `onError`. `undefined` during\n * `onRequest` / `preHandler` (which fire before the handler runs).\n * - `ctx` / `requestId` — same semantics as the IncomingMessage path.\n */\nexport interface WebPluginContext {\n request: Request\n responseHeaders: Headers\n response?: Response\n ctx: Record<string, unknown>\n requestId: string\n}\n\nexport interface WebPluginErrorContext extends WebPluginContext {\n error: unknown\n}\n\nexport type WebOnRequestHook = (ctx: WebPluginContext) => void | Promise<void>\nexport type WebPreHandlerHook = (ctx: WebPluginContext) => void | Promise<void>\nexport type WebOnResponseHook = (ctx: WebPluginContext) => void | Promise<void>\nexport type WebOnErrorHook = (ctx: WebPluginErrorContext) => void | Promise<void>\n\nexport type WebHookByName<K extends HookName> = K extends 'onError'\n ? WebOnErrorHook\n : K extends 'onRequest'\n ? WebOnRequestHook\n : K extends 'preHandler'\n ? WebPreHandlerHook\n : K extends 'onResponse'\n ? WebOnResponseHook\n : never\n\n/**\n * Web-Standards `TheoApp` facade. Same `addHook` + `decorateRequest`\n * surface as the IncomingMessage path; only the hook function signatures\n * differ (they receive `WebPluginContext` instead of `PluginContext`).\n *\n * Plugin authors who target both shapes can branch on the context type\n * via a type guard (`'responseHeaders' in ctx`) OR ship two separate\n * `register()` exports — one for each runtime adapter. Most plugins\n * register hooks at the IncomingMessage path today (legacy); future\n * Web-native plugins will register against `WebTheoApp`.\n */\nexport interface WebTheoApp {\n addHook<K extends HookName>(name: K, fn: WebHookByName<K>): void\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters -- T documents the value type for plugin authors\n decorateRequest<T>(key: string, value: T): void\n}\n\n/**\n * Web-Standards plugin shape. Identical to `TheoPlugin` except the\n * `register(app)` argument is `WebTheoApp` instead of `TheoApp`.\n *\n * Cross-runtime plugins ship BOTH `TheoPlugin` + `WebTheoPlugin` exports;\n * the adapter (Node, CF Workers, Bun, Deno) picks the matching one based\n * on which runtime executes the plugin chain. This is the canonical\n * Hono/Nitro pattern.\n */\nexport interface WebTheoPlugin {\n name: string\n register(app: WebTheoApp): void | Promise<void>\n}\n\n/**\n * Identity function for Web plugin authors — same auto-completion +\n * type-inference DX as `definePlugin`, but for the Web-shaped `WebTheoApp`.\n */\nexport function defineWebPlugin(plugin: WebTheoPlugin): WebTheoPlugin {\n return plugin\n}\n","/**\n * theokit/server — DEPRECATED umbrella barrel.\n *\n * **Per plan theokit-arch-gaps-implementation T2.5 (M1) + EC-2:**\n *\n * Importing from `theokit/server` is DEPRECATED as of this release. The\n * 16 sub-domain exports (auth, jobs, http, security, observability, etc.)\n * are now individually addressable via `package.json#exports`:\n *\n * import { defineAuth } from 'theokit/server/auth'\n * import { defineJob } from 'theokit/server/jobs'\n * import { defineRoute, defineAction } from 'theokit/server/define'\n * // ...etc per `packages/theo/package.json` exports field\n *\n * This umbrella barrel will continue to work for ONE minor cycle\n * (0.x → 0.x+1) with a single runtime warning printed on first import.\n * Final removal in 0.x+2 per CHANGELOG.\n *\n * Why: `export *` wildcards across 16 sub-domains violate ISP at the\n * package-surface level (consumers pay for 376 transitive exports when\n * they wanted 6). See `architecture-output/consolidated_final_report.md`\n * M1 finding + blueprint Q4 D4 (Hono-shape exports field adoption).\n *\n * Migration codemod (provided in this release):\n * pnpm exec theokit migrate server-umbrella-to-subpaths\n *\n * For body-parser, serialization, transformer, webhook helpers, trace context\n * propagation, error pages, plugin types, and config/env helpers — re-exported\n * inline because they don't fit a single sub-barrel cleanly. Those will land\n * in a `theokit/server/runtime` sub-path in the 0.x+2 cleanup.\n */\n\n// One-time deprecation warning. Fires on first import in the process and\n// is silenced afterwards via a module-scoped flag. Tree-shake-safe: the\n// IIFE body executes on module load (no DCE), but the cost is one\n// console.warn at startup — negligible. Apps that grep for this string\n// see exactly which entry point logged it.\nif (typeof globalThis !== 'undefined') {\n const WARN_KEY = '__theokit_server_umbrella_warn_emitted__'\n const g = globalThis as Record<string, unknown>\n if (g[WARN_KEY] !== true) {\n g[WARN_KEY] = true\n\n console.warn(\n '[theokit] umbrella import \"theokit/server\" is DEPRECATED. ' +\n 'Use sub-paths (theokit/server/<domain>): auth, jobs, http, security, ' +\n 'observability, etc. See packages/theo/package.json#exports for the ' +\n 'full list. Removal scheduled for 0.x+2.',\n )\n }\n}\n\n// Core defines + low-level pipeline primitives (used by adapter templates)\nexport * from './define/index.js'\nexport * from './http/index.js'\nexport * from './scan/index.js'\n\n// G3 action protocol: ActionError + ActionInputError + helpers, re-exported\n// from core/contracts for consumer ergonomics (`from 'theokit/server'`).\nexport {\n ActionError,\n ActionInputError,\n extractUniversalIssues,\n isActionError,\n isInputError,\n type ActionErrorCode,\n type ActionManifestEntry,\n type ActionResult,\n type SerializedActionResult,\n type UniversalZodIssue,\n} from '../core/contracts/action-protocol.js'\n\n// Subdomain sub-barrels (consumers can also import direct: theokit/server/<sub>)\nexport * from './agent/index.js'\nexport * from './auth/index.js'\nexport * from './cost/index.js'\nexport * from './cron/index.js'\nexport * from './jobs/index.js'\nexport * from './observability/index.js'\nexport * from './plugins/index.js'\nexport * from './rate-limit/index.js'\nexport * from './realtime/index.js'\nexport * from './security/index.js'\nexport * from './storage/index.js'\nexport * from './webhook/index.js'\nexport * from './openapi/index.js'\n\n// Cross-module: cache lives at packages/theo/src/cache/ (not server/cache)\nexport * from '../cache/index.js'\n\n// Inline re-exports — items that don't belong to a single sub-barrel\nexport { parseRequestBody, FileTooLargeError } from './body-parser.js'\nexport type { UploadedFile, ParsedBody, BodyParserOptions } from './body-parser.js'\n\nexport { serializeResponse, deserializeResponse } from './serialization.js'\nexport type { SerializedResponse } from './serialization.js'\n\nexport { superjsonTransformer, jsonTransformer, resolveTransformer } from './transformer.js'\nexport type { TheoTransformer } from './transformer.js'\n\n// T5a.2 Phase A — Web-Standards entry-point (R3a per ADR-0028). Accepts\n// a native Web Request and returns a native Web Response. Narrow scope:\n// method dispatch + Zod validation + envelope-shaped errors. Plugin\n// runner / CSRF / CORS / middleware integration deferred to Phase B-G\n// per docs/plans/t5a2-incoming-message-to-request-shape-refactor-plan.md.\nexport { executeWebRequest } from './web-handler.js'\n\n// Plugin types (used by definePlugin + extension authors)\nexport type {\n TheoPlugin,\n TheoApp,\n PluginContext,\n PluginErrorContext,\n HookName,\n HookResult,\n OnRequestHook,\n PreHandlerHook,\n OnResponseHook,\n OnErrorHook,\n RunHookOptions,\n} from './plugin-types.js'\nexport { definePlugin } from './plugin-types.js'\n\n// Config helpers — auto-load .env for standalone server scripts (Telegram bots,\n// queue consumers, cron jobs) that bypass the CLI.\nexport { loadEnv, _resetEnvCache } from '../config/load-env.js'\nexport type { LoadEnvOptions, LoadEnvResult } from '../config/load-env-types.js'\n\n// Wave 2 — Polyglot services orchestration types (types only — runtime in services/)\nexport type {\n ServiceDefinition,\n ServicesConfig,\n ServicesConfigInput,\n ServicesConfigOutput,\n} from '../services/index.js'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,SAAS,YAAY,cAAc,gBAAgB;AACnD,SAAS,eAAe;AAexB,IAAM,iBAAiB,KAAK,OAAO;AACnC,IAAM,cAAc;AAMb,SAAS,qBAAqB,OAA2B,CAAC,GAAG;AAClE,QAAM,WAAW,KAAK,YAAY;AAClC,QAAM,WAAW,KAAK,mBAAmB;AACzC,QAAM,cAAc,KAAK,gBAAgB;AACzC,QAAM,QAAQ,KAAK,aAAa;AAChC,QAAM,MAAM,KAAK,UAAU;AAO3B,MAAI,iBAAiB,WAAW,GAAG;AACjC,UAAM,IAAI,MAAM,kEAAkE,WAAW,EAAE;AAAA,EACjG;AACA,QAAM,WAAW,QAAQ,WAAW;AAEpC,SAAO,CAAC,YAAsC;AAC5C,QAAI,QAAQ,WAAW,MAAO,QAAO;AAErC,UAAM,MAAM,IAAI,IAAI,QAAQ,GAAG;AAE/B,QAAI,IAAI,aAAa,UAAU;AAC7B,YAAM,UAAU,IAAI,IAAI,GAAG,EAAE;AAC7B,aAAO,IAAI,SAAS,iBAAiB,OAAO,UAAU,GAAG,GAAG;AAAA,QAC1D,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,2BAA2B,mCAAmC,OAAO,4DAA4D,OAAO,2BAA2B,OAAO,wBAAwB,OAAO;AAAA,QAC3M;AAAA,MACF,CAAC;AAAA,IACH;AAEA,QAAI,IAAI,aAAa,UAAU;AAC7B,aAAO,cAAc,QAAQ;AAAA,IAC/B;AAEA,WAAO;AAAA,EACT;AACF;AAGA,SAAS,iBAAiB,GAAoB;AAC5C,SAAO,EAAE,MAAM,OAAO,EAAE,SAAS,IAAI;AACvC;AAIA,SAAS,WAAW,GAAmB;AACrC,SAAO,EACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ;AAC3B;AAEA,SAAS,iBAAiB,OAAe,SAAiB,QAAwB;AAChF,SAAO;AAAA;AAAA;AAAA;AAAA,WAIE,WAAW,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,yCAIa,WAAW,OAAO,CAAC;AAAA,iBAC3C,WAAW,MAAM,CAAC;AAAA;AAAA;AAGnC;AAIA,SAAS,cAAc,UAA4B;AACjD,MAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAO,aAAa,KAAK;AAAA,MACvB,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,MACX;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI;AACF,UAAM,OAAO,SAAS,QAAQ;AAC9B,QAAI,KAAK,OAAO,gBAAgB;AAC9B,aAAO,aAAa,KAAK;AAAA,QACvB,OAAO;AAAA,UACL,MAAM;AAAA,UACN,SAAS,qBAAqB,iBAAiB,OAAO,IAAI;AAAA,QAC5D;AAAA,MACF,CAAC;AAAA,IACH;AAEA,UAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,WAAO,IAAI,SAAS,SAAS;AAAA,MAC3B,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,iBAAiB,WAAW;AAAA,IAC7E,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,WAAO,aAAa,KAAK;AAAA,MACvB,OAAO,EAAE,MAAM,uBAAuB,QAAQ;AAAA,IAChD,CAAC;AAAA,EACH;AACF;AAEA,SAAS,aAAa,QAAgB,MAAyB;AAC7D,SAAO,IAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AAAA,IACxC;AAAA,IACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,EAChD,CAAC;AACH;;;AC7IO,IAAM,uBAAuB;AAC7B,IAAM,sBAAsB;AAC5B,IAAM,gBAAgB;AAEtB,IAAM,kBAAkB;AACxB,IAAM,yBAAyB;;;ACQ/B,SAAS,aAAa,MAAe,aAA+C;AAEzF,MAAI,CAAC,MAAM,QAAQ,IAAI,GAAG;AACxB,UAAM,SAAmC;AAAA,MACvC,OAAO,CAAC;AAAA,MACR,SAAS,CAAC,EAAE,OAAO,MAAM,QAAQ,uBAAuB,OAAO,IAAI,GAAG,CAAC;AAAA,IACzE;AACA,gBAAY,QAAQ,WAAW;AAC/B,WAAO;AAAA,EACT;AAIA,QAAM,SAAoB;AAC1B,QAAM,QAAkB,CAAC;AACzB,QAAM,UAAgD,CAAC;AAEvD,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,QAAI,MAAM,UAAU,qBAAqB;AACvC,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,gBAAQ,KAAK,EAAE,OAAO,OAAO,CAAC,GAAG,QAAQ,0BAA0B,CAAC;AAAA,MACtE;AACA;AAAA,IACF;AACA,UAAM,MAAe,OAAO,CAAC;AAC7B,QAAI,OAAO,QAAQ,UAAU;AAC3B,cAAQ,KAAK,EAAE,OAAO,KAAK,QAAQ,iCAAiC,CAAC;AACrE;AAAA,IACF;AACA,QAAI,IAAI,SAAS,sBAAsB;AACrC,cAAQ,KAAK;AAAA,QACX,OAAO;AAAA,QACP,QAAQ,0BAA0B,oBAAoB;AAAA,MACxD,CAAC;AACD;AAAA,IACF;AACA,QAAI,IAAI,WAAW,aAAa,GAAG;AACjC,cAAQ,KAAK;AAAA,QACX,OAAO;AAAA,QACP,QAAQ,oBAAoB,aAAa;AAAA,MAC3C,CAAC;AACD;AAAA,IACF;AACA,UAAM,KAAK,GAAG;AAAA,EAChB;AAEA,cAAY,EAAE,OAAO,QAAQ,GAAG,WAAW;AAC3C,SAAO,EAAE,OAAO,QAAQ;AAC1B;AAOO,SAAS,eAAe,QAAiB,aAA6B;AAC3E,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI,OAAO,WAAW,YAAY,OAAO,SAAS,MAAM,KAAK,UAAU,GAAG;AACxE,WAAO;AAAA,EACT;AACA,QAAM,IAAI;AAAA,IACR,mBAAmB,KAAK,UAAU,MAAM,CAAC,QAAQ,WAAW;AAAA,EAC9D;AACF;AAMO,SAAS,eACd,QACA,YACA,aACoB;AACpB,MAAI,WAAW,OAAW,QAAO;AACjC,MAAI,OAAO,WAAW,YAAY,CAAC,OAAO,SAAS,MAAM,KAAK,SAAS,GAAG;AACxE,UAAM,IAAI;AAAA,MACR,mBAAmB,KAAK,UAAU,MAAM,CAAC,QAAQ,WAAW;AAAA,IAC9D;AAAA,EACF;AACA,MAAI,eAAe,UAAa,SAAS,YAAY;AACnD,UAAM,IAAI;AAAA,MACR,kBAAkB,MAAM,OAAO,WAAW,iDAAiD,UAAU;AAAA,IACvG;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,YAAY,QAAkC,aAA2B;AAChF,MAAI,OAAO,QAAQ,WAAW,EAAG;AACjC,UAAQ,KAAK,mBAAmB,WAAW,aAAa,OAAO,QAAQ,MAAM,kBAAkB;AAC/F,aAAW,EAAE,OAAO,OAAO,KAAK,OAAO,SAAS;AAC9C,YAAQ,KAAK,OAAO,KAAK,UAAU,KAAK,CAAC,KAAK,MAAM,EAAE;AAAA,EACxD;AACF;;;AC3EO,SAAS,qBACd,QACA,IACA,MACgC;AAChC,MAAI,OAAO,KAAK,SAAS,YAAY,KAAK,KAAK,WAAW,GAAG;AAC3D,UAAM,IAAI,MAAM,gEAAgE;AAAA,EAClF;AACA,QAAM,SAAS,eAAe,KAAK,QAAQ,wBAAwB,KAAK,IAAI,GAAG;AAC/E,QAAM,MAAM,eAAe,KAAK,KAAK,QAAQ,wBAAwB,KAAK,IAAI,GAAG;AAEjF,QAAM,SAAS,MAAM,KAAK,IAAI;AAE9B,WAAS,eAAe,MAAqB;AAC3C,UAAM,OAAO,KAAK,SAAS,KAAK,OAAO,GAAG,IAAI,IAAI,KAAK,UAAU,IAAI;AACrE,WAAO,GAAG,MAAM,IAAI,IAAI;AAAA,EAC1B;AAEA,WAAS,YAAY,MAAuB;AAC1C,UAAM,MAAM,OAAO,KAAK,SAAS,aAAa,KAAK,KAAK,GAAG,IAAI,IAAK,KAAK,QAAQ,CAAC;AAClF,UAAM,EAAE,MAAM,IAAI,aAAa,KAAK,wBAAwB,KAAK,IAAI,GAAG;AACxE,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,UAAU,SAAgB;AACzC,UAAM,MAAM,eAAe,IAAI;AAC/B,UAAM,OAAO,YAAY,IAAI;AAC7B,QAAI;AACF,YAAM,EAAE,MAAM,IAAI,MAAM,OAAO,aAAsB,KAAK,YAAY,GAAG,GAAG,IAAI,GAAG;AAAA,QACjF;AAAA,QACA,KAAK,OAAO,SAAS;AAAA,QACrB;AAAA,QACA,cAAc,KAAK;AAAA,QACnB,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK;AAAA,MACjB,CAAC;AACD,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,WAAK,UAAU,KAAK,EAAE,KAAK,CAAC;AAC5B,YAAM;AAAA,IACR;AAAA,EACF;AAEA,UAAQ,aAAa,UAAU,SAAgB;AAC7C,UAAM,MAAM,eAAe,IAAI;AAC/B,UAAM,OAAO,WAAW,GAAG;AAAA,EAC7B;AAEA,SAAO;AACT;;;AC9EA,IAAM,kBAAkB;AAWjB,SAAS,sBAAsB,OAAkC;AACtE,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,QAAkB,CAAC;AACzB,MAAI,MAAM,UAAW,OAAM,KAAK,SAAS;AACzC,QAAM,KAAK,YAAY,MAAM,MAAM,EAAE;AACrC,MAAI,MAAM,QAAQ,UAAa,MAAM,MAAM,GAAG;AAC5C,UAAM,KAAK,0BAA0B,MAAM,GAAG,EAAE;AAAA,EAClD;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;;;ACxBO,IAAM,gCAAgC;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAwBA,eAAsBA,WAAU,KAAc,OAA6B,CAAC,GAAoB;AAC9F,MAAI,KAAK,QAAQ;AACf,UAAM,IAAI,MAAM,KAAK,OAAO,GAAG;AAC/B,QAAI,OAAO,MAAM,UAAU;AACzB,YAAM,IAAI,MAAM,oCAAoC,OAAO,CAAC,EAAE;AAAA,IAChE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,IAAI,IAAI,IAAI,GAAG;AAC3B,QAAM,cAAc,iBAAiB,KAAK,IAAI;AAC9C,QAAM,OAAO,GAAG,KAAK,SAAS,KAAK,SAAS,MAAM,EAAE,GAAG,IAAI,QAAQ,KAAK,IAAI,SAAS,YAAY,CAAC,GAAG,IAAI,QAAQ,GAAG,cAAc,MAAM,cAAc,EAAE;AAExJ,MAAI,CAAC,KAAK,UAAU,KAAK,OAAO,WAAW,EAAG,QAAO;AACrD,QAAM,QAAkB,CAAC;AACzB,aAAW,UAAU,KAAK,QAAQ;AAChC,UAAM,KAAK,GAAG,MAAM,IAAI,IAAI,QAAQ,IAAI,MAAM,KAAK,EAAE,EAAE;AAAA,EACzD;AACA,SAAO,OAAO,OAAO,MAAM,KAAK,IAAI;AACtC;AAEA,SAAS,iBAAiB,KAAU,MAAoC;AACtE,QAAM,SAAS,IAAI,gBAAgB,IAAI,YAAY;AACnD,QAAM,UAAU,KAAK,gBAAgB;AACrC,QAAM,aAAa,IAAI,IAAI,OAAO;AAClC,aAAW,OAAO,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG;AACpC,QAAI,WAAW,IAAI,GAAG,EAAG,QAAO,OAAO,GAAG;AAAA,EAC5C;AACA,MAAI,KAAK,cAAc,MAAO,QAAO,KAAK;AAC1C,SAAO,OAAO,SAAS;AACzB;;;AC7EO,IAAM,yBAAyB,KAAK,OAAO;AAClD,IAAM,iBAAiB,oBAAI,IAAI,CAAC,UAAU,YAAY,CAAC;AAuCvD,IAAM,wBAAwB,oBAAI,QAAQ;AAC1C,IAAM,2BAA2B,oBAAI,QAAQ;AAC7C,IAAM,wBAAwB,oBAAI,QAAQ;AAsBnC,SAAS,kBAMd,QACA,QACqD;AACrD,QAAM,EAAE,OAAO,SAAS,GAAG,KAAK,IAAI;AAEpC,QAAM,SAAS,eAAe,MAAM,QAAQ,mBAAmB;AAC/D,QAAM,MAAM,eAAe,MAAM,KAAK,QAAQ,mBAAmB;AACjE,MAAI,MAAM,iBAAiB,UAAa,MAAM,iBAAiB,IAAI;AACjE,UAAM,IAAI,MAAM,+DAA+D;AAAA,EACjF;AAEA,QAAM,eAAe,MAAM,gBAAgB;AAC3C,MAAI,CAAC,OAAO,SAAS,YAAY,KAAK,eAAe,GAAG;AACtD,UAAM,IAAI;AAAA,MACR,yBAAyB,OAAO,MAAM,YAAY,CAAC;AAAA,IACrD;AAAA,EACF;AACA,QAAM,UAAU,IAAI,KAAK,MAAM,WAAW,CAAC,OAAO,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACtF,QAAM,WAAW,aAAa,MAAM,QAAQ,CAAC,GAAG,mBAAmB,EAAE;AAGrE,QAAM,YAAY,MAAM,UAAU,CAAC;AACnC,QAAM,cAAc,UAAU,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AACxD,QAAM,gBAAgB,YAAY,KAAK,CAAC,MAAM,eAAe,IAAI,CAAC,CAAC;AACnE,QAAM,aAAa,YAAY,OAAO,CAAC,MAAM,CAAC,eAAe,IAAI,CAAC,CAAC;AACnE,MAAI,iBAAiB,CAAC,yBAAyB,IAAI,MAAM,GAAG;AAC1D,6BAAyB,IAAI,MAAM;AACnC,YAAQ;AAAA,MACN;AAAA,IACF;AAAA,EACF;AAEA,QAAM,iBAAiB,OAAO,QAML;AAGvB,UAAM,aAAa,aAAa,IAAI,OAAO;AAE3C,QAAI,CAAC,QAAQ,IAAI,WAAW,OAAO,YAAY,CAAC,GAAG;AACjD,aAAO,wBAAwB,SAAS,GAAG;AAAA,IAC7C;AACA,QAAI,MAAM,cAAe,MAAM,MAAM,WAAW,UAAU,GAAI;AAC5D,aAAO,wBAAwB,SAAS,GAAG;AAAA,IAC7C;AACA,QAAI,WAAW,GAAG;AAChB,aAAO,wBAAwB,SAAS,GAAG;AAAA,IAC7C;AAEA,UAAM,MAAM,MAAMC,WAAU,YAAY;AAAA,MACtC,QAAQ,WAAW,WAAW,OAAO,YAAY;AAAA,MACjD,QAAQ;AAAA,MACR,QAAQ,MAAM;AAAA,IAChB,CAAC;AAGD,UAAM,SAAS,MAAM,OAAO,cAA+B,KAAK;AAAA,MAC9D,cAAc,MAAM;AAAA,IACtB,CAAC;AAGD,UAAM,gBAA+B;AAAA,MACnC;AAAA,MACA;AAAA,MACA;AAAA,MACA,aAAa;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAEA,QAAI,QAAQ;AACV,UAAI,OAAO,WAAW,OAAO;AAC3B,eAAO,uBAAuB,OAAO,OAAO,OAAO,QAAQ,GAAG;AAAA,MAChE;AAGA,8BAAwB,eAAe,SAAS,GAAG;AACnD,aAAO,uBAAuB,OAAO,OAAO,SAAS,QAAQ,GAAG;AAAA,IAClE;AAGA,UAAM,WAAW,MAAM,wBAAwB,SAAS,GAAG;AAC3D,WAAO,iBAAiB,eAAe,QAAQ;AAAA,EACjD;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS;AAAA,EACX;AACF;AA2BA,SAAS,qBAAqB,OAAwB,KAAgC;AACpF,QAAM,UAAU,gBAAgB,IAAI,IAAI,IAAI,WAAW,GAAG,EAAE;AAC5D,SAAO;AAAA,IACL,MAAM,KAAK,UAAU,KAAK;AAAA,IAC1B,QAAQ;AAAA,IACR,SAAS,CAAC;AAAA,IACV,UAAU,KAAK,IAAI;AAAA,IACnB,QAAQ,IAAI;AAAA,IACZ,KAAK,IAAI,OAAO,IAAI,SAAS;AAAA,IAC7B,MAAM,CAAC,GAAG,IAAI,UAAU,OAAO;AAAA,IAC/B,cAAc,IAAI,MAAM;AAAA,EAC1B;AACF;AAEA,eAAe,iBAAiB,KAAoB,UAAuC;AACzF,QAAM,kBAAkB,MAAM;AAAA,IAC5B;AAAA,IACA,IAAI;AAAA,IACJ,IAAI;AAAA,IACJ,IAAI;AAAA,EACN;AACA,MAAI,CAAC,iBAAiB;AAEpB,WAAO;AAAA,EACT;AACA,QAAM,IAAI,OAAO,IAAI,IAAI,KAAK,qBAAqB,iBAAiB,GAAG,CAAC;AACxE,SAAO,uBAAuB,iBAAiB,QAAQ,IAAI,QAAQ,IAAI,GAAG;AAC5E;AAEA,SAAS,wBACP,KACA,SACA,YACM;AACN,QAAM,YAAY;AAChB,QAAI;AACF,YAAM,WAAW,MAAM,wBAAwB,SAAS,UAAU;AAClE,YAAM,SAAS,MAAM,iBAAiB,UAAU,IAAI,OAAO,IAAI,aAAa,IAAI,YAAY;AAC5F,UAAI,CAAC,OAAQ;AACb,YAAM,IAAI,OAAO,IAAI,IAAI,KAAK,qBAAqB,QAAQ,GAAG,CAAC;AAAA,IACjE,QAAQ;AAAA,IAER;AAAA,EACF,GAAG;AACL;AAmBA,SAAS,aAAa,KAAyC;AAE7D,MAAI,OAAQ,IAAgB,UAAU,cAAe,IAAgB,mBAAmB,SAAS;AAC/F,WAAO;AAAA,EACT;AACA,QAAM,OAAO;AACb,QAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS,WAAW,KAAK,QAAQ,OAAO,SAAS;AACnF,QAAM,WAAW,KAAK,QAAQ,YAAY,UAAU;AACpD,QAAM,OAAO,KAAK,OAAO;AACzB,QAAM,MAAM,KAAK,WAAW,MAAM,IAAI,OAAO,GAAG,QAAQ,MAAM,IAAI,GAAG,IAAI;AACzE,QAAM,UAAU,IAAI,QAAQ;AAC5B,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,OAAO,GAAG;AACjD,QAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,iBAAW,QAAQ,EAAG,SAAQ,OAAO,GAAG,IAAI;AAAA,IAC9C,WAAW,OAAO,MAAM,UAAU;AAChC,cAAQ,IAAI,GAAG,CAAC;AAAA,IAClB;AAAA,EACF;AACA,SAAO,IAAI,QAAQ,KAAK;AAAA,IACtB,SAAS,KAAK,UAAU,OAAO,YAAY;AAAA,IAC3C;AAAA,EACF,CAAC;AACH;AAEA,eAAe,wBACb,SACA,KACmB;AACnB,QAAM,MAAM,MAAM,QAAQ,GAAG;AAC7B,MAAI,eAAe,SAAU,QAAO;AACpC,SAAO,SAAS,KAAK,GAAG;AAC1B;AAMA,eAAe,iBACb,UACA,OACA,aACA,cACsC;AAEtC,MAAI,SAAS,QAAQ,IAAI,YAAY,GAAG;AACtC,QAAI,CAAC,sBAAsB,IAAI,WAAW,GAAG;AAC3C,4BAAsB,IAAI,WAAW;AACrC,cAAQ,KAAK,0EAAqE;AAAA,IACpF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAC5D,MAAI,YAAY,SAAS,mBAAmB,EAAG,QAAO;AAMtD,MAAI,SAAS,QAAQ,IAAI,mBAAmB,GAAG,YAAY,MAAM,WAAW;AAC1E,WAAO;AAAA,EACT;AAEA,MAAI,SAAS,UAAU,OAAO,CAAC,MAAM,YAAa,QAAO;AAEzD,MAAI,MAAM,aAAa,CAAC,MAAM,UAAU,QAAQ,EAAG,QAAO;AAG1D,QAAM,OAAO,MAAM,SAAS,MAAM,EAAE,KAAK;AAGzC,MAAI,KAAK,SAAS,cAAc;AAC9B,QAAI,CAAC,sBAAsB,IAAI,WAAW,GAAG;AAC3C,4BAAsB,IAAI,WAAW;AACrC,cAAQ;AAAA,QACN,iCAAiC,KAAK,MAAM,+BAA+B,YAAY;AAAA,MACzF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAA8B,CAAC;AACrC,WAAS,QAAQ,QAAQ,CAAC,GAAG,MAAM;AACjC,QAAI,EAAE,YAAY,MAAM,aAAc;AACtC,YAAQ,KAAK,CAAC,GAAG,CAAC,CAAC;AAAA,EACrB,CAAC;AACD,SAAO,EAAE,MAAM,MAAM,QAAQ,SAAS,QAAQ,QAAQ;AACxD;AAEA,SAAS,uBACP,OACA,QACA,QACA,KACU;AACV,QAAM,UAAU,IAAI,QAAQ,MAAM,OAAO;AACzC,MAAI,CAAC,QAAQ,IAAI,eAAe,GAAG;AACjC,YAAQ,IAAI,iBAAiB,sBAAsB,EAAE,QAAQ,KAAK,OAAO,SAAS,GAAG,CAAC,CAAC;AAAA,EACzF;AACA,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,QAAI,oBAA8C;AAClD,QAAI,WAAW,MAAO,qBAAoB;AAAA,aACjC,WAAW,QAAS,qBAAoB;AACjD,YAAQ,IAAI,gBAAgB,iBAAiB;AAAA,EAC/C;AACA,SAAO,IAAI,SAAS,MAAM,MAAM,EAAE,QAAQ,MAAM,QAAQ,QAAQ,CAAC;AACnE;;;ACnSO,SAAS,kBAAkB,MAAuC;AACvE,QAAM,EAAE,SAAS,QAAQ,IAAI;AAC7B,QAAM,WAAW,oBAAI,IAA8B;AACnD,QAAM,aAAa,oBAAI,IAAY;AACnC,QAAM,wBAAwB,oBAAI,IAAY;AAE9C,iBAAe,aACb,KACA,IACA,SAC4C;AAE5C,UAAM,UAAU,SAAS,IAAI,GAAG;AAChC,QAAI,SAAS;AACX,YAAM,QAAS,MAAM;AACrB,aAAO,EAAE,OAAO,QAAQ,OAAO;AAAA,IACjC;AAGA,QAAI,QAAQ,WAAW,GAAG;AACxB,aAAO;AAAA,QAAY;AAAA,QAAK;AAAA,QAAI;AAAA;AAAA,QAAyB;AAAA,MAAI;AAAA,IAC3D;AAGA,WAAO,YAAY,KAAK,IAAI,SAAS,KAAK;AAAA,EAC5C;AAMA,WAAS,YACP,KACA,IACA,SACA,WAC4C;AAC5C,QAAI;AACJ,QAAI;AACJ,UAAM,eAAe,IAAI,QAAW,CAACC,UAAS,WAAW;AACvD,qBAAeA;AACf,oBAAc;AAAA,IAChB,CAAC;AAED,SAAK,aAAa,MAAM,MAAM;AAAA,IAE9B,CAAC;AACD,aAAS,IAAI,KAAK,YAAY;AAE9B,UAAM,QAAQ,YAAwD;AACpE,UAAI;AACF,YAAI,CAAC,WAAW;AACd,gBAAM,SAAS,MAAM,cAAiB,KAAK,OAAO;AAClD,cAAI,QAAQ;AAEV,yBAAa,OAAO,KAAK;AACzB,gBAAI,OAAO,WAAW,SAAS;AAC7B,2CAA6B,KAAK,IAAI,OAAO;AAAA,YAC/C;AACA,mBAAO;AAAA,UACT;AAAA,QACF;AAEA,cAAM,QAAQ,MAAM,UAAU,KAAK,IAAI,SAAS,SAAS;AACzD,qBAAa,KAAK;AAClB,eAAO,EAAE,OAAO,QAAQ,OAAgB;AAAA,MAC1C,SAAS,KAAK;AACZ,oBAAY,GAAG;AACf,cAAM;AAAA,MACR,UAAE;AACA,iBAAS,OAAO,GAAG;AAAA,MACrB;AAAA,IACF,GAAG;AAEH,WAAO;AAAA,EACT;AAMA,iBAAe,cACb,KACA,SACwD;AACxD,QAAI;AACJ,QAAI;AACF,cAAQ,MAAM,QAAQ,IAAI,GAAG;AAAA,IAC/B,SAAS,KAAK;AACZ,gBAAU,KAAK,EAAE,OAAO,OAAO,IAAI,CAAC;AACpC,aAAO;AAAA,IACT;AACA,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,QAAQ,iBAAiB,UAAa,MAAM,iBAAiB,QAAQ,cAAc;AACrF,aAAO;AAAA,IACT;AACA,QAAI,OAAO,MAAM,SAAS,SAAU,QAAO;AAC3C,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,MAAM,IAAI;AAAA,IAChC,SAAS,KAAK;AACZ,gBAAU,KAAK,EAAE,OAAO,OAAO,IAAI,CAAC;AACpC,aAAO;AAAA,IACT;AAEA,QAAI,QAAQ,UAAU;AACpB,UAAI;AACF,YAAI,CAAC,QAAQ,SAAS,MAAM,EAAG,QAAO;AAAA,MACxC,SAAS,KAAK;AACZ,kBAAU,KAAK,EAAE,OAAO,OAAO,IAAI,CAAC;AACpC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,MAAM,KAAK,IAAI,IAAI,KAAK,IAAI,IAAI,MAAM,YAAY,GAAI;AAC5D,QAAI,OAAO,MAAM,QAAQ;AACvB,YAAM,QAAQ,QAAQ,YAAY,QAAQ,UAAU,MAAM,IAAI;AAC9D,aAAO,EAAE,OAAO,QAAQ,MAAM;AAAA,IAChC;AACA,QAAI,OAAO,MAAM,SAAS,MAAM,KAAK;AACnC,YAAM,QAAQ,QAAQ,YAAY,QAAQ,UAAU,MAAM,IAAI;AAC9D,aAAO,EAAE,OAAO,QAAQ,QAAQ;AAAA,IAClC;AACA,WAAO;AAAA,EACT;AAKA,iBAAe,UACb,KACA,IACA,SACA,YAAY,OACA;AACZ,UAAM,MAAM,MAAM,GAAG;AACrB,UAAM,QAAQ,QAAQ,YAAY,QAAQ,UAAU,GAAG,IAAI;AAE3D,QAAI,UAAW,QAAO;AAGtB,QAAI,QAAQ,gBAAgB,KAAK,EAAG,QAAO;AAG3C,QAAI,QAAQ,UAAU;AACpB,UAAI,UAAU;AACd,UAAI;AACF,kBAAU,QAAQ,SAAS,KAAK;AAAA,MAClC,SAAS,KAAK;AACZ,kBAAU,KAAK,EAAE,OAAO,OAAO,IAAI,CAAC;AACpC,eAAO;AAAA,MACT;AACA,UAAI,CAAC,QAAS,QAAO;AAAA,IACvB;AAGA,QAAI,UAAU,QAAW;AACvB,UAAI,CAAC,sBAAsB,IAAI,GAAG,GAAG;AACnC,8BAAsB,IAAI,GAAG;AAC7B,gBAAQ,KAAK,sDAAsD,GAAG,qBAAqB;AAAA,MAC7F;AACA,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,QAAoB;AAAA,QACxB,MAAM,KAAK,UAAU,KAAK;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS,CAAC;AAAA,QACV,UAAU,KAAK,IAAI;AAAA,QACnB,QAAQ,QAAQ;AAAA,QAChB,KAAK,QAAQ,OAAO;AAAA,QACpB,MAAM,QAAQ,QAAQ,CAAC;AAAA,QACvB,cAAc,QAAQ;AAAA,MACxB;AACA,YAAM,QAAQ,IAAI,KAAK,KAAK;AAAA,IAC9B,SAAS,KAAK;AACZ,gBAAU,KAAK,EAAE,OAAO,OAAO,IAAI,CAAC;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAEA,WAAS,6BACP,KACA,IACA,SACM;AACN,QAAI,WAAW,IAAI,GAAG,EAAG;AACzB,eAAW,IAAI,GAAG;AAClB,SAAK,UAAU,KAAK,IAAI,OAAO,EAC5B,MAAM,CAAC,QAAiB;AACvB,gBAAU,KAAK,EAAE,OAAO,cAAc,IAAI,CAAC;AAAA,IAC7C,CAAC,EACA,QAAQ,MAAM;AACb,iBAAW,OAAO,GAAG;AAAA,IACvB,CAAC;AAAA,EACL;AAEA,SAAO;AAAA,IACL;AAAA,IAEA;AAAA,IAEA,MAAM,cACJ,KACAC,OAC4D;AAI5D,YAAM,SAAS,MAAM,cAAiB,KAAK;AAAA,QACzC,GAAGA;AAAA,QACH,QAAQ;AAAA;AAAA,MACV,CAAC;AACD,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO;AAAA,IACT;AAAA,IAEA,MAAM,IAAI,KAAK,OAAO;AACpB,YAAM,QAAQ,IAAI,KAAK,KAAK;AAAA,IAC9B;AAAA,IAEA,MAAM,WAAW,KAAK;AAEpB,eAAS,OAAO,GAAG;AACnB,aAAO,QAAQ,OAAO,GAAG;AAAA,IAC3B;AAAA,IAEA,MAAM,cAAc,KAAK;AACvB,aAAO,QAAQ,YAAY,GAAG;AAAA,IAChC;AAAA,IAEA,MAAM,eAAe,MAAM,MAAM;AAC/B,YAAM,MAAM,gBAAgB,QAAQ,OAAO,MAAM,OAAO;AACxD,aAAO,QAAQ,YAAY,GAAG;AAAA,IAChC;AAAA,EACF;AACF;;;ACzSO,IAAM,uBAAN,MAA0D;AAAA,EACtD,OAAO;AAAA,EACP,WAAW,oBAAI,IAAwB;AAAA,EACvC,YAAY,oBAAI,IAAyB;AAAA,EACzC;AAAA,EAET,YAAY,OAAoC,CAAC,GAAG;AAClD,SAAK,cAAc,KAAK,cAAc;AAAA,EACxC;AAAA,EAEA,MAAM,IAAI,KAA8C;AACtD,UAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;AACnC,QAAI,UAAU,OAAW,QAAO;AAEhC,SAAK,SAAS,OAAO,GAAG;AACxB,SAAK,SAAS,IAAI,KAAK,KAAK;AAC5B,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAI,KAAa,OAAkC;AAEvD,UAAM,WAAW,KAAK,SAAS,IAAI,GAAG;AACtC,QAAI,UAAU;AACZ,WAAK,uBAAuB,KAAK,SAAS,IAAI;AAC9C,WAAK,SAAS,OAAO,GAAG;AAAA,IAC1B,WAAW,KAAK,SAAS,QAAQ,KAAK,aAAa;AAEjD,YAAM,aAAa,KAAK,SAAS,KAAK,EAAE,KAAK;AAC7C,UAAI,CAAC,WAAW,MAAM;AACpB,cAAM,YAAY,WAAW;AAC7B,cAAM,cAAc,KAAK,SAAS,IAAI,SAAS;AAC/C,YAAI,aAAa;AACf,eAAK,uBAAuB,WAAW,YAAY,IAAI;AAAA,QACzD;AACA,aAAK,SAAS,OAAO,SAAS;AAAA,MAChC;AAAA,IACF;AACA,SAAK,SAAS,IAAI,KAAK,KAAK;AAC5B,eAAW,OAAO,MAAM,MAAM;AAC5B,UAAI,SAAS,KAAK,UAAU,IAAI,GAAG;AACnC,UAAI,CAAC,QAAQ;AACX,iBAAS,oBAAI,IAAI;AACjB,aAAK,UAAU,IAAI,KAAK,MAAM;AAAA,MAChC;AACA,aAAO,IAAI,GAAG;AAAA,IAChB;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,KAA+B;AAC1C,UAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;AACnC,QAAI,CAAC,MAAO,QAAO;AACnB,SAAK,uBAAuB,KAAK,MAAM,IAAI;AAC3C,SAAK,SAAS,OAAO,GAAG;AACxB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,KAA8B;AAC9C,UAAM,SAAS,KAAK,UAAU,IAAI,GAAG;AACrC,QAAI,CAAC,UAAU,OAAO,SAAS,EAAG,QAAO;AACzC,UAAM,OAAO,CAAC,GAAG,MAAM;AACvB,eAAW,OAAO,MAAM;AACtB,YAAM,QAAQ,KAAK,SAAS,IAAI,GAAG;AACnC,UAAI,OAAO;AACT,aAAK,sBAAsB,KAAK,MAAM,MAAM,GAAG;AAAA,MACjD;AACA,WAAK,SAAS,OAAO,GAAG;AAAA,IAC1B;AACA,SAAK,UAAU,OAAO,GAAG;AACzB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,OAAwB;AAC5B,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,MAAM,QAAuB;AAC3B,SAAK,SAAS,MAAM;AACpB,SAAK,UAAU,MAAM;AAAA,EACvB;AAAA,EAEA,OAAO,KAAK,QAAgD;AAC1D,eAAW,OAAO,KAAK,SAAS,KAAK,GAAG;AACtC,UAAI,UAAU,CAAC,IAAI,WAAW,MAAM,EAAG;AACvC,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,uBAAuB,KAAa,MAAsB;AACxD,eAAW,OAAO,MAAM;AACtB,YAAM,SAAS,KAAK,UAAU,IAAI,GAAG;AACrC,UAAI,CAAC,OAAQ;AACb,aAAO,OAAO,GAAG;AACjB,UAAI,OAAO,SAAS,EAAG,MAAK,UAAU,OAAO,GAAG;AAAA,IAClD;AAAA,EACF;AAAA,EAEA,sBAAsB,KAAa,MAAgB,SAAuB;AACxE,eAAW,YAAY,MAAM;AAC3B,UAAI,aAAa,QAAS;AAC1B,YAAM,cAAc,KAAK,UAAU,IAAI,QAAQ;AAC/C,UAAI,CAAC,YAAa;AAClB,kBAAY,OAAO,GAAG;AACtB,UAAI,YAAY,SAAS,EAAG,MAAK,UAAU,OAAO,QAAQ;AAAA,IAC5D;AAAA,EACF;AACF;;;AChHA,IAAI;AAMG,SAAS,gBACd,QACA,QAA6C,CAAC,GACjC;AACb,MAAI,SAAS;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,QAAM,UACJ,OAAO,YAAY,WACf,IAAI,qBAAqB,EAAE,YAAY,OAAO,WAAW,CAAC,IAC1D,OAAO;AACb,YAAU,kBAAkB;AAAA,IAC1B,SAAS;AAAA,IACT,UAAU,OAAO;AAAA,IACjB,SAAS,MAAM;AAAA,EACjB,CAAC;AACD,SAAO;AACT;AAOO,SAAS,iBAA8B;AAC5C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMO,SAAS,oBAA0B;AACxC,YAAU;AACZ;;;ACtDA,eAAsB,cACpB,KACA,MAC2B;AAC3B,MAAI,MAAM,WAAW,UAAa,KAAK,SAAS,GAAG;AACjD,IAAAC;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,EAAE,MAAM,IAAI,aAAa,CAAC,GAAG,GAAG,eAAe;AACrD,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE,SAAS,EAAE;AAC5C,QAAM,SAAS,eAAe;AAC9B,QAAM,UAAU,MAAM,OAAO,cAAc,MAAM,CAAC,CAAC;AACnD,SAAO,EAAE,QAAQ;AACnB;AAOA,eAAsB,UAAU,KAAwC;AACtE,QAAM,EAAE,MAAM,IAAI,aAAa,CAAC,GAAG,GAAG,WAAW;AACjD,MAAI,MAAM,WAAW,EAAG,QAAO,EAAE,SAAS,EAAE;AAC5C,QAAM,SAAS,eAAe;AAC9B,QAAM,UAAU,MAAM,OAAO,cAAc,MAAM,CAAC,CAAC;AACnD,SAAO,EAAE,QAAQ;AACnB;AAMA,eAAsB,eACpB,MACA,MAC2B;AAC3B,MAAI,MAAM,WAAW,UAAa,KAAK,SAAS,GAAG;AACjD,IAAAA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,QAAM,SAAS,eAAe;AAC9B,QAAM,UAAU,MAAM,OAAO,eAAe,MAAM,MAAM,IAAI;AAC5D,SAAO,EAAE,QAAQ;AACnB;AAEA,IAAM,aAAa,oBAAI,IAAY;AACnC,SAASA,UAAS,KAAa,SAAuB;AACpD,MAAI,WAAW,IAAI,GAAG,EAAG;AACzB,aAAW,IAAI,GAAG;AAClB,UAAQ,KAAK,OAAO;AACtB;;;ACpEA,OAAO,eAAe;AAuBf,SAAS,kBAAkB,OAAwC;AACxE,SAAO,OAAO,QAAQ,KAAK,EAAE,IAAI,CAAC,CAAC,SAAS,IAAI,OAAO;AAAA,IACrD,SAAS,UAAU,SAAS,EAAE,KAAK,KAAK,CAAC;AAAA,IACzC;AAAA,IACA;AAAA,EACF,EAAE;AACJ;AAKO,SAAS,iBACd,MACA,UACuB;AACvB,aAAW,KAAK,UAAU;AACxB,QAAI,EAAE,QAAQ,IAAI,EAAG,QAAO,EAAE;AAAA,EAChC;AACA,SAAO;AACT;;;AC1CA,OAAO,eAAe;AAUf,SAAS,kBAAkB,MAAmC;AACnE,SAAO,UAAU,UAAU,IAAI;AACjC;AAKO,SAAS,oBAAoB,YAAyC;AAC3E,SAAO,UAAU,YAAY,UAAyD;AACxF;;;ACgDO,SAAS,aAAa,QAAgC;AAC3D,SAAO;AACT;;;AChCA,IAAI,OAAO,eAAe,aAAa;AACrC,QAAM,WAAW;AACjB,QAAM,IAAI;AACV,MAAI,EAAE,QAAQ,MAAM,MAAM;AACxB,MAAE,QAAQ,IAAI;AAEd,YAAQ;AAAA,MACN;AAAA,IAIF;AAAA,EACF;AACF;","names":["deriveKey","deriveKey","resolve","opts","warnOnce"]}
|
|
@@ -54,6 +54,16 @@ declare function scanServerActions(serverDir: string): ActionNode[];
|
|
|
54
54
|
*/
|
|
55
55
|
declare function scanServerActionsEnriched(serverDir: string): ActionManifestEntry[];
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* A discovered agent file. `name` is the client-facing key; `agentPath` is the mounted
|
|
59
|
+
* SSE route (M0/M1 `UIMessageStream`).
|
|
60
|
+
*/
|
|
61
|
+
interface AgentNode {
|
|
62
|
+
filePath: string;
|
|
63
|
+
agentPath: string;
|
|
64
|
+
name: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
57
67
|
declare function scanServerRoutes(serverDir: string): ServerRouteNode[];
|
|
58
68
|
|
|
59
69
|
interface WebSocketRouteNode {
|
|
@@ -78,19 +88,29 @@ interface ManifestWebSocket {
|
|
|
78
88
|
filePath: string;
|
|
79
89
|
wsPath: string;
|
|
80
90
|
}
|
|
91
|
+
/** M2 — a top-level `agents/*.ts` convention entry. `filePath` is relative to the
|
|
92
|
+
* project root (agents live OUTSIDE `serverDir`), unlike routes/actions/ws. */
|
|
93
|
+
interface ManifestAgent {
|
|
94
|
+
filePath: string;
|
|
95
|
+
agentPath: string;
|
|
96
|
+
name: string;
|
|
97
|
+
}
|
|
81
98
|
interface TheoManifest {
|
|
82
99
|
version: 1;
|
|
83
100
|
generatedAt: string;
|
|
84
101
|
routes: ManifestRoute[];
|
|
85
102
|
actions: ManifestAction[];
|
|
86
103
|
websockets: ManifestWebSocket[];
|
|
104
|
+
/** M2 — optional for backward compat: manifests generated before M2 omit it. */
|
|
105
|
+
agents?: ManifestAgent[];
|
|
87
106
|
}
|
|
88
107
|
interface LoadedManifest {
|
|
89
108
|
routes: ServerRouteNode[];
|
|
90
109
|
actions: ActionNode[];
|
|
91
110
|
websockets: WebSocketRouteNode[];
|
|
111
|
+
agents: AgentNode[];
|
|
92
112
|
}
|
|
93
|
-
declare function generateManifest(serverDir: string): TheoManifest;
|
|
113
|
+
declare function generateManifest(serverDir: string, projectRoot?: string): TheoManifest;
|
|
94
114
|
declare function writeManifest(manifest: TheoManifest, outputDir: string): void;
|
|
95
115
|
declare function loadManifest(distDir: string, serverDir: string): LoadedManifest;
|
|
96
116
|
|
|
@@ -103,4 +123,4 @@ declare function loadManifest(distDir: string, serverDir: string): LoadedManifes
|
|
|
103
123
|
*/
|
|
104
124
|
declare function scanMiddlewares(serverDir: string): string[];
|
|
105
125
|
|
|
106
|
-
export { type ActionManifestEntry, type ActionNode, ActionScanError, type LoadedManifest, type ManifestAction, type ManifestRoute, type ManifestWebSocket, ServerRouteNode, type TheoManifest, type WebSocketRouteNode, generateManifest, loadManifest, scanMiddlewares, scanServerActions, scanServerActionsEnriched, scanServerRoutes, scanWebSocketRoutes, writeManifest };
|
|
126
|
+
export { type ActionManifestEntry, type ActionNode, ActionScanError, type LoadedManifest, type ManifestAction, type ManifestAgent, type ManifestRoute, type ManifestWebSocket, ServerRouteNode, type TheoManifest, type WebSocketRouteNode, generateManifest, loadManifest, scanMiddlewares, scanServerActions, scanServerActionsEnriched, scanServerRoutes, scanWebSocketRoutes, writeManifest };
|