[{"data":1,"prerenderedAt":2656},["ShallowReactive",2],{"navigation":3,"\u002Fdocs\u002Fcore-concepts\u002Fdecorators\u002Fcustom":176,"\u002Fdocs\u002Fcore-concepts\u002Fdecorators\u002Fcustom-surround":2651},[4,21,86,158],{"title":5,"path":6,"stem":7,"children":8,"status":11,"icon":20},"Getting Started","\u002Fdocs\u002Fgetting-started","1.docs\u002F1.getting-started\u002F1.index",[9,12,16],{"title":10,"path":6,"stem":7,"status":11},"Introduction",null,{"title":13,"path":14,"stem":15,"status":11},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","1.docs\u002F1.getting-started\u002F2.installation",{"title":17,"path":18,"stem":19,"status":11},"Quick Start","\u002Fdocs\u002Fgetting-started\u002Fquick-start","1.docs\u002F1.getting-started\u002F3.quick-start",false,{"title":22,"path":23,"stem":24,"children":25,"status":11,"icon":20},"Core Concepts","\u002Fdocs\u002Fcore-concepts","1.docs\u002F2.core-concepts\u002F1.index",[26,28,32,36,46,50,54,58,62,66,70,74,78,82],{"title":27,"path":23,"stem":24,"status":11},"Overview",{"title":29,"path":30,"stem":31,"status":11},"Response","\u002Fdocs\u002Fcore-concepts\u002Fresponse","1.docs\u002F2.core-concepts\u002F10.response",{"title":33,"path":34,"stem":35,"status":11},"Testing","\u002Fdocs\u002Fcore-concepts\u002Ftesting","1.docs\u002F2.core-concepts\u002F12.testing",{"title":37,"path":38,"stem":39,"children":40,"status":11,"icon":20,"defaultOpen":20},"Decorators","\u002Fdocs\u002Fcore-concepts\u002Fdecorators","1.docs\u002F2.core-concepts\u002F13.decorators\u002F1.index",[41,42],{"title":27,"path":38,"stem":39,"status":11},{"title":43,"path":44,"stem":45,"status":11},"Custom","\u002Fdocs\u002Fcore-concepts\u002Fdecorators\u002Fcustom","1.docs\u002F2.core-concepts\u002F13.decorators\u002F2.custom",{"title":47,"path":48,"stem":49,"status":11},"Discovery Service","\u002Fdocs\u002Fcore-concepts\u002Fdiscovery","1.docs\u002F2.core-concepts\u002F14.discovery",{"title":51,"path":52,"stem":53,"status":11},"Application Lifecycle","\u002Fdocs\u002Fcore-concepts\u002Fapp-lifecycle","1.docs\u002F2.core-concepts\u002F15.app-lifecycle",{"title":55,"path":56,"stem":57,"status":11},"Controllers","\u002Fdocs\u002Fcore-concepts\u002Fcontrollers","1.docs\u002F2.core-concepts\u002F2.controllers",{"title":59,"path":60,"stem":61,"status":11},"Routing","\u002Fdocs\u002Fcore-concepts\u002Frouting","1.docs\u002F2.core-concepts\u002F3.routing",{"title":63,"path":64,"stem":65,"status":11},"Providers","\u002Fdocs\u002Fcore-concepts\u002Fproviders","1.docs\u002F2.core-concepts\u002F4.providers",{"title":67,"path":68,"stem":69,"status":11},"Modules","\u002Fdocs\u002Fcore-concepts\u002Fmodules","1.docs\u002F2.core-concepts\u002F5.modules",{"title":71,"path":72,"stem":73,"status":11},"Configuration","\u002Fdocs\u002Fcore-concepts\u002Fconfiguration","1.docs\u002F2.core-concepts\u002F6.configuration",{"title":75,"path":76,"stem":77,"status":11},"Middleware","\u002Fdocs\u002Fcore-concepts\u002Fmiddleware","1.docs\u002F2.core-concepts\u002F7.middleware",{"title":79,"path":80,"stem":81,"status":11},"Guards","\u002Fdocs\u002Fcore-concepts\u002Fguards","1.docs\u002F2.core-concepts\u002F8.guards",{"title":83,"path":84,"stem":85,"status":11},"Exceptions","\u002Fdocs\u002Fcore-concepts\u002Fexceptions","1.docs\u002F2.core-concepts\u002F9.exceptions",{"title":87,"path":88,"stem":89,"children":90,"status":11,"icon":20},"Packages","\u002Fdocs\u002Fpackages","1.docs\u002F3.packages\u002F1.index",[91,92,97,108,112,130,134,138,142,146,150,154],{"title":27,"path":88,"stem":89,"status":11},{"title":93,"path":94,"stem":95,"status":96},"CLI","\u002Fdocs\u002Fpackages\u002Fcli","1.docs\u002F3.packages\u002F10.cli","experimental",{"title":98,"path":99,"stem":100,"children":101,"status":11,"icon":20,"defaultOpen":20},"Events","\u002Fdocs\u002Fpackages\u002Fmessaging","1.docs\u002F3.packages\u002F11.messaging\u002F1.index",[102,104],{"title":27,"path":99,"stem":100,"status":103},"beta",{"title":105,"path":106,"stem":107,"status":96},"Redis","\u002Fdocs\u002Fpackages\u002Fmessaging\u002Fredis","1.docs\u002F3.packages\u002F11.messaging\u002F2.redis",{"title":109,"path":110,"stem":111,"status":103},"Serve Static","\u002Fdocs\u002Fpackages\u002Fserve-static","1.docs\u002F3.packages\u002F12.serve-static",{"title":113,"path":114,"stem":115,"children":116,"status":11,"icon":20,"defaultOpen":20},"Auth","\u002Fdocs\u002Fpackages\u002Fauth","1.docs\u002F3.packages\u002F2.auth\u002F1.index",[117,118,122,126],{"title":27,"path":114,"stem":115,"status":103},{"title":119,"path":120,"stem":121,"status":103},"JWT Provider","\u002Fdocs\u002Fpackages\u002Fauth\u002Fjwt","1.docs\u002F3.packages\u002F2.auth\u002F2.jwt",{"title":123,"path":124,"stem":125,"status":103},"Local Provider","\u002Fdocs\u002Fpackages\u002Fauth\u002Flocal","1.docs\u002F3.packages\u002F2.auth\u002F3.local",{"title":127,"path":128,"stem":129,"status":96},"OAuth2 Provider","\u002Fdocs\u002Fpackages\u002Fauth\u002Foauth2","1.docs\u002F3.packages\u002F2.auth\u002F4.oauth2",{"title":131,"path":132,"stem":133,"status":103},"JWT","\u002Fdocs\u002Fpackages\u002Fjwt","1.docs\u002F3.packages\u002F3.jwt",{"title":135,"path":136,"stem":137,"status":103},"Drizzle","\u002Fdocs\u002Fpackages\u002Fdrizzle","1.docs\u002F3.packages\u002F4.drizzle",{"title":139,"path":140,"stem":141,"status":103},"Papr","\u002Fdocs\u002Fpackages\u002Fpapr","1.docs\u002F3.packages\u002F5.papr",{"title":143,"path":144,"stem":145,"status":103},"Mongoose","\u002Fdocs\u002Fpackages\u002Fmongoose","1.docs\u002F3.packages\u002F6.mongoose",{"title":147,"path":148,"stem":149,"status":103},"Swagger","\u002Fdocs\u002Fpackages\u002Fswagger","1.docs\u002F3.packages\u002F7.swagger",{"title":151,"path":152,"stem":153,"status":103},"Node Server","\u002Fdocs\u002Fpackages\u002Fnode-server","1.docs\u002F3.packages\u002F8.node-server",{"title":155,"path":156,"stem":157,"status":103},"uWS Server","\u002Fdocs\u002Fpackages\u002Fuws-server","1.docs\u002F3.packages\u002F9.uws-server",{"title":159,"path":160,"stem":161,"children":162,"status":11,"icon":20},"Roadmap","\u002Fdocs\u002Froadmap","1.docs\u002F4.roadmap\u002F1.index",[163,164,168,172],{"title":27,"path":160,"stem":161,"status":11},{"title":165,"path":166,"stem":167,"status":11},"Short term (0-3 months)","\u002Fdocs\u002Froadmap\u002Fshort-term","1.docs\u002F4.roadmap\u002F2.short-term",{"title":169,"path":170,"stem":171,"status":11},"Mid term (3-9 months)","\u002Fdocs\u002Froadmap\u002Fmid-term","1.docs\u002F4.roadmap\u002F3.mid-term",{"title":173,"path":174,"stem":175,"status":11},"Long term (9-12+ months)","\u002Fdocs\u002Froadmap\u002Flong-term","1.docs\u002F4.roadmap\u002F4.long-term",{"id":177,"title":43,"body":178,"description":2646,"extension":2647,"meta":2648,"navigation":620,"path":44,"seo":2649,"status":11,"stem":45,"__hash__":2650},"docs\u002F1.docs\u002F2.core-concepts\u002F13.decorators\u002F2.custom.md",{"type":179,"value":180,"toc":2627},"minimark",[181,189,194,218,231,235,309,418,421,425,436,474,477,563,577,581,584,746,749,808,824,840,844,851,1005,1007,1141,1156,1172,1176,1184,1406,1409,1543,1552,1559,1565,1745,1748,1834,1839,1877,1887,1891,1928,1932,1947,1951,1963,1967,1977,2215,2218,2313,2319,2323,2329,2488,2501,2505,2508,2541,2555,2559,2623],[182,183,184,188],"p",{},[185,186,187],"code",{},"@miiajs\u002Fcore"," exposes the same factory API that the framework uses internally. Four factories cover every decorator shape you need; five metadata helpers cover every storage shape. You never touch the raw TC39 decorator signatures yourself - the factories wrap them and forward your handler the arguments you declared.",[190,191,193],"h2",{"id":192},"when-to-reach-for-a-custom-decorator","When to reach for a custom decorator",[195,196,197,212,215],"ul",{},[198,199,200,201,204,205,204,208,211],"li",{},"Tag controllers or routes with domain-specific metadata (",[185,202,203],{},"@Cache(60)",", ",[185,206,207],{},"@RateLimit(100)",[185,209,210],{},"@Audit('user.delete')",").",[198,213,214],{},"Register a method with an external system at load time (a scheduled job, an event handler, an RPC endpoint).",[198,216,217],{},"Expose a declarative knob that some middleware or guard reads at runtime.",[182,219,220,221,225,226,230],{},"The rule of thumb: ",[222,223,224],"strong",{},"decorators attach data, middleware does things",". If the behaviour is a request\u002Fresponse transformation, write ",[227,228,229],"a",{"href":76},"middleware"," instead.",[190,232,234],{"id":233},"the-four-factories","The four factories",[236,237,242],"pre",{"className":238,"code":239,"language":240,"meta":241,"style":241},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import {\n  createClassDecorator,\n  createMethodDecorator,\n  createFieldDecorator,\n  createDecorator,\n} from '@miiajs\u002Fcore'\n","typescript","",[185,243,244,257,267,275,283,291],{"__ignoreMap":241},[245,246,249,253],"span",{"class":247,"line":248},"line",1,[245,250,252],{"class":251},"s7zQu","import",[245,254,256],{"class":255},"sMK4o"," {\n",[245,258,260,264],{"class":247,"line":259},2,[245,261,263],{"class":262},"sTEyZ","  createClassDecorator",[245,265,266],{"class":255},",\n",[245,268,270,273],{"class":247,"line":269},3,[245,271,272],{"class":262},"  createMethodDecorator",[245,274,266],{"class":255},[245,276,278,281],{"class":247,"line":277},4,[245,279,280],{"class":262},"  createFieldDecorator",[245,282,266],{"class":255},[245,284,286,289],{"class":247,"line":285},5,[245,287,288],{"class":262},"  createDecorator",[245,290,266],{"class":255},[245,292,294,297,300,303,306],{"class":247,"line":293},6,[245,295,296],{"class":255},"}",[245,298,299],{"class":251}," from",[245,301,302],{"class":255}," '",[245,304,187],{"class":305},"sfazB",[245,307,308],{"class":255},"'\n",[310,311,312,328],"table",{},[313,314,315],"thead",{},[316,317,318,322,325],"tr",{},[319,320,321],"th",{},"Factory",[319,323,324],{},"Applies to",[319,326,327],{},"Built-in example",[329,330,331,353,377,390],"tbody",{},[316,332,333,339,342],{},[334,335,336],"td",{},[185,337,338],{},"createClassDecorator",[334,340,341],{},"Classes",[334,343,344,204,347,204,350],{},[185,345,346],{},"@Injectable",[185,348,349],{},"@Controller",[185,351,352],{},"@ApiTag",[316,354,355,360,363],{},[334,356,357],{},[185,358,359],{},"createMethodDecorator",[334,361,362],{},"Methods",[334,364,365,204,368,204,371,204,374],{},[185,366,367],{},"@Get",[185,369,370],{},"@Status",[185,372,373],{},"@ValidateBody",[185,375,376],{},"@ApiOperation",[316,378,379,384,387],{},[334,380,381],{},[185,382,383],{},"createFieldDecorator",[334,385,386],{},"Class fields",[334,388,389],{},"(rarely used - see below)",[316,391,392,397,404],{},[334,393,394],{},[185,395,396],{},"createDecorator",[334,398,399,400,403],{},"Class ",[222,401,402],{},"or"," method",[334,405,406,204,409,204,412,204,415],{},[185,407,408],{},"@Use",[185,410,411],{},"@UseGuard",[185,413,414],{},"@SkipGuard",[185,416,417],{},"@ApiSecurity",[182,419,420],{},"Each factory takes a handler that receives the decorator context (plus any arguments you declared) and returns a ready-to-use decorator function.",[190,422,424],{"id":423},"metadata-helpers","Metadata helpers",[182,426,427,428,431,432,435],{},"Use a ",[185,429,430],{},"Symbol"," for the storage key - string keys would collide with user-land properties on ",[185,433,434],{},"Symbol.metadata",":",[236,437,439],{"className":238,"code":438,"language":240,"meta":241,"style":241},"export const CACHE = Symbol('cache')\n",[185,440,441],{"__ignoreMap":241},[245,442,443,446,450,453,456,460,463,466,469,471],{"class":247,"line":248},[245,444,445],{"class":251},"export",[245,447,449],{"class":448},"spNyl"," const",[245,451,452],{"class":262}," CACHE ",[245,454,455],{"class":255},"=",[245,457,459],{"class":458},"s2Zo4"," Symbol",[245,461,462],{"class":262},"(",[245,464,465],{"class":255},"'",[245,467,468],{"class":305},"cache",[245,470,465],{"class":255},[245,472,473],{"class":262},")\n",[182,475,476],{},"Then pick the helper that matches the shape you need:",[310,478,479,492],{},[313,480,481],{},[316,482,483,486,489],{},[319,484,485],{},"Helper",[319,487,488],{},"Shape",[319,490,491],{},"Use case",[329,493,494,507,520,533,548],{},[316,495,496,501,504],{},[334,497,498],{},[185,499,500],{},"setMeta(meta, key, value)",[334,502,503],{},"scalar",[334,505,506],{},"One value per class (e.g. route prefix)",[316,508,509,514,517],{},[334,510,511],{},[185,512,513],{},"getMeta\u003CT>(ctor, key)",[334,515,516],{},"scalar read",[334,518,519],{},"Read back from the constructor",[316,521,522,527,530],{},[334,523,524],{},[185,525,526],{},"pushMeta(meta, key, item)",[334,528,529],{},"array",[334,531,532],{},"Append-only list (e.g. all routes on a controller)",[316,534,535,540,545],{},[334,536,537],{},[185,538,539],{},"addToMapMeta(meta, key, name, items)",[334,541,542],{},[185,543,544],{},"Map\u003Cstring, T[]>",[334,546,547],{},"Per-method list (e.g. middlewares per handler)",[316,549,550,555,560],{},[334,551,552],{},[185,553,554],{},"setInMapMeta(meta, key, name, value)",[334,556,557],{},[185,558,559],{},"Map\u003Cstring, T>",[334,561,562],{},"Per-method single value (e.g. status code per handler)",[182,564,565,566,569,570,573,574,576],{},"All write helpers expect the ",[185,567,568],{},"context.metadata"," object inside a handler. ",[185,571,572],{},"getMeta"," takes the class itself and reads ",[185,575,434],{}," later.",[190,578,580],{"id":579},"walkthrough-class-decorator","Walkthrough: class decorator",[182,582,583],{},"A minimal example - tag a controller with a feature flag:",[236,585,587],{"className":238,"code":586,"language":240,"meta":241,"style":241},"import { createClassDecorator, setMeta } from '@miiajs\u002Fcore'\n\nexport const FEATURE = Symbol('feature')\n\nexport const Feature = createClassDecorator\u003C[name: string]>((_target, context, name) => {\n  setMeta(context.metadata!, FEATURE, name)\n})\n",[185,588,589,616,622,646,650,710,739],{"__ignoreMap":241},[245,590,591,593,596,599,602,605,608,610,612,614],{"class":247,"line":248},[245,592,252],{"class":251},[245,594,595],{"class":255}," {",[245,597,598],{"class":262}," createClassDecorator",[245,600,601],{"class":255},",",[245,603,604],{"class":262}," setMeta",[245,606,607],{"class":255}," }",[245,609,299],{"class":251},[245,611,302],{"class":255},[245,613,187],{"class":305},[245,615,308],{"class":255},[245,617,618],{"class":247,"line":259},[245,619,621],{"emptyLinePlaceholder":620},true,"\n",[245,623,624,626,628,631,633,635,637,639,642,644],{"class":247,"line":269},[245,625,445],{"class":251},[245,627,449],{"class":448},[245,629,630],{"class":262}," FEATURE ",[245,632,455],{"class":255},[245,634,459],{"class":458},[245,636,462],{"class":262},[245,638,465],{"class":255},[245,640,641],{"class":305},"feature",[245,643,465],{"class":255},[245,645,473],{"class":262},[245,647,648],{"class":247,"line":277},[245,649,621],{"emptyLinePlaceholder":620},[245,651,652,654,656,659,661,663,666,669,673,675,678,681,684,686,688,692,694,697,699,702,705,708],{"class":247,"line":285},[245,653,445],{"class":251},[245,655,449],{"class":448},[245,657,658],{"class":262}," Feature ",[245,660,455],{"class":255},[245,662,598],{"class":458},[245,664,665],{"class":255},"\u003C",[245,667,668],{"class":262},"[",[245,670,672],{"class":671},"sBMFI","name",[245,674,435],{"class":255},[245,676,677],{"class":671}," string",[245,679,680],{"class":262},"]",[245,682,683],{"class":255},">",[245,685,462],{"class":262},[245,687,462],{"class":255},[245,689,691],{"class":690},"sHdIc","_target",[245,693,601],{"class":255},[245,695,696],{"class":690}," context",[245,698,601],{"class":255},[245,700,701],{"class":690}," name",[245,703,704],{"class":255},")",[245,706,707],{"class":448}," =>",[245,709,256],{"class":255},[245,711,712,715,718,721,724,727,730,733,735,737],{"class":247,"line":293},[245,713,714],{"class":458},"  setMeta",[245,716,462],{"class":717},"swJcz",[245,719,720],{"class":262},"context",[245,722,723],{"class":255},".",[245,725,726],{"class":262},"metadata",[245,728,729],{"class":255},"!,",[245,731,732],{"class":262}," FEATURE",[245,734,601],{"class":255},[245,736,701],{"class":262},[245,738,473],{"class":717},[245,740,742,744],{"class":247,"line":741},7,[245,743,296],{"class":255},[245,745,473],{"class":262},[182,747,748],{},"Usage:",[236,750,752],{"className":238,"code":751,"language":240,"meta":241,"style":241},"@Feature('billing')\n@Controller('\u002Finvoices')\nclass InvoiceController { \u002F* ... *\u002F }\n",[185,753,754,773,791],{"__ignoreMap":241},[245,755,756,759,762,764,766,769,771],{"class":247,"line":248},[245,757,758],{"class":255},"@",[245,760,761],{"class":458},"Feature",[245,763,462],{"class":262},[245,765,465],{"class":255},[245,767,768],{"class":305},"billing",[245,770,465],{"class":255},[245,772,473],{"class":262},[245,774,775,777,780,782,784,787,789],{"class":247,"line":259},[245,776,758],{"class":255},[245,778,779],{"class":458},"Controller",[245,781,462],{"class":262},[245,783,465],{"class":255},[245,785,786],{"class":305},"\u002Finvoices",[245,788,465],{"class":255},[245,790,473],{"class":262},[245,792,793,796,799,801,805],{"class":247,"line":269},[245,794,795],{"class":448},"class",[245,797,798],{"class":671}," InvoiceController",[245,800,595],{"class":255},[245,802,804],{"class":803},"sHwdD"," \u002F* ... *\u002F",[245,806,807],{"class":255}," }\n",[182,809,810,811,814,815,817,818,821,822,723],{},"The generic ",[185,812,813],{},"\u003C[name: string]>"," declares the decorator's arguments. ",[185,816,338],{}," forwards them to the handler after ",[185,819,820],{},"target"," and ",[185,823,720],{},[825,826,827],"blockquote",{},[182,828,829,836,837,839],{},[222,830,831,832,835],{},"Why ",[185,833,834],{},"context.metadata!","?"," TC39 types ",[185,838,568],{}," as optionally undefined because decorators can theoretically be applied to non-class targets. In MiiaJS it's always defined - the polyfill guarantees it - so the non-null assertion is idiomatic and safe inside a handler.",[190,841,843],{"id":842},"walkthrough-method-decorator","Walkthrough: method decorator",[182,845,846,847,850],{},"A ",[185,848,849],{},"@Cache(ttl)"," decorator that records a TTL per method:",[236,852,854],{"className":238,"code":853,"language":240,"meta":241,"style":241},"import { createMethodDecorator, setInMapMeta } from '@miiajs\u002Fcore'\n\nexport const CACHE = Symbol('cache')\n\nexport const Cache = createMethodDecorator\u003C[ttlSeconds: number]>((_target, context, ttlSeconds) => {\n  setInMapMeta(context.metadata!, CACHE, String(context.name), ttlSeconds)\n})\n",[185,855,856,880,884,906,910,960,999],{"__ignoreMap":241},[245,857,858,860,862,865,867,870,872,874,876,878],{"class":247,"line":248},[245,859,252],{"class":251},[245,861,595],{"class":255},[245,863,864],{"class":262}," createMethodDecorator",[245,866,601],{"class":255},[245,868,869],{"class":262}," setInMapMeta",[245,871,607],{"class":255},[245,873,299],{"class":251},[245,875,302],{"class":255},[245,877,187],{"class":305},[245,879,308],{"class":255},[245,881,882],{"class":247,"line":259},[245,883,621],{"emptyLinePlaceholder":620},[245,885,886,888,890,892,894,896,898,900,902,904],{"class":247,"line":269},[245,887,445],{"class":251},[245,889,449],{"class":448},[245,891,452],{"class":262},[245,893,455],{"class":255},[245,895,459],{"class":458},[245,897,462],{"class":262},[245,899,465],{"class":255},[245,901,468],{"class":305},[245,903,465],{"class":255},[245,905,473],{"class":262},[245,907,908],{"class":247,"line":277},[245,909,621],{"emptyLinePlaceholder":620},[245,911,912,914,916,919,921,923,925,927,930,932,935,937,939,941,943,945,947,949,951,954,956,958],{"class":247,"line":285},[245,913,445],{"class":251},[245,915,449],{"class":448},[245,917,918],{"class":262}," Cache ",[245,920,455],{"class":255},[245,922,864],{"class":458},[245,924,665],{"class":255},[245,926,668],{"class":262},[245,928,929],{"class":671},"ttlSeconds",[245,931,435],{"class":255},[245,933,934],{"class":671}," number",[245,936,680],{"class":262},[245,938,683],{"class":255},[245,940,462],{"class":262},[245,942,462],{"class":255},[245,944,691],{"class":690},[245,946,601],{"class":255},[245,948,696],{"class":690},[245,950,601],{"class":255},[245,952,953],{"class":690}," ttlSeconds",[245,955,704],{"class":255},[245,957,707],{"class":448},[245,959,256],{"class":255},[245,961,962,965,967,969,971,973,975,978,980,983,985,987,989,991,993,995,997],{"class":247,"line":293},[245,963,964],{"class":458},"  setInMapMeta",[245,966,462],{"class":717},[245,968,720],{"class":262},[245,970,723],{"class":255},[245,972,726],{"class":262},[245,974,729],{"class":255},[245,976,977],{"class":262}," CACHE",[245,979,601],{"class":255},[245,981,982],{"class":458}," String",[245,984,462],{"class":717},[245,986,720],{"class":262},[245,988,723],{"class":255},[245,990,672],{"class":262},[245,992,704],{"class":717},[245,994,601],{"class":255},[245,996,953],{"class":262},[245,998,473],{"class":717},[245,1000,1001,1003],{"class":247,"line":741},[245,1002,296],{"class":255},[245,1004,473],{"class":262},[182,1006,748],{},[236,1008,1010],{"className":238,"code":1009,"language":240,"meta":241,"style":241},"@Controller('\u002Fproducts')\nclass ProductController {\n  @Get('\u002F')\n  @Cache(60)\n  list() { \u002F* ... *\u002F }\n\n  @Get('\u002F:id')\n  @Cache(300)\n  findOne() { \u002F* ... *\u002F }\n}\n",[185,1011,1012,1029,1038,1057,1072,1086,1090,1107,1121,1135],{"__ignoreMap":241},[245,1013,1014,1016,1018,1020,1022,1025,1027],{"class":247,"line":248},[245,1015,758],{"class":255},[245,1017,779],{"class":458},[245,1019,462],{"class":262},[245,1021,465],{"class":255},[245,1023,1024],{"class":305},"\u002Fproducts",[245,1026,465],{"class":255},[245,1028,473],{"class":262},[245,1030,1031,1033,1036],{"class":247,"line":259},[245,1032,795],{"class":448},[245,1034,1035],{"class":671}," ProductController",[245,1037,256],{"class":255},[245,1039,1040,1043,1046,1048,1050,1053,1055],{"class":247,"line":269},[245,1041,1042],{"class":255},"  @",[245,1044,1045],{"class":458},"Get",[245,1047,462],{"class":262},[245,1049,465],{"class":255},[245,1051,1052],{"class":305},"\u002F",[245,1054,465],{"class":255},[245,1056,473],{"class":262},[245,1058,1059,1061,1064,1066,1070],{"class":247,"line":277},[245,1060,1042],{"class":255},[245,1062,1063],{"class":458},"Cache",[245,1065,462],{"class":262},[245,1067,1069],{"class":1068},"sbssI","60",[245,1071,473],{"class":262},[245,1073,1074,1077,1080,1082,1084],{"class":247,"line":285},[245,1075,1076],{"class":717},"  list",[245,1078,1079],{"class":255},"()",[245,1081,595],{"class":255},[245,1083,804],{"class":803},[245,1085,807],{"class":255},[245,1087,1088],{"class":247,"line":293},[245,1089,621],{"emptyLinePlaceholder":620},[245,1091,1092,1094,1096,1098,1100,1103,1105],{"class":247,"line":741},[245,1093,1042],{"class":255},[245,1095,1045],{"class":458},[245,1097,462],{"class":262},[245,1099,465],{"class":255},[245,1101,1102],{"class":305},"\u002F:id",[245,1104,465],{"class":255},[245,1106,473],{"class":262},[245,1108,1110,1112,1114,1116,1119],{"class":247,"line":1109},8,[245,1111,1042],{"class":255},[245,1113,1063],{"class":458},[245,1115,462],{"class":262},[245,1117,1118],{"class":1068},"300",[245,1120,473],{"class":262},[245,1122,1124,1127,1129,1131,1133],{"class":247,"line":1123},9,[245,1125,1126],{"class":717},"  findOne",[245,1128,1079],{"class":255},[245,1130,595],{"class":255},[245,1132,804],{"class":803},[245,1134,807],{"class":255},[245,1136,1138],{"class":247,"line":1137},10,[245,1139,1140],{"class":255},"}\n",[182,1142,1143,1146,1147,1150,1151,821,1153,1155],{},[185,1144,1145],{},"setInMapMeta"," stores one value per method name. Use ",[185,1148,1149],{},"addToMapMeta"," instead when a decorator can be applied multiple times and you need the accumulated list - that's how ",[185,1152,408],{},[185,1154,411],{}," work internally.",[182,1157,1158,1159,1161,1162,1164,1165,1167,1168,1171],{},"A more complex example from the framework itself: ",[185,1160,373],{}," writes both a schema (via ",[185,1163,1145],{},") and a middleware (via ",[185,1166,1149],{},") in a single handler - see ",[185,1169,1170],{},"packages\u002Fcore\u002Fsrc\u002Fdecorators\u002Fmiddleware.ts"," for the source.",[190,1173,1175],{"id":1174},"walkthrough-dual-classmethod-decorator","Walkthrough: dual class\u002Fmethod decorator",[182,1177,1178,1180,1181,435],{},[185,1179,396],{}," handles decorators that can be stacked on either a class or an individual method. The handler receives a union context, so you branch on ",[185,1182,1183],{},"context.kind",[236,1185,1187],{"className":238,"code":1186,"language":240,"meta":241,"style":241},"import { createDecorator, setMeta, setInMapMeta } from '@miiajs\u002Fcore'\n\nexport const RATE_LIMIT = Symbol('rateLimit')\n\nexport const RateLimit = createDecorator\u003C[perMinute: number]>((context, perMinute) => {\n  if (context.kind === 'class') {\n    setMeta(context.metadata!, RATE_LIMIT, perMinute)\n  } else {\n    setInMapMeta(context.metadata!, RATE_LIMIT, String(context.name), perMinute)\n  }\n})\n",[185,1188,1189,1216,1220,1244,1248,1293,1323,1347,1357,1394,1399],{"__ignoreMap":241},[245,1190,1191,1193,1195,1198,1200,1202,1204,1206,1208,1210,1212,1214],{"class":247,"line":248},[245,1192,252],{"class":251},[245,1194,595],{"class":255},[245,1196,1197],{"class":262}," createDecorator",[245,1199,601],{"class":255},[245,1201,604],{"class":262},[245,1203,601],{"class":255},[245,1205,869],{"class":262},[245,1207,607],{"class":255},[245,1209,299],{"class":251},[245,1211,302],{"class":255},[245,1213,187],{"class":305},[245,1215,308],{"class":255},[245,1217,1218],{"class":247,"line":259},[245,1219,621],{"emptyLinePlaceholder":620},[245,1221,1222,1224,1226,1229,1231,1233,1235,1237,1240,1242],{"class":247,"line":269},[245,1223,445],{"class":251},[245,1225,449],{"class":448},[245,1227,1228],{"class":262}," RATE_LIMIT ",[245,1230,455],{"class":255},[245,1232,459],{"class":458},[245,1234,462],{"class":262},[245,1236,465],{"class":255},[245,1238,1239],{"class":305},"rateLimit",[245,1241,465],{"class":255},[245,1243,473],{"class":262},[245,1245,1246],{"class":247,"line":277},[245,1247,621],{"emptyLinePlaceholder":620},[245,1249,1250,1252,1254,1257,1259,1261,1263,1265,1268,1270,1272,1274,1276,1278,1280,1282,1284,1287,1289,1291],{"class":247,"line":285},[245,1251,445],{"class":251},[245,1253,449],{"class":448},[245,1255,1256],{"class":262}," RateLimit ",[245,1258,455],{"class":255},[245,1260,1197],{"class":458},[245,1262,665],{"class":255},[245,1264,668],{"class":262},[245,1266,1267],{"class":671},"perMinute",[245,1269,435],{"class":255},[245,1271,934],{"class":671},[245,1273,680],{"class":262},[245,1275,683],{"class":255},[245,1277,462],{"class":262},[245,1279,462],{"class":255},[245,1281,720],{"class":690},[245,1283,601],{"class":255},[245,1285,1286],{"class":690}," perMinute",[245,1288,704],{"class":255},[245,1290,707],{"class":448},[245,1292,256],{"class":255},[245,1294,1295,1298,1301,1303,1305,1308,1311,1313,1315,1317,1320],{"class":247,"line":293},[245,1296,1297],{"class":251},"  if",[245,1299,1300],{"class":717}," (",[245,1302,720],{"class":262},[245,1304,723],{"class":255},[245,1306,1307],{"class":262},"kind",[245,1309,1310],{"class":255}," ===",[245,1312,302],{"class":255},[245,1314,795],{"class":305},[245,1316,465],{"class":255},[245,1318,1319],{"class":717},") ",[245,1321,1322],{"class":255},"{\n",[245,1324,1325,1328,1330,1332,1334,1336,1338,1341,1343,1345],{"class":247,"line":741},[245,1326,1327],{"class":458},"    setMeta",[245,1329,462],{"class":717},[245,1331,720],{"class":262},[245,1333,723],{"class":255},[245,1335,726],{"class":262},[245,1337,729],{"class":255},[245,1339,1340],{"class":262}," RATE_LIMIT",[245,1342,601],{"class":255},[245,1344,1286],{"class":262},[245,1346,473],{"class":717},[245,1348,1349,1352,1355],{"class":247,"line":1109},[245,1350,1351],{"class":255},"  }",[245,1353,1354],{"class":251}," else",[245,1356,256],{"class":255},[245,1358,1359,1362,1364,1366,1368,1370,1372,1374,1376,1378,1380,1382,1384,1386,1388,1390,1392],{"class":247,"line":1123},[245,1360,1361],{"class":458},"    setInMapMeta",[245,1363,462],{"class":717},[245,1365,720],{"class":262},[245,1367,723],{"class":255},[245,1369,726],{"class":262},[245,1371,729],{"class":255},[245,1373,1340],{"class":262},[245,1375,601],{"class":255},[245,1377,982],{"class":458},[245,1379,462],{"class":717},[245,1381,720],{"class":262},[245,1383,723],{"class":255},[245,1385,672],{"class":262},[245,1387,704],{"class":717},[245,1389,601],{"class":255},[245,1391,1286],{"class":262},[245,1393,473],{"class":717},[245,1395,1396],{"class":247,"line":1137},[245,1397,1398],{"class":255},"  }\n",[245,1400,1402,1404],{"class":247,"line":1401},11,[245,1403,296],{"class":255},[245,1405,473],{"class":262},[182,1407,1408],{},"Now both forms work, and a method-level value overrides the class-level default:",[236,1410,1412],{"className":238,"code":1411,"language":240,"meta":241,"style":241},"@Controller('\u002Fapi')\n@RateLimit(100)           \u002F\u002F 100 rpm for the whole controller\nclass ApiController {\n  @Get('\u002Fstatus')\n  status() { \u002F* ... *\u002F }\n\n  @Post('\u002Fexpensive')\n  @RateLimit(5)           \u002F\u002F 5 rpm - overrides the controller default\n  expensive() { \u002F* ... *\u002F }\n}\n",[185,1413,1414,1431,1449,1458,1475,1488,1492,1510,1526,1539],{"__ignoreMap":241},[245,1415,1416,1418,1420,1422,1424,1427,1429],{"class":247,"line":248},[245,1417,758],{"class":255},[245,1419,779],{"class":458},[245,1421,462],{"class":262},[245,1423,465],{"class":255},[245,1425,1426],{"class":305},"\u002Fapi",[245,1428,465],{"class":255},[245,1430,473],{"class":262},[245,1432,1433,1435,1438,1440,1443,1446],{"class":247,"line":259},[245,1434,758],{"class":255},[245,1436,1437],{"class":458},"RateLimit",[245,1439,462],{"class":262},[245,1441,1442],{"class":1068},"100",[245,1444,1445],{"class":262},")           ",[245,1447,1448],{"class":803},"\u002F\u002F 100 rpm for the whole controller\n",[245,1450,1451,1453,1456],{"class":247,"line":269},[245,1452,795],{"class":448},[245,1454,1455],{"class":671}," ApiController",[245,1457,256],{"class":255},[245,1459,1460,1462,1464,1466,1468,1471,1473],{"class":247,"line":277},[245,1461,1042],{"class":255},[245,1463,1045],{"class":458},[245,1465,462],{"class":262},[245,1467,465],{"class":255},[245,1469,1470],{"class":305},"\u002Fstatus",[245,1472,465],{"class":255},[245,1474,473],{"class":262},[245,1476,1477,1480,1482,1484,1486],{"class":247,"line":285},[245,1478,1479],{"class":717},"  status",[245,1481,1079],{"class":255},[245,1483,595],{"class":255},[245,1485,804],{"class":803},[245,1487,807],{"class":255},[245,1489,1490],{"class":247,"line":293},[245,1491,621],{"emptyLinePlaceholder":620},[245,1493,1494,1496,1499,1501,1503,1506,1508],{"class":247,"line":741},[245,1495,1042],{"class":255},[245,1497,1498],{"class":458},"Post",[245,1500,462],{"class":262},[245,1502,465],{"class":255},[245,1504,1505],{"class":305},"\u002Fexpensive",[245,1507,465],{"class":255},[245,1509,473],{"class":262},[245,1511,1512,1514,1516,1518,1521,1523],{"class":247,"line":1109},[245,1513,1042],{"class":255},[245,1515,1437],{"class":458},[245,1517,462],{"class":262},[245,1519,1520],{"class":1068},"5",[245,1522,1445],{"class":262},[245,1524,1525],{"class":803},"\u002F\u002F 5 rpm - overrides the controller default\n",[245,1527,1528,1531,1533,1535,1537],{"class":247,"line":1123},[245,1529,1530],{"class":717},"  expensive",[245,1532,1079],{"class":255},[245,1534,595],{"class":255},[245,1536,804],{"class":803},[245,1538,807],{"class":255},[245,1540,1541],{"class":247,"line":1137},[245,1542,1140],{"class":255},[182,1544,1545,1546,1548,1549,1551],{},"This pattern mirrors how ",[185,1547,411],{}," works internally - see ",[185,1550,1170],{}," for the reference implementation.",[190,1553,1555,1556],{"id":1554},"composing-decorators-with-applydecorators","Composing decorators with ",[185,1557,1558],{},"applyDecorators",[182,1560,1561,1562,1564],{},"Sometimes a route needs several decorators that always appear together - a guard, a validator, a status code, a cache tag. Copy-pasting the stack on every method is noisy and easy to get out of sync. ",[185,1563,1558],{}," collapses the stack into a single reusable decorator without you touching metadata helpers at all - you just hand it the existing decorators and get back a composed one.",[236,1566,1568],{"className":238,"code":1567,"language":240,"meta":241,"style":241},"import { applyDecorators, UseGuard, Status } from '@miiajs\u002Fcore'\nimport { AuthGuard } from '@miiajs\u002Fauth'\nimport { Roles } from '.\u002Froles.guard.js'\nimport { JwtAuth } from '.\u002Fproviders\u002Fjwt-auth.provider.js'\n\nexport const AdminOnly = () =>\n  applyDecorators(\n    UseGuard(AuthGuard(JwtAuth)),\n    UseGuard(Roles('admin')),\n    Status(204),\n  )\n",[185,1569,1570,1599,1619,1639,1659,1663,1680,1688,1703,1726,1740],{"__ignoreMap":241},[245,1571,1572,1574,1576,1579,1581,1584,1586,1589,1591,1593,1595,1597],{"class":247,"line":248},[245,1573,252],{"class":251},[245,1575,595],{"class":255},[245,1577,1578],{"class":262}," applyDecorators",[245,1580,601],{"class":255},[245,1582,1583],{"class":262}," UseGuard",[245,1585,601],{"class":255},[245,1587,1588],{"class":262}," Status",[245,1590,607],{"class":255},[245,1592,299],{"class":251},[245,1594,302],{"class":255},[245,1596,187],{"class":305},[245,1598,308],{"class":255},[245,1600,1601,1603,1605,1608,1610,1612,1614,1617],{"class":247,"line":259},[245,1602,252],{"class":251},[245,1604,595],{"class":255},[245,1606,1607],{"class":262}," AuthGuard",[245,1609,607],{"class":255},[245,1611,299],{"class":251},[245,1613,302],{"class":255},[245,1615,1616],{"class":305},"@miiajs\u002Fauth",[245,1618,308],{"class":255},[245,1620,1621,1623,1625,1628,1630,1632,1634,1637],{"class":247,"line":269},[245,1622,252],{"class":251},[245,1624,595],{"class":255},[245,1626,1627],{"class":262}," Roles",[245,1629,607],{"class":255},[245,1631,299],{"class":251},[245,1633,302],{"class":255},[245,1635,1636],{"class":305},".\u002Froles.guard.js",[245,1638,308],{"class":255},[245,1640,1641,1643,1645,1648,1650,1652,1654,1657],{"class":247,"line":277},[245,1642,252],{"class":251},[245,1644,595],{"class":255},[245,1646,1647],{"class":262}," JwtAuth",[245,1649,607],{"class":255},[245,1651,299],{"class":251},[245,1653,302],{"class":255},[245,1655,1656],{"class":305},".\u002Fproviders\u002Fjwt-auth.provider.js",[245,1658,308],{"class":255},[245,1660,1661],{"class":247,"line":285},[245,1662,621],{"emptyLinePlaceholder":620},[245,1664,1665,1667,1669,1672,1674,1677],{"class":247,"line":293},[245,1666,445],{"class":251},[245,1668,449],{"class":448},[245,1670,1671],{"class":262}," AdminOnly ",[245,1673,455],{"class":255},[245,1675,1676],{"class":255}," ()",[245,1678,1679],{"class":448}," =>\n",[245,1681,1682,1685],{"class":247,"line":741},[245,1683,1684],{"class":458},"  applyDecorators",[245,1686,1687],{"class":262},"(\n",[245,1689,1690,1693,1695,1698,1701],{"class":247,"line":1109},[245,1691,1692],{"class":458},"    UseGuard",[245,1694,462],{"class":262},[245,1696,1697],{"class":458},"AuthGuard",[245,1699,1700],{"class":262},"(JwtAuth))",[245,1702,266],{"class":255},[245,1704,1705,1707,1709,1712,1714,1716,1719,1721,1724],{"class":247,"line":1123},[245,1706,1692],{"class":458},[245,1708,462],{"class":262},[245,1710,1711],{"class":458},"Roles",[245,1713,462],{"class":262},[245,1715,465],{"class":255},[245,1717,1718],{"class":305},"admin",[245,1720,465],{"class":255},[245,1722,1723],{"class":262},"))",[245,1725,266],{"class":255},[245,1727,1728,1731,1733,1736,1738],{"class":247,"line":1137},[245,1729,1730],{"class":458},"    Status",[245,1732,462],{"class":262},[245,1734,1735],{"class":1068},"204",[245,1737,704],{"class":262},[245,1739,266],{"class":255},[245,1741,1742],{"class":247,"line":1401},[245,1743,1744],{"class":262},"  )\n",[182,1746,1747],{},"Usage is a single decorator at the call site:",[236,1749,1751],{"className":238,"code":1750,"language":240,"meta":241,"style":241},"@Controller('users')\nclass UsersController {\n  @Delete(':id')\n  @AdminOnly()\n  remove(ctx: RequestContext) { \u002F* ... *\u002F }\n}\n",[185,1752,1753,1770,1779,1797,1807,1830],{"__ignoreMap":241},[245,1754,1755,1757,1759,1761,1763,1766,1768],{"class":247,"line":248},[245,1756,758],{"class":255},[245,1758,779],{"class":458},[245,1760,462],{"class":262},[245,1762,465],{"class":255},[245,1764,1765],{"class":305},"users",[245,1767,465],{"class":255},[245,1769,473],{"class":262},[245,1771,1772,1774,1777],{"class":247,"line":259},[245,1773,795],{"class":448},[245,1775,1776],{"class":671}," UsersController",[245,1778,256],{"class":255},[245,1780,1781,1783,1786,1788,1790,1793,1795],{"class":247,"line":269},[245,1782,1042],{"class":255},[245,1784,1785],{"class":458},"Delete",[245,1787,462],{"class":262},[245,1789,465],{"class":255},[245,1791,1792],{"class":305},":id",[245,1794,465],{"class":255},[245,1796,473],{"class":262},[245,1798,1799,1801,1804],{"class":247,"line":277},[245,1800,1042],{"class":255},[245,1802,1803],{"class":458},"AdminOnly",[245,1805,1806],{"class":262},"()\n",[245,1808,1809,1812,1814,1817,1819,1822,1824,1826,1828],{"class":247,"line":285},[245,1810,1811],{"class":717},"  remove",[245,1813,462],{"class":255},[245,1815,1816],{"class":690},"ctx",[245,1818,435],{"class":255},[245,1820,1821],{"class":671}," RequestContext",[245,1823,704],{"class":255},[245,1825,595],{"class":255},[245,1827,804],{"class":803},[245,1829,807],{"class":255},[245,1831,1832],{"class":247,"line":293},[245,1833,1140],{"class":255},[1835,1836,1838],"h3",{"id":1837},"how-it-works","How it works",[182,1840,1841,1842,1844,1845,1848,1849,204,1851,1853,1854,1856,1857,821,1860,1862,1863,1865,1866,204,1869,204,1872,204,1874,1876],{},"Each decorator you pass into ",[185,1843,1558],{}," is a plain function ",[185,1846,1847],{},"(value, context) => ..."," - exactly what ",[185,1850,359],{},[185,1852,396],{},", and hand-written method decorators produce. ",[185,1855,1558],{}," returns a new method decorator that invokes every input in order, forwarding the same ",[185,1858,1859],{},"value",[185,1861,720],{},". Because they share the ",[185,1864,568],{}," object, every side-effect (",[185,1867,1868],{},"setMeta",[185,1870,1871],{},"pushMeta",[185,1873,1149],{},[185,1875,1145],{},") lands on the same target - exactly as if you had stacked them by hand.",[182,1878,1879,1880,1883,1884,1886],{},"There is ",[222,1881,1882],{},"no runtime cost"," on requests: composition happens once, when the class is declared. At request time the framework sees the accumulated metadata, not ",[185,1885,1558],{}," itself.",[1835,1888,1890],{"id":1889},"when-to-use-it","When to use it",[195,1892,1893,1905,1914],{},[198,1894,1895,1896,204,1899,204,1902,211],{},"A common cross-cutting recipe you apply to many routes (",[185,1897,1898],{},"@AdminOnly()",[185,1900,1901],{},"@PublicJson()",[185,1903,1904],{},"@IdempotentPost()",[198,1906,1907,1908,204,1911,211],{},"Parameterised combinations (",[185,1909,1910],{},"RequireRole(...roles)",[185,1912,1913],{},"Cached(ttl, tags)",[198,1915,1916,1917,1920,1921,1924,1925,211],{},"Wrapping third-party decorators with your project's defaults (",[185,1918,1919],{},"@ValidateBody(YourSchema)"," + ",[185,1922,1923],{},"@Status(201)"," as a single ",[185,1926,1927],{},"@CreateEndpoint(schema)",[1835,1929,1931],{"id":1930},"when-not-to-use-it","When not to use it",[195,1933,1934,1937],{},[198,1935,1936],{},"A single decorator - just use it directly.",[198,1938,1939,1940,1943,1944,1946],{},"Decorators that write to ",[222,1941,1942],{},"different"," metadata shapes under the same key and expect a specific order. Stacked decorators always run bottom-up, and ",[185,1945,1558],{}," runs its arguments left-to-right; if you're relying on precise ordering, document it where the composite is defined.",[1835,1948,1950],{"id":1949},"limitations","Limitations",[182,1952,1953,1955,1956,1959,1960,1962],{},[185,1954,1558],{}," is ",[222,1957,1958],{},"method-level only",". Composing class decorators has a different shape (different ",[185,1961,1183],{},", different return semantics) and isn't covered by this helper yet. If you need that, stack the class decorators directly on the target class or write a tiny class-specific composer that mirrors the same pattern.",[190,1964,1966],{"id":1965},"reading-metadata-back","Reading metadata back",[182,1968,1969,1970,1973,1974,1976],{},"Attaching metadata is only half the job - something has to consume it. The consumer is usually a middleware, a guard, or an ",[185,1971,1972],{},"onInit()"," hook that walks every controller. It reads metadata with ",[185,1975,572],{},", passing the class constructor:",[236,1978,1980],{"className":238,"code":1979,"language":240,"meta":241,"style":241},"import { getMeta, type Middleware } from '@miiajs\u002Fcore'\nimport { CACHE } from '.\u002Fcache.decorator.js'\n\nexport const cacheMiddleware =\n  (controller: Function, handlerName: string): Middleware =>\n  async (ctx, next) => {\n    const map = getMeta\u003CMap\u003Cstring, number>>(controller as any, CACHE)\n    const ttl = map?.get(handlerName)\n    if (!ttl) return next()\n\n    \u002F\u002F Use the TTL to look up \u002F store a cached response.\n    \u002F\u002F ...\n    await next()\n  }\n",[185,1981,1982,2009,2028,2032,2044,2073,2093,2139,2163,2185,2189,2194,2200,2210],{"__ignoreMap":241},[245,1983,1984,1986,1988,1991,1993,1996,1999,2001,2003,2005,2007],{"class":247,"line":248},[245,1985,252],{"class":251},[245,1987,595],{"class":255},[245,1989,1990],{"class":262}," getMeta",[245,1992,601],{"class":255},[245,1994,1995],{"class":251}," type",[245,1997,1998],{"class":262}," Middleware",[245,2000,607],{"class":255},[245,2002,299],{"class":251},[245,2004,302],{"class":255},[245,2006,187],{"class":305},[245,2008,308],{"class":255},[245,2010,2011,2013,2015,2017,2019,2021,2023,2026],{"class":247,"line":259},[245,2012,252],{"class":251},[245,2014,595],{"class":255},[245,2016,977],{"class":262},[245,2018,607],{"class":255},[245,2020,299],{"class":251},[245,2022,302],{"class":255},[245,2024,2025],{"class":305},".\u002Fcache.decorator.js",[245,2027,308],{"class":255},[245,2029,2030],{"class":247,"line":269},[245,2031,621],{"emptyLinePlaceholder":620},[245,2033,2034,2036,2038,2041],{"class":247,"line":277},[245,2035,445],{"class":251},[245,2037,449],{"class":448},[245,2039,2040],{"class":262}," cacheMiddleware ",[245,2042,2043],{"class":255},"=\n",[245,2045,2046,2049,2052,2054,2057,2059,2062,2064,2066,2069,2071],{"class":247,"line":285},[245,2047,2048],{"class":255},"  (",[245,2050,2051],{"class":458},"controller",[245,2053,435],{"class":255},[245,2055,2056],{"class":671}," Function",[245,2058,601],{"class":255},[245,2060,2061],{"class":690}," handlerName",[245,2063,435],{"class":255},[245,2065,677],{"class":671},[245,2067,2068],{"class":255},"):",[245,2070,1998],{"class":671},[245,2072,1679],{"class":448},[245,2074,2075,2078,2080,2082,2084,2087,2089,2091],{"class":247,"line":293},[245,2076,2077],{"class":448},"  async",[245,2079,1300],{"class":255},[245,2081,1816],{"class":690},[245,2083,601],{"class":255},[245,2085,2086],{"class":690}," next",[245,2088,704],{"class":255},[245,2090,707],{"class":448},[245,2092,256],{"class":255},[245,2094,2095,2098,2101,2104,2106,2108,2111,2113,2116,2118,2120,2123,2125,2127,2130,2133,2135,2137],{"class":247,"line":741},[245,2096,2097],{"class":448},"    const",[245,2099,2100],{"class":262}," map",[245,2102,2103],{"class":255}," =",[245,2105,1990],{"class":458},[245,2107,665],{"class":255},[245,2109,2110],{"class":671},"Map",[245,2112,665],{"class":255},[245,2114,2115],{"class":671},"string",[245,2117,601],{"class":255},[245,2119,934],{"class":671},[245,2121,2122],{"class":255},">>",[245,2124,462],{"class":717},[245,2126,2051],{"class":262},[245,2128,2129],{"class":251}," as",[245,2131,2132],{"class":671}," any",[245,2134,601],{"class":255},[245,2136,977],{"class":262},[245,2138,473],{"class":717},[245,2140,2141,2143,2146,2148,2150,2153,2156,2158,2161],{"class":247,"line":1109},[245,2142,2097],{"class":448},[245,2144,2145],{"class":262}," ttl",[245,2147,2103],{"class":255},[245,2149,2100],{"class":262},[245,2151,2152],{"class":255},"?.",[245,2154,2155],{"class":458},"get",[245,2157,462],{"class":717},[245,2159,2160],{"class":262},"handlerName",[245,2162,473],{"class":717},[245,2164,2165,2168,2170,2173,2176,2178,2181,2183],{"class":247,"line":1123},[245,2166,2167],{"class":251},"    if",[245,2169,1300],{"class":717},[245,2171,2172],{"class":255},"!",[245,2174,2175],{"class":262},"ttl",[245,2177,1319],{"class":717},[245,2179,2180],{"class":251},"return",[245,2182,2086],{"class":458},[245,2184,1806],{"class":717},[245,2186,2187],{"class":247,"line":1137},[245,2188,621],{"emptyLinePlaceholder":620},[245,2190,2191],{"class":247,"line":1401},[245,2192,2193],{"class":803},"    \u002F\u002F Use the TTL to look up \u002F store a cached response.\n",[245,2195,2197],{"class":247,"line":2196},12,[245,2198,2199],{"class":803},"    \u002F\u002F ...\n",[245,2201,2203,2206,2208],{"class":247,"line":2202},13,[245,2204,2205],{"class":251},"    await",[245,2207,2086],{"class":458},[245,2209,1806],{"class":717},[245,2211,2213],{"class":247,"line":2212},14,[245,2214,1398],{"class":255},[182,2216,2217],{},"Reading metadata is a built-in pattern across the framework, not a Swagger-only trick. Three canonical examples:",[195,2219,2220,2252,2270],{},[198,2221,2222,2227,2228,2231,2232,204,2235,204,2238,204,2241,204,2244,2247,2248,2251],{},[222,2223,2224],{},[185,2225,2226],{},"packages\u002Fcore\u002Fsrc\u002Fapp\u002Frouter-explorer.ts"," - the router walks every controller during ",[185,2229,2230],{},"app.init()"," and pulls ",[185,2233,2234],{},"ROUTES",[185,2236,2237],{},"CLASS_MW",[185,2239,2240],{},"METHOD_MW",[185,2242,2243],{},"CLASS_GUARDS",[185,2245,2246],{},"METHOD_GUARDS",", and ",[185,2249,2250],{},"STATUSES"," to assemble each route's pipeline.",[198,2253,2254,2259,2260,204,2263,2247,2266,2269],{},[222,2255,2256],{},[185,2257,2258],{},"packages\u002Fcore\u002Fsrc\u002Fapp\u002Fmodule-loader.ts"," - the module loader reads ",[185,2261,2262],{},"MODULE",[185,2264,2265],{},"INJECTABLE",[185,2267,2268],{},"PREFIX"," from class metadata to wire up DI registration and route prefixes.",[198,2271,2272,2277,2278,204,2280,204,2282,204,2284,204,2287,204,2290,1319,2293,2296,2297,204,2300,204,2303,204,2306,204,2309,2312],{},[222,2273,2274],{},[185,2275,2276],{},"packages\u002Fswagger\u002Fsrc\u002Fbuilder\u002Fspec-builder.ts"," - the Swagger spec builder reads core metadata (",[185,2279,2234],{},[185,2281,2243],{},[185,2283,2250],{},[185,2285,2286],{},"BODY_SCHEMAS",[185,2288,2289],{},"QUERY_SCHEMAS",[185,2291,2292],{},"PARAMS_SCHEMAS",[222,2294,2295],{},"plus"," its own custom keys (",[185,2298,2299],{},"API_TAG",[185,2301,2302],{},"API_OPERATIONS",[185,2304,2305],{},"API_RESPONSES",[185,2307,2308],{},"API_SECURITY",[185,2310,2311],{},"API_HEADERS",") to generate an OpenAPI 3.1 document.",[182,2314,2315,2316,2318],{},"The pattern is the same in all three: walk the controllers, call ",[185,2317,572],{}," with the keys you care about, and act on the data.",[190,2320,2322],{"id":2321},"field-decorators","Field decorators",[182,2324,2325,2326,2328],{},"Field decorators fire when a field initializer runs. ",[185,2327,383],{}," passes only the context - there's no runtime \"target\" for a field at decoration time - so you typically register an initializer that runs against the constructed instance:",[236,2330,2332],{"className":238,"code":2331,"language":240,"meta":241,"style":241},"import { createFieldDecorator } from '@miiajs\u002Fcore'\n\nexport const Tracked = createFieldDecorator\u003C[label: string]>((context, label) => {\n  context.addInitializer(function () {\n    console.log(`[tracked] ${label}: ${String(context.name)} initialized on`, this)\n  })\n})\n",[185,2333,2334,2353,2357,2402,2421,2476,2482],{"__ignoreMap":241},[245,2335,2336,2338,2340,2343,2345,2347,2349,2351],{"class":247,"line":248},[245,2337,252],{"class":251},[245,2339,595],{"class":255},[245,2341,2342],{"class":262}," createFieldDecorator",[245,2344,607],{"class":255},[245,2346,299],{"class":251},[245,2348,302],{"class":255},[245,2350,187],{"class":305},[245,2352,308],{"class":255},[245,2354,2355],{"class":247,"line":259},[245,2356,621],{"emptyLinePlaceholder":620},[245,2358,2359,2361,2363,2366,2368,2370,2372,2374,2377,2379,2381,2383,2385,2387,2389,2391,2393,2396,2398,2400],{"class":247,"line":269},[245,2360,445],{"class":251},[245,2362,449],{"class":448},[245,2364,2365],{"class":262}," Tracked ",[245,2367,455],{"class":255},[245,2369,2342],{"class":458},[245,2371,665],{"class":255},[245,2373,668],{"class":262},[245,2375,2376],{"class":671},"label",[245,2378,435],{"class":255},[245,2380,677],{"class":671},[245,2382,680],{"class":262},[245,2384,683],{"class":255},[245,2386,462],{"class":262},[245,2388,462],{"class":255},[245,2390,720],{"class":690},[245,2392,601],{"class":255},[245,2394,2395],{"class":690}," label",[245,2397,704],{"class":255},[245,2399,707],{"class":448},[245,2401,256],{"class":255},[245,2403,2404,2407,2409,2412,2414,2417,2419],{"class":247,"line":277},[245,2405,2406],{"class":262},"  context",[245,2408,723],{"class":255},[245,2410,2411],{"class":458},"addInitializer",[245,2413,462],{"class":717},[245,2415,2416],{"class":448},"function",[245,2418,1676],{"class":255},[245,2420,256],{"class":255},[245,2422,2423,2426,2428,2431,2433,2436,2439,2442,2444,2446,2449,2451,2454,2457,2459,2462,2464,2467,2469,2471,2474],{"class":247,"line":285},[245,2424,2425],{"class":262},"    console",[245,2427,723],{"class":255},[245,2429,2430],{"class":458},"log",[245,2432,462],{"class":717},[245,2434,2435],{"class":255},"`",[245,2437,2438],{"class":305},"[tracked] ",[245,2440,2441],{"class":255},"${",[245,2443,2376],{"class":262},[245,2445,296],{"class":255},[245,2447,2448],{"class":305},": ",[245,2450,2441],{"class":255},[245,2452,2453],{"class":458},"String",[245,2455,2456],{"class":262},"(context",[245,2458,723],{"class":255},[245,2460,2461],{"class":262},"name)",[245,2463,296],{"class":255},[245,2465,2466],{"class":305}," initialized on",[245,2468,2435],{"class":255},[245,2470,601],{"class":255},[245,2472,2473],{"class":255}," this",[245,2475,473],{"class":717},[245,2477,2478,2480],{"class":247,"line":293},[245,2479,1351],{"class":255},[245,2481,473],{"class":717},[245,2483,2484,2486],{"class":247,"line":741},[245,2485,296],{"class":255},[245,2487,473],{"class":262},[182,2489,2490,2491,2494,2495,2497,2498,2500],{},"In practice, field decorators are rare in MiiaJS apps - ",[185,2492,2493],{},"inject()"," as a field initializer already covers the most common case (wiring a field through the DI container). Reach for ",[185,2496,383],{}," only when you're building a sibling framework on top of ",[185,2499,187],{}," and need to express something that doesn't fit on a class or a method.",[190,2502,2504],{"id":2503},"exporting-symbol-keys-from-a-library","Exporting symbol keys from a library",[182,2506,2507],{},"If your decorator lives in a published package, export its symbol from the public entry point. That lets downstream consumers (middleware, guards, plugins, other libraries) read the metadata without re-declaring their own symbols:",[236,2509,2511],{"className":238,"code":2510,"language":240,"meta":241,"style":241},"\u002F\u002F my-pkg\u002Fsrc\u002Findex.ts\nexport { Cache, CACHE } from '.\u002Fcache.decorator.js'\n",[185,2512,2513,2518],{"__ignoreMap":241},[245,2514,2515],{"class":247,"line":248},[245,2516,2517],{"class":803},"\u002F\u002F my-pkg\u002Fsrc\u002Findex.ts\n",[245,2519,2520,2522,2524,2527,2529,2531,2533,2535,2537,2539],{"class":247,"line":259},[245,2521,445],{"class":251},[245,2523,595],{"class":255},[245,2525,2526],{"class":262}," Cache",[245,2528,601],{"class":255},[245,2530,977],{"class":262},[245,2532,607],{"class":255},[245,2534,299],{"class":251},[245,2536,302],{"class":255},[245,2538,2025],{"class":305},[245,2540,308],{"class":255},[182,2542,2543,2545,2546,204,2548,204,2550,204,2552,2554],{},[185,2544,187],{}," itself does this - ",[185,2547,2265],{},[185,2549,2234],{},[185,2551,2243],{},[185,2553,2286],{},", and the rest are all public exports specifically so other packages (Swagger, Auth) can read core metadata without reaching into internals.",[190,2556,2558],{"id":2557},"gotchas","Gotchas",[195,2560,2561,2567,2584,2593,2604],{},[198,2562,2563,2566],{},[222,2564,2565],{},"Metadata is per-class, not per-instance."," Decorators run once when the class is declared. If you store a counter or any kind of mutable state, every instance of the class shares it.",[198,2568,2569,2572,2573,2575,2576,2578,2579,2583],{},[222,2570,2571],{},"The DI container is not active inside a decorator handler."," Decorators run at class definition time, long before ",[185,2574,2230],{},". If you need a resolved service, attach metadata now and read it inside an ",[185,2577,1972],{}," hook or a middleware where the container ",[2580,2581,2582],"em",{},"is"," active.",[198,2585,2586,2592],{},[222,2587,2588,2589,2591],{},"Stacked decorators run bottom-up, but they all share the same ",[185,2590,568],{}," object."," Order only matters when one decorator reads what another just wrote - most decorators only write, so it's usually irrelevant. The exception is easy to miss and hard to debug, so keep it in mind if you build interacting decorators.",[198,2594,2595,2601,2602,723],{},[222,2596,2597,2598,2600],{},"Always use ",[185,2599,430],{}," keys."," String keys would collide with user-land properties on ",[185,2603,434],{},[198,2605,2606,2609,2610,2612,2613,2616,2617,2619,2620,2622],{},[222,2607,2608],{},"The polyfill is already loaded."," ",[185,2611,187],{}," imports ",[185,2614,2615],{},".\u002Fpolyfill.js"," as the first line of its entry - as long as your code (including tests) imports anything from ",[185,2618,187],{}," transitively, ",[185,2621,434],{}," is ready.",[2624,2625,2626],"style",{},"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 .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}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 .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}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}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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 .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}",{"title":241,"searchDepth":259,"depth":259,"links":2628},[2629,2630,2631,2632,2633,2634,2635,2642,2643,2644,2645],{"id":192,"depth":259,"text":193},{"id":233,"depth":259,"text":234},{"id":423,"depth":259,"text":424},{"id":579,"depth":259,"text":580},{"id":842,"depth":259,"text":843},{"id":1174,"depth":259,"text":1175},{"id":1554,"depth":259,"text":2636,"children":2637},"Composing decorators with applyDecorators",[2638,2639,2640,2641],{"id":1837,"depth":269,"text":1838},{"id":1889,"depth":269,"text":1890},{"id":1930,"depth":269,"text":1931},{"id":1949,"depth":269,"text":1950},{"id":1965,"depth":259,"text":1966},{"id":2321,"depth":259,"text":2322},{"id":2503,"depth":259,"text":2504},{"id":2557,"depth":259,"text":2558},"Build your own decorators with createClassDecorator, createMethodDecorator, createDecorator, createFieldDecorator, and the metadata helpers.","md",{},{"title":43,"description":2646},"HESMyEDx8djAAG2rw8rEbtFZ00SfTb0mHQTINdAsJGs",[2652,2654],{"title":27,"path":38,"stem":39,"description":2653,"children":-1},"How decorators work in MiiaJS - TC39 native, Symbol.metadata, and the catalogue of built-ins.",{"title":47,"path":48,"stem":49,"description":2655,"children":-1},"Scan every singleton provider for decorator metadata at startup - the foundation of ambient discovery in MiiaJS.",1778575277744]