[{"data":1,"prerenderedAt":549},["ShallowReactive",2],{"navigation_docs":3,"-how-the-codegen-works":36,"-how-the-codegen-works-surround":544},[4,8,12,16,20,24,28,32],{"title":5,"path":6,"stem":7},"Getting started","\u002Fgetting-started","1.getting-started",{"title":9,"path":10,"stem":11},"Core concepts","\u002Fcore-concepts","2.core-concepts",{"title":13,"path":14,"stem":15},"Cross-cutting concerns","\u002Fcross-cutting","3.cross-cutting",{"title":17,"path":18,"stem":19},"Tenancy & RLS","\u002Ftenancy-and-rls","4.tenancy-and-rls",{"title":21,"path":22,"stem":23},"How the codegen works","\u002Fhow-the-codegen-works","5.how-the-codegen-works",{"title":25,"path":26,"stem":27},"vs. NestJS","\u002Fvs-nestjs","6.vs-nestjs",{"title":29,"path":30,"stem":31},"Before \u002F after","\u002Fbefore-after","7.before-after",{"title":33,"path":34,"stem":35},"Modules","\u002Fmodules","8.modules",{"id":37,"title":21,"body":38,"description":537,"extension":538,"links":539,"meta":540,"navigation":541,"path":22,"seo":542,"stem":23,"__hash__":543},"docs\u002F5.how-the-codegen-works.md",{"type":39,"value":40,"toc":531},"minimark",[41,73,78,257,261,268,278,285,469,491,495,506,516,520,527],[42,43,44,45,52,53,61,62,67,68,72],"p",{},"A ",[46,47,51],"a",{"href":48,"rel":49},"https:\u002F\u002Fgithub.com\u002Fnuxt-roost\u002Froost\u002Fblob\u002Fmain\u002Fsrc\u002Fmodule.ts",[50],"nofollow","Nuxt module"," runs the codegen\n(",[46,54,57],{"href":55,"rel":56},"https:\u002F\u002Fgithub.com\u002Fnuxt-roost\u002Froost\u002Fblob\u002Fmain\u002Fsrc\u002Fcodegen.ts",[50],[58,59,60],"code",{},"src\u002Fcodegen.ts",") at build\ntime. It opens your feature files with ",[46,63,66],{"href":64,"rel":65},"https:\u002F\u002Fts-morph.com",[50],"ts-morph"," and reads them as a syntax\ntree — it ",[69,70,71],"strong",{},"never executes the decorators",".",[74,75,77],"h2",{"id":76},"what-it-reads","What it reads",[79,80,81,99,116,131,218,234],"ul",{},[82,83,84,87,88,91,92,95,96,72],"li",{},[69,85,86],{},"Provider classes"," — every class in ",[58,89,90],{},"*.{controller,service,repo,guard,filter,interceptor}.ts","\nthat carries ",[58,93,94],{},"@Injectable"," or ",[58,97,98],{},"@Controller",[82,100,101,104,105,107,108,111,112,115],{},[69,102,103],{},"Lifetime"," — ",[58,106,98],{}," and cross-cutting providers default to SCOPED; ",[58,109,110],{},"@Injectable({ scope })"," overrides; plain ",[58,113,114],{},"@Injectable()"," is SINGLETON.",[82,117,118,121,122,125,126,130],{},[69,119,120],{},"The dependency graph"," — read ",[69,123,124],{},"syntactically"," from the constructor parameter ",[127,128,129],"em",{},"type\nannotations"," (no type-checker, so it works regardless of your tsconfig).",[82,132,133,104,136,139,140,143,144,147,148,139,151,154,155,139,158,161,162,165,166,169,170,173,174,177,178,173,181,184,185,173,188,191,192,195,196,199,200,95,203,173,206,209,210,213,214,217],{},[69,134,135],{},"Routes",[58,137,138],{},"@Controller(base)"," + ",[58,141,142],{},"@Get\u002F@Post(path)",". Paths are joined by ",[69,145,146],{},"segment",", so a\nleading slash is optional — ",[58,149,150],{},"@Controller('cats')",[58,152,153],{},"@Get(':id')"," (NestJS style) and\n",[58,156,157],{},"@Controller('\u002Fcats')",[58,159,160],{},"@Get('\u002F:id')"," produce the identical route. A bare ",[58,163,164],{},"@Controller()","\ndefaults the base to the controller's ",[69,167,168],{},"feature folder"," relative to the features dir\n(",[58,171,172],{},"features\u002Fprojects\u002F…"," → ",[58,175,176],{},"\u002Fprojects",", ",[58,179,180],{},"features\u002Fadmin\u002Fusers\u002F…",[58,182,183],{},"\u002Fadmin\u002Fusers","); an explicit path\noverrides it. Path segments map to Nitro's file-based router: ",[58,186,187],{},":id",[58,189,190],{},"[id]"," (dynamic), ",[58,193,194],{},"*"," →\n",[58,197,198],{},"[...]"," (unnamed catch-all), and ",[58,201,202],{},"*slug",[58,204,205],{},":slug*",[58,207,208],{},"[...slug]"," (named catch-all, where\n",[58,211,212],{},"ctx.params.slug"," holds the rest of the path, e.g. ",[58,215,216],{},"a\u002Fb\u002Fc",").",[82,219,220,104,223,226,227,226,230,233],{},[69,221,222],{},"Cross-cutting",[58,224,225],{},"@UseGuards"," \u002F ",[58,228,229],{},"@UseInterceptors",[58,231,232],{},"@UseFilters"," class refs, merged with the\nright precedence and mapped to DI tokens.",[82,235,236,239,240,243,244,226,247,226,250,226,253,256],{},[69,237,238],{},"Validation & injection"," — the method-level ",[58,241,242],{},"@ValidateBody(schema)",", and each handler\nparameter's ",[58,245,246],{},"@Body",[58,248,249],{},"@Param",[58,251,252],{},"@Query",[58,254,255],{},"@Ctx"," decorator (+ its schema), turned into a\nper-argument binding list.",[74,258,260],{"id":259},"what-it-emits","What it emits",[42,262,263,264,267],{},"Into a gitignored ",[58,265,266],{},".roost\u002F",":",[269,270,275],"pre",{"className":271,"code":273,"language":274},[272],"language-text",".roost\u002F\n  api\u002F**             route delegates (one per verb)       → scanned by Nitro\n  di.generated.ts    awilix container.register({...})\n  di.plugin.ts       registers providers + global chains at startup\n  manifest.json      the full effective route table (surfacing + test snapshot)\n","text",[58,276,273],{"__ignoreMap":277},"",[42,279,280,281,284],{},"A route delegate is a one-liner — guards\u002Ffilters\u002Finterceptors are passed as ",[69,282,283],{},"tokens"," (resolved\nfrom the scope at runtime), so the file imports only the runtime + any body schema:",[269,286,290],{"className":287,"code":288,"language":289,"meta":277,"style":277},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F\u002F .roost\u002Fapi\u002Fprojects.post.ts  — for `create(@Body(createProjectSchema) dto)`\nimport { route } from 'nuxt-roost\u002Fruntime'\nimport { createProjectSchema } from '..\u002F..\u002Fserver\u002Ffeatures\u002Fprojects\u002Fdto'\nexport default route('projectsController', 'create', {\n  guards: ['memberGuard'],\n  bindings: [{ kind: 'body', schema: createProjectSchema }], \u002F\u002F validated body → handler arg 0\n})\n","ts",[58,291,292,301,332,353,391,416,461],{"__ignoreMap":277},[293,294,297],"span",{"class":295,"line":296},"line",1,[293,298,300],{"class":299},"sHwdD","\u002F\u002F .roost\u002Fapi\u002Fprojects.post.ts  — for `create(@Body(createProjectSchema) dto)`\n",[293,302,304,308,312,316,319,322,325,329],{"class":295,"line":303},2,[293,305,307],{"class":306},"s7zQu","import",[293,309,311],{"class":310},"sMK4o"," {",[293,313,315],{"class":314},"sTEyZ"," route",[293,317,318],{"class":310}," }",[293,320,321],{"class":306}," from",[293,323,324],{"class":310}," '",[293,326,328],{"class":327},"sfazB","nuxt-roost\u002Fruntime",[293,330,331],{"class":310},"'\n",[293,333,335,337,339,342,344,346,348,351],{"class":295,"line":334},3,[293,336,307],{"class":306},[293,338,311],{"class":310},[293,340,341],{"class":314}," createProjectSchema",[293,343,318],{"class":310},[293,345,321],{"class":306},[293,347,324],{"class":310},[293,349,350],{"class":327},"..\u002F..\u002Fserver\u002Ffeatures\u002Fprojects\u002Fdto",[293,352,331],{"class":310},[293,354,356,359,362,365,368,371,374,376,379,381,384,386,388],{"class":295,"line":355},4,[293,357,358],{"class":306},"export",[293,360,361],{"class":306}," default",[293,363,315],{"class":364},"s2Zo4",[293,366,367],{"class":314},"(",[293,369,370],{"class":310},"'",[293,372,373],{"class":327},"projectsController",[293,375,370],{"class":310},[293,377,378],{"class":310},",",[293,380,324],{"class":310},[293,382,383],{"class":327},"create",[293,385,370],{"class":310},[293,387,378],{"class":310},[293,389,390],{"class":310}," {\n",[293,392,394,398,400,403,405,408,410,413],{"class":295,"line":393},5,[293,395,397],{"class":396},"swJcz","  guards",[293,399,267],{"class":310},[293,401,402],{"class":314}," [",[293,404,370],{"class":310},[293,406,407],{"class":327},"memberGuard",[293,409,370],{"class":310},[293,411,412],{"class":314},"]",[293,414,415],{"class":310},",\n",[293,417,419,422,424,426,429,432,434,436,439,441,443,446,448,451,454,456,458],{"class":295,"line":418},6,[293,420,421],{"class":396},"  bindings",[293,423,267],{"class":310},[293,425,402],{"class":314},[293,427,428],{"class":310},"{",[293,430,431],{"class":396}," kind",[293,433,267],{"class":310},[293,435,324],{"class":310},[293,437,438],{"class":327},"body",[293,440,370],{"class":310},[293,442,378],{"class":310},[293,444,445],{"class":396}," schema",[293,447,267],{"class":310},[293,449,450],{"class":314}," createProjectSchema ",[293,452,453],{"class":310},"}",[293,455,412],{"class":314},[293,457,378],{"class":310},[293,459,460],{"class":299}," \u002F\u002F validated body → handler arg 0\n",[293,462,464,466],{"class":295,"line":463},7,[293,465,453],{"class":310},[293,467,468],{"class":314},")\n",[42,470,471,472,475,476,478,479,482,483,486,487,490],{},"(A ",[58,473,474],{},"ctx","-style method with ",[58,477,242],{}," emits ",[58,480,481],{},"body: schema"," instead of ",[58,484,485],{},"bindings",", and\nthe runtime validates ",[58,488,489],{},"ctx.body"," in place rather than binding an argument.)",[74,492,494],{"id":493},"dev-watch","Dev watch",[42,496,497,498,501,502,505],{},"In dev, the module watches your server dir and regenerates on change. It writes\n",[69,499,500],{},"only the files whose content actually changed"," (and prunes stale ones), so Nitro's own watcher\nonly reloads what truly moved — add a ",[58,503,504],{},"@Get"," and the route is live without a restart.",[507,508,509],"blockquote",{},[42,510,511,512,515],{},"The current watcher does a debounced ",[127,513,514],{},"full"," regen with write-on-change. True incremental\nregeneration (re-read only the changed file, patch the manifest) is an open contribution.",[74,517,519],{"id":518},"why-a-build-time-ast-not-runtime-reflection","Why a build-time AST, not runtime reflection",[42,521,522,523,526],{},"Reading constructor types at build time and emitting explicit factories means the runtime ships\n",[69,524,525],{},"zero reflection and zero ts-morph",". The generated route files keep Nitro's file-based router as\nthe source of truth, so the build\u002Ftree-shaking\u002Fedge story is exactly normal Nitro.",[528,529,530],"style",{},"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 .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 pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}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);}",{"title":277,"searchDepth":303,"depth":303,"links":532},[533,534,535,536],{"id":76,"depth":303,"text":77},{"id":259,"depth":303,"text":260},{"id":493,"depth":303,"text":494},{"id":518,"depth":303,"text":519},"The build-time AST pass — what it reads, what it emits, and the dev-watch loop.","md",null,{},true,{"title":21,"description":537},"w1EDm1Ly2_f8-RJgWNjRhKk50Tz5OwUo358DfrJfG-g",[545,547],{"title":17,"path":18,"stem":19,"description":546,"children":-1},"Tenant isolation by construction — no tenantId threading — and the same pattern against real Postgres RLS.",{"title":25,"path":26,"stem":27,"description":548,"children":-1},"What's the same, what's deliberately different, and what roost is not.",1780506502632]