[{"data":1,"prerenderedAt":1527},["ShallowReactive",2],{"navigation_docs":3,"-before-after":36,"-before-after-surround":1522},[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":29,"body":38,"description":1516,"extension":1517,"links":1518,"meta":1519,"navigation":297,"path":30,"seo":1520,"stem":31,"__hash__":1521},"docs\u002F7.before-after.md",{"type":39,"value":40,"toc":1510},"minimark",[41,64,69,72,849,856,860,863,1347,1350,1354,1488,1492,1506],[42,43,44,45,54,55,59,60,63],"p",{},"Here's the ",[46,47,48,49,53],"strong",{},"same ",[50,51,52],"code",{},"projects"," feature",", written two ways: idiomatic plain Nitro + awilix (the\n\"before\"), and with roost (the \"after\"). They boot and behave ",[56,57,58],"em",{},"identically"," — same\n401\u002F403\u002F200\u002F400, same request-scoped tenant isolation. That parity is the point: ",[46,61,62],{},"roost is an\nergonomics layer, not a capability gap."," The difference is the wiring you stop writing.",[65,66,68],"h2",{"id":67},"_1-the-route-file","1. The route file",[42,70,71],{},"Every protected, validated route repeats the whole dance by hand. With roost it's a decorated\nmethod — roost generates the delegate for you:",[73,74,75,690],"code-group",{},[76,77,83],"pre",{"className":78,"code":79,"filename":80,"language":81,"meta":82,"style":82},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { defineEventHandler, createError } from 'h3'\nimport { asValue } from 'awilix'\nimport { ZodError } from 'zod'\nimport { container } from '..\u002F..\u002Fcontainer'\nimport { buildCtx } from '..\u002F..\u002Fctx'\nimport { memberGuard } from '..\u002F..\u002Fguards'\nimport { withAudit } from '..\u002F..\u002Faudit'\nimport { ProjectsService } from '..\u002F..\u002Fprojects\u002Fprojects.service'\nimport { createProjectSchema } from '..\u002F..\u002Fprojects\u002Fdto'\n\nexport default defineEventHandler(async (event) => {\n  const ctx = await buildCtx(event)\n  memberGuard(ctx) \u002F\u002F guard, by hand\n  try {\n    return await withAudit(ctx, () => {\n      \u002F\u002F audit wrap, by hand\n      const body = createProjectSchema.parse(ctx.body) \u002F\u002F validate, by hand\n      const scope = container.createScope() \u002F\u002F request scope, by hand\n      scope.register({ requestContext: asValue({ tenantId: ctx.tenantId, user: ctx.user }) })\n      return scope.resolve\u003CProjectsService>('projectsService').create(body.name)\n    })\n  } catch (err) {\n    if (err instanceof ZodError) throw createError({ statusCode: 400, data: err.issues }) \u002F\u002F map, by hand\n    throw err\n  }\n})\n","Before — projects.post.ts","ts","",[50,84,85,124,145,166,187,208,229,250,271,292,299,334,359,377,385,408,414,447,470,533,584,592,611,668,677,683],{"__ignoreMap":82},[86,87,90,94,98,102,105,108,111,114,117,121],"span",{"class":88,"line":89},"line",1,[86,91,93],{"class":92},"s7zQu","import",[86,95,97],{"class":96},"sMK4o"," {",[86,99,101],{"class":100},"sTEyZ"," defineEventHandler",[86,103,104],{"class":96},",",[86,106,107],{"class":100}," createError",[86,109,110],{"class":96}," }",[86,112,113],{"class":92}," from",[86,115,116],{"class":96}," '",[86,118,120],{"class":119},"sfazB","h3",[86,122,123],{"class":96},"'\n",[86,125,127,129,131,134,136,138,140,143],{"class":88,"line":126},2,[86,128,93],{"class":92},[86,130,97],{"class":96},[86,132,133],{"class":100}," asValue",[86,135,110],{"class":96},[86,137,113],{"class":92},[86,139,116],{"class":96},[86,141,142],{"class":119},"awilix",[86,144,123],{"class":96},[86,146,148,150,152,155,157,159,161,164],{"class":88,"line":147},3,[86,149,93],{"class":92},[86,151,97],{"class":96},[86,153,154],{"class":100}," ZodError",[86,156,110],{"class":96},[86,158,113],{"class":92},[86,160,116],{"class":96},[86,162,163],{"class":119},"zod",[86,165,123],{"class":96},[86,167,169,171,173,176,178,180,182,185],{"class":88,"line":168},4,[86,170,93],{"class":92},[86,172,97],{"class":96},[86,174,175],{"class":100}," container",[86,177,110],{"class":96},[86,179,113],{"class":92},[86,181,116],{"class":96},[86,183,184],{"class":119},"..\u002F..\u002Fcontainer",[86,186,123],{"class":96},[86,188,190,192,194,197,199,201,203,206],{"class":88,"line":189},5,[86,191,93],{"class":92},[86,193,97],{"class":96},[86,195,196],{"class":100}," buildCtx",[86,198,110],{"class":96},[86,200,113],{"class":92},[86,202,116],{"class":96},[86,204,205],{"class":119},"..\u002F..\u002Fctx",[86,207,123],{"class":96},[86,209,211,213,215,218,220,222,224,227],{"class":88,"line":210},6,[86,212,93],{"class":92},[86,214,97],{"class":96},[86,216,217],{"class":100}," memberGuard",[86,219,110],{"class":96},[86,221,113],{"class":92},[86,223,116],{"class":96},[86,225,226],{"class":119},"..\u002F..\u002Fguards",[86,228,123],{"class":96},[86,230,232,234,236,239,241,243,245,248],{"class":88,"line":231},7,[86,233,93],{"class":92},[86,235,97],{"class":96},[86,237,238],{"class":100}," withAudit",[86,240,110],{"class":96},[86,242,113],{"class":92},[86,244,116],{"class":96},[86,246,247],{"class":119},"..\u002F..\u002Faudit",[86,249,123],{"class":96},[86,251,253,255,257,260,262,264,266,269],{"class":88,"line":252},8,[86,254,93],{"class":92},[86,256,97],{"class":96},[86,258,259],{"class":100}," ProjectsService",[86,261,110],{"class":96},[86,263,113],{"class":92},[86,265,116],{"class":96},[86,267,268],{"class":119},"..\u002F..\u002Fprojects\u002Fprojects.service",[86,270,123],{"class":96},[86,272,274,276,278,281,283,285,287,290],{"class":88,"line":273},9,[86,275,93],{"class":92},[86,277,97],{"class":96},[86,279,280],{"class":100}," createProjectSchema",[86,282,110],{"class":96},[86,284,113],{"class":92},[86,286,116],{"class":96},[86,288,289],{"class":119},"..\u002F..\u002Fprojects\u002Fdto",[86,291,123],{"class":96},[86,293,295],{"class":88,"line":294},10,[86,296,298],{"emptyLinePlaceholder":297},true,"\n",[86,300,302,305,308,311,314,318,321,325,328,331],{"class":88,"line":301},11,[86,303,304],{"class":92},"export",[86,306,307],{"class":92}," default",[86,309,101],{"class":310},"s2Zo4",[86,312,313],{"class":100},"(",[86,315,317],{"class":316},"spNyl","async",[86,319,320],{"class":96}," (",[86,322,324],{"class":323},"sHdIc","event",[86,326,327],{"class":96},")",[86,329,330],{"class":316}," =>",[86,332,333],{"class":96}," {\n",[86,335,337,340,343,346,349,351,354,356],{"class":88,"line":336},12,[86,338,339],{"class":316},"  const",[86,341,342],{"class":100}," ctx",[86,344,345],{"class":96}," =",[86,347,348],{"class":92}," await",[86,350,196],{"class":310},[86,352,313],{"class":353},"swJcz",[86,355,324],{"class":100},[86,357,358],{"class":353},")\n",[86,360,362,365,367,370,373],{"class":88,"line":361},13,[86,363,364],{"class":310},"  memberGuard",[86,366,313],{"class":353},[86,368,369],{"class":100},"ctx",[86,371,372],{"class":353},") ",[86,374,376],{"class":375},"sHwdD","\u002F\u002F guard, by hand\n",[86,378,380,383],{"class":88,"line":379},14,[86,381,382],{"class":92},"  try",[86,384,333],{"class":96},[86,386,388,391,393,395,397,399,401,404,406],{"class":88,"line":387},15,[86,389,390],{"class":92},"    return",[86,392,348],{"class":92},[86,394,238],{"class":310},[86,396,313],{"class":353},[86,398,369],{"class":100},[86,400,104],{"class":96},[86,402,403],{"class":96}," ()",[86,405,330],{"class":316},[86,407,333],{"class":96},[86,409,411],{"class":88,"line":410},16,[86,412,413],{"class":375},"      \u002F\u002F audit wrap, by hand\n",[86,415,417,420,423,425,427,430,433,435,437,439,442,444],{"class":88,"line":416},17,[86,418,419],{"class":316},"      const",[86,421,422],{"class":100}," body",[86,424,345],{"class":96},[86,426,280],{"class":100},[86,428,429],{"class":96},".",[86,431,432],{"class":310},"parse",[86,434,313],{"class":353},[86,436,369],{"class":100},[86,438,429],{"class":96},[86,440,441],{"class":100},"body",[86,443,372],{"class":353},[86,445,446],{"class":375},"\u002F\u002F validate, by hand\n",[86,448,450,452,455,457,459,461,464,467],{"class":88,"line":449},18,[86,451,419],{"class":316},[86,453,454],{"class":100}," scope",[86,456,345],{"class":96},[86,458,175],{"class":100},[86,460,429],{"class":96},[86,462,463],{"class":310},"createScope",[86,465,466],{"class":353},"() ",[86,468,469],{"class":375},"\u002F\u002F request scope, by hand\n",[86,471,473,476,478,481,483,486,489,492,494,496,498,501,503,505,507,510,512,515,517,519,521,524,526,528,531],{"class":88,"line":472},19,[86,474,475],{"class":100},"      scope",[86,477,429],{"class":96},[86,479,480],{"class":310},"register",[86,482,313],{"class":353},[86,484,485],{"class":96},"{",[86,487,488],{"class":353}," requestContext",[86,490,491],{"class":96},":",[86,493,133],{"class":310},[86,495,313],{"class":353},[86,497,485],{"class":96},[86,499,500],{"class":353}," tenantId",[86,502,491],{"class":96},[86,504,342],{"class":100},[86,506,429],{"class":96},[86,508,509],{"class":100},"tenantId",[86,511,104],{"class":96},[86,513,514],{"class":353}," user",[86,516,491],{"class":96},[86,518,342],{"class":100},[86,520,429],{"class":96},[86,522,523],{"class":100},"user",[86,525,110],{"class":96},[86,527,372],{"class":353},[86,529,530],{"class":96},"}",[86,532,358],{"class":353},[86,534,536,539,541,543,546,549,553,556,558,561,564,566,568,570,573,575,577,579,582],{"class":88,"line":535},20,[86,537,538],{"class":92},"      return",[86,540,454],{"class":100},[86,542,429],{"class":96},[86,544,545],{"class":310},"resolve",[86,547,548],{"class":96},"\u003C",[86,550,552],{"class":551},"sBMFI","ProjectsService",[86,554,555],{"class":96},">",[86,557,313],{"class":353},[86,559,560],{"class":96},"'",[86,562,563],{"class":119},"projectsService",[86,565,560],{"class":96},[86,567,327],{"class":353},[86,569,429],{"class":96},[86,571,572],{"class":310},"create",[86,574,313],{"class":353},[86,576,441],{"class":100},[86,578,429],{"class":96},[86,580,581],{"class":100},"name",[86,583,358],{"class":353},[86,585,587,590],{"class":88,"line":586},21,[86,588,589],{"class":96},"    }",[86,591,358],{"class":353},[86,593,595,598,601,603,606,608],{"class":88,"line":594},22,[86,596,597],{"class":96},"  }",[86,599,600],{"class":92}," catch",[86,602,320],{"class":353},[86,604,605],{"class":100},"err",[86,607,372],{"class":353},[86,609,610],{"class":96},"{\n",[86,612,614,617,619,621,624,626,628,631,633,635,637,640,642,646,648,651,653,656,658,661,663,665],{"class":88,"line":613},23,[86,615,616],{"class":92},"    if",[86,618,320],{"class":353},[86,620,605],{"class":100},[86,622,623],{"class":96}," instanceof",[86,625,154],{"class":551},[86,627,372],{"class":353},[86,629,630],{"class":92},"throw",[86,632,107],{"class":310},[86,634,313],{"class":353},[86,636,485],{"class":96},[86,638,639],{"class":353}," statusCode",[86,641,491],{"class":96},[86,643,645],{"class":644},"sbssI"," 400",[86,647,104],{"class":96},[86,649,650],{"class":353}," data",[86,652,491],{"class":96},[86,654,655],{"class":100}," err",[86,657,429],{"class":96},[86,659,660],{"class":100},"issues",[86,662,110],{"class":96},[86,664,372],{"class":353},[86,666,667],{"class":375},"\u002F\u002F map, by hand\n",[86,669,671,674],{"class":88,"line":670},24,[86,672,673],{"class":92},"    throw",[86,675,676],{"class":100}," err\n",[86,678,680],{"class":88,"line":679},25,[86,681,682],{"class":96},"  }\n",[86,684,686,688],{"class":88,"line":685},26,[86,687,530],{"class":96},[86,689,358],{"class":100},[76,691,694],{"className":78,"code":692,"filename":693,"language":81,"meta":82,"style":82},"\u002F\u002F Zero imports: the decorators\u002Ftypes AND your own MemberGuard \u002F ProjectsService \u002F\n\u002F\u002F createProjectSchema are all auto-imported (roost's runtime + the feature dir, like server\u002Futils).\n@Controller() \u002F\u002F path defaults to the feature folder → \u002Fprojects\n@UseGuards(MemberGuard)\nexport class ProjectsController {\n  constructor(private readonly projects: ProjectsService) {}\n\n  @Post()\n  \u002F\u002F @Body(schema) injects the VALIDATED body as the argument (Nest-style). Bad input ->\n  \u002F\u002F ZodError -> 400 via the built-in filter, before this method runs. `dto` is typed by the DTO.\n  create(@Body(createProjectSchema) dto: CreateProjectDto) {\n    return this.projects.create(dto.name)\n  }\n}\n","After — projects.controller.ts",[50,695,696,701,706,719,729,741,766,770,781,786,791,817,840,844],{"__ignoreMap":82},[86,697,698],{"class":88,"line":89},[86,699,700],{"class":375},"\u002F\u002F Zero imports: the decorators\u002Ftypes AND your own MemberGuard \u002F ProjectsService \u002F\n",[86,702,703],{"class":88,"line":126},[86,704,705],{"class":375},"\u002F\u002F createProjectSchema are all auto-imported (roost's runtime + the feature dir, like server\u002Futils).\n",[86,707,708,711,714,716],{"class":88,"line":147},[86,709,710],{"class":96},"@",[86,712,713],{"class":310},"Controller",[86,715,466],{"class":100},[86,717,718],{"class":375},"\u002F\u002F path defaults to the feature folder → \u002Fprojects\n",[86,720,721,723,726],{"class":88,"line":168},[86,722,710],{"class":96},[86,724,725],{"class":310},"UseGuards",[86,727,728],{"class":100},"(MemberGuard)\n",[86,730,731,733,736,739],{"class":88,"line":189},[86,732,304],{"class":92},[86,734,735],{"class":316}," class",[86,737,738],{"class":551}," ProjectsController",[86,740,333],{"class":96},[86,742,743,746,748,751,754,757,759,761,763],{"class":88,"line":210},[86,744,745],{"class":316},"  constructor",[86,747,313],{"class":96},[86,749,750],{"class":316},"private",[86,752,753],{"class":316}," readonly",[86,755,756],{"class":323}," projects",[86,758,491],{"class":96},[86,760,259],{"class":551},[86,762,327],{"class":96},[86,764,765],{"class":96}," {}\n",[86,767,768],{"class":88,"line":231},[86,769,298],{"emptyLinePlaceholder":297},[86,771,772,775,778],{"class":88,"line":252},[86,773,774],{"class":96},"  @",[86,776,777],{"class":310},"Post",[86,779,780],{"class":100},"()\n",[86,782,783],{"class":88,"line":273},[86,784,785],{"class":375},"  \u002F\u002F @Body(schema) injects the VALIDATED body as the argument (Nest-style). Bad input ->\n",[86,787,788],{"class":88,"line":294},[86,789,790],{"class":375},"  \u002F\u002F ZodError -> 400 via the built-in filter, before this method runs. `dto` is typed by the DTO.\n",[86,792,793,796,799,802,805,808,810,813,815],{"class":88,"line":301},[86,794,795],{"class":353},"  create",[86,797,798],{"class":96},"(@",[86,800,801],{"class":310},"Body",[86,803,804],{"class":100},"(createProjectSchema) ",[86,806,807],{"class":323},"dto",[86,809,491],{"class":96},[86,811,812],{"class":551}," CreateProjectDto",[86,814,327],{"class":96},[86,816,333],{"class":96},[86,818,819,821,824,826,828,830,832,834,836,838],{"class":88,"line":336},[86,820,390],{"class":92},[86,822,823],{"class":96}," this.",[86,825,52],{"class":100},[86,827,429],{"class":96},[86,829,572],{"class":310},[86,831,313],{"class":353},[86,833,807],{"class":100},[86,835,429],{"class":96},[86,837,581],{"class":100},[86,839,358],{"class":353},[86,841,842],{"class":88,"line":361},[86,843,682],{"class":96},[86,845,846],{"class":88,"line":379},[86,847,848],{"class":96},"}\n",[42,850,851,852,855],{},"The guard is class-level (inherited by every method), the audit interceptor is one global config\nline, and ",[50,853,854],{},"ZodError → 400"," is the built-in filter. Forget the guard \u002F audit \u002F error-map on a\nhand-written route and it silently misbehaves; here they can't be forgotten.",[65,857,859],{"id":858},"_2-the-di-composition-root","2. The DI composition root",[42,861,862],{},"The exact same three providers — same lifetimes, same dependency graph — expressed as\nhand-written registrations you maintain vs. a decorator on the class that owns the scope:",[73,864,865,1194],{},[76,866,869],{"className":78,"code":867,"filename":868,"language":81,"meta":82,"style":82},"import { createContainer, asFunction, Lifetime } from 'awilix'\nimport { ProjectStore } from '.\u002Fprojects\u002Fprojects.store'\nimport { ProjectsRepo } from '.\u002Fprojects\u002Fprojects.repo'\nimport { ProjectsService } from '.\u002Fprojects\u002Fprojects.service'\n\nexport const container = createContainer({ strict: true })\n\ncontainer.register({\n  projectStore: asFunction(() => new ProjectStore(), { lifetime: Lifetime.SINGLETON }),\n  projectsRepo: asFunction(\n    ({ projectStore, requestContext }) => new ProjectsRepo(projectStore, requestContext),\n    { lifetime: Lifetime.SCOPED },\n  ),\n  projectsService: asFunction(({ projectsRepo }) => new ProjectsService(projectsRepo), {\n    lifetime: Lifetime.SCOPED,\n  }),\n  \u002F\u002F a second feature → six more lines, by hand…\n})\n","Before — container.ts",[50,870,871,900,920,940,959,963,995,999,1012,1058,1070,1101,1120,1127,1159,1175,1183,1188],{"__ignoreMap":82},[86,872,873,875,877,880,882,885,887,890,892,894,896,898],{"class":88,"line":89},[86,874,93],{"class":92},[86,876,97],{"class":96},[86,878,879],{"class":100}," createContainer",[86,881,104],{"class":96},[86,883,884],{"class":100}," asFunction",[86,886,104],{"class":96},[86,888,889],{"class":100}," Lifetime",[86,891,110],{"class":96},[86,893,113],{"class":92},[86,895,116],{"class":96},[86,897,142],{"class":119},[86,899,123],{"class":96},[86,901,902,904,906,909,911,913,915,918],{"class":88,"line":126},[86,903,93],{"class":92},[86,905,97],{"class":96},[86,907,908],{"class":100}," ProjectStore",[86,910,110],{"class":96},[86,912,113],{"class":92},[86,914,116],{"class":96},[86,916,917],{"class":119},".\u002Fprojects\u002Fprojects.store",[86,919,123],{"class":96},[86,921,922,924,926,929,931,933,935,938],{"class":88,"line":147},[86,923,93],{"class":92},[86,925,97],{"class":96},[86,927,928],{"class":100}," ProjectsRepo",[86,930,110],{"class":96},[86,932,113],{"class":92},[86,934,116],{"class":96},[86,936,937],{"class":119},".\u002Fprojects\u002Fprojects.repo",[86,939,123],{"class":96},[86,941,942,944,946,948,950,952,954,957],{"class":88,"line":168},[86,943,93],{"class":92},[86,945,97],{"class":96},[86,947,259],{"class":100},[86,949,110],{"class":96},[86,951,113],{"class":92},[86,953,116],{"class":96},[86,955,956],{"class":119},".\u002Fprojects\u002Fprojects.service",[86,958,123],{"class":96},[86,960,961],{"class":88,"line":189},[86,962,298],{"emptyLinePlaceholder":297},[86,964,965,967,970,973,976,978,980,982,985,987,991,993],{"class":88,"line":210},[86,966,304],{"class":92},[86,968,969],{"class":316}," const",[86,971,972],{"class":100}," container ",[86,974,975],{"class":96},"=",[86,977,879],{"class":310},[86,979,313],{"class":100},[86,981,485],{"class":96},[86,983,984],{"class":353}," strict",[86,986,491],{"class":96},[86,988,990],{"class":989},"sfNiH"," true",[86,992,110],{"class":96},[86,994,358],{"class":100},[86,996,997],{"class":88,"line":231},[86,998,298],{"emptyLinePlaceholder":297},[86,1000,1001,1004,1006,1008,1010],{"class":88,"line":252},[86,1002,1003],{"class":100},"container",[86,1005,429],{"class":96},[86,1007,480],{"class":310},[86,1009,313],{"class":100},[86,1011,610],{"class":96},[86,1013,1014,1017,1019,1021,1023,1026,1028,1031,1033,1035,1037,1039,1042,1044,1046,1048,1051,1053,1055],{"class":88,"line":273},[86,1015,1016],{"class":353},"  projectStore",[86,1018,491],{"class":96},[86,1020,884],{"class":310},[86,1022,313],{"class":100},[86,1024,1025],{"class":96},"()",[86,1027,330],{"class":316},[86,1029,1030],{"class":96}," new",[86,1032,908],{"class":310},[86,1034,1025],{"class":100},[86,1036,104],{"class":96},[86,1038,97],{"class":96},[86,1040,1041],{"class":353}," lifetime",[86,1043,491],{"class":96},[86,1045,889],{"class":100},[86,1047,429],{"class":96},[86,1049,1050],{"class":100},"SINGLETON ",[86,1052,530],{"class":96},[86,1054,327],{"class":100},[86,1056,1057],{"class":96},",\n",[86,1059,1060,1063,1065,1067],{"class":88,"line":294},[86,1061,1062],{"class":353},"  projectsRepo",[86,1064,491],{"class":96},[86,1066,884],{"class":310},[86,1068,1069],{"class":100},"(\n",[86,1071,1072,1075,1078,1080,1082,1085,1087,1089,1091,1094,1096,1099],{"class":88,"line":301},[86,1073,1074],{"class":96},"    ({",[86,1076,1077],{"class":323}," projectStore",[86,1079,104],{"class":96},[86,1081,488],{"class":323},[86,1083,1084],{"class":96}," })",[86,1086,330],{"class":316},[86,1088,1030],{"class":96},[86,1090,928],{"class":310},[86,1092,1093],{"class":100},"(projectStore",[86,1095,104],{"class":96},[86,1097,1098],{"class":100}," requestContext)",[86,1100,1057],{"class":96},[86,1102,1103,1106,1108,1110,1112,1114,1117],{"class":88,"line":336},[86,1104,1105],{"class":96},"    {",[86,1107,1041],{"class":353},[86,1109,491],{"class":96},[86,1111,889],{"class":100},[86,1113,429],{"class":96},[86,1115,1116],{"class":100},"SCOPED ",[86,1118,1119],{"class":96},"},\n",[86,1121,1122,1125],{"class":88,"line":361},[86,1123,1124],{"class":100},"  )",[86,1126,1057],{"class":96},[86,1128,1129,1132,1134,1136,1138,1141,1144,1146,1148,1150,1152,1155,1157],{"class":88,"line":379},[86,1130,1131],{"class":353},"  projectsService",[86,1133,491],{"class":96},[86,1135,884],{"class":310},[86,1137,313],{"class":100},[86,1139,1140],{"class":96},"({",[86,1142,1143],{"class":323}," projectsRepo",[86,1145,1084],{"class":96},[86,1147,330],{"class":316},[86,1149,1030],{"class":96},[86,1151,259],{"class":310},[86,1153,1154],{"class":100},"(projectsRepo)",[86,1156,104],{"class":96},[86,1158,333],{"class":96},[86,1160,1161,1164,1166,1168,1170,1173],{"class":88,"line":387},[86,1162,1163],{"class":353},"    lifetime",[86,1165,491],{"class":96},[86,1167,889],{"class":100},[86,1169,429],{"class":96},[86,1171,1172],{"class":100},"SCOPED",[86,1174,1057],{"class":96},[86,1176,1177,1179,1181],{"class":88,"line":410},[86,1178,597],{"class":96},[86,1180,327],{"class":100},[86,1182,1057],{"class":96},[86,1184,1185],{"class":88,"line":416},[86,1186,1187],{"class":375},"  \u002F\u002F a second feature → six more lines, by hand…\n",[86,1189,1190,1192],{"class":88,"line":449},[86,1191,530],{"class":96},[86,1193,358],{"class":100},[76,1195,1198],{"className":78,"code":1196,"filename":1197,"language":81,"meta":82,"style":82},"\u002F\u002F No imports — Injectable \u002F Scoped and the RequestContext type are auto-imported.\n@Injectable() \u002F\u002F SINGLETON\nexport class ProjectStore {}\n\n@Scoped() \u002F\u002F tenant-bound, per request (shorthand for @Injectable({ scope: 'scoped' }))\nexport class ProjectsRepo {\n  constructor(\n    private readonly store: ProjectStore,\n    private readonly ctx: RequestContext,\n  ) {}\n}\n\n@Scoped()\nexport class ProjectsService {\n  constructor(private readonly repo: ProjectsRepo) {}\n}\n","After — the classes themselves",[50,1199,1200,1205,1217,1227,1231,1243,1253,1259,1275,1290,1296,1300,1304,1312,1322,1343],{"__ignoreMap":82},[86,1201,1202],{"class":88,"line":89},[86,1203,1204],{"class":375},"\u002F\u002F No imports — Injectable \u002F Scoped and the RequestContext type are auto-imported.\n",[86,1206,1207,1209,1212,1214],{"class":88,"line":126},[86,1208,710],{"class":96},[86,1210,1211],{"class":310},"Injectable",[86,1213,466],{"class":100},[86,1215,1216],{"class":375},"\u002F\u002F SINGLETON\n",[86,1218,1219,1221,1223,1225],{"class":88,"line":147},[86,1220,304],{"class":92},[86,1222,735],{"class":316},[86,1224,908],{"class":551},[86,1226,765],{"class":96},[86,1228,1229],{"class":88,"line":168},[86,1230,298],{"emptyLinePlaceholder":297},[86,1232,1233,1235,1238,1240],{"class":88,"line":189},[86,1234,710],{"class":96},[86,1236,1237],{"class":310},"Scoped",[86,1239,466],{"class":100},[86,1241,1242],{"class":375},"\u002F\u002F tenant-bound, per request (shorthand for @Injectable({ scope: 'scoped' }))\n",[86,1244,1245,1247,1249,1251],{"class":88,"line":210},[86,1246,304],{"class":92},[86,1248,735],{"class":316},[86,1250,928],{"class":551},[86,1252,333],{"class":96},[86,1254,1255,1257],{"class":88,"line":231},[86,1256,745],{"class":316},[86,1258,1069],{"class":96},[86,1260,1261,1264,1266,1269,1271,1273],{"class":88,"line":252},[86,1262,1263],{"class":316},"    private",[86,1265,753],{"class":316},[86,1267,1268],{"class":323}," store",[86,1270,491],{"class":96},[86,1272,908],{"class":551},[86,1274,1057],{"class":96},[86,1276,1277,1279,1281,1283,1285,1288],{"class":88,"line":273},[86,1278,1263],{"class":316},[86,1280,753],{"class":316},[86,1282,342],{"class":323},[86,1284,491],{"class":96},[86,1286,1287],{"class":551}," RequestContext",[86,1289,1057],{"class":96},[86,1291,1292,1294],{"class":88,"line":294},[86,1293,1124],{"class":96},[86,1295,765],{"class":96},[86,1297,1298],{"class":88,"line":301},[86,1299,848],{"class":96},[86,1301,1302],{"class":88,"line":336},[86,1303,298],{"emptyLinePlaceholder":297},[86,1305,1306,1308,1310],{"class":88,"line":361},[86,1307,710],{"class":96},[86,1309,1237],{"class":310},[86,1311,780],{"class":100},[86,1313,1314,1316,1318,1320],{"class":88,"line":379},[86,1315,304],{"class":92},[86,1317,735],{"class":316},[86,1319,259],{"class":551},[86,1321,333],{"class":96},[86,1323,1324,1326,1328,1330,1332,1335,1337,1339,1341],{"class":88,"line":387},[86,1325,745],{"class":316},[86,1327,313],{"class":96},[86,1329,750],{"class":316},[86,1331,753],{"class":316},[86,1333,1334],{"class":323}," repo",[86,1336,491],{"class":96},[86,1338,928],{"class":551},[86,1340,327],{"class":96},[86,1342,765],{"class":96},[86,1344,1345],{"class":88,"line":410},[86,1346,848],{"class":96},[42,1348,1349],{},"There's no composition root to maintain: the lifetime and the dependency graph live on the\nclasses, and roost aggregates the container. 4 providers or 400, nobody hand-edits it.",[65,1351,1353],{"id":1352},"the-delta","The delta",[1355,1356,1357,1372],"table",{},[1358,1359,1360],"thead",{},[1361,1362,1363,1366,1369],"tr",{},[1364,1365],"th",{},[1364,1367,1368],{},"Hand-rolled",[1364,1370,1371],{},"roost",[1373,1374,1375,1392,1406,1422,1435,1450,1463,1475],"tbody",{},[1361,1376,1377,1383,1386],{},[1378,1379,1380],"td",{},[46,1381,1382],{},"Route files you write",[1378,1384,1385],{},"one per endpoint, ~15–25 lines of wiring each",[1378,1387,1388,1391],{},[46,1389,1390],{},"0"," (generated)",[1361,1393,1394,1399,1402],{},[1378,1395,1396],{},[46,1397,1398],{},"DI container you maintain",[1378,1400,1401],{},"one file, edited on every provider change",[1378,1403,1404,1391],{},[46,1405,1390],{},[1361,1407,1408,1413,1416],{},[1378,1409,1410],{},[46,1411,1412],{},"Imports per feature file",[1378,1414,1415],{},"every dependency, by hand",[1378,1417,1418,1421],{},[46,1419,1420],{},"none"," — decorators\u002Ftypes + your own pieces are auto-imported",[1361,1423,1424,1429,1432],{},[1378,1425,1426],{},[46,1427,1428],{},"Cross-cutting (guard\u002Faudit\u002Ferror-map)",[1378,1430,1431],{},"threaded by hand into every handler",[1378,1433,1434],{},"decorators + globals",[1361,1436,1437,1442,1445],{},[1378,1438,1439],{},[46,1440,1441],{},"Add an endpoint",[1378,1443,1444],{},"new route file + wire guard\u002Faudit\u002Fscope\u002Fvalidate",[1378,1446,1447],{},[46,1448,1449],{},"add one decorated method",[1361,1451,1452,1457,1460],{},[1378,1453,1454],{},[46,1455,1456],{},"Forget a guard \u002F audit on a route?",[1378,1458,1459],{},"silently public \u002F un-audited",[1378,1461,1462],{},"class-level + global, can't be missed",[1361,1464,1465,1470,1472],{},[1378,1466,1467],{},[46,1468,1469],{},"Runtime reflection",[1378,1471,1420],{},[1378,1473,1474],{},"none (AST read at build time)",[1361,1476,1477,1482,1485],{},[1378,1478,1479],{},[46,1480,1481],{},"Behavior",[1378,1483,1484],{},"← identical →",[1378,1486,1487],{},"identical",[65,1489,1491],{"id":1490},"the-tell","The tell",[42,1493,1494,1495,1498,1499,1505],{},"Notice the repeated guard \u002F scope \u002F audit \u002F error-map in every hand-written handler. The obvious\nnext step is to factor that shared dance into a ",[50,1496,1497],{},"runRequest(token, method, opts)"," helper — at\nwhich point ",[46,1500,1501,1502],{},"you've hand-written roost's ",[50,1503,1504],{},"route()",", and the container is roost's generated DI\nmanifest. roost is what you converge on; it just generates it instead of asking you to maintain it.",[1507,1508,1509],"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 pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}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}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 .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}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 .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":82,"searchDepth":126,"depth":126,"links":1511},[1512,1513,1514,1515],{"id":67,"depth":126,"text":68},{"id":858,"depth":126,"text":859},{"id":1352,"depth":126,"text":1353},{"id":1490,"depth":126,"text":1491},"The same feature hand-wired in plain Nitro vs. with roost — proven identical in behavior.","md",null,{},{"title":29,"description":1516},"DyhXFhXDsURsOF2OaZ0ifryTF72cDKZxNRY9GY89VfQ",[1523,1525],{"title":25,"path":26,"stem":27,"description":1524,"children":-1},"What's the same, what's deliberately different, and what roost is not.",{"title":33,"path":34,"stem":35,"description":1526,"children":-1},"@RoostModule: cross-feature wiring and folder-wide chains, with encapsulation enforced at build time over a flat container.",1780506501961]