[{"data":1,"prerenderedAt":2458},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Fbusiness\u002Fhandlers":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":81,"body":188,"description":2453,"extension":2454,"meta":2455,"navigation":305,"path":82,"seo":2456,"stem":83,"__hash__":2457},"docs_en\u002Fdocs\u002F4.business\u002F1.handlers.md",{"type":189,"value":190,"toc":2438},"minimark",[191,195,204,990,993,1034,1179,1182,1187,1196,1286,1367,1378,1382,1385,1486,1489,1539,1544,1547,1597,1625,1629,1640,1644,1664,1668,1674,1737,1911,1972,1983,1987,1993,2038,2041,2052,2056,2067,2238,2241,2293,2303,2307,2321,2327,2331,2341,2423,2428,2434],[192,193,81],"h1",{"id":194},"handlers",[196,197,198,199,203],"p",{},"A handler groups the operations of one entity. ",[200,201,202],"code",{},"Crud(Entity)"," provides the five CRUD\noperations. Each additional public method becomes an operation.",[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 { Crud, FougereError, ErrorCode } from '@fougere\u002Fcore';\nimport Post from '..\u002Fentities\u002FPost.js';\nimport User from '..\u002F..\u002Fuser\u002Fentities\u002FUser.js';\n\nexport default class PostHandler extends Crud(Post) {\n  \u002F** Public reading: published only. *\u002F\n  async list(): Promise\u003CPostCard[]> {\n    const all = await this.storage.list();\n    return all.filter((p) => p.status === 'published') \u002F* … *\u002F;\n  }\n\n  \u002F** The draft→published transition — an operation, not a field write. *\u002F\n  async publish(id: string, user?: User): Promise\u003CPost> {\n    if (!user) throw new FougereError({ code: ErrorCode.UNAUTHORIZED, message: 'Sign in to publish', entity: 'post', operation: 'publish' });\n    const post = await this.storage.findById(id);\n    if (!post) throw new FougereError({ code: ErrorCode.NOT_FOUND, message: `Post '${id}' not found`, entity: 'post', operation: 'publish' });\n    if (post.authorId !== user.id) throw new FougereError({ code: ErrorCode.FORBIDDEN, message: 'Only the author can publish', entity: 'post', operation: 'publish' });\n    if (post.status === 'published') throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Already published', entity: 'post', operation: 'publish' });\n    return this.storage.update(id, { status: 'published', publishedAt: new Date().toISOString() });\n  }\n}\n","ts","",[200,212,213,261,281,300,307,336,343,378,414,467,473,478,484,529,619,650,740,829,916,979,984],{"__ignoreMap":210},[214,215,218,222,226,230,233,236,238,241,244,247,251,255,258],"span",{"class":216,"line":217},"line",1,[214,219,221],{"class":220},"sVHd0","import",[214,223,225],{"class":224},"sP7_E"," {",[214,227,229],{"class":228},"su5hD"," Crud",[214,231,232],{"class":224},",",[214,234,235],{"class":228}," FougereError",[214,237,232],{"class":224},[214,239,240],{"class":228}," ErrorCode",[214,242,243],{"class":224}," }",[214,245,246],{"class":220}," from",[214,248,250],{"class":249},"sjJ54"," '",[214,252,254],{"class":253},"s_sjI","@fougere\u002Fcore",[214,256,257],{"class":249},"'",[214,259,260],{"class":224},";\n",[214,262,264,266,269,272,274,277,279],{"class":216,"line":263},2,[214,265,221],{"class":220},[214,267,268],{"class":228}," Post ",[214,270,271],{"class":220},"from",[214,273,250],{"class":249},[214,275,276],{"class":253},"..\u002Fentities\u002FPost.js",[214,278,257],{"class":249},[214,280,260],{"class":224},[214,282,284,286,289,291,293,296,298],{"class":216,"line":283},3,[214,285,221],{"class":220},[214,287,288],{"class":228}," User ",[214,290,271],{"class":220},[214,292,250],{"class":249},[214,294,295],{"class":253},"..\u002F..\u002Fuser\u002Fentities\u002FUser.js",[214,297,257],{"class":249},[214,299,260],{"class":224},[214,301,303],{"class":216,"line":302},4,[214,304,306],{"emptyLinePlaceholder":305},true,"\n",[214,308,310,313,316,320,324,327,330,333],{"class":216,"line":309},5,[214,311,312],{"class":220},"export",[214,314,315],{"class":220}," default",[214,317,319],{"class":318},"sbsja"," class",[214,321,323],{"class":322},"sbgvK"," PostHandler",[214,325,326],{"class":318}," extends",[214,328,229],{"class":329},"sGLFI",[214,331,332],{"class":228},"(Post) ",[214,334,335],{"class":224},"{\n",[214,337,339],{"class":216,"line":338},6,[214,340,342],{"class":341},"sutJx","  \u002F** Public reading: published only. *\u002F\n",[214,344,346,349,353,356,360,363,366,369,372,375],{"class":216,"line":345},7,[214,347,348],{"class":318},"  async",[214,350,352],{"class":351},"sVXei"," list",[214,354,355],{"class":224},"()",[214,357,359],{"class":358},"smGrS",":",[214,361,362],{"class":322}," Promise",[214,364,365],{"class":224},"\u003C",[214,367,368],{"class":322},"PostCard",[214,370,371],{"class":228},"[]",[214,373,374],{"class":224},">",[214,376,377],{"class":224}," {\n",[214,379,381,384,388,391,394,398,401,404,406,409,412],{"class":216,"line":380},8,[214,382,383],{"class":318},"    const",[214,385,387],{"class":386},"s_hVV"," all",[214,389,390],{"class":358}," =",[214,392,393],{"class":220}," await",[214,395,397],{"class":396},"s39Yj"," this",[214,399,400],{"class":224},".",[214,402,403],{"class":228},"storage",[214,405,400],{"class":224},[214,407,408],{"class":329},"list",[214,410,355],{"class":411},"skxfh",[214,413,260],{"class":224},[214,415,417,420,422,424,427,430,432,435,438,441,444,446,449,452,454,457,459,462,465],{"class":216,"line":416},9,[214,418,419],{"class":220},"    return",[214,421,387],{"class":228},[214,423,400],{"class":224},[214,425,426],{"class":329},"filter",[214,428,429],{"class":411},"(",[214,431,429],{"class":224},[214,433,196],{"class":434},"s99_P",[214,436,437],{"class":224},")",[214,439,440],{"class":318}," =>",[214,442,443],{"class":228}," p",[214,445,400],{"class":224},[214,447,448],{"class":228},"status",[214,450,451],{"class":358}," ===",[214,453,250],{"class":249},[214,455,456],{"class":253},"published",[214,458,257],{"class":249},[214,460,461],{"class":411},") ",[214,463,464],{"class":341},"\u002F* … *\u002F",[214,466,260],{"class":224},[214,468,470],{"class":216,"line":469},10,[214,471,472],{"class":224},"  }\n",[214,474,476],{"class":216,"line":475},11,[214,477,306],{"emptyLinePlaceholder":305},[214,479,481],{"class":216,"line":480},12,[214,482,483],{"class":341},"  \u002F** The draft→published transition — an operation, not a field write. *\u002F\n",[214,485,487,489,492,494,497,499,503,505,508,511,514,516,518,520,522,525,527],{"class":216,"line":486},13,[214,488,348],{"class":318},[214,490,491],{"class":351}," publish",[214,493,429],{"class":224},[214,495,496],{"class":434},"id",[214,498,359],{"class":358},[214,500,502],{"class":501},"sZMiF"," string",[214,504,232],{"class":224},[214,506,507],{"class":434}," user",[214,509,510],{"class":358},"?:",[214,512,513],{"class":322}," User",[214,515,437],{"class":224},[214,517,359],{"class":358},[214,519,362],{"class":322},[214,521,365],{"class":224},[214,523,524],{"class":322},"Post",[214,526,374],{"class":224},[214,528,377],{"class":224},[214,530,532,535,538,541,544,546,549,552,554,556,559,562,564,566,568,571,573,576,578,580,583,585,587,590,592,594,597,599,601,604,606,608,611,613,615,617],{"class":216,"line":531},14,[214,533,534],{"class":220},"    if",[214,536,537],{"class":411}," (",[214,539,540],{"class":358},"!",[214,542,543],{"class":228},"user",[214,545,461],{"class":411},[214,547,548],{"class":220},"throw",[214,550,551],{"class":358}," new",[214,553,235],{"class":329},[214,555,429],{"class":411},[214,557,558],{"class":224},"{",[214,560,561],{"class":411}," code",[214,563,359],{"class":224},[214,565,240],{"class":228},[214,567,400],{"class":224},[214,569,570],{"class":386},"UNAUTHORIZED",[214,572,232],{"class":224},[214,574,575],{"class":411}," message",[214,577,359],{"class":224},[214,579,250],{"class":249},[214,581,582],{"class":253},"Sign in to publish",[214,584,257],{"class":249},[214,586,232],{"class":224},[214,588,589],{"class":411}," entity",[214,591,359],{"class":224},[214,593,250],{"class":249},[214,595,596],{"class":253},"post",[214,598,257],{"class":249},[214,600,232],{"class":224},[214,602,603],{"class":411}," operation",[214,605,359],{"class":224},[214,607,250],{"class":249},[214,609,610],{"class":253},"publish",[214,612,257],{"class":249},[214,614,243],{"class":224},[214,616,437],{"class":411},[214,618,260],{"class":224},[214,620,622,624,627,629,631,633,635,637,639,642,644,646,648],{"class":216,"line":621},15,[214,623,383],{"class":318},[214,625,626],{"class":386}," post",[214,628,390],{"class":358},[214,630,393],{"class":220},[214,632,397],{"class":396},[214,634,400],{"class":224},[214,636,403],{"class":228},[214,638,400],{"class":224},[214,640,641],{"class":329},"findById",[214,643,429],{"class":411},[214,645,496],{"class":228},[214,647,437],{"class":411},[214,649,260],{"class":224},[214,651,653,655,657,659,661,663,665,667,669,671,673,675,677,679,681,684,686,688,690,693,696,699,701,704,707,710,712,714,716,718,720,722,724,726,728,730,732,734,736,738],{"class":216,"line":652},16,[214,654,534],{"class":220},[214,656,537],{"class":411},[214,658,540],{"class":358},[214,660,596],{"class":228},[214,662,461],{"class":411},[214,664,548],{"class":220},[214,666,551],{"class":358},[214,668,235],{"class":329},[214,670,429],{"class":411},[214,672,558],{"class":224},[214,674,561],{"class":411},[214,676,359],{"class":224},[214,678,240],{"class":228},[214,680,400],{"class":224},[214,682,683],{"class":386},"NOT_FOUND",[214,685,232],{"class":224},[214,687,575],{"class":411},[214,689,359],{"class":224},[214,691,692],{"class":249}," `",[214,694,695],{"class":253},"Post '",[214,697,698],{"class":249},"${",[214,700,496],{"class":228},[214,702,703],{"class":249},"}",[214,705,706],{"class":253},"' not found",[214,708,709],{"class":249},"`",[214,711,232],{"class":224},[214,713,589],{"class":411},[214,715,359],{"class":224},[214,717,250],{"class":249},[214,719,596],{"class":253},[214,721,257],{"class":249},[214,723,232],{"class":224},[214,725,603],{"class":411},[214,727,359],{"class":224},[214,729,250],{"class":249},[214,731,610],{"class":253},[214,733,257],{"class":249},[214,735,243],{"class":224},[214,737,437],{"class":411},[214,739,260],{"class":224},[214,741,743,745,747,749,751,754,757,759,761,763,765,767,769,771,773,775,777,779,781,783,786,788,790,792,794,797,799,801,803,805,807,809,811,813,815,817,819,821,823,825,827],{"class":216,"line":742},17,[214,744,534],{"class":220},[214,746,537],{"class":411},[214,748,596],{"class":228},[214,750,400],{"class":224},[214,752,753],{"class":228},"authorId",[214,755,756],{"class":358}," !==",[214,758,507],{"class":228},[214,760,400],{"class":224},[214,762,496],{"class":228},[214,764,461],{"class":411},[214,766,548],{"class":220},[214,768,551],{"class":358},[214,770,235],{"class":329},[214,772,429],{"class":411},[214,774,558],{"class":224},[214,776,561],{"class":411},[214,778,359],{"class":224},[214,780,240],{"class":228},[214,782,400],{"class":224},[214,784,785],{"class":386},"FORBIDDEN",[214,787,232],{"class":224},[214,789,575],{"class":411},[214,791,359],{"class":224},[214,793,250],{"class":249},[214,795,796],{"class":253},"Only the author can publish",[214,798,257],{"class":249},[214,800,232],{"class":224},[214,802,589],{"class":411},[214,804,359],{"class":224},[214,806,250],{"class":249},[214,808,596],{"class":253},[214,810,257],{"class":249},[214,812,232],{"class":224},[214,814,603],{"class":411},[214,816,359],{"class":224},[214,818,250],{"class":249},[214,820,610],{"class":253},[214,822,257],{"class":249},[214,824,243],{"class":224},[214,826,437],{"class":411},[214,828,260],{"class":224},[214,830,832,834,836,838,840,842,844,846,848,850,852,854,856,858,860,862,864,866,868,870,873,875,877,879,881,884,886,888,890,892,894,896,898,900,902,904,906,908,910,912,914],{"class":216,"line":831},18,[214,833,534],{"class":220},[214,835,537],{"class":411},[214,837,596],{"class":228},[214,839,400],{"class":224},[214,841,448],{"class":228},[214,843,451],{"class":358},[214,845,250],{"class":249},[214,847,456],{"class":253},[214,849,257],{"class":249},[214,851,461],{"class":411},[214,853,548],{"class":220},[214,855,551],{"class":358},[214,857,235],{"class":329},[214,859,429],{"class":411},[214,861,558],{"class":224},[214,863,561],{"class":411},[214,865,359],{"class":224},[214,867,240],{"class":228},[214,869,400],{"class":224},[214,871,872],{"class":386},"CONFLICT",[214,874,232],{"class":224},[214,876,575],{"class":411},[214,878,359],{"class":224},[214,880,250],{"class":249},[214,882,883],{"class":253},"Already published",[214,885,257],{"class":249},[214,887,232],{"class":224},[214,889,589],{"class":411},[214,891,359],{"class":224},[214,893,250],{"class":249},[214,895,596],{"class":253},[214,897,257],{"class":249},[214,899,232],{"class":224},[214,901,603],{"class":411},[214,903,359],{"class":224},[214,905,250],{"class":249},[214,907,610],{"class":253},[214,909,257],{"class":249},[214,911,243],{"class":224},[214,913,437],{"class":411},[214,915,260],{"class":224},[214,917,919,921,923,925,927,929,932,934,936,938,940,943,945,947,949,951,953,956,958,960,963,965,967,970,973,975,977],{"class":216,"line":918},19,[214,920,419],{"class":220},[214,922,397],{"class":396},[214,924,400],{"class":224},[214,926,403],{"class":228},[214,928,400],{"class":224},[214,930,931],{"class":329},"update",[214,933,429],{"class":411},[214,935,496],{"class":228},[214,937,232],{"class":224},[214,939,225],{"class":224},[214,941,942],{"class":411}," status",[214,944,359],{"class":224},[214,946,250],{"class":249},[214,948,456],{"class":253},[214,950,257],{"class":249},[214,952,232],{"class":224},[214,954,955],{"class":411}," publishedAt",[214,957,359],{"class":224},[214,959,551],{"class":358},[214,961,962],{"class":329}," Date",[214,964,355],{"class":411},[214,966,400],{"class":224},[214,968,969],{"class":329},"toISOString",[214,971,972],{"class":411},"() ",[214,974,703],{"class":224},[214,976,437],{"class":411},[214,978,260],{"class":224},[214,980,982],{"class":216,"line":981},20,[214,983,472],{"class":224},[214,985,987],{"class":216,"line":986},21,[214,988,989],{"class":224},"}\n",[196,991,992],{},"Fougere does not require a service layer. A handler can contain an operation's logic\ndirectly or delegate to a service when the domain needs it.",[196,994,995,996,998,999,1002,1003,1005,1006,1009,1010,1009,1013,1009,1016,1019,1020,1009,1023,1009,1026,1029,1030,1033],{},"In a ",[200,997,202],{}," handler, the inherited ",[200,1000,1001],{},"this.storage"," is backed by the entity's repository;\ndo not inject ",[200,1004,109],{}," yourself. Reads:\n",[200,1007,1008],{},"list(options?)",", ",[200,1011,1012],{},"findById(id)",[200,1014,1015],{},"findBy(criteria)",[200,1017,1018],{},"findAllBy(criteria)",". Writes:\n",[200,1021,1022],{},"create(input)",[200,1024,1025],{},"update(id, input)",[200,1027,1028],{},"delete(id)",". ",[200,1031,1032],{},"output(view)"," returns a storage scoped\nto a view's fields.",[205,1035,1037],{"className":207,"code":1036,"language":209,"meta":210,"style":210},"await this.storage.findBy({ slug });                    \u002F\u002F the one row matching\nawait this.storage.findAllBy({ authorId: user.id });    \u002F\u002F every row matching\nawait this.storage.list({ where: { status: 'published' }, limit: 20, orderBy: 'createdAt' });\n",[200,1038,1039,1072,1112],{"__ignoreMap":210},[214,1040,1041,1044,1046,1048,1050,1052,1055,1057,1059,1062,1064,1066,1069],{"class":216,"line":217},[214,1042,1043],{"class":220},"await",[214,1045,397],{"class":396},[214,1047,400],{"class":224},[214,1049,403],{"class":228},[214,1051,400],{"class":224},[214,1053,1054],{"class":329},"findBy",[214,1056,429],{"class":228},[214,1058,558],{"class":224},[214,1060,1061],{"class":228}," slug ",[214,1063,703],{"class":224},[214,1065,437],{"class":228},[214,1067,1068],{"class":224},";",[214,1070,1071],{"class":341},"                    \u002F\u002F the one row matching\n",[214,1073,1074,1076,1078,1080,1082,1084,1087,1089,1091,1094,1096,1098,1100,1103,1105,1107,1109],{"class":216,"line":263},[214,1075,1043],{"class":220},[214,1077,397],{"class":396},[214,1079,400],{"class":224},[214,1081,403],{"class":228},[214,1083,400],{"class":224},[214,1085,1086],{"class":329},"findAllBy",[214,1088,429],{"class":228},[214,1090,558],{"class":224},[214,1092,1093],{"class":411}," authorId",[214,1095,359],{"class":224},[214,1097,507],{"class":228},[214,1099,400],{"class":224},[214,1101,1102],{"class":228},"id ",[214,1104,703],{"class":224},[214,1106,437],{"class":228},[214,1108,1068],{"class":224},[214,1110,1111],{"class":341},"    \u002F\u002F every row matching\n",[214,1113,1114,1116,1118,1120,1122,1124,1126,1128,1130,1133,1135,1137,1139,1141,1143,1145,1147,1150,1153,1155,1159,1161,1164,1166,1168,1171,1173,1175,1177],{"class":216,"line":283},[214,1115,1043],{"class":220},[214,1117,397],{"class":396},[214,1119,400],{"class":224},[214,1121,403],{"class":228},[214,1123,400],{"class":224},[214,1125,408],{"class":329},[214,1127,429],{"class":228},[214,1129,558],{"class":224},[214,1131,1132],{"class":411}," where",[214,1134,359],{"class":224},[214,1136,225],{"class":224},[214,1138,942],{"class":411},[214,1140,359],{"class":224},[214,1142,250],{"class":249},[214,1144,456],{"class":253},[214,1146,257],{"class":249},[214,1148,1149],{"class":224}," },",[214,1151,1152],{"class":411}," limit",[214,1154,359],{"class":224},[214,1156,1158],{"class":1157},"srdBf"," 20",[214,1160,232],{"class":224},[214,1162,1163],{"class":411}," orderBy",[214,1165,359],{"class":224},[214,1167,250],{"class":249},[214,1169,1170],{"class":253},"createdAt",[214,1172,257],{"class":249},[214,1174,243],{"class":224},[214,1176,437],{"class":228},[214,1178,260],{"class":224},[196,1180,1181],{},"The façade validates input before calling the method. The storage then applies lifecycle rules\nsuch as automatic values and defaults. The handler's return value is projected and\nvalidated before leaving the façade.",[1183,1184,1186],"h2",{"id":1185},"where-a-named-query-lives","Where a named query lives",[196,1188,1189,1191,1192,1195],{},[200,1190,109],{}," is a port: five generic gestures, no flavour of domain. \"The loud readings\" is\nnot one of them, so that query ends up spelled at the call site, in the middle of the\ncalculation it feeds. ",[200,1193,1194],{},"Repository(Entity)"," gives it a home:",[205,1197,1199],{"className":207,"code":1198,"language":209,"meta":210,"style":210},"\u002F\u002F repositories\u002FReadingRepository.ts\nexport default class ReadingRepository extends Repository(Reading) {\n  loud(): Promise\u003CReading[]> {\n    return this.findAllBy({ loud: true });\n  }\n}\n",[200,1200,1201,1206,1227,1249,1278,1282],{"__ignoreMap":210},[214,1202,1203],{"class":216,"line":217},[214,1204,1205],{"class":341},"\u002F\u002F repositories\u002FReadingRepository.ts\n",[214,1207,1208,1210,1212,1214,1217,1219,1222,1225],{"class":216,"line":263},[214,1209,312],{"class":220},[214,1211,315],{"class":220},[214,1213,319],{"class":318},[214,1215,1216],{"class":322}," ReadingRepository",[214,1218,326],{"class":318},[214,1220,1221],{"class":329}," Repository",[214,1223,1224],{"class":228},"(Reading) ",[214,1226,335],{"class":224},[214,1228,1229,1232,1234,1236,1238,1240,1243,1245,1247],{"class":216,"line":283},[214,1230,1231],{"class":351},"  loud",[214,1233,355],{"class":224},[214,1235,359],{"class":358},[214,1237,362],{"class":322},[214,1239,365],{"class":224},[214,1241,1242],{"class":322},"Reading",[214,1244,371],{"class":228},[214,1246,374],{"class":224},[214,1248,377],{"class":224},[214,1250,1251,1253,1255,1257,1259,1261,1263,1266,1268,1272,1274,1276],{"class":216,"line":302},[214,1252,419],{"class":220},[214,1254,397],{"class":396},[214,1256,400],{"class":224},[214,1258,1086],{"class":329},[214,1260,429],{"class":411},[214,1262,558],{"class":224},[214,1264,1265],{"class":411}," loud",[214,1267,359],{"class":224},[214,1269,1271],{"class":1270},"syTEX"," true",[214,1273,243],{"class":224},[214,1275,437],{"class":411},[214,1277,260],{"class":224},[214,1279,1280],{"class":216,"line":309},[214,1281,472],{"class":224},[214,1283,1284],{"class":216,"line":338},[214,1285,989],{"class":224},[205,1287,1289],{"className":207,"code":1288,"language":209,"meta":210,"style":210},"\u002F\u002F handlers\u002FReadingHandler.ts — asks the question, never spells the storage\nexport default class ReadingHandler {\n  constructor(private readings: ReadingRepository) {}\n  async loud() { return this.readings.loud(); }\n}\n",[200,1290,1291,1296,1309,1331,1363],{"__ignoreMap":210},[214,1292,1293],{"class":216,"line":217},[214,1294,1295],{"class":341},"\u002F\u002F handlers\u002FReadingHandler.ts — asks the question, never spells the storage\n",[214,1297,1298,1300,1302,1304,1307],{"class":216,"line":263},[214,1299,312],{"class":220},[214,1301,315],{"class":220},[214,1303,319],{"class":318},[214,1305,1306],{"class":322}," ReadingHandler",[214,1308,377],{"class":224},[214,1310,1311,1314,1316,1319,1322,1324,1326,1328],{"class":216,"line":283},[214,1312,1313],{"class":318},"  constructor",[214,1315,429],{"class":224},[214,1317,1318],{"class":318},"private",[214,1320,1321],{"class":434}," readings",[214,1323,359],{"class":358},[214,1325,1216],{"class":322},[214,1327,437],{"class":224},[214,1329,1330],{"class":224}," {}\n",[214,1332,1333,1335,1337,1339,1341,1344,1346,1348,1351,1353,1356,1358,1360],{"class":216,"line":302},[214,1334,348],{"class":318},[214,1336,1265],{"class":351},[214,1338,355],{"class":224},[214,1340,225],{"class":224},[214,1342,1343],{"class":220}," return",[214,1345,397],{"class":396},[214,1347,400],{"class":224},[214,1349,1350],{"class":228},"readings",[214,1352,400],{"class":224},[214,1354,1355],{"class":329},"loud",[214,1357,355],{"class":411},[214,1359,1068],{"class":224},[214,1361,1362],{"class":224}," }\n",[214,1364,1365],{"class":216,"line":309},[214,1366,989],{"class":224},[196,1368,1369,1370,1374,1375,400],{},"Every entity has one whether or not you wrote the file, and it is ",[1371,1372,1373],"strong",{},"not a door"," — so the\njudge stays here, in the handler. See ",[1376,1377,117],"a",{"href":118},[1183,1379,1381],{"id":1380},"the-four-binding-rules","The four binding rules",[196,1383,1384],{},"Fougere reads your method signature in the project's TypeScript program at boot and binds each parameter from the\ninvocation — in this order:",[1386,1387,1388,1404],"table",{},[1389,1390,1391],"thead",{},[1392,1393,1394,1398,1401],"tr",{},[1395,1396,1397],"th",{},"#",[1395,1399,1400],{},"Parameter looks like",[1395,1402,1403],{},"Bound from",[1405,1406,1407,1432,1445,1469],"tbody",{},[1392,1408,1409,1413,1426],{},[1410,1411,1412],"td",{},"1",[1410,1414,1415,1416,1419,1420,537,1423,437],{},"a type with a ",[1376,1417,1418],{"href":94},"Collector"," ",[1371,1421,1422],{},"declared in this Frond",[200,1424,1425],{},"user?: User",[1410,1427,1428,1429],{},"the collector's ",[200,1430,1431],{},"collect(ctx)",[1392,1433,1434,1437,1442],{},[1410,1435,1436],{},"2",[1410,1438,1439],{},[200,1440,1441],{},"ctx: InvocationContext",[1410,1443,1444],{},"the whole invocation",[1392,1446,1447,1450,1459],{},[1410,1448,1449],{},"3",[1410,1451,1452,1453,1009,1456,437],{},"a primitive (",[200,1454,1455],{},"id: string",[200,1457,1458],{},"page: number",[1410,1460,1461,1464,1465,1468],{},[200,1462,1463],{},"params[name]",", then ",[200,1466,1467],{},"query[name]"," — coerced to number\u002Fboolean",[1392,1470,1471,1474,1480],{},[1410,1472,1473],{},"4",[1410,1475,1476,1477,437],{},"anything else (",[200,1478,1479],{},"input: PostDraft",[1410,1481,1482,1483],{},"the request ",[200,1484,1485],{},"body",[196,1487,1488],{},"The scanner keeps the AST for what you declared and uses the type checker for what the\ndeclaration means. In particular:",[1490,1491,1492,1503,1515],"ul",{},[1493,1494,1495,1498,1499,400],"li",{},[1371,1496,1497],{},"A collector binds only inside its own Frond."," Rule 1 reads that Frond's own\ncollectors, so a type it cannot resolve falls to rule 4 and the parameter carries the\nrequest body — see\n",[1376,1500,1502],{"href":1501},"\u002Fdocs\u002Fbusiness\u002Fcollectors#a-collector-does-not-cross-a-frond-boundary","A collector does not cross a Frond boundary",[1493,1504,1505,1419,1508,1510,1511,1514],{},[1371,1506,1507],{},"Aliases resolve.",[200,1509,1425],{}," binds directly, and\n",[200,1512,1513],{},"type CurrentUser = User | undefined; user: CurrentUser"," binds to the same collector.",[1493,1516,1517,1520,1521,1523,1524,1527,1528,1009,1531,1534,1535,1538],{},[1371,1518,1519],{},"The door is what you declare public."," A ",[200,1522,1318],{}," or ",[200,1525,1526],{},"protected"," method is not an\noperation: the scan skips it, because TypeScript already has the word for it. A helper\nthe handler names by intent (",[200,1529,1530],{},"mustOwn",[200,1532,1533],{},"refuse",") therefore stays a helper, inside the\nclass. ",[200,1536,1537],{},"#name"," works too.",[1540,1541,1543],"h3",{"id":1542},"canonical-absence","Canonical absence",[196,1545,1546],{},"The façade normalizes every door before calling the handler. Absence follows TypeScript,\nnot the transport:",[205,1548,1550],{"className":207,"code":1549,"language":209,"meta":210,"style":210},"foo?: T          \u002F\u002F omitted → undefined\nfoo: T | null    \u002F\u002F null is an explicit domain value and stays null\nfoo?: T | null   \u002F\u002F omitted → undefined; explicit null → null\n",[200,1551,1552,1565,1582],{"__ignoreMap":210},[214,1553,1554,1557,1559,1562],{"class":216,"line":217},[214,1555,1556],{"class":228},"foo",[214,1558,510],{"class":358},[214,1560,1561],{"class":386}," T",[214,1563,1564],{"class":341},"          \u002F\u002F omitted → undefined\n",[214,1566,1567,1569,1571,1573,1576,1579],{"class":216,"line":263},[214,1568,1556],{"class":322},[214,1570,359],{"class":224},[214,1572,1561],{"class":386},[214,1574,1575],{"class":358}," |",[214,1577,1578],{"class":396}," null",[214,1580,1581],{"class":341},"    \u002F\u002F null is an explicit domain value and stays null\n",[214,1583,1584,1586,1588,1590,1592,1594],{"class":216,"line":283},[214,1585,1556],{"class":228},[214,1587,510],{"class":358},[214,1589,1561],{"class":386},[214,1591,1575],{"class":358},[214,1593,1578],{"class":396},[214,1595,1596],{"class":341},"   \u002F\u002F omitted → undefined; explicit null → null\n",[196,1598,1599,1600,1603,1604,1607,1608,1611,1612,1614,1615,1617,1618,1621,1622,1624],{},"JSON cannot carry an ",[200,1601,1602],{},"undefined"," property, so a remote or browser call omits it. The local\npath applies the same rule: ",[200,1605,1606],{},"{ foo: undefined }"," is canonicalized to ",[200,1609,1610],{},"{}",", and reading\n",[200,1613,1556],{}," still returns ",[200,1616,1602],{},". No adapter may translate an explicit ",[200,1619,1620],{},"null"," to\n",[200,1623,1602],{},". This applies to primitive bindings and to optional fields inside a body.",[1183,1626,1628],{"id":1627},"input-validation","Input validation",[196,1630,1631,1632,1635,1636,1639],{},"When a parameter's type is a schema class (entity or view), the façade validates the body\nbefore calling the method. Invalid input produces ",[200,1633,1634],{},"VALIDATION_FAILED"," with per-field\ndetails. A ",[200,1637,1638],{},"partial()"," view uses patch mode.",[1540,1641,1643],{"id":1642},"unknown-keys-are-refused","Unknown keys are refused",[196,1645,1646,1647,1650,1651,1653,1654,537,1656,1659,1660,1663],{},"A key outside the contract produces an error instead of being silently dropped: a body\n",[200,1648,1649],{},"{ …, status: 'published' }"," against a view that does not declare ",[200,1652,448],{}," comes back as\n",[200,1655,1634],{},[200,1657,1658],{},"status: Unknown field",") before the method runs. Accepted input can\ntherefore be passed to the storage without another projection. ",[200,1661,1662],{},"useFormFor"," applies the same\nvalidation in the browser before the network request.",[1183,1665,1667],{"id":1666},"a-state-changes-through-an-operation-never-through-a-field-write","A state changes through an operation, never through a field write",[196,1669,1670,1673],{},[200,1671,1672],{},"publish()"," illustrates an explicit state transition for an order, subscription, or\nticket. It combines the following rules:",[205,1675,1677],{"className":207,"code":1676,"language":209,"meta":210,"style":210},"\u002F\u002F the entity — the set of values, and the one it is born with\nstatus: readOnly(oneOf('draft', 'published', { default: 'draft' })),\n",[200,1678,1679,1684],{"__ignoreMap":210},[214,1680,1681],{"class":216,"line":217},[214,1682,1683],{"class":341},"\u002F\u002F the entity — the set of values, and the one it is born with\n",[214,1685,1686,1688,1690,1693,1695,1698,1700,1702,1705,1707,1709,1711,1713,1715,1717,1719,1721,1723,1725,1727,1729,1731,1734],{"class":216,"line":263},[214,1687,448],{"class":322},[214,1689,359],{"class":224},[214,1691,1692],{"class":329}," readOnly",[214,1694,429],{"class":228},[214,1696,1697],{"class":329},"oneOf",[214,1699,429],{"class":228},[214,1701,257],{"class":249},[214,1703,1704],{"class":253},"draft",[214,1706,257],{"class":249},[214,1708,232],{"class":224},[214,1710,250],{"class":249},[214,1712,456],{"class":253},[214,1714,257],{"class":249},[214,1716,232],{"class":224},[214,1718,225],{"class":224},[214,1720,315],{"class":411},[214,1722,359],{"class":224},[214,1724,250],{"class":249},[214,1726,1704],{"class":253},[214,1728,257],{"class":249},[214,1730,243],{"class":224},[214,1732,1733],{"class":228},"))",[214,1735,1736],{"class":224},",\n",[205,1738,1740],{"className":207,"code":1739,"language":209,"meta":210,"style":210},"\u002F\u002F the handler — the passage, and the refusal\nasync publish(id: string): Promise\u003CPost> {\n  const post = await this.storage.findById(id);\n  if (post.status === 'published') {\n    throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Already published',\n      entity: 'post', operation: 'publish' });\n  }\n  return this.storage.update(id, { status: 'published' });\n}\n",[200,1741,1742,1747,1768,1791,1811,1848,1879,1883,1907],{"__ignoreMap":210},[214,1743,1744],{"class":216,"line":217},[214,1745,1746],{"class":341},"\u002F\u002F the handler — the passage, and the refusal\n",[214,1748,1749,1752,1754,1757,1760,1762,1764,1766],{"class":216,"line":263},[214,1750,1751],{"class":228},"async ",[214,1753,610],{"class":329},[214,1755,1756],{"class":228},"(id: string): ",[214,1758,1759],{"class":501},"Promise",[214,1761,365],{"class":358},[214,1763,524],{"class":228},[214,1765,374],{"class":358},[214,1767,377],{"class":224},[214,1769,1770,1773,1776,1778,1780,1782,1784,1786,1788],{"class":216,"line":283},[214,1771,1772],{"class":228},"  const post ",[214,1774,1775],{"class":358},"=",[214,1777,393],{"class":220},[214,1779,397],{"class":396},[214,1781,400],{"class":224},[214,1783,403],{"class":228},[214,1785,400],{"class":224},[214,1787,641],{"class":329},[214,1789,1790],{"class":228},"(id);\n",[214,1792,1793,1796,1798,1801,1803,1805,1807,1809],{"class":216,"line":302},[214,1794,1795],{"class":351},"  if",[214,1797,537],{"class":224},[214,1799,1800],{"class":228},"post.status === ",[214,1802,257],{"class":249},[214,1804,456],{"class":253},[214,1806,257],{"class":249},[214,1808,437],{"class":224},[214,1810,377],{"class":224},[214,1812,1813,1816,1818,1820,1822,1824,1826,1828,1830,1832,1834,1836,1838,1840,1842,1844,1846],{"class":216,"line":309},[214,1814,1815],{"class":220},"    throw",[214,1817,551],{"class":358},[214,1819,235],{"class":329},[214,1821,429],{"class":411},[214,1823,558],{"class":224},[214,1825,561],{"class":411},[214,1827,359],{"class":224},[214,1829,240],{"class":228},[214,1831,400],{"class":224},[214,1833,872],{"class":386},[214,1835,232],{"class":224},[214,1837,575],{"class":411},[214,1839,359],{"class":224},[214,1841,250],{"class":249},[214,1843,883],{"class":253},[214,1845,257],{"class":249},[214,1847,1736],{"class":224},[214,1849,1850,1853,1855,1857,1859,1861,1863,1865,1867,1869,1871,1873,1875,1877],{"class":216,"line":338},[214,1851,1852],{"class":411},"      entity",[214,1854,359],{"class":224},[214,1856,250],{"class":249},[214,1858,596],{"class":253},[214,1860,257],{"class":249},[214,1862,232],{"class":224},[214,1864,603],{"class":411},[214,1866,359],{"class":224},[214,1868,250],{"class":249},[214,1870,610],{"class":253},[214,1872,257],{"class":249},[214,1874,243],{"class":224},[214,1876,437],{"class":411},[214,1878,260],{"class":224},[214,1880,1881],{"class":216,"line":345},[214,1882,472],{"class":224},[214,1884,1885,1888,1890,1892,1894,1896,1898,1900,1902,1904],{"class":216,"line":380},[214,1886,1887],{"class":228},"  return this.storage.update(id",[214,1889,232],{"class":224},[214,1891,225],{"class":224},[214,1893,942],{"class":322},[214,1895,359],{"class":224},[214,1897,250],{"class":249},[214,1899,456],{"class":253},[214,1901,257],{"class":249},[214,1903,243],{"class":224},[214,1905,1906],{"class":228},");\n",[214,1908,1909],{"class":216,"line":416},[214,1910,989],{"class":224},[1490,1912,1913,1928,1940,1959],{},[1493,1914,1915,1919,1920,1923,1924,1927],{},[1371,1916,1917],{},[200,1918,1697],{}," limits the values. A form can render a ",[200,1921,1922],{},"\u003Cselect>",", the DDL emits\n",[200,1925,1926],{},"CHECK status in (…)",", and GraphQL declares an enum type.",[1493,1929,1930,1935,1936,1939],{},[1371,1931,1932],{},[200,1933,1934],{},"{ default: 'draft' }"," is the initial state — a create rule on the\n",[1376,1937,1938],{"href":57},"lifecycle axis",", so nobody supplies it.",[1493,1941,1942,1947,1948,1951,1952,1955,1956,1958],{},[1371,1943,1944],{},[200,1945,1946],{},"readOnly"," prevents clients from writing the field: ",[200,1949,1950],{},"boundary.in"," is ",[200,1953,1954],{},"closed",", so ",[200,1957,448],{}," is\nabsent from every input view. A body carrying it is refused as an unknown key.",[1493,1960,1961,1964,1965,1967,1968,1971],{},[1371,1962,1963],{},"The named operation"," owns the transition and returns ",[200,1966,872],{},", projected as\n",[1376,1969,1970],{"href":98},"409",", when the current state prevents it.",[196,1973,1974,1975,1978,1979,1982],{},"The method alone controls the transition order. A direct SQL update, or another handler\ncalling ",[200,1976,1977],{},"this.storage.update(id, { status })",", can bypass it as long as the value satisfies the\n",[200,1980,1981],{},"CHECK",". Fougere does not provide a state graph: the field declares possible values and\nthe operation controls transitions between them.",[1183,1984,1986],{"id":1985},"output-view","Output view",[196,1988,1989,1992],{},[200,1990,1991],{},"Crud","'s second argument selects the output view. Two forms are available:",[205,1994,1996],{"className":207,"code":1995,"language":209,"meta":210,"style":210},"Crud(Post, { list: PostCard })   \u002F\u002F list returns cards, the rest return Post\nCrud(Post, PostPublic)           \u002F\u002F the whole handler returns PostPublic\n",[200,1997,1998,2024],{"__ignoreMap":210},[214,1999,2000,2002,2005,2007,2009,2011,2013,2016,2018,2021],{"class":216,"line":217},[214,2001,1991],{"class":329},[214,2003,2004],{"class":228},"(Post",[214,2006,232],{"class":224},[214,2008,225],{"class":224},[214,2010,352],{"class":411},[214,2012,359],{"class":224},[214,2014,2015],{"class":228}," PostCard ",[214,2017,703],{"class":224},[214,2019,2020],{"class":228},")   ",[214,2022,2023],{"class":341},"\u002F\u002F list returns cards, the rest return Post\n",[214,2025,2026,2028,2030,2032,2035],{"class":216,"line":263},[214,2027,1991],{"class":329},[214,2029,2004],{"class":228},[214,2031,232],{"class":224},[214,2033,2034],{"class":228}," PostPublic)           ",[214,2036,2037],{"class":341},"\u002F\u002F the whole handler returns PostPublic\n",[196,2039,2040],{},"With per-operation configuration, the storage returns the whole row and the façade applies the\nview to the output. Other operations keep their own views.",[196,2042,2043,2044,2047,2048,2051],{},"With a view for the whole handler, the injected storage itself is restricted. A second handler\nfile can therefore define another audience: ",[200,2045,2046],{},"handlers\u002FPostHandler.ts"," (full) and\n",[200,2049,2050],{},"handlers\u002Fpublic\u002FPostHandler.ts"," (restricted).",[1183,2053,2055],{"id":2054},"reaching-another-frond","Reaching another Frond",[196,2057,2058,2059,2062,2063,2066],{},"A Frond does not import another's files: it goes through its door. ",[200,2060,2061],{},"Facade\u003CT>"," is the\nframework's second port, resolved by type like a repository (",[200,2064,2065],{},"RepositoryOf\u003CPost>","):",[205,2068,2070],{"className":207,"code":2069,"language":209,"meta":210,"style":210},"import type { Facade } from '@fougere\u002Fcore';\nimport type ArticleHandler from '@fronds\u002Fstock\u002Fhandlers\u002FArticleHandler';\n\nexport default class CommandeHandler {\n  constructor(private articleFacade: Facade\u003CArticleHandler>) {}\n\n  \u002F** Can this order be served from the shelf? *\u002F\n  async servable(): Promise\u003Cboolean> {\n    const onHand = await this.articleFacade.onHand();\n    return onHand > 0;\n  }\n}\n",[200,2071,2072,2096,2116,2120,2133,2158,2162,2167,2189,2216,2230,2234],{"__ignoreMap":210},[214,2073,2074,2076,2079,2081,2084,2086,2088,2090,2092,2094],{"class":216,"line":217},[214,2075,221],{"class":220},[214,2077,2078],{"class":220}," type",[214,2080,225],{"class":224},[214,2082,2083],{"class":228}," Facade",[214,2085,243],{"class":224},[214,2087,246],{"class":220},[214,2089,250],{"class":249},[214,2091,254],{"class":253},[214,2093,257],{"class":249},[214,2095,260],{"class":224},[214,2097,2098,2100,2102,2105,2107,2109,2112,2114],{"class":216,"line":263},[214,2099,221],{"class":220},[214,2101,2078],{"class":220},[214,2103,2104],{"class":228}," ArticleHandler ",[214,2106,271],{"class":220},[214,2108,250],{"class":249},[214,2110,2111],{"class":253},"@fronds\u002Fstock\u002Fhandlers\u002FArticleHandler",[214,2113,257],{"class":249},[214,2115,260],{"class":224},[214,2117,2118],{"class":216,"line":283},[214,2119,306],{"emptyLinePlaceholder":305},[214,2121,2122,2124,2126,2128,2131],{"class":216,"line":302},[214,2123,312],{"class":220},[214,2125,315],{"class":220},[214,2127,319],{"class":318},[214,2129,2130],{"class":322}," CommandeHandler",[214,2132,377],{"class":224},[214,2134,2135,2137,2139,2141,2144,2146,2148,2150,2153,2156],{"class":216,"line":309},[214,2136,1313],{"class":318},[214,2138,429],{"class":224},[214,2140,1318],{"class":318},[214,2142,2143],{"class":434}," articleFacade",[214,2145,359],{"class":358},[214,2147,2083],{"class":322},[214,2149,365],{"class":224},[214,2151,2152],{"class":322},"ArticleHandler",[214,2154,2155],{"class":224},">)",[214,2157,1330],{"class":224},[214,2159,2160],{"class":216,"line":338},[214,2161,306],{"emptyLinePlaceholder":305},[214,2163,2164],{"class":216,"line":345},[214,2165,2166],{"class":341},"  \u002F** Can this order be served from the shelf? *\u002F\n",[214,2168,2169,2171,2174,2176,2178,2180,2182,2185,2187],{"class":216,"line":380},[214,2170,348],{"class":318},[214,2172,2173],{"class":351}," servable",[214,2175,355],{"class":224},[214,2177,359],{"class":358},[214,2179,362],{"class":322},[214,2181,365],{"class":224},[214,2183,2184],{"class":501},"boolean",[214,2186,374],{"class":224},[214,2188,377],{"class":224},[214,2190,2191,2193,2196,2198,2200,2202,2204,2207,2209,2212,2214],{"class":216,"line":416},[214,2192,383],{"class":318},[214,2194,2195],{"class":386}," onHand",[214,2197,390],{"class":358},[214,2199,393],{"class":220},[214,2201,397],{"class":396},[214,2203,400],{"class":224},[214,2205,2206],{"class":228},"articleFacade",[214,2208,400],{"class":224},[214,2210,2211],{"class":329},"onHand",[214,2213,355],{"class":411},[214,2215,260],{"class":224},[214,2217,2218,2220,2222,2225,2228],{"class":216,"line":469},[214,2219,419],{"class":220},[214,2221,2195],{"class":228},[214,2223,2224],{"class":358}," >",[214,2226,2227],{"class":1157}," 0",[214,2229,260],{"class":224},[214,2231,2232],{"class":216,"line":475},[214,2233,472],{"class":224},[214,2235,2236],{"class":216,"line":480},[214,2237,989],{"class":224},[196,2239,2240],{},"Three things to see:",[1490,2242,2243,2255,2279],{},[1493,2244,2245,2248,2249,2251,2252,400],{},[1371,2246,2247],{},"It is the door, not the handler."," Nobody injects a handler — its methods take\npositional arguments. The façade takes the invocation: every operation on ",[200,2250,2061],{}," has\nthe signature ",[200,2253,2254],{},"(invocation?) => Promise\u003C…>",[1493,2256,2257,2263,2264,2266,2267,2269,2270,2274,2275,2278],{},[1371,2258,2259,2262],{},[200,2260,2261],{},"keyof T"," is exactly the right set."," The scan skips ",[200,2265,1318],{}," and ",[200,2268,1526],{},", so a\nhandler's public methods ",[2271,2272,2273],"em",{},"are"," its operations — and ",[200,2276,2277],{},"keyof"," excludes the rest for the\nsame reason.",[1493,2280,2281,2284,2285,2288,2289,2292],{},[1371,2282,2283],{},"The signature says nothing about where the other Frond runs."," The same type resolves\nthe local façade or a doublure. Declaring ",[200,2286,2287],{},"stock"," in ",[200,2290,2291],{},"remotes:"," does not change this line.",[196,2294,2295,2296,2299,2300,2302],{},"What does not travel on its own is ",[1371,2297,2298],{},"identity",". The called operation receives the\ninvocation you hand it and nothing else: for a collector on the far side to see the same\nuser, declare ",[200,2301,1441],{}," (binding rule 2) and pass it through.",[1540,2304,2306],{"id":2305},"announcing-a-fact","Announcing a fact",[196,2308,2309,2310,1029,2313,2316,2317,2320],{},"A call names one recipient; an emission names a ",[1371,2311,2312],{},"subject",[200,2314,2315],{},"Emit\u003CT>"," is a constructor\ndependency like any other, and accepting a ",[200,2318,2319],{},"Fact\u003CT>"," IS the subscription — no topic, no\nregister call.",[196,2322,2323,2324,2326],{},"See ",[1376,2325,105],{"href":106}," — the resolver, what crosses a process, and what to do\nwhen a fact's shape moves.",[1540,2328,2330],{"id":2329},"writing-several-entities-as-one","Writing several entities as one",[196,2332,2333,2336,2337,2340],{},[200,2334,2335],{},"Storage\u003CT>","'s unit is one statement. When two writes must both land or neither,\n",[200,2338,2339],{},"Together\u003C[Account, Ledger]>"," is the fourth port, read like the other three — its unit is a\nblock:",[205,2342,2344],{"className":207,"code":2343,"language":209,"meta":210,"style":210},"constructor(private together: Together\u003C[Account, Ledger]>) {}\n\nawait this.together.run(async ([accounts, ledger]) => { … });\n",[200,2345,2346,2371,2375],{"__ignoreMap":210},[214,2347,2348,2351,2354,2356,2359,2361,2364,2366,2368],{"class":216,"line":217},[214,2349,2350],{"class":329},"constructor",[214,2352,2353],{"class":228},"(private together: Together",[214,2355,365],{"class":358},[214,2357,2358],{"class":228},"[Account",[214,2360,232],{"class":224},[214,2362,2363],{"class":228}," Ledger]",[214,2365,374],{"class":358},[214,2367,461],{"class":228},[214,2369,2370],{"class":224},"{}\n",[214,2372,2373],{"class":216,"line":263},[214,2374,306],{"emptyLinePlaceholder":305},[214,2376,2377,2379,2381,2383,2386,2388,2391,2393,2396,2399,2402,2404,2407,2410,2412,2414,2417,2419,2421],{"class":216,"line":283},[214,2378,1043],{"class":220},[214,2380,397],{"class":396},[214,2382,400],{"class":224},[214,2384,2385],{"class":228},"together",[214,2387,400],{"class":224},[214,2389,2390],{"class":329},"run",[214,2392,429],{"class":228},[214,2394,2395],{"class":318},"async",[214,2397,2398],{"class":224}," ([",[214,2400,2401],{"class":434},"accounts",[214,2403,232],{"class":224},[214,2405,2406],{"class":434}," ledger",[214,2408,2409],{"class":224},"])",[214,2411,440],{"class":318},[214,2413,225],{"class":224},[214,2415,2416],{"class":411}," … ",[214,2418,703],{"class":224},[214,2420,437],{"class":228},[214,2422,260],{"class":224},[196,2424,2323,2425,2427],{},[1376,2426,113],{"href":114}," — the two realizations, and\nwhich one you get.",[196,2429,2430,2431,2433],{},"Next: ",[1376,2432,89],{"href":90}," — computed fields added to an entity's output.",[2435,2436,2437],"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 .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 .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 .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .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);}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":210,"searchDepth":263,"depth":263,"links":2439},[2440,2441,2444,2447,2448,2449],{"id":1185,"depth":263,"text":1186},{"id":1380,"depth":263,"text":1381,"children":2442},[2443],{"id":1542,"depth":283,"text":1543},{"id":1627,"depth":263,"text":1628,"children":2445},[2446],{"id":1642,"depth":283,"text":1643},{"id":1666,"depth":263,"text":1667},{"id":1985,"depth":263,"text":1986},{"id":2054,"depth":263,"text":2055,"children":2450},[2451,2452],{"id":2305,"depth":283,"text":2306},{"id":2329,"depth":283,"text":2330},"Crud(Entity), operations, the four binding rules, the output view.","md",{},{"title":81,"description":2453},"vaez69Rq6Ovb0W20KrlvPpWR3vawchpSNWp0eVXTuqc",1788560230204]