[{"data":1,"prerenderedAt":660},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Fbusiness\u002Fcollectors":186},[4],{"title":5,"path":6,"stem":7,"children":8},"Docs","\u002Fdocs","docs",[9,12,16,20,24,28,50,75,120,145],{"title":10,"path":6,"stem":11},"What is Fougere","docs\u002Findex",{"title":13,"path":14,"stem":15},"Getting started","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",{"title":17,"path":18,"stem":19},"An app you already have","\u002Fdocs\u002Fexisting-app","docs\u002F02.existing-app",{"title":21,"path":22,"stem":23},"Bring your schema","\u002Fdocs\u002Fbring-your-schema","docs\u002F03.bring-your-schema",{"title":25,"path":26,"stem":27},"The CLI","\u002Fdocs\u002Fcli","docs\u002F04.cli",{"title":29,"path":30,"stem":31,"children":32,"page":49},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[33,37,41,45],{"title":34,"path":35,"stem":36},"Philosophy","\u002Fdocs\u002Fconcepts\u002Fphilosophy","docs\u002F2.concepts\u002F1.philosophy",{"title":38,"path":39,"stem":40},"The Frond","\u002Fdocs\u002Fconcepts\u002Ffrond","docs\u002F2.concepts\u002F2.frond",{"title":42,"path":43,"stem":44},"The shortest path","\u002Fdocs\u002Fconcepts\u002Fshortest-path","docs\u002F2.concepts\u002F3.shortest-path",{"title":46,"path":47,"stem":48},"The base","\u002Fdocs\u002Fconcepts\u002Fthe-base","docs\u002F2.concepts\u002F4.the-base",false,{"title":51,"path":52,"stem":53,"children":54,"page":49},"Schema","\u002Fdocs\u002Fschema","docs\u002F3.schema",[55,59,63,67,71],{"title":56,"path":57,"stem":58},"Entities","\u002Fdocs\u002Fschema\u002Fentities","docs\u002F3.schema\u002F1.entities",{"title":60,"path":61,"stem":62},"Views","\u002Fdocs\u002Fschema\u002Fviews","docs\u002F3.schema\u002F2.views",{"title":64,"path":65,"stem":66},"The identity card","\u002Fdocs\u002Fschema\u002Fcard","docs\u002F3.schema\u002F3.card",{"title":68,"path":69,"stem":70},"Standard Schema","\u002Fdocs\u002Fschema\u002Fstandard-schema","docs\u002F3.schema\u002F4.standard-schema",{"title":72,"path":73,"stem":74},"How a schema moves","\u002Fdocs\u002Fschema\u002Fevolution","docs\u002F3.schema\u002F5.evolution",{"title":76,"path":77,"stem":78,"children":79,"page":49},"Business","\u002Fdocs\u002Fbusiness","docs\u002F4.business",[80,84,88,92,96,100,104,108,112,116],{"title":81,"path":82,"stem":83},"Handlers","\u002Fdocs\u002Fbusiness\u002Fhandlers","docs\u002F4.business\u002F1.handlers",{"title":85,"path":86,"stem":87},"Ports","\u002Fdocs\u002Fbusiness\u002Fports","docs\u002F4.business\u002F10.ports",{"title":89,"path":90,"stem":91},"Presenters","\u002Fdocs\u002Fbusiness\u002Fpresenters","docs\u002F4.business\u002F2.presenters",{"title":93,"path":94,"stem":95},"Collectors","\u002Fdocs\u002Fbusiness\u002Fcollectors","docs\u002F4.business\u002F3.collectors",{"title":97,"path":98,"stem":99},"Errors","\u002Fdocs\u002Fbusiness\u002Ferrors","docs\u002F4.business\u002F4.errors",{"title":101,"path":102,"stem":103},"Seeds","\u002Fdocs\u002Fbusiness\u002Fseeds","docs\u002F4.business\u002F5.seeds",{"title":105,"path":106,"stem":107},"Facts","\u002Fdocs\u002Fbusiness\u002Ffacts","docs\u002F4.business\u002F6.facts",{"title":109,"path":110,"stem":111},"Storage","\u002Fdocs\u002Fbusiness\u002Fstorage","docs\u002F4.business\u002F7.storage",{"title":113,"path":114,"stem":115},"Writes that stand or fall as one","\u002Fdocs\u002Fbusiness\u002Ftogether","docs\u002F4.business\u002F8.together",{"title":117,"path":118,"stem":119},"Repositories","\u002Fdocs\u002Fbusiness\u002Frepositories","docs\u002F4.business\u002F9.repositories",{"title":121,"path":122,"stem":123,"children":124,"page":49},"Client","\u002Fdocs\u002Fclient","docs\u002F5.client",[125,129,133,137,141],{"title":126,"path":127,"stem":128},"Queries & commands","\u002Fdocs\u002Fclient\u002Fqueries-commands","docs\u002F5.client\u002F1.queries-commands",{"title":130,"path":131,"stem":132},"Forms","\u002Fdocs\u002Fclient\u002Fforms","docs\u002F5.client\u002F2.forms",{"title":134,"path":135,"stem":136},"Session","\u002Fdocs\u002Fclient\u002Fsession","docs\u002F5.client\u002F3.session",{"title":138,"path":139,"stem":140},"invoke","\u002Fdocs\u002Fclient\u002Finvoke","docs\u002F5.client\u002F4.invoke",{"title":142,"path":143,"stem":144},"Back-office","\u002Fdocs\u002Fclient\u002Fadmin","docs\u002F5.client\u002F5.admin",{"title":146,"path":147,"stem":148,"children":149,"page":49},"Infrastructure","\u002Fdocs\u002Finfra","docs\u002F6.infra",[150,154,158,162,166,170,174,178,182],{"title":151,"path":152,"stem":153},"The gradient","\u002Fdocs\u002Finfra\u002Fgradient","docs\u002F6.infra\u002F1.gradient",{"title":155,"path":156,"stem":157},"Surfaces — REST & GraphQL","\u002Fdocs\u002Finfra\u002Fsurfaces","docs\u002F6.infra\u002F2.surfaces",{"title":159,"path":160,"stem":161},"Deployment","\u002Fdocs\u002Finfra\u002Fdeployment","docs\u002F6.infra\u002F3.deployment",{"title":163,"path":164,"stem":165},"Hosts","\u002Fdocs\u002Finfra\u002Fhosts","docs\u002F6.infra\u002F4.hosts",{"title":167,"path":168,"stem":169},"Sources","\u002Fdocs\u002Finfra\u002Fsources","docs\u002F6.infra\u002F5.sources",{"title":171,"path":172,"stem":173},"Lifecycle","\u002Fdocs\u002Finfra\u002Flifecycle","docs\u002F6.infra\u002F6.lifecycle",{"title":175,"path":176,"stem":177},"Identity across Fronds","\u002Fdocs\u002Finfra\u002Fidentity","docs\u002F6.infra\u002F7.identity",{"title":179,"path":180,"stem":181},"Observability","\u002Fdocs\u002Finfra\u002Fobservability","docs\u002F6.infra\u002F8.observability",{"title":183,"path":184,"stem":185},"Testing","\u002Fdocs\u002Finfra\u002Ftesting","docs\u002F6.infra\u002F9.testing",{"id":187,"title":93,"body":188,"description":655,"extension":656,"meta":657,"navigation":301,"path":94,"seo":658,"stem":95,"__hash__":659},"docs_en\u002Fdocs\u002F4.business\u002F3.collectors.md",{"type":189,"value":190,"toc":650},"minimark",[191,195,204,433,436,512,517,569,573,583,587,590,620,633,639,646],[192,193,93],"h1",{"id":194},"collectors",[196,197,198,199,203],"p",{},"A collector resolves a handler parameter ",[200,201,202],"strong",{},"by its type"," from the invocation context.\nOperations declaring that parameter receive the value automatically.",[205,206,211],"pre",{"className":207,"code":208,"language":209,"meta":210,"style":210},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","import { Collector } from '@fougere\u002Fcore';\nimport type { InvocationContext } from '@fougere\u002Fcore';\nimport User from '..\u002F..\u002Fuser\u002Fentities\u002FUser.js';\n\n\u002F**\n * The auth middleware puts the session user on ctx.state.user —\n * this surfaces it to any handler that declares `user?: User`.\n *\u002F\nexport default class CurrentUserCollector extends Collector(User) {\n  async collect(ctx: InvocationContext) {\n    return ctx.state.user as User | undefined;\n  }\n}\n","ts","",[212,213,214,251,276,296,303,310,316,322,328,357,386,421,427],"code",{"__ignoreMap":210},[215,216,219,223,227,231,234,237,241,245,248],"span",{"class":217,"line":218},"line",1,[215,220,222],{"class":221},"sVHd0","import",[215,224,226],{"class":225},"sP7_E"," {",[215,228,230],{"class":229},"su5hD"," Collector",[215,232,233],{"class":225}," }",[215,235,236],{"class":221}," from",[215,238,240],{"class":239},"sjJ54"," '",[215,242,244],{"class":243},"s_sjI","@fougere\u002Fcore",[215,246,247],{"class":239},"'",[215,249,250],{"class":225},";\n",[215,252,254,256,259,261,264,266,268,270,272,274],{"class":217,"line":253},2,[215,255,222],{"class":221},[215,257,258],{"class":221}," type",[215,260,226],{"class":225},[215,262,263],{"class":229}," InvocationContext",[215,265,233],{"class":225},[215,267,236],{"class":221},[215,269,240],{"class":239},[215,271,244],{"class":243},[215,273,247],{"class":239},[215,275,250],{"class":225},[215,277,279,281,284,287,289,292,294],{"class":217,"line":278},3,[215,280,222],{"class":221},[215,282,283],{"class":229}," User ",[215,285,286],{"class":221},"from",[215,288,240],{"class":239},[215,290,291],{"class":243},"..\u002F..\u002Fuser\u002Fentities\u002FUser.js",[215,293,247],{"class":239},[215,295,250],{"class":225},[215,297,299],{"class":217,"line":298},4,[215,300,302],{"emptyLinePlaceholder":301},true,"\n",[215,304,306],{"class":217,"line":305},5,[215,307,309],{"class":308},"sutJx","\u002F**\n",[215,311,313],{"class":217,"line":312},6,[215,314,315],{"class":308}," * The auth middleware puts the session user on ctx.state.user —\n",[215,317,319],{"class":217,"line":318},7,[215,320,321],{"class":308}," * this surfaces it to any handler that declares `user?: User`.\n",[215,323,325],{"class":217,"line":324},8,[215,326,327],{"class":308}," *\u002F\n",[215,329,331,334,337,341,345,348,351,354],{"class":217,"line":330},9,[215,332,333],{"class":221},"export",[215,335,336],{"class":221}," default",[215,338,340],{"class":339},"sbsja"," class",[215,342,344],{"class":343},"sbgvK"," CurrentUserCollector",[215,346,347],{"class":339}," extends",[215,349,230],{"class":350},"sGLFI",[215,352,353],{"class":229},"(User) ",[215,355,356],{"class":225},"{\n",[215,358,360,363,367,370,374,378,380,383],{"class":217,"line":359},10,[215,361,362],{"class":339},"  async",[215,364,366],{"class":365},"sVXei"," collect",[215,368,369],{"class":225},"(",[215,371,373],{"class":372},"s99_P","ctx",[215,375,377],{"class":376},"smGrS",":",[215,379,263],{"class":343},[215,381,382],{"class":225},")",[215,384,385],{"class":225}," {\n",[215,387,389,392,395,398,401,403,406,409,412,415,419],{"class":217,"line":388},11,[215,390,391],{"class":221},"    return",[215,393,394],{"class":229}," ctx",[215,396,397],{"class":225},".",[215,399,400],{"class":229},"state",[215,402,397],{"class":225},[215,404,405],{"class":229},"user",[215,407,408],{"class":221}," as",[215,410,411],{"class":343}," User",[215,413,414],{"class":376}," |",[215,416,418],{"class":417},"sZMiF"," undefined",[215,420,250],{"class":225},[215,422,424],{"class":217,"line":423},12,[215,425,426],{"class":225},"  }\n",[215,428,430],{"class":217,"line":429},13,[215,431,432],{"class":225},"}\n",[196,434,435],{},"From then on, in any handler of the Frond:",[205,437,439],{"className":207,"code":438,"language":209,"meta":210,"style":210},"async mine(user?: User): Promise\u003CPost[]> { … }\nasync publish(id: string, user?: User): Promise\u003CPost> { … }\n",[212,440,441,477],{"__ignoreMap":210},[215,442,443,446,449,452,455,458,461,464,467,470,472,475],{"class":217,"line":218},[215,444,445],{"class":229},"async ",[215,447,448],{"class":350},"mine",[215,450,451],{"class":229},"(user",[215,453,454],{"class":376},"?:",[215,456,457],{"class":229}," User): ",[215,459,460],{"class":417},"Promise",[215,462,463],{"class":376},"\u003C",[215,465,466],{"class":229},"Post[]",[215,468,469],{"class":376},">",[215,471,226],{"class":225},[215,473,474],{"class":229}," … ",[215,476,432],{"class":225},[215,478,479,481,484,487,490,493,495,497,499,501,504,506,508,510],{"class":217,"line":253},[215,480,445],{"class":229},[215,482,483],{"class":350},"publish",[215,485,486],{"class":229},"(id: string",[215,488,489],{"class":225},",",[215,491,492],{"class":229}," user",[215,494,454],{"class":376},[215,496,457],{"class":229},[215,498,460],{"class":417},[215,500,463],{"class":376},[215,502,503],{"class":229},"Post",[215,505,469],{"class":376},[215,507,226],{"class":225},[215,509,474],{"class":229},[215,511,432],{"class":225},[513,514,516],"h2",{"id":515},"resolution-rules","Resolution rules",[518,519,520,544,551,566],"ul",{},[521,522,523,524,527,528,531,532,535,536,539,540,543],"li",{},"The match is ",[200,525,526],{},"by checked type",": ",[212,529,530],{},"Collector(User)"," resolves ",[212,533,534],{},"user?: User",",\n",[212,537,538],{},"user: User | undefined",", and aliases with the same meaning. The class is registered as\n",[212,541,542],{},"UserCollector"," by convention.",[521,545,546,547,550],{},"The target needs no schema. Nothing reads its fields, so an ordinary class works —\n",[212,548,549],{},"Collector(Ability)"," for a value you build from state at every call.",[521,552,553,559,560,562,563,397],{},[200,554,555,556,397],{},"Absence is ",[212,557,558],{},"undefined"," Prefer the concise ",[212,561,534],{},". The type checker also\nresolves an alias such as ",[212,564,565],{},"type CurrentUser = User | undefined",[521,567,568],{},"Collectors are DI-injectable classes — declare constructor dependencies as usual.",[513,570,572],{"id":571},"state-and-the-gradient","State and the gradient",[196,574,575,578,579,582],{},[212,576,577],{},"ctx.state"," is the request state built by the consuming application, for example from the\nsession. During a remote call, this state is sent with the invocation, so the collector\nreceives the same ",[212,580,581],{},"ctx.state.user"," as it would locally.",[513,584,586],{"id":585},"a-collector-does-not-cross-a-frond-boundary","A collector does not cross a Frond boundary",[196,588,589],{},"Keep the collector in each Frond that consumes it. What happens otherwise deserves to be\nsaid precisely, because it is neither \"later\" nor \"nothing\":",[518,591,592,603],{},[521,593,594,597,598,602],{},[200,595,596],{},"it is not at the split."," The binding is decided at boot, in a single process, from\nthe collectors ",[599,600,601],"em",{},"of that Frond itself",";",[521,604,605,608,609,612,613,616,617,397],{},[200,606,607],{},"the parameter is not empty."," A type the Frond cannot resolve falls into the fourth\nbinding rule, ",[212,610,611],{},"body",". So the handler receives ",[200,614,615],{},"the request body"," where it expects a\n",[212,618,619],{},"User",[196,621,622,623,625,626,628,629,632],{},"Put plainly: ",[212,624,534],{}," declared in a Frond that has no ",[212,627,542],{}," receives\nwhatever the client sent. A handler judging on ",[212,630,631],{},"user.role"," judges a caller-supplied value.",[634,635,636],"blockquote",{},[196,637,638],{},"An earlier version of this page said the collector was \"lost after a process split\".\nThat was wrong twice, and the truth is less comfortable.",[196,640,641,642,645],{},"Next: ",[643,644,97],"a",{"href":98}," — typed errors across layers.",[647,648,649],"style",{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}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":210,"searchDepth":253,"depth":253,"links":651},[652,653,654],{"id":515,"depth":253,"text":516},{"id":571,"depth":253,"text":572},{"id":585,"depth":253,"text":586},"Resolving handler parameters by type from the invocation context.","md",{},{"title":93,"description":655},"esX0du_dTnppYqPeuMnWGbOROvNeh-lIK-DfF-T6LYg",1788560230296]