[{"data":1,"prerenderedAt":1854},["ShallowReactive",2],{"navigation_docs":3,"-docs-capabilities-caching":493,"-docs-capabilities-caching-surround":1849},[4,46,135,218,257,292,355,377,420],{"title":5,"icon":6,"path":7,"stem":8,"children":9,"page":45},"Getting Started","i-lucide-rocket","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",[10,15,20,25,30,35,40],{"title":11,"path":12,"stem":13,"icon":14},"What is Heximon?","\u002Fdocs\u002Fgetting-started\u002Fintroduction","docs\u002F01.getting-started\u002F01.introduction","i-lucide-info",{"title":16,"path":17,"stem":18,"icon":19},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F01.getting-started\u002F02.installation","i-lucide-download",{"title":21,"path":22,"stem":23,"icon":24},"Quick Start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","docs\u002F01.getting-started\u002F03.quick-start","i-lucide-zap",{"title":26,"path":27,"stem":28,"icon":29},"Core Concepts","\u002Fdocs\u002Fgetting-started\u002Fcore-concepts","docs\u002F01.getting-started\u002F04.core-concepts","i-lucide-shapes",{"title":31,"path":32,"stem":33,"icon":34},"Examples","\u002Fdocs\u002Fgetting-started\u002Fexamples","docs\u002F01.getting-started\u002F05.examples","i-lucide-library",{"title":36,"path":37,"stem":38,"icon":39},"Where Next?","\u002Fdocs\u002Fgetting-started\u002Fwhere-next","docs\u002F01.getting-started\u002F06.where-next","i-lucide-signpost",{"title":41,"path":42,"stem":43,"icon":44},"Troubleshooting","\u002Fdocs\u002Fgetting-started\u002Ftroubleshooting","docs\u002F01.getting-started\u002F07.troubleshooting","i-lucide-life-buoy",false,{"title":47,"icon":48,"path":49,"stem":50,"children":51,"page":45},"Essentials","i-lucide-boxes","\u002Fdocs\u002Fessentials","docs\u002F02.essentials",[52,56,61,66,71,76,81,86,91,96,101,105,110,115,120,125,130],{"title":53,"path":54,"stem":55,"icon":48},"Modules & DI","\u002Fdocs\u002Fessentials\u002Fmodules-and-di","docs\u002F02.essentials\u002F01.modules-and-di",{"title":57,"path":58,"stem":59,"icon":60},"Controllers","\u002Fdocs\u002Fessentials\u002Fcontrollers","docs\u002F02.essentials\u002F02.controllers","i-lucide-route",{"title":62,"path":63,"stem":64,"icon":65},"Validation & DTOs","\u002Fdocs\u002Fessentials\u002Fvalidation","docs\u002F02.essentials\u002F03.validation","i-lucide-shield-check",{"title":67,"path":68,"stem":69,"icon":70},"Contracts","\u002Fdocs\u002Fessentials\u002Fcontracts","docs\u002F02.essentials\u002F04.contracts","i-lucide-file-signature",{"title":72,"path":73,"stem":74,"icon":75},"REST Client","\u002Fdocs\u002Fessentials\u002Fclient","docs\u002F02.essentials\u002F05.client","i-lucide-plug",{"title":77,"path":78,"stem":79,"icon":80},"Database","\u002Fdocs\u002Fessentials\u002Fdatabase","docs\u002F02.essentials\u002F06.database","i-lucide-database",{"title":82,"path":83,"stem":84,"icon":85},"Drizzle ORM","\u002Fdocs\u002Fessentials\u002Fdrizzle","docs\u002F02.essentials\u002F07.drizzle","i-lucide-database-zap",{"title":87,"path":88,"stem":89,"icon":90},"Persistence & Transactions","\u002Fdocs\u002Fessentials\u002Fpersistence","docs\u002F02.essentials\u002F08.persistence","i-lucide-database-backup",{"title":92,"path":93,"stem":94,"icon":95},"Migrations","\u002Fdocs\u002Fessentials\u002Fmigrations","docs\u002F02.essentials\u002F09.migrations","i-lucide-git-branch",{"title":97,"path":98,"stem":99,"icon":100},"Authentication","\u002Fdocs\u002Fessentials\u002Fauthentication","docs\u002F02.essentials\u002F10.authentication","i-lucide-key-round",{"title":102,"path":103,"stem":104,"icon":65},"Permissions & scopes","\u002Fdocs\u002Fessentials\u002Fpermissions","docs\u002F02.essentials\u002F11.permissions",{"title":106,"path":107,"stem":108,"icon":109},"Error Handling","\u002Fdocs\u002Fessentials\u002Ferrors","docs\u002F02.essentials\u002F12.errors","i-lucide-triangle-alert",{"title":111,"path":112,"stem":113,"icon":114},"Testing a Heximon App","\u002Fdocs\u002Fessentials\u002Ftesting","docs\u002F02.essentials\u002F13.testing","i-lucide-flask-conical",{"title":116,"path":117,"stem":118,"icon":119},"Test app harness","\u002Fdocs\u002Fessentials\u002Fcreatetestapp","docs\u002F02.essentials\u002F14.createtestapp","i-lucide-test-tube",{"title":121,"path":122,"stem":123,"icon":124},"Logging & Observability","\u002Fdocs\u002Fessentials\u002Flogging","docs\u002F02.essentials\u002F17.logging","i-lucide-scroll-text",{"title":126,"path":127,"stem":128,"icon":129},"Context & Lifecycle","\u002Fdocs\u002Fessentials\u002Fcontext-and-lifecycle","docs\u002F02.essentials\u002F18.context-and-lifecycle","i-lucide-radio",{"title":131,"path":132,"stem":133,"icon":134},"Project Structure","\u002Fdocs\u002Fessentials\u002Fproject-structure","docs\u002F02.essentials\u002F19.project-structure","i-lucide-folder-tree",{"title":136,"icon":137,"path":138,"stem":139,"children":140,"page":45},"Capabilities","i-lucide-sparkles","\u002Fdocs\u002Fcapabilities","docs\u002F03.capabilities",[141,145,150,155,160,164,169,174,179,184,188,193,198,203,208,213],{"title":142,"path":143,"stem":144,"icon":137},"Overview","\u002Fdocs\u002Fcapabilities\u002Foverview","docs\u002F03.capabilities\u002F01.overview",{"title":146,"path":147,"stem":148,"icon":149},"Events","\u002Fdocs\u002Fcapabilities\u002Fevents","docs\u002F03.capabilities\u002F02.events","i-lucide-radio-tower",{"title":151,"path":152,"stem":153,"icon":154},"Queue","\u002Fdocs\u002Fcapabilities\u002Fbackground-jobs","docs\u002F03.capabilities\u002F03.background-jobs","i-lucide-list-ordered",{"title":156,"path":157,"stem":158,"icon":159},"Scheduled Work","\u002Fdocs\u002Fcapabilities\u002Fscheduled-jobs","docs\u002F03.capabilities\u002F04.scheduled-jobs","i-lucide-clock",{"title":161,"path":162,"stem":163,"icon":85},"Caching","\u002Fdocs\u002Fcapabilities\u002Fcaching","docs\u002F03.capabilities\u002F05.caching",{"title":165,"path":166,"stem":167,"icon":168},"Key\u002FValue Storage","\u002Fdocs\u002Fcapabilities\u002Fkey-value","docs\u002F03.capabilities\u002F06.key-value","i-lucide-hard-drive",{"title":170,"path":171,"stem":172,"icon":173},"Blob Storage","\u002Fdocs\u002Fcapabilities\u002Fblob","docs\u002F03.capabilities\u002F07.blob","i-lucide-folder-open",{"title":175,"path":176,"stem":177,"icon":178},"Health & Readiness","\u002Fdocs\u002Fcapabilities\u002Fhealth","docs\u002F03.capabilities\u002F08.health","i-lucide-heart-pulse",{"title":180,"path":181,"stem":182,"icon":183},"Notifications","\u002Fdocs\u002Fcapabilities\u002Fnotifications","docs\u002F03.capabilities\u002F09.notifications","i-lucide-mail",{"title":185,"path":186,"stem":187,"icon":65},"Idempotency","\u002Fdocs\u002Fcapabilities\u002Fidempotency","docs\u002F03.capabilities\u002F10.idempotency",{"title":189,"path":190,"stem":191,"icon":192},"Security Hardening","\u002Fdocs\u002Fcapabilities\u002Fsecurity","docs\u002F03.capabilities\u002F11.security","i-lucide-shield",{"title":194,"path":195,"stem":196,"icon":197},"Observability & Tracing","\u002Fdocs\u002Fcapabilities\u002Fobservability","docs\u002F03.capabilities\u002F12.observability","i-lucide-activity",{"title":199,"path":200,"stem":201,"icon":202},"OpenAPI","\u002Fdocs\u002Fcapabilities\u002Fopenapi","docs\u002F03.capabilities\u002F13.openapi","i-lucide-book-marked",{"title":204,"path":205,"stem":206,"icon":207},"MCP Server","\u002Fdocs\u002Fcapabilities\u002Fmcp","docs\u002F03.capabilities\u002F14.mcp","i-lucide-bot",{"title":209,"path":210,"stem":211,"icon":212},"Schema DTOs","\u002Fdocs\u002Fcapabilities\u002Fschema","docs\u002F03.capabilities\u002F15.schema","i-lucide-file-json",{"title":214,"path":215,"stem":216,"icon":217},"Branded IDs","\u002Fdocs\u002Fcapabilities\u002Fbranded-ids","docs\u002F03.capabilities\u002F16.branded-ids","i-lucide-tag",{"title":219,"icon":220,"path":221,"stem":222,"children":223,"page":45},"Architecture","i-lucide-building","\u002Fdocs\u002Farchitecture","docs\u002F04.architecture",[224,227,232,237,242,247,252],{"title":142,"path":225,"stem":226,"icon":220},"\u002Fdocs\u002Farchitecture\u002Foverview","docs\u002F04.architecture\u002F01.overview",{"title":228,"path":229,"stem":230,"icon":231},"Domain-Driven Design","\u002Fdocs\u002Farchitecture\u002Fddd","docs\u002F04.architecture\u002F02.ddd","i-lucide-hexagon",{"title":233,"path":234,"stem":235,"icon":236},"CQRS","\u002Fdocs\u002Farchitecture\u002Fcqrs","docs\u002F04.architecture\u002F03.cqrs","i-lucide-split",{"title":238,"path":239,"stem":240,"icon":241},"Repository Pattern","\u002Fdocs\u002Farchitecture\u002Frepository-pattern","docs\u002F04.architecture\u002F04.repository-pattern","i-lucide-archive",{"title":243,"path":244,"stem":245,"icon":246},"Integration Events","\u002Fdocs\u002Farchitecture\u002Fintegration-events","docs\u002F04.architecture\u002F05.integration-events","i-lucide-send-horizontal",{"title":248,"path":249,"stem":250,"icon":251},"Saga Orchestration","\u002Fdocs\u002Farchitecture\u002Fsagas","docs\u002F04.architecture\u002F06.sagas","i-lucide-hourglass",{"title":253,"path":254,"stem":255,"icon":256},"Durable Workflows","\u002Fdocs\u002Farchitecture\u002Fworkflows","docs\u002F04.architecture\u002F07.workflows","i-lucide-workflow",{"title":258,"icon":259,"path":260,"stem":261,"children":262,"page":45},"Realtime","i-lucide-cable","\u002Fdocs\u002Frealtime","docs\u002F05.realtime",[263,266,270,274,279,284,288],{"title":142,"path":264,"stem":265,"icon":259},"\u002Fdocs\u002Frealtime\u002Foverview","docs\u002F05.realtime\u002F01.overview",{"title":267,"path":268,"stem":269,"icon":129},"WebSockets","\u002Fdocs\u002Frealtime\u002Fwebsockets","docs\u002F05.realtime\u002F02.websockets",{"title":271,"path":272,"stem":273,"icon":70},"WebSocket Contracts","\u002Fdocs\u002Frealtime\u002Fwebsocket-contracts","docs\u002F05.realtime\u002F03.websocket-contracts",{"title":275,"path":276,"stem":277,"icon":278},"Server-Sent Events","\u002Fdocs\u002Frealtime\u002Fserver-sent-events","docs\u002F05.realtime\u002F04.server-sent-events","i-lucide-rss",{"title":280,"path":281,"stem":282,"icon":283},"Typed Clients","\u002Fdocs\u002Frealtime\u002Ftyped-clients","docs\u002F05.realtime\u002F05.typed-clients","i-lucide-plug-zap",{"title":285,"path":286,"stem":287,"icon":259},"AsyncAPI","\u002Fdocs\u002Frealtime\u002Fasyncapi","docs\u002F05.realtime\u002F06.asyncapi",{"title":289,"path":290,"stem":291,"icon":80},"Durable Objects","\u002Fdocs\u002Frealtime\u002Fdurable-objects","docs\u002F05.realtime\u002F07.durable-objects",{"title":293,"icon":294,"path":295,"stem":296,"children":297,"page":45},"Deploy","i-lucide-cloud-upload","\u002Fdocs\u002Fdeploy","docs\u002F06.deploy",[298,301,306,311,316,321,326,331,336,341,345,350],{"title":142,"path":299,"stem":300,"icon":6},"\u002Fdocs\u002Fdeploy\u002Foverview","docs\u002F06.deploy\u002F01.overview",{"title":302,"path":303,"stem":304,"icon":305},"Node","\u002Fdocs\u002Fdeploy\u002Fnode","docs\u002F06.deploy\u002F02.node","la:node-js",{"title":307,"path":308,"stem":309,"icon":310},"Cloudflare","\u002Fdocs\u002Fdeploy\u002Fcloudflare","docs\u002F06.deploy\u002F03.cloudflare","simple-icons:cloudflare",{"title":312,"path":313,"stem":314,"icon":315},"Vercel","\u002Fdocs\u002Fdeploy\u002Fvercel","docs\u002F06.deploy\u002F04.vercel","ri:vercel-fill",{"title":317,"path":318,"stem":319,"icon":320},"Netlify","\u002Fdocs\u002Fdeploy\u002Fnetlify","docs\u002F06.deploy\u002F05.netlify","i-simple-icons-netlify",{"title":322,"path":323,"stem":324,"icon":325},"AWS Lambda","\u002Fdocs\u002Fdeploy\u002Faws-lambda","docs\u002F06.deploy\u002F06.aws-lambda","hugeicons:aws-lambda",{"title":327,"path":328,"stem":329,"icon":330},"Deno Deploy","\u002Fdocs\u002Fdeploy\u002Fdeno-deploy","docs\u002F06.deploy\u002F07.deno-deploy","simple-icons:denodeploy",{"title":332,"path":333,"stem":334,"icon":335},"Nitro","\u002Fdocs\u002Fdeploy\u002Fnitro","docs\u002F06.deploy\u002F08.nitro","i-unjs-nitro",{"title":337,"path":338,"stem":339,"icon":340},"Nuxt","\u002Fdocs\u002Fdeploy\u002Fnuxt","docs\u002F06.deploy\u002F09.nuxt","i-devicon-nuxt",{"title":342,"path":343,"stem":344,"icon":236},"Mixed-runtime split","\u002Fdocs\u002Fdeploy\u002Fmixed-runtime-split","docs\u002F06.deploy\u002F10.mixed-runtime-split",{"title":346,"path":347,"stem":348,"icon":349},"Hosts","\u002Fdocs\u002Fdeploy\u002Fhosts","docs\u002F06.deploy\u002F11.hosts","i-lucide-map",{"title":351,"path":352,"stem":353,"icon":354},"Cross-service events","\u002Fdocs\u002Fdeploy\u002Fcross-service-events","docs\u002F06.deploy\u002F12.cross-service-events","i-lucide-network",{"title":356,"icon":357,"path":358,"stem":359,"children":360,"page":45},"CLI","i-lucide-terminal","\u002Fdocs\u002Fcli","docs\u002F07.cli",[361,365,369,373],{"title":362,"path":363,"stem":364,"icon":357},"The heximon CLI","\u002Fdocs\u002Fcli\u002Foverview","docs\u002F07.cli\u002F01.overview",{"title":366,"path":367,"stem":368,"icon":137},"Create a New App","\u002Fdocs\u002Fcli\u002Fcreate","docs\u002F07.cli\u002F02.create",{"title":370,"path":371,"stem":372,"icon":95},"Migrations from the CLI","\u002Fdocs\u002Fcli\u002Fmigrations","docs\u002F07.cli\u002F03.migrations",{"title":374,"path":375,"stem":376,"icon":6},"Deploying a Heximon app","\u002Fdocs\u002Fcli\u002Fdeploy","docs\u002F07.cli\u002F04.deploy",{"title":378,"icon":379,"path":380,"stem":381,"children":382,"page":45},"Under the Hood","i-lucide-wrench","\u002Fdocs\u002Funder-the-hood","docs\u002F08.under-the-hood",[383,388,393,397,402,407,411,415],{"title":384,"path":385,"stem":386,"icon":387},"How the Compiler Works","\u002Fdocs\u002Funder-the-hood\u002Fhow-the-compiler-works","docs\u002F08.under-the-hood\u002F01.how-the-compiler-works","i-lucide-cog",{"title":389,"path":390,"stem":391,"icon":392},"Compiler","\u002Fdocs\u002Funder-the-hood\u002Fextending-the-compiler","docs\u002F08.under-the-hood\u002F02.extending-the-compiler","i-lucide-cpu",{"title":394,"path":395,"stem":396,"icon":357},"Compiler Diagnostics","\u002Fdocs\u002Funder-the-hood\u002Fcompiler-diagnostics","docs\u002F08.under-the-hood\u002F03.compiler-diagnostics",{"title":398,"path":399,"stem":400,"icon":401},"Library Mode","\u002Fdocs\u002Funder-the-hood\u002Flibrary-mode","docs\u002F08.under-the-hood\u002F04.library-mode","i-lucide-package",{"title":403,"path":404,"stem":405,"icon":406},"Other bundlers","\u002Fdocs\u002Funder-the-hood\u002Falternate-bundlers","docs\u002F08.under-the-hood\u002F05.alternate-bundlers","i-lucide-blocks",{"title":408,"path":409,"stem":410,"icon":401},"Packages","\u002Fdocs\u002Funder-the-hood\u002Fpackages","docs\u002F08.under-the-hood\u002F06.packages",{"title":412,"path":413,"stem":414,"icon":207},"AI Skills","\u002Fdocs\u002Funder-the-hood\u002Fai-and-agents","docs\u002F08.under-the-hood\u002F07.ai-and-agents",{"title":416,"path":417,"stem":418,"icon":419},"Advanced DI","\u002Fdocs\u002Funder-the-hood\u002Fadvanced-di","docs\u002F08.under-the-hood\u002F08.advanced-di","i-lucide-git-fork",{"title":421,"icon":422,"path":423,"stem":424,"children":425,"page":45},"Recipes","i-lucide-book-open-check","\u002Fdocs\u002Frecipes","docs\u002F09.recipes",[426,431,436,440,444,448,452,456,460,465,469,474,479,484,488],{"title":427,"path":428,"stem":429,"icon":430},"Send an Email","\u002Fdocs\u002Frecipes\u002Fsend-an-email","docs\u002F09.recipes\u002F01.send-an-email","i-lucide-send",{"title":432,"path":433,"stem":434,"icon":435},"Upload a File","\u002Fdocs\u002Frecipes\u002Fupload-a-file","docs\u002F09.recipes\u002F02.upload-a-file","i-lucide-upload",{"title":437,"path":438,"stem":439,"icon":24},"Cache an Expensive Call","\u002Fdocs\u002Frecipes\u002Fcache-an-expensive-call","docs\u002F09.recipes\u002F03.cache-an-expensive-call",{"title":441,"path":442,"stem":443,"icon":154},"Paginate a List","\u002Fdocs\u002Frecipes\u002Fpaginate-a-list","docs\u002F09.recipes\u002F04.paginate-a-list",{"title":445,"path":446,"stem":447,"icon":65},"Protect Admin Routes","\u002Fdocs\u002Frecipes\u002Fprotect-admin-routes","docs\u002F09.recipes\u002F05.protect-admin-routes",{"title":449,"path":450,"stem":451,"icon":178},"Health Probes and a Nightly Job","\u002Fdocs\u002Frecipes\u002Fhealth-probes-and-a-nightly-job","docs\u002F09.recipes\u002F06.health-probes-and-a-nightly-job",{"title":453,"path":454,"stem":455,"icon":80},"Build a CRUD API","\u002Fdocs\u002Frecipes\u002Fcrud-api","docs\u002F09.recipes\u002F07.crud-api",{"title":457,"path":458,"stem":459,"icon":100},"Add Authentication","\u002Fdocs\u002Frecipes\u002Fadd-authentication","docs\u002F09.recipes\u002F08.add-authentication",{"title":461,"path":462,"stem":463,"icon":464},"Background Jobs","\u002Fdocs\u002Frecipes\u002Fbackground-jobs","docs\u002F09.recipes\u002F09.background-jobs","i-lucide-timer",{"title":466,"path":467,"stem":468,"icon":149},"Decouple Features with Events","\u002Fdocs\u002Frecipes\u002Fevent-driven-module","docs\u002F09.recipes\u002F10.event-driven-module",{"title":470,"path":471,"stem":472,"icon":473},"Build a DDD Aggregate","\u002Fdocs\u002Frecipes\u002Fddd-aggregate","docs\u002F09.recipes\u002F11.ddd-aggregate","i-lucide-box",{"title":475,"path":476,"stem":477,"icon":478},"Publish a Feature Package","\u002Fdocs\u002Frecipes\u002Ffeature-package","docs\u002F09.recipes\u002F12.feature-package","i-lucide-package-open",{"title":480,"path":481,"stem":482,"icon":483},"Reliable Integration Events","\u002Fdocs\u002Frecipes\u002Freliable-integration-events","docs\u002F09.recipes\u002F13.reliable-integration-events","i-lucide-inbox",{"title":485,"path":486,"stem":487,"icon":95},"Reactive Choreography","\u002Fdocs\u002Frecipes\u002Freactive-choreography","docs\u002F09.recipes\u002F14.reactive-choreography",{"title":489,"path":490,"stem":491,"icon":492},"Build a Production CRUD API","\u002Fdocs\u002Frecipes\u002Fproduction-crud-api","docs\u002F09.recipes\u002F15.production-crud-api","i-lucide-layers",{"id":494,"title":161,"body":495,"description":1842,"extension":1843,"links":1844,"meta":1845,"navigation":1846,"path":162,"seo":1847,"stem":163,"__hash__":1848},"docs\u002Fdocs\u002F03.capabilities\u002F05.caching.md",{"type":496,"value":497,"toc":1828},"minimark",[498,514,519,529,551,562,587,591,660,667,682,773,777,799,1149,1162,1168,1175,1183,1328,1342,1366,1479,1486,1507,1615,1645,1669,1673,1687,1719,1726,1730,1748,1777,1784,1788,1824],[499,500,501,502,506,507,510,511,513],"p",{},"A read you've already computed shouldn't be computed again. Heximon's caching tier puts one ",[503,504,505],"code",{},"Cache"," over an\nordered chain of ",[503,508,509],{},"Storage"," tiers, then caches reads at two layers on top of it: whole HTTP responses and\nindividual CQRS query results. Both share the one ",[503,512,505],{},", so a single tag invalidation clears every layer\nat once.",[515,516,518],"h2",{"id":517},"one-cache-two-consumers","One cache, two consumers",[520,521,526],"pre",{"className":522,"code":524,"language":525},[523],"language-text","            HTTP request                         QueryBus.execute(query)\n                 │                                        │\n          CacheMiddleware                        QueryCacheInterceptor\n        (caches the response)                  (caches the query result)\n                 └───────────────┬──────────────────────┘\n                                 ▼\n                              Cache  ── getOrSet · invalidate(tag)\n                                 ▼\n                           TieredCache  ── L1 (memory) → L2 (Redis \u002F KV) → …\n","text",[503,527,524],{"__ignoreMap":528},"",[499,530,531,532,534,535,538,539,543,544,546,547,550],{},"The ",[503,533,509],{}," port underneath is a plain key\u002Fvalue store — caching adds a ",[503,536,537],{},"{ value, expiresAt }"," envelope\non top, so the ",[540,541,542],"em",{},"cache"," decides freshness, not the driver. That keeps ",[503,545,509],{}," swappable and sidesteps\ndriver TTL floors (Cloudflare KV rounds its ",[503,548,549],{},"expirationTtl"," up to 60s).",[499,552,553,554,556,557,561],{},"Because everything round-trips through a JSON-domain ",[503,555,509],{},", cache only ",[558,559,560],"strong",{},"plain data"," (DTOs): a cache\nhit returns plain data, not an instance of the original class.",[499,563,531,564,567,568,571,572,575,576,575,579,582,583,586],{},[503,565,566],{},"QueryCacheInterceptor"," ",[558,569,570],{},"enforces"," this —\na query whose result is (or contains) a class instance, ",[503,573,574],{},"Date",", ",[503,577,578],{},"Map",[503,580,581],{},"Set",", or ",[503,584,585],{},"bigint"," throws a clear error\nat the first cache miss, naming the query class and the offending path. Return a read DTO from the query\nhandler, not a domain entity.",[515,588,590],{"id":589},"install","Install",[592,593,594,617,632,646],"code-group",{},[520,595,600],{"className":596,"code":597,"filename":598,"language":599,"meta":528,"style":528},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @heximon\u002Fcache\n","pnpm","bash",[503,601,602],{"__ignoreMap":528},[603,604,607,610,614],"span",{"class":605,"line":606},"line",1,[603,608,598],{"class":609},"sBMFI",[603,611,613],{"class":612},"sfazB"," add",[603,615,616],{"class":612}," @heximon\u002Fcache\n",[520,618,621],{"className":596,"code":619,"filename":620,"language":599,"meta":528,"style":528},"npm install @heximon\u002Fcache\n","npm",[503,622,623],{"__ignoreMap":528},[603,624,625,627,630],{"class":605,"line":606},[603,626,620],{"class":609},[603,628,629],{"class":612}," install",[603,631,616],{"class":612},[520,633,636],{"className":596,"code":634,"filename":635,"language":599,"meta":528,"style":528},"yarn add @heximon\u002Fcache\n","yarn",[503,637,638],{"__ignoreMap":528},[603,639,640,642,644],{"class":605,"line":606},[603,641,635],{"class":609},[603,643,613],{"class":612},[603,645,616],{"class":612},[520,647,650],{"className":596,"code":648,"filename":649,"language":599,"meta":528,"style":528},"bun add @heximon\u002Fcache\n","bun",[503,651,652],{"__ignoreMap":528},[603,653,654,656,658],{"class":605,"line":606},[603,655,649],{"class":609},[603,657,613],{"class":612},[603,659,616],{"class":612},[515,661,663,664],{"id":662},"the-tier-chain-tieredcache","The tier chain — ",[503,665,666],{},"TieredCache",[499,668,669,670,673,674,677,678,681],{},"Tiers are fastest-first. A read walks ",[503,671,672],{},"L1 → L2 → …","; the first non-expired hit is returned and ",[558,675,676],{},"promoted","\ninto every faster tier, so the next read resolves closer to the front. Writes and deletes fan out to all\ntiers. Each tier honors both its own ",[503,679,680],{},"ttlSeconds"," cap and the caller's TTL — the shorter wins — so an\nin-process L1 can hold a value for seconds while a shared L2 holds it for minutes.",[520,683,687],{"className":684,"code":685,"language":686,"meta":528,"style":528},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","new TieredCache([\n  { storage: l1, ttlSeconds: 5 },    \u002F\u002F in-process hot tier\n  { storage: l2, ttlSeconds: 300 },  \u002F\u002F shared networked tier (Redis \u002F Cloudflare KV)\n]);\n","ts",[503,688,689,703,738,764],{"__ignoreMap":528},[603,690,691,695,699],{"class":605,"line":606},[603,692,694],{"class":693},"sMK4o","new",[603,696,698],{"class":697},"s2Zo4"," TieredCache",[603,700,702],{"class":701},"sTEyZ","([\n",[603,704,706,709,713,716,719,722,725,727,731,734],{"class":605,"line":705},2,[603,707,708],{"class":693},"  {",[603,710,712],{"class":711},"swJcz"," storage",[603,714,715],{"class":693},":",[603,717,718],{"class":701}," l1",[603,720,721],{"class":693},",",[603,723,724],{"class":711}," ttlSeconds",[603,726,715],{"class":693},[603,728,730],{"class":729},"sbssI"," 5",[603,732,733],{"class":693}," },",[603,735,737],{"class":736},"sHwdD","    \u002F\u002F in-process hot tier\n",[603,739,741,743,745,747,750,752,754,756,759,761],{"class":605,"line":740},3,[603,742,708],{"class":693},[603,744,712],{"class":711},[603,746,715],{"class":693},[603,748,749],{"class":701}," l2",[603,751,721],{"class":693},[603,753,724],{"class":711},[603,755,715],{"class":693},[603,757,758],{"class":729}," 300",[603,760,733],{"class":693},[603,762,763],{"class":736},"  \u002F\u002F shared networked tier (Redis \u002F Cloudflare KV)\n",[603,765,767,770],{"class":605,"line":766},4,[603,768,769],{"class":701},"])",[603,771,772],{"class":693},";\n",[515,774,776],{"id":775},"wiring","Wiring",[499,778,779,781,782,784,785,787,788,791,792,795,796,798],{},[503,780,505],{}," takes a config object: a single ",[503,783,509],{}," (the common case) or a ",[503,786,666],{}," for multiple tiers.\nThere are no framework tier tokens — bind your concrete storage classes (each is its own DI token) and pass\nthem to a ",[503,789,790],{},"useFactory",". For dev, one ",[503,793,794],{},"MemoryStorage"," is enough; for production, pass a ",[503,797,666],{}," with a\nnetworked L2.",[520,800,803],{"className":684,"code":801,"filename":802,"language":686,"meta":528,"style":528},"import {\n  Cache,\n  CacheMiddleware,\n  QueryCacheInterceptor,\n  QueryCachePoliciesConfig,\n} from \"@heximon\u002Fcache\";\nimport { Module } from \"@heximon\u002Fruntime\";\nimport { MemoryStorage } from \"@heximon\u002Fkv\u002Fmemory\";\n\nexport class AppModule extends Module({\n  providers: [\n    { provide: MemoryStorage, useFactory: () => new MemoryStorage() },\n    { provide: Cache, useFactory: (storage: MemoryStorage) => new Cache({ storage, ttlSeconds: 86_400 }) },\n    \u002F\u002F The interceptor injects this. It can be empty when each cached query declares its policy on its\n    \u002F\u002F handler (below); it is also where a `keyFn` \u002F a not-your-handler override would live.\n    { provide: QueryCachePoliciesConfig, useValue: new QueryCachePoliciesConfig() },\n  ],\n  http: { middlewares: [CacheMiddleware] },\n  cqrs: { queryInterceptors: [QueryCacheInterceptor] },\n}) {}\n","src\u002Fapp.module.ts",[503,804,805,814,822,829,836,844,864,889,912,919,943,954,991,1052,1058,1064,1091,1099,1119,1139],{"__ignoreMap":528},[603,806,807,811],{"class":605,"line":606},[603,808,810],{"class":809},"s7zQu","import",[603,812,813],{"class":693}," {\n",[603,815,816,819],{"class":605,"line":705},[603,817,818],{"class":701},"  Cache",[603,820,821],{"class":693},",\n",[603,823,824,827],{"class":605,"line":740},[603,825,826],{"class":701},"  CacheMiddleware",[603,828,821],{"class":693},[603,830,831,834],{"class":605,"line":766},[603,832,833],{"class":701},"  QueryCacheInterceptor",[603,835,821],{"class":693},[603,837,839,842],{"class":605,"line":838},5,[603,840,841],{"class":701},"  QueryCachePoliciesConfig",[603,843,821],{"class":693},[603,845,847,850,853,856,859,862],{"class":605,"line":846},6,[603,848,849],{"class":693},"}",[603,851,852],{"class":809}," from",[603,854,855],{"class":693}," \"",[603,857,858],{"class":612},"@heximon\u002Fcache",[603,860,861],{"class":693},"\"",[603,863,772],{"class":693},[603,865,867,869,872,875,878,880,882,885,887],{"class":605,"line":866},7,[603,868,810],{"class":809},[603,870,871],{"class":693}," {",[603,873,874],{"class":701}," Module",[603,876,877],{"class":693}," }",[603,879,852],{"class":809},[603,881,855],{"class":693},[603,883,884],{"class":612},"@heximon\u002Fruntime",[603,886,861],{"class":693},[603,888,772],{"class":693},[603,890,892,894,896,899,901,903,905,908,910],{"class":605,"line":891},8,[603,893,810],{"class":809},[603,895,871],{"class":693},[603,897,898],{"class":701}," MemoryStorage",[603,900,877],{"class":693},[603,902,852],{"class":809},[603,904,855],{"class":693},[603,906,907],{"class":612},"@heximon\u002Fkv\u002Fmemory",[603,909,861],{"class":693},[603,911,772],{"class":693},[603,913,915],{"class":605,"line":914},9,[603,916,918],{"emptyLinePlaceholder":917},true,"\n",[603,920,922,925,929,932,935,937,940],{"class":605,"line":921},10,[603,923,924],{"class":809},"export",[603,926,928],{"class":927},"spNyl"," class",[603,930,931],{"class":609}," AppModule",[603,933,934],{"class":927}," extends",[603,936,874],{"class":697},[603,938,939],{"class":701},"(",[603,941,942],{"class":693},"{\n",[603,944,946,949,951],{"class":605,"line":945},11,[603,947,948],{"class":711},"  providers",[603,950,715],{"class":693},[603,952,953],{"class":701}," [\n",[603,955,957,960,963,965,967,969,972,974,977,980,983,985,988],{"class":605,"line":956},12,[603,958,959],{"class":693},"    {",[603,961,962],{"class":711}," provide",[603,964,715],{"class":693},[603,966,898],{"class":701},[603,968,721],{"class":693},[603,970,971],{"class":697}," useFactory",[603,973,715],{"class":693},[603,975,976],{"class":693}," ()",[603,978,979],{"class":927}," =>",[603,981,982],{"class":693}," new",[603,984,898],{"class":697},[603,986,987],{"class":701},"() ",[603,989,990],{"class":693},"},\n",[603,992,994,996,998,1000,1003,1005,1007,1009,1012,1016,1018,1020,1023,1025,1027,1029,1031,1034,1036,1038,1040,1042,1045,1047,1050],{"class":605,"line":993},13,[603,995,959],{"class":693},[603,997,962],{"class":711},[603,999,715],{"class":693},[603,1001,1002],{"class":701}," Cache",[603,1004,721],{"class":693},[603,1006,971],{"class":697},[603,1008,715],{"class":693},[603,1010,1011],{"class":693}," (",[603,1013,1015],{"class":1014},"sHdIc","storage",[603,1017,715],{"class":693},[603,1019,898],{"class":609},[603,1021,1022],{"class":693},")",[603,1024,979],{"class":927},[603,1026,982],{"class":693},[603,1028,1002],{"class":697},[603,1030,939],{"class":701},[603,1032,1033],{"class":693},"{",[603,1035,712],{"class":701},[603,1037,721],{"class":693},[603,1039,724],{"class":711},[603,1041,715],{"class":693},[603,1043,1044],{"class":729}," 86_400",[603,1046,877],{"class":693},[603,1048,1049],{"class":701},") ",[603,1051,990],{"class":693},[603,1053,1055],{"class":605,"line":1054},14,[603,1056,1057],{"class":736},"    \u002F\u002F The interceptor injects this. It can be empty when each cached query declares its policy on its\n",[603,1059,1061],{"class":605,"line":1060},15,[603,1062,1063],{"class":736},"    \u002F\u002F handler (below); it is also where a `keyFn` \u002F a not-your-handler override would live.\n",[603,1065,1067,1069,1071,1073,1076,1078,1081,1083,1085,1087,1089],{"class":605,"line":1066},16,[603,1068,959],{"class":693},[603,1070,962],{"class":711},[603,1072,715],{"class":693},[603,1074,1075],{"class":701}," QueryCachePoliciesConfig",[603,1077,721],{"class":693},[603,1079,1080],{"class":711}," useValue",[603,1082,715],{"class":693},[603,1084,982],{"class":693},[603,1086,1075],{"class":697},[603,1088,987],{"class":701},[603,1090,990],{"class":693},[603,1092,1094,1097],{"class":605,"line":1093},17,[603,1095,1096],{"class":701},"  ]",[603,1098,821],{"class":693},[603,1100,1102,1105,1107,1109,1112,1114,1117],{"class":605,"line":1101},18,[603,1103,1104],{"class":711},"  http",[603,1106,715],{"class":693},[603,1108,871],{"class":693},[603,1110,1111],{"class":711}," middlewares",[603,1113,715],{"class":693},[603,1115,1116],{"class":701}," [CacheMiddleware] ",[603,1118,990],{"class":693},[603,1120,1122,1125,1127,1129,1132,1134,1137],{"class":605,"line":1121},19,[603,1123,1124],{"class":711},"  cqrs",[603,1126,715],{"class":693},[603,1128,871],{"class":693},[603,1130,1131],{"class":711}," queryInterceptors",[603,1133,715],{"class":693},[603,1135,1136],{"class":701}," [QueryCacheInterceptor] ",[603,1138,990],{"class":693},[603,1140,1142,1144,1146],{"class":605,"line":1141},20,[603,1143,849],{"class":693},[603,1145,1049],{"class":701},[603,1147,1148],{"class":693},"{}\n",[499,1150,1151,1153,1154,1157,1158,1161],{},[503,1152,680],{}," is the default TTL (overridable per call; ",[503,1155,1156],{},"0"," = no expiry). For multiple tiers, pass\n",[503,1159,1160],{},"storage: new TieredCache([{ storage: l1, ttlSeconds: 5 }, { storage: l2, ttlSeconds: 300 }])"," — tag markers\nspan the whole chain, so the durable tier retains them even if a bounded L1 evicts.",[515,1163,1165,1166],{"id":1164},"caching-query-results-querycacheinterceptor","Caching query results — ",[503,1167,566],{},[499,1169,1170,1171,1174],{},"Listed under ",[503,1172,1173],{},"cqrs.queryInterceptors",", it caches the result of any query that has a policy; a query with none\npasses straight through and a hit short-circuits the handler. A policy comes from one of two places.",[499,1176,1177,567,1180,1182],{},[558,1178,1179],{},"On the handler — the default, and what scales.",[503,1181,858],{}," augments the query handler options, so a\nhandler declares its own policy as its second type argument. The policy sits next to the handler — nothing to\nregister centrally, nothing left dangling when the query is deleted:",[520,1184,1187],{"className":684,"code":1185,"filename":1186,"language":686,"meta":528,"style":528},"export class GetProductQueryHandler implements QueryHandler\u003CGetProductQuery, { cache: { ttlSeconds: 30; tags: [\"product\"] } }> {\n  public handle(query: GetProductQuery): Product | null {\n    return this.store.find(query.id);\n  }\n}\n","src\u002Fcatalog\u002Fget-product.handler.ts",[503,1188,1189,1256,1288,1318,1323],{"__ignoreMap":528},[603,1190,1191,1193,1195,1198,1201,1204,1207,1210,1212,1214,1217,1219,1221,1223,1225,1228,1231,1234,1236,1239,1241,1244,1246,1249,1251,1254],{"class":605,"line":606},[603,1192,924],{"class":809},[603,1194,928],{"class":927},[603,1196,1197],{"class":609}," GetProductQueryHandler",[603,1199,1200],{"class":927}," implements",[603,1202,1203],{"class":609}," QueryHandler",[603,1205,1206],{"class":693},"\u003C",[603,1208,1209],{"class":609},"GetProductQuery",[603,1211,721],{"class":693},[603,1213,871],{"class":693},[603,1215,1216],{"class":711}," cache",[603,1218,715],{"class":693},[603,1220,871],{"class":693},[603,1222,724],{"class":711},[603,1224,715],{"class":693},[603,1226,1227],{"class":729}," 30",[603,1229,1230],{"class":693},";",[603,1232,1233],{"class":711}," tags",[603,1235,715],{"class":693},[603,1237,1238],{"class":701}," [",[603,1240,861],{"class":693},[603,1242,1243],{"class":612},"product",[603,1245,861],{"class":693},[603,1247,1248],{"class":701},"] ",[603,1250,849],{"class":693},[603,1252,1253],{"class":693}," }>",[603,1255,813],{"class":693},[603,1257,1258,1261,1264,1266,1269,1271,1274,1277,1280,1283,1286],{"class":605,"line":705},[603,1259,1260],{"class":927},"  public",[603,1262,1263],{"class":711}," handle",[603,1265,939],{"class":693},[603,1267,1268],{"class":1014},"query",[603,1270,715],{"class":693},[603,1272,1273],{"class":609}," GetProductQuery",[603,1275,1276],{"class":693},"):",[603,1278,1279],{"class":609}," Product",[603,1281,1282],{"class":693}," |",[603,1284,1285],{"class":609}," null",[603,1287,813],{"class":693},[603,1289,1290,1293,1296,1299,1302,1305,1307,1309,1311,1314,1316],{"class":605,"line":740},[603,1291,1292],{"class":809},"    return",[603,1294,1295],{"class":693}," this.",[603,1297,1298],{"class":701},"store",[603,1300,1301],{"class":693},".",[603,1303,1304],{"class":697},"find",[603,1306,939],{"class":711},[603,1308,1268],{"class":701},[603,1310,1301],{"class":693},[603,1312,1313],{"class":701},"id",[603,1315,1022],{"class":711},[603,1317,772],{"class":693},[603,1319,1320],{"class":605,"line":766},[603,1321,1322],{"class":693},"  }\n",[603,1324,1325],{"class":605,"line":838},[603,1326,1327],{"class":693},"}\n",[499,1329,1330,1331,1333,1334,1337,1338,1341],{},"The compiler forwards this policy onto the query class and the interceptor reads it. Because it is a\ncompile-time type literal, each value must be a single literal — only ",[503,1332,680],{}," + ",[503,1335,1336],{},"tags"," are declarable\nhere, and a non-literal (e.g. a union ",[503,1339,1340],{},"ttlSeconds: 30 | 60",") is a build error rather than a silent default.",[499,1343,1344,1354,1355,1358,1359,1362,1363,1365],{},[558,1345,1346,1347,1350,1351,1301],{},"In the central ",[503,1348,1349],{},"QueryCachePoliciesConfig"," — the override seam, and the home for a ",[503,1352,1353],{},"keyFn"," Keyed by the\n",[558,1356,1357],{},"query class"," you already import (never the compiler's internal message id). An explicit entry ",[558,1360,1361],{},"wins"," over\nthe handler-declared policy, and a ",[503,1364,1353],{}," (a runtime function, not expressible as a type literal) belongs\nhere:",[520,1367,1369],{"className":684,"code":1368,"language":686,"meta":528,"style":528},"new QueryCachePoliciesConfig()\n  .for(ReportQuery, { ttlSeconds: 60, keyFn: (q) => `report:${q.from.toISOString()}` })\n  .for(GetProductQuery, { ttlSeconds: 10 }); \u002F\u002F overrides this query's handler-declared policy\n",[503,1370,1371,1380,1450],{"__ignoreMap":528},[603,1372,1373,1375,1377],{"class":605,"line":606},[603,1374,694],{"class":693},[603,1376,1075],{"class":697},[603,1378,1379],{"class":701},"()\n",[603,1381,1382,1385,1388,1391,1393,1395,1397,1399,1402,1404,1407,1409,1411,1414,1416,1418,1421,1424,1427,1429,1431,1434,1436,1439,1442,1445,1447],{"class":605,"line":705},[603,1383,1384],{"class":693},"  .",[603,1386,1387],{"class":697},"for",[603,1389,1390],{"class":701},"(ReportQuery",[603,1392,721],{"class":693},[603,1394,871],{"class":693},[603,1396,724],{"class":711},[603,1398,715],{"class":693},[603,1400,1401],{"class":729}," 60",[603,1403,721],{"class":693},[603,1405,1406],{"class":697}," keyFn",[603,1408,715],{"class":693},[603,1410,1011],{"class":693},[603,1412,1413],{"class":1014},"q",[603,1415,1022],{"class":693},[603,1417,979],{"class":927},[603,1419,1420],{"class":693}," `",[603,1422,1423],{"class":612},"report:",[603,1425,1426],{"class":693},"${",[603,1428,1413],{"class":701},[603,1430,1301],{"class":693},[603,1432,1433],{"class":701},"from",[603,1435,1301],{"class":693},[603,1437,1438],{"class":697},"toISOString",[603,1440,1441],{"class":701},"()",[603,1443,1444],{"class":693},"}`",[603,1446,877],{"class":693},[603,1448,1449],{"class":701},")\n",[603,1451,1452,1454,1456,1459,1461,1463,1465,1467,1470,1472,1474,1476],{"class":605,"line":740},[603,1453,1384],{"class":693},[603,1455,1387],{"class":697},[603,1457,1458],{"class":701},"(GetProductQuery",[603,1460,721],{"class":693},[603,1462,871],{"class":693},[603,1464,724],{"class":711},[603,1466,715],{"class":693},[603,1468,1469],{"class":729}," 10",[603,1471,877],{"class":693},[603,1473,1022],{"class":701},[603,1475,1230],{"class":693},[603,1477,1478],{"class":736}," \u002F\u002F overrides this query's handler-declared policy\n",[515,1480,1482,1483],{"id":1481},"caching-http-responses-cachemiddleware","Caching HTTP responses — ",[503,1484,1485],{},"CacheMiddleware",[499,1487,1170,1488,1491,1492,1494,1495,1498,1499,1502,1503,1506],{},[503,1489,1490],{},"http.middlewares",", it is driven by the matched route's ",[503,1493,542],{}," options. Declare them on the\nroute — with the ",[503,1496,1497],{},"Route"," builder, or inline on a controller action (",[503,1500,1501],{},"Get\u003C\"\u002Fproducts\u002F:id\", { cache: { maxAge: 2 } }>",");\nthe same directives drive both the server-side cache and the client-facing ",[503,1504,1505],{},"Cache-Control"," header.",[520,1508,1511],{"className":684,"code":1509,"filename":1510,"language":686,"meta":528,"style":528},"Route.get(\"\u002Fproducts\u002F:id\")\n  .pathParams(productParams)\n  .responses({ 200: productResponse })\n  .cache({ maxAge: 2, staleMaxAge: 5, tags: [\"product\"] });\n","src\u002Fcatalog\u002Fcatalog.contract.ts",[503,1512,1513,1533,1543,1566],{"__ignoreMap":528},[603,1514,1515,1517,1519,1522,1524,1526,1529,1531],{"class":605,"line":606},[603,1516,1497],{"class":701},[603,1518,1301],{"class":693},[603,1520,1521],{"class":697},"get",[603,1523,939],{"class":701},[603,1525,861],{"class":693},[603,1527,1528],{"class":612},"\u002Fproducts\u002F:id",[603,1530,861],{"class":693},[603,1532,1449],{"class":701},[603,1534,1535,1537,1540],{"class":605,"line":705},[603,1536,1384],{"class":693},[603,1538,1539],{"class":697},"pathParams",[603,1541,1542],{"class":701},"(productParams)\n",[603,1544,1545,1547,1550,1552,1554,1557,1559,1562,1564],{"class":605,"line":740},[603,1546,1384],{"class":693},[603,1548,1549],{"class":697},"responses",[603,1551,939],{"class":701},[603,1553,1033],{"class":693},[603,1555,1556],{"class":729}," 200",[603,1558,715],{"class":693},[603,1560,1561],{"class":701}," productResponse ",[603,1563,849],{"class":693},[603,1565,1449],{"class":701},[603,1567,1568,1570,1572,1574,1576,1579,1581,1584,1586,1589,1591,1593,1595,1597,1599,1601,1603,1605,1607,1609,1611,1613],{"class":605,"line":766},[603,1569,1384],{"class":693},[603,1571,542],{"class":697},[603,1573,939],{"class":701},[603,1575,1033],{"class":693},[603,1577,1578],{"class":711}," maxAge",[603,1580,715],{"class":693},[603,1582,1583],{"class":729}," 2",[603,1585,721],{"class":693},[603,1587,1588],{"class":711}," staleMaxAge",[603,1590,715],{"class":693},[603,1592,730],{"class":729},[603,1594,721],{"class":693},[603,1596,1233],{"class":711},[603,1598,715],{"class":693},[603,1600,1238],{"class":701},[603,1602,861],{"class":693},[603,1604,1243],{"class":612},[603,1606,861],{"class":693},[603,1608,1248],{"class":701},[603,1610,849],{"class":693},[603,1612,1022],{"class":701},[603,1614,772],{"class":693},[499,1616,1617,1618,1621,1622,1625,1626,1628,1629,1632,1633,1636,1637,1640,1641,1644],{},"A repeat ",[503,1619,1620],{},"GET"," is served from the cache before the controller runs, tagged ",[503,1623,1624],{},"X-Cache: HIT",". Only safe\nmethods (",[503,1627,1620],{},"\u002F",[503,1630,1631],{},"HEAD",") and ",[503,1634,1635],{},"2xx"," JSON responses are cached; within ",[503,1638,1639],{},"staleMaxAge"," past ",[503,1642,1643],{},"maxAge"," a stale\nresponse is served immediately and refreshed in the background.",[1646,1647,1649,1652,1653,1656,1657,567,1659,1662,1663,1665,1666,1301],"callout",{"type":1648},"warning",[558,1650,1651],{},"Authenticated routes."," A scopes-guarded route is cached only when an app-supplied ",[503,1654,1655],{},"principalKey"," resolves\nan identity (folded into the cache key), so a protected response is never served to another principal. List\n",[503,1658,1485],{},[558,1660,1661],{},"after"," your authentication middleware, and provide the key via a ",[503,1664,790],{},":\n",[503,1667,1668],{},"new CacheMiddleware(cache, context, { principalKey: () => auth.subject() })",[515,1670,1672],{"id":1671},"invalidation","Invalidation",[499,1674,1675,1676,1679,1680,1683,1684,1686],{},"TTL expiry is the zero-config backstop. ",[558,1677,1678],{},"Tags"," are the opt-in precision layer: a route's ",[503,1681,1682],{},"cache.tags"," and\na query policy's ",[503,1685,1336],{}," register the entry under each tag, and one call clears every layer registered under\nit.",[520,1688,1690],{"className":684,"code":1689,"language":686,"meta":528,"style":528},"await cache.invalidate(\"product\"); \u002F\u002F drops the cached response AND the cached query result\n",[503,1691,1692],{"__ignoreMap":528},[603,1693,1694,1697,1699,1701,1704,1706,1708,1710,1712,1714,1716],{"class":605,"line":606},[603,1695,1696],{"class":809},"await",[603,1698,1216],{"class":701},[603,1700,1301],{"class":693},[603,1702,1703],{"class":697},"invalidate",[603,1705,939],{"class":701},[603,1707,861],{"class":693},[603,1709,1243],{"class":612},[603,1711,861],{"class":693},[603,1713,1022],{"class":701},[603,1715,1230],{"class":693},[603,1717,1718],{"class":736}," \u002F\u002F drops the cached response AND the cached query result\n",[499,1720,1721,1722,1725],{},"Bind a ",[503,1723,1724],{},"CdnPurgeAdapter"," to also purge an edge cache (Cloudflare Cache-Tags, Fastly surrogate keys) on\ninvalidation; when none is bound the purge is skipped.",[515,1727,1729],{"id":1728},"bounding-the-in-process-tier","Bounding the in-process tier",[499,1731,1732,1733,1735,1736,1739,1740,1743,1744,1747],{},"An in-process ",[503,1734,794],{}," tier grows unbounded on a long-lived process — and entry count alone doesn't\nbound memory (100 entries × 1 MiB each is 100 MiB). Cap it by ",[558,1737,1738],{},"size"," with ",[503,1741,1742],{},"maxBytes"," (the recommended\nout-of-memory guard for caching large values), and\u002For by count with ",[503,1745,1746],{},"maxEntries","; eviction is\nleast-recently-used:",[520,1749,1751],{"className":684,"code":1750,"language":686,"meta":528,"style":528},"new MemoryStorage({ maxBytes: 64_000_000 });\n",[503,1752,1753],{"__ignoreMap":528},[603,1754,1755,1757,1759,1761,1763,1766,1768,1771,1773,1775],{"class":605,"line":606},[603,1756,694],{"class":693},[603,1758,898],{"class":697},[603,1760,939],{"class":701},[603,1762,1033],{"class":693},[603,1764,1765],{"class":711}," maxBytes",[603,1767,715],{"class":693},[603,1769,1770],{"class":729}," 64_000_000",[603,1772,877],{"class":693},[603,1774,1022],{"class":701},[603,1776,772],{"class":693},[499,1778,1779,1780,1783],{},"Bound the ",[558,1781,1782],{},"value"," tier, not the durable tier that holds tag markers — markers are write-only, so an LRU\nbound would evict them first. In a multi-tier setup the markers default into the unbounded durable tier.",[515,1785,1787],{"id":1786},"see-also","See also",[1789,1790,1791,1802,1810],"ul",{},[1792,1793,1794,1798,1799,1801],"li",{},[1795,1796,1797],"a",{"href":166},"Key-value storage"," — the ",[503,1800,509],{}," DI token the tiers bind, and how to swap the driver.",[1792,1803,1804,1806,1807,1809],{},[1795,1805,233],{"href":234}," — the query bus whose results ",[503,1808,566],{}," caches.",[1792,1811,1812,1820,1821,1823],{},[1795,1813,1817],{"href":1814,"rel":1815},"https:\u002F\u002Fgithub.com\u002Fheximon\u002Fheximon\u002Ftree\u002Fmain\u002Fexamples\u002Fflagship",[1816],"nofollow",[558,1818,1819],{},"the flagship app's caching","\n— both consumers over one ",[503,1822,505],{},", with tag invalidation, end-to-end tested.",[1825,1826,1827],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":528,"searchDepth":705,"depth":705,"links":1829},[1830,1831,1832,1834,1835,1837,1839,1840,1841],{"id":517,"depth":705,"text":518},{"id":589,"depth":705,"text":590},{"id":662,"depth":705,"text":1833},"The tier chain — TieredCache",{"id":775,"depth":705,"text":776},{"id":1164,"depth":705,"text":1836},"Caching query results — QueryCacheInterceptor",{"id":1481,"depth":705,"text":1838},"Caching HTTP responses — CacheMiddleware",{"id":1671,"depth":705,"text":1672},{"id":1728,"depth":705,"text":1729},{"id":1786,"depth":705,"text":1787},"Multi-tier caching — TieredCache over the Storage port, the Cache facade with single-flight and tag invalidation, QueryCacheInterceptor for CQRS query results, CacheMiddleware for HTTP responses, CacheEnvelope TTL, CdnPurgeAdapter.","md",null,{},{"icon":85},{"title":161,"description":1842},"SM3rIDQ9nKeDzQepfTKb3QBoXwizUQpvH6OUCNm3Vko",[1850,1852],{"title":156,"path":157,"stem":158,"description":1851,"icon":159,"children":-1},"Run work on a wall-clock cron schedule with ScheduledHandler — a process-local timer on Node, the platform cron trigger on Cloudflare, validated at build time.",{"title":165,"path":166,"stem":167,"description":1853,"icon":168,"children":-1},"The abstract Storage DI token and its BatchStorage capability, MemoryStorage with TTL and LRU bounds, and the first-class UpstashStorage, CloudflareKvStorage, RedisStorage, and UnstorageStorage adapters.",1783300761903]