[{"data":1,"prerenderedAt":2466},["ShallowReactive",2],{"docs-nav:fr":3,"docs:\u002Ffr\u002Fdocs\u002Fbusiness\u002Fhandlers":189},[4],{"title":5,"path":6,"stem":7,"children":8,"page":52},"Fr","\u002Ffr","fr",[9],{"title":10,"path":11,"stem":12,"children":13},"Qu'est-ce que Fougere","\u002Ffr\u002Fdocs","fr\u002Fdocs\u002Findex",[14,15,19,23,27,31,53,78,123,148],{"title":10,"path":11,"stem":12},{"title":16,"path":17,"stem":18},"Démarrer","\u002Ffr\u002Fdocs\u002Fgetting-started","fr\u002Fdocs\u002F01.getting-started",{"title":20,"path":21,"stem":22},"Une app que vous avez déjà","\u002Ffr\u002Fdocs\u002Fexisting-app","fr\u002Fdocs\u002F02.existing-app",{"title":24,"path":25,"stem":26},"Apportez votre schéma","\u002Ffr\u002Fdocs\u002Fbring-your-schema","fr\u002Fdocs\u002F03.bring-your-schema",{"title":28,"path":29,"stem":30},"La CLI","\u002Ffr\u002Fdocs\u002Fcli","fr\u002Fdocs\u002F04.cli",{"title":32,"path":33,"stem":34,"children":35,"page":52},"Concepts","\u002Ffr\u002Fdocs\u002Fconcepts","fr\u002Fdocs\u002F2.concepts",[36,40,44,48],{"title":37,"path":38,"stem":39},"Philosophie","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fphilosophy","fr\u002Fdocs\u002F2.concepts\u002F1.philosophy",{"title":41,"path":42,"stem":43},"La Frond","\u002Ffr\u002Fdocs\u002Fconcepts\u002Ffrond","fr\u002Fdocs\u002F2.concepts\u002F2.frond",{"title":45,"path":46,"stem":47},"Le chemin le plus court","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fshortest-path","fr\u002Fdocs\u002F2.concepts\u002F3.shortest-path",{"title":49,"path":50,"stem":51},"Le socle","\u002Ffr\u002Fdocs\u002Fconcepts\u002Fthe-base","fr\u002Fdocs\u002F2.concepts\u002F4.the-base",false,{"title":54,"path":55,"stem":56,"children":57,"page":52},"Schéma","\u002Ffr\u002Fdocs\u002Fschema","fr\u002Fdocs\u002F3.schema",[58,62,66,70,74],{"title":59,"path":60,"stem":61},"Entités","\u002Ffr\u002Fdocs\u002Fschema\u002Fentities","fr\u002Fdocs\u002F3.schema\u002F1.entities",{"title":63,"path":64,"stem":65},"Vues","\u002Ffr\u002Fdocs\u002Fschema\u002Fviews","fr\u002Fdocs\u002F3.schema\u002F2.views",{"title":67,"path":68,"stem":69},"La carte d'identité","\u002Ffr\u002Fdocs\u002Fschema\u002Fcard","fr\u002Fdocs\u002F3.schema\u002F3.card",{"title":71,"path":72,"stem":73},"Standard Schema","\u002Ffr\u002Fdocs\u002Fschema\u002Fstandard-schema","fr\u002Fdocs\u002F3.schema\u002F4.standard-schema",{"title":75,"path":76,"stem":77},"Comment un schéma bouge","\u002Ffr\u002Fdocs\u002Fschema\u002Fevolution","fr\u002Fdocs\u002F3.schema\u002F5.evolution",{"title":79,"path":80,"stem":81,"children":82,"page":52},"Métier","\u002Ffr\u002Fdocs\u002Fbusiness","fr\u002Fdocs\u002F4.business",[83,87,91,95,99,103,107,111,115,119],{"title":84,"path":85,"stem":86},"Handlers","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fhandlers","fr\u002Fdocs\u002F4.business\u002F1.handlers",{"title":88,"path":89,"stem":90},"Les ports","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fports","fr\u002Fdocs\u002F4.business\u002F10.ports",{"title":92,"path":93,"stem":94},"Presenters","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fpresenters","fr\u002Fdocs\u002F4.business\u002F2.presenters",{"title":96,"path":97,"stem":98},"Collectors","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fcollectors","fr\u002Fdocs\u002F4.business\u002F3.collectors",{"title":100,"path":101,"stem":102},"Erreurs","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ferrors","fr\u002Fdocs\u002F4.business\u002F4.errors",{"title":104,"path":105,"stem":106},"Seeds","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fseeds","fr\u002Fdocs\u002F4.business\u002F5.seeds",{"title":108,"path":109,"stem":110},"Les faits","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ffacts","fr\u002Fdocs\u002F4.business\u002F6.facts",{"title":112,"path":113,"stem":114},"Le port Storage","\u002Ffr\u002Fdocs\u002Fbusiness\u002Fstorage","fr\u002Fdocs\u002F4.business\u002F7.storage",{"title":116,"path":117,"stem":118},"Des écritures qui tiennent ou tombent ensemble","\u002Ffr\u002Fdocs\u002Fbusiness\u002Ftogether","fr\u002Fdocs\u002F4.business\u002F8.together",{"title":120,"path":121,"stem":122},"Les repositories","\u002Ffr\u002Fdocs\u002Fbusiness\u002Frepositories","fr\u002Fdocs\u002F4.business\u002F9.repositories",{"title":124,"path":125,"stem":126,"children":127,"page":52},"Client","\u002Ffr\u002Fdocs\u002Fclient","fr\u002Fdocs\u002F5.client",[128,132,136,140,144],{"title":129,"path":130,"stem":131},"Queries & commands","\u002Ffr\u002Fdocs\u002Fclient\u002Fqueries-commands","fr\u002Fdocs\u002F5.client\u002F1.queries-commands",{"title":133,"path":134,"stem":135},"Formulaires","\u002Ffr\u002Fdocs\u002Fclient\u002Fforms","fr\u002Fdocs\u002F5.client\u002F2.forms",{"title":137,"path":138,"stem":139},"Session","\u002Ffr\u002Fdocs\u002Fclient\u002Fsession","fr\u002Fdocs\u002F5.client\u002F3.session",{"title":141,"path":142,"stem":143},"invoke","\u002Ffr\u002Fdocs\u002Fclient\u002Finvoke","fr\u002Fdocs\u002F5.client\u002F4.invoke",{"title":145,"path":146,"stem":147},"Back-office","\u002Ffr\u002Fdocs\u002Fclient\u002Fadmin","fr\u002Fdocs\u002F5.client\u002F5.admin",{"title":149,"path":150,"stem":151,"children":152,"page":52},"Infrastructure","\u002Ffr\u002Fdocs\u002Finfra","fr\u002Fdocs\u002F6.infra",[153,157,161,165,169,173,177,181,185],{"title":154,"path":155,"stem":156},"Le gradient","\u002Ffr\u002Fdocs\u002Finfra\u002Fgradient","fr\u002Fdocs\u002F6.infra\u002F1.gradient",{"title":158,"path":159,"stem":160},"Surfaces — REST & GraphQL","\u002Ffr\u002Fdocs\u002Finfra\u002Fsurfaces","fr\u002Fdocs\u002F6.infra\u002F2.surfaces",{"title":162,"path":163,"stem":164},"Déploiement","\u002Ffr\u002Fdocs\u002Finfra\u002Fdeployment","fr\u002Fdocs\u002F6.infra\u002F3.deployment",{"title":166,"path":167,"stem":168},"Hôtes","\u002Ffr\u002Fdocs\u002Finfra\u002Fhosts","fr\u002Fdocs\u002F6.infra\u002F4.hosts",{"title":170,"path":171,"stem":172},"Les sources","\u002Ffr\u002Fdocs\u002Finfra\u002Fsources","fr\u002Fdocs\u002F6.infra\u002F5.sources",{"title":174,"path":175,"stem":176},"Le cycle de vie","\u002Ffr\u002Fdocs\u002Finfra\u002Flifecycle","fr\u002Fdocs\u002F6.infra\u002F6.lifecycle",{"title":178,"path":179,"stem":180},"L'identité entre Fronds","\u002Ffr\u002Fdocs\u002Finfra\u002Fidentity","fr\u002Fdocs\u002F6.infra\u002F7.identity",{"title":182,"path":183,"stem":184},"Observabilité","\u002Ffr\u002Fdocs\u002Finfra\u002Fobservability","fr\u002Fdocs\u002F6.infra\u002F8.observability",{"title":186,"path":187,"stem":188},"Tests","\u002Ffr\u002Fdocs\u002Finfra\u002Ftesting","fr\u002Fdocs\u002F6.infra\u002F9.testing",{"id":190,"title":84,"body":191,"description":2461,"extension":2462,"meta":2463,"navigation":308,"path":85,"seo":2464,"stem":86,"__hash__":2465},"docs_fr\u002Ffr\u002Fdocs\u002F4.business\u002F1.handlers.md",{"type":192,"value":193,"toc":2446},"minimark",[194,198,207,993,996,1038,1183,1186,1191,1200,1290,1371,1382,1386,1389,1491,1494,1544,1549,1552,1602,1630,1634,1645,1649,1669,1673,1679,1742,1917,1978,1989,1993,2000,2045,2048,2059,2063,2074,2245,2248,2300,2310,2314,2328,2334,2338,2349,2431,2436,2442],[195,196,84],"h1",{"id":197},"handlers",[199,200,201,202,206],"p",{},"Un handler regroupe les opérations d'une entité. ",[203,204,205],"code",{},"Crud(Entity)"," fournit les cinq opérations\nCRUD. Chaque méthode publique supplémentaire devient une opération.",[208,209,214],"pre",{"className":210,"code":211,"language":212,"meta":213,"style":213},"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** Lecture publique : publiés seulement. *\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** La transition draft→published — une opération, pas une écriture de champ. *\u002F\n  async publish(id: string, user?: User): Promise\u003CPost> {\n    if (!user) throw new FougereError({ code: ErrorCode.UNAUTHORIZED, message: 'Connectez-vous pour publier', 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}' introuvable`, entity: 'post', operation: 'publish' });\n    if (post.authorId !== user.id) throw new FougereError({ code: ErrorCode.FORBIDDEN, message: 'Seul l’auteur peut publier', entity: 'post', operation: 'publish' });\n    if (post.status === 'published') throw new FougereError({ code: ErrorCode.CONFLICT, message: 'Déjà publié', entity: 'post', operation: 'publish' });\n    return this.storage.update(id, { status: 'published', publishedAt: new Date().toISOString() });\n  }\n}\n","ts","",[203,215,216,264,284,303,310,339,346,381,417,470,476,481,487,532,622,653,743,832,919,982,987],{"__ignoreMap":213},[217,218,221,225,229,233,236,239,241,244,247,250,254,258,261],"span",{"class":219,"line":220},"line",1,[217,222,224],{"class":223},"sVHd0","import",[217,226,228],{"class":227},"sP7_E"," {",[217,230,232],{"class":231},"su5hD"," Crud",[217,234,235],{"class":227},",",[217,237,238],{"class":231}," FougereError",[217,240,235],{"class":227},[217,242,243],{"class":231}," ErrorCode",[217,245,246],{"class":227}," }",[217,248,249],{"class":223}," from",[217,251,253],{"class":252},"sjJ54"," '",[217,255,257],{"class":256},"s_sjI","@fougere\u002Fcore",[217,259,260],{"class":252},"'",[217,262,263],{"class":227},";\n",[217,265,267,269,272,275,277,280,282],{"class":219,"line":266},2,[217,268,224],{"class":223},[217,270,271],{"class":231}," Post ",[217,273,274],{"class":223},"from",[217,276,253],{"class":252},[217,278,279],{"class":256},"..\u002Fentities\u002FPost.js",[217,281,260],{"class":252},[217,283,263],{"class":227},[217,285,287,289,292,294,296,299,301],{"class":219,"line":286},3,[217,288,224],{"class":223},[217,290,291],{"class":231}," User ",[217,293,274],{"class":223},[217,295,253],{"class":252},[217,297,298],{"class":256},"..\u002F..\u002Fuser\u002Fentities\u002FUser.js",[217,300,260],{"class":252},[217,302,263],{"class":227},[217,304,306],{"class":219,"line":305},4,[217,307,309],{"emptyLinePlaceholder":308},true,"\n",[217,311,313,316,319,323,327,330,333,336],{"class":219,"line":312},5,[217,314,315],{"class":223},"export",[217,317,318],{"class":223}," default",[217,320,322],{"class":321},"sbsja"," class",[217,324,326],{"class":325},"sbgvK"," PostHandler",[217,328,329],{"class":321}," extends",[217,331,232],{"class":332},"sGLFI",[217,334,335],{"class":231},"(Post) ",[217,337,338],{"class":227},"{\n",[217,340,342],{"class":219,"line":341},6,[217,343,345],{"class":344},"sutJx","  \u002F** Lecture publique : publiés seulement. *\u002F\n",[217,347,349,352,356,359,363,366,369,372,375,378],{"class":219,"line":348},7,[217,350,351],{"class":321},"  async",[217,353,355],{"class":354},"sVXei"," list",[217,357,358],{"class":227},"()",[217,360,362],{"class":361},"smGrS",":",[217,364,365],{"class":325}," Promise",[217,367,368],{"class":227},"\u003C",[217,370,371],{"class":325},"PostCard",[217,373,374],{"class":231},"[]",[217,376,377],{"class":227},">",[217,379,380],{"class":227}," {\n",[217,382,384,387,391,394,397,401,404,407,409,412,415],{"class":219,"line":383},8,[217,385,386],{"class":321},"    const",[217,388,390],{"class":389},"s_hVV"," all",[217,392,393],{"class":361}," =",[217,395,396],{"class":223}," await",[217,398,400],{"class":399},"s39Yj"," this",[217,402,403],{"class":227},".",[217,405,406],{"class":231},"storage",[217,408,403],{"class":227},[217,410,411],{"class":332},"list",[217,413,358],{"class":414},"skxfh",[217,416,263],{"class":227},[217,418,420,423,425,427,430,433,435,438,441,444,447,449,452,455,457,460,462,465,468],{"class":219,"line":419},9,[217,421,422],{"class":223},"    return",[217,424,390],{"class":231},[217,426,403],{"class":227},[217,428,429],{"class":332},"filter",[217,431,432],{"class":414},"(",[217,434,432],{"class":227},[217,436,199],{"class":437},"s99_P",[217,439,440],{"class":227},")",[217,442,443],{"class":321}," =>",[217,445,446],{"class":231}," p",[217,448,403],{"class":227},[217,450,451],{"class":231},"status",[217,453,454],{"class":361}," ===",[217,456,253],{"class":252},[217,458,459],{"class":256},"published",[217,461,260],{"class":252},[217,463,464],{"class":414},") ",[217,466,467],{"class":344},"\u002F* … *\u002F",[217,469,263],{"class":227},[217,471,473],{"class":219,"line":472},10,[217,474,475],{"class":227},"  }\n",[217,477,479],{"class":219,"line":478},11,[217,480,309],{"emptyLinePlaceholder":308},[217,482,484],{"class":219,"line":483},12,[217,485,486],{"class":344},"  \u002F** La transition draft→published — une opération, pas une écriture de champ. *\u002F\n",[217,488,490,492,495,497,500,502,506,508,511,514,517,519,521,523,525,528,530],{"class":219,"line":489},13,[217,491,351],{"class":321},[217,493,494],{"class":354}," publish",[217,496,432],{"class":227},[217,498,499],{"class":437},"id",[217,501,362],{"class":361},[217,503,505],{"class":504},"sZMiF"," string",[217,507,235],{"class":227},[217,509,510],{"class":437}," user",[217,512,513],{"class":361},"?:",[217,515,516],{"class":325}," User",[217,518,440],{"class":227},[217,520,362],{"class":361},[217,522,365],{"class":325},[217,524,368],{"class":227},[217,526,527],{"class":325},"Post",[217,529,377],{"class":227},[217,531,380],{"class":227},[217,533,535,538,541,544,547,549,552,555,557,559,562,565,567,569,571,574,576,579,581,583,586,588,590,593,595,597,600,602,604,607,609,611,614,616,618,620],{"class":219,"line":534},14,[217,536,537],{"class":223},"    if",[217,539,540],{"class":414}," (",[217,542,543],{"class":361},"!",[217,545,546],{"class":231},"user",[217,548,464],{"class":414},[217,550,551],{"class":223},"throw",[217,553,554],{"class":361}," new",[217,556,238],{"class":332},[217,558,432],{"class":414},[217,560,561],{"class":227},"{",[217,563,564],{"class":414}," code",[217,566,362],{"class":227},[217,568,243],{"class":231},[217,570,403],{"class":227},[217,572,573],{"class":389},"UNAUTHORIZED",[217,575,235],{"class":227},[217,577,578],{"class":414}," message",[217,580,362],{"class":227},[217,582,253],{"class":252},[217,584,585],{"class":256},"Connectez-vous pour publier",[217,587,260],{"class":252},[217,589,235],{"class":227},[217,591,592],{"class":414}," entity",[217,594,362],{"class":227},[217,596,253],{"class":252},[217,598,599],{"class":256},"post",[217,601,260],{"class":252},[217,603,235],{"class":227},[217,605,606],{"class":414}," operation",[217,608,362],{"class":227},[217,610,253],{"class":252},[217,612,613],{"class":256},"publish",[217,615,260],{"class":252},[217,617,246],{"class":227},[217,619,440],{"class":414},[217,621,263],{"class":227},[217,623,625,627,630,632,634,636,638,640,642,645,647,649,651],{"class":219,"line":624},15,[217,626,386],{"class":321},[217,628,629],{"class":389}," post",[217,631,393],{"class":361},[217,633,396],{"class":223},[217,635,400],{"class":399},[217,637,403],{"class":227},[217,639,406],{"class":231},[217,641,403],{"class":227},[217,643,644],{"class":332},"findById",[217,646,432],{"class":414},[217,648,499],{"class":231},[217,650,440],{"class":414},[217,652,263],{"class":227},[217,654,656,658,660,662,664,666,668,670,672,674,676,678,680,682,684,687,689,691,693,696,699,702,704,707,710,713,715,717,719,721,723,725,727,729,731,733,735,737,739,741],{"class":219,"line":655},16,[217,657,537],{"class":223},[217,659,540],{"class":414},[217,661,543],{"class":361},[217,663,599],{"class":231},[217,665,464],{"class":414},[217,667,551],{"class":223},[217,669,554],{"class":361},[217,671,238],{"class":332},[217,673,432],{"class":414},[217,675,561],{"class":227},[217,677,564],{"class":414},[217,679,362],{"class":227},[217,681,243],{"class":231},[217,683,403],{"class":227},[217,685,686],{"class":389},"NOT_FOUND",[217,688,235],{"class":227},[217,690,578],{"class":414},[217,692,362],{"class":227},[217,694,695],{"class":252}," `",[217,697,698],{"class":256},"Post '",[217,700,701],{"class":252},"${",[217,703,499],{"class":231},[217,705,706],{"class":252},"}",[217,708,709],{"class":256},"' introuvable",[217,711,712],{"class":252},"`",[217,714,235],{"class":227},[217,716,592],{"class":414},[217,718,362],{"class":227},[217,720,253],{"class":252},[217,722,599],{"class":256},[217,724,260],{"class":252},[217,726,235],{"class":227},[217,728,606],{"class":414},[217,730,362],{"class":227},[217,732,253],{"class":252},[217,734,613],{"class":256},[217,736,260],{"class":252},[217,738,246],{"class":227},[217,740,440],{"class":414},[217,742,263],{"class":227},[217,744,746,748,750,752,754,757,760,762,764,766,768,770,772,774,776,778,780,782,784,786,789,791,793,795,797,800,802,804,806,808,810,812,814,816,818,820,822,824,826,828,830],{"class":219,"line":745},17,[217,747,537],{"class":223},[217,749,540],{"class":414},[217,751,599],{"class":231},[217,753,403],{"class":227},[217,755,756],{"class":231},"authorId",[217,758,759],{"class":361}," !==",[217,761,510],{"class":231},[217,763,403],{"class":227},[217,765,499],{"class":231},[217,767,464],{"class":414},[217,769,551],{"class":223},[217,771,554],{"class":361},[217,773,238],{"class":332},[217,775,432],{"class":414},[217,777,561],{"class":227},[217,779,564],{"class":414},[217,781,362],{"class":227},[217,783,243],{"class":231},[217,785,403],{"class":227},[217,787,788],{"class":389},"FORBIDDEN",[217,790,235],{"class":227},[217,792,578],{"class":414},[217,794,362],{"class":227},[217,796,253],{"class":252},[217,798,799],{"class":256},"Seul l’auteur peut publier",[217,801,260],{"class":252},[217,803,235],{"class":227},[217,805,592],{"class":414},[217,807,362],{"class":227},[217,809,253],{"class":252},[217,811,599],{"class":256},[217,813,260],{"class":252},[217,815,235],{"class":227},[217,817,606],{"class":414},[217,819,362],{"class":227},[217,821,253],{"class":252},[217,823,613],{"class":256},[217,825,260],{"class":252},[217,827,246],{"class":227},[217,829,440],{"class":414},[217,831,263],{"class":227},[217,833,835,837,839,841,843,845,847,849,851,853,855,857,859,861,863,865,867,869,871,873,876,878,880,882,884,887,889,891,893,895,897,899,901,903,905,907,909,911,913,915,917],{"class":219,"line":834},18,[217,836,537],{"class":223},[217,838,540],{"class":414},[217,840,599],{"class":231},[217,842,403],{"class":227},[217,844,451],{"class":231},[217,846,454],{"class":361},[217,848,253],{"class":252},[217,850,459],{"class":256},[217,852,260],{"class":252},[217,854,464],{"class":414},[217,856,551],{"class":223},[217,858,554],{"class":361},[217,860,238],{"class":332},[217,862,432],{"class":414},[217,864,561],{"class":227},[217,866,564],{"class":414},[217,868,362],{"class":227},[217,870,243],{"class":231},[217,872,403],{"class":227},[217,874,875],{"class":389},"CONFLICT",[217,877,235],{"class":227},[217,879,578],{"class":414},[217,881,362],{"class":227},[217,883,253],{"class":252},[217,885,886],{"class":256},"Déjà publié",[217,888,260],{"class":252},[217,890,235],{"class":227},[217,892,592],{"class":414},[217,894,362],{"class":227},[217,896,253],{"class":252},[217,898,599],{"class":256},[217,900,260],{"class":252},[217,902,235],{"class":227},[217,904,606],{"class":414},[217,906,362],{"class":227},[217,908,253],{"class":252},[217,910,613],{"class":256},[217,912,260],{"class":252},[217,914,246],{"class":227},[217,916,440],{"class":414},[217,918,263],{"class":227},[217,920,922,924,926,928,930,932,935,937,939,941,943,946,948,950,952,954,956,959,961,963,966,968,970,973,976,978,980],{"class":219,"line":921},19,[217,923,422],{"class":223},[217,925,400],{"class":399},[217,927,403],{"class":227},[217,929,406],{"class":231},[217,931,403],{"class":227},[217,933,934],{"class":332},"update",[217,936,432],{"class":414},[217,938,499],{"class":231},[217,940,235],{"class":227},[217,942,228],{"class":227},[217,944,945],{"class":414}," status",[217,947,362],{"class":227},[217,949,253],{"class":252},[217,951,459],{"class":256},[217,953,260],{"class":252},[217,955,235],{"class":227},[217,957,958],{"class":414}," publishedAt",[217,960,362],{"class":227},[217,962,554],{"class":361},[217,964,965],{"class":332}," Date",[217,967,358],{"class":414},[217,969,403],{"class":227},[217,971,972],{"class":332},"toISOString",[217,974,975],{"class":414},"() ",[217,977,706],{"class":227},[217,979,440],{"class":414},[217,981,263],{"class":227},[217,983,985],{"class":219,"line":984},20,[217,986,475],{"class":227},[217,988,990],{"class":219,"line":989},21,[217,991,992],{"class":227},"}\n",[199,994,995],{},"Fougere n'impose pas de couche service. Un handler peut contenir directement la logique\nd'une opération ou déléguer à un service selon les besoins du domaine.",[199,997,998,999,1001,1002,1005,1006,1009,1010,1013,1014,1013,1017,1013,1020,1023,1024,1013,1027,1013,1030,1033,1034,1037],{},"Dans un handler ",[203,1000,205],{},", le ",[203,1003,1004],{},"this.storage"," hérité est alimenté par le repository de\nl'entité ; n'injectez pas vous-même ",[203,1007,1008],{},"Storage",". Lectures :\n",[203,1011,1012],{},"list(options?)",", ",[203,1015,1016],{},"findById(id)",[203,1018,1019],{},"findBy(criteria)",[203,1021,1022],{},"findAllBy(criteria)",". Écritures :\n",[203,1025,1026],{},"create(input)",[203,1028,1029],{},"update(id, input)",[203,1031,1032],{},"delete(id)",". ",[203,1035,1036],{},"output(vue)"," rend un storage scopé aux\nchamps d'une vue.",[208,1039,1041],{"className":210,"code":1040,"language":212,"meta":213,"style":213},"await this.storage.findBy({ slug });                    \u002F\u002F la ligne qui correspond\nawait this.storage.findAllBy({ authorId: user.id });    \u002F\u002F toutes celles qui correspondent\nawait this.storage.list({ where: { status: 'published' }, limit: 20, orderBy: 'createdAt' });\n",[203,1042,1043,1076,1116],{"__ignoreMap":213},[217,1044,1045,1048,1050,1052,1054,1056,1059,1061,1063,1066,1068,1070,1073],{"class":219,"line":220},[217,1046,1047],{"class":223},"await",[217,1049,400],{"class":399},[217,1051,403],{"class":227},[217,1053,406],{"class":231},[217,1055,403],{"class":227},[217,1057,1058],{"class":332},"findBy",[217,1060,432],{"class":231},[217,1062,561],{"class":227},[217,1064,1065],{"class":231}," slug ",[217,1067,706],{"class":227},[217,1069,440],{"class":231},[217,1071,1072],{"class":227},";",[217,1074,1075],{"class":344},"                    \u002F\u002F la ligne qui correspond\n",[217,1077,1078,1080,1082,1084,1086,1088,1091,1093,1095,1098,1100,1102,1104,1107,1109,1111,1113],{"class":219,"line":266},[217,1079,1047],{"class":223},[217,1081,400],{"class":399},[217,1083,403],{"class":227},[217,1085,406],{"class":231},[217,1087,403],{"class":227},[217,1089,1090],{"class":332},"findAllBy",[217,1092,432],{"class":231},[217,1094,561],{"class":227},[217,1096,1097],{"class":414}," authorId",[217,1099,362],{"class":227},[217,1101,510],{"class":231},[217,1103,403],{"class":227},[217,1105,1106],{"class":231},"id ",[217,1108,706],{"class":227},[217,1110,440],{"class":231},[217,1112,1072],{"class":227},[217,1114,1115],{"class":344},"    \u002F\u002F toutes celles qui correspondent\n",[217,1117,1118,1120,1122,1124,1126,1128,1130,1132,1134,1137,1139,1141,1143,1145,1147,1149,1151,1154,1157,1159,1163,1165,1168,1170,1172,1175,1177,1179,1181],{"class":219,"line":286},[217,1119,1047],{"class":223},[217,1121,400],{"class":399},[217,1123,403],{"class":227},[217,1125,406],{"class":231},[217,1127,403],{"class":227},[217,1129,411],{"class":332},[217,1131,432],{"class":231},[217,1133,561],{"class":227},[217,1135,1136],{"class":414}," where",[217,1138,362],{"class":227},[217,1140,228],{"class":227},[217,1142,945],{"class":414},[217,1144,362],{"class":227},[217,1146,253],{"class":252},[217,1148,459],{"class":256},[217,1150,260],{"class":252},[217,1152,1153],{"class":227}," },",[217,1155,1156],{"class":414}," limit",[217,1158,362],{"class":227},[217,1160,1162],{"class":1161},"srdBf"," 20",[217,1164,235],{"class":227},[217,1166,1167],{"class":414}," orderBy",[217,1169,362],{"class":227},[217,1171,253],{"class":252},[217,1173,1174],{"class":256},"createdAt",[217,1176,260],{"class":252},[217,1178,246],{"class":227},[217,1180,440],{"class":231},[217,1182,263],{"class":227},[199,1184,1185],{},"La façade valide l'entrée avant d'appeler la méthode. Le storage applique ensuite les règles de\ncycle de vie, comme les valeurs automatiques et les défauts. La valeur renvoyée par le\nhandler est projetée et validée avant de quitter la façade.",[1187,1188,1190],"h2",{"id":1189},"où-vit-une-requête-nommée","Où vit une requête nommée",[199,1192,1193,1195,1196,1199],{},[203,1194,1008],{}," est un port : cinq gestes génériques, aucun parfum de domaine. « Les relevés\nbruyants » n'en est pas un, donc cette requête finit épelée sur place, au milieu du calcul\nqu'elle alimente. ",[203,1197,1198],{},"Repository(Entity)"," lui donne un endroit :",[208,1201,1203],{"className":210,"code":1202,"language":212,"meta":213,"style":213},"\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",[203,1204,1205,1210,1231,1253,1282,1286],{"__ignoreMap":213},[217,1206,1207],{"class":219,"line":220},[217,1208,1209],{"class":344},"\u002F\u002F repositories\u002FReadingRepository.ts\n",[217,1211,1212,1214,1216,1218,1221,1223,1226,1229],{"class":219,"line":266},[217,1213,315],{"class":223},[217,1215,318],{"class":223},[217,1217,322],{"class":321},[217,1219,1220],{"class":325}," ReadingRepository",[217,1222,329],{"class":321},[217,1224,1225],{"class":332}," Repository",[217,1227,1228],{"class":231},"(Reading) ",[217,1230,338],{"class":227},[217,1232,1233,1236,1238,1240,1242,1244,1247,1249,1251],{"class":219,"line":286},[217,1234,1235],{"class":354},"  loud",[217,1237,358],{"class":227},[217,1239,362],{"class":361},[217,1241,365],{"class":325},[217,1243,368],{"class":227},[217,1245,1246],{"class":325},"Reading",[217,1248,374],{"class":231},[217,1250,377],{"class":227},[217,1252,380],{"class":227},[217,1254,1255,1257,1259,1261,1263,1265,1267,1270,1272,1276,1278,1280],{"class":219,"line":305},[217,1256,422],{"class":223},[217,1258,400],{"class":399},[217,1260,403],{"class":227},[217,1262,1090],{"class":332},[217,1264,432],{"class":414},[217,1266,561],{"class":227},[217,1268,1269],{"class":414}," loud",[217,1271,362],{"class":227},[217,1273,1275],{"class":1274},"syTEX"," true",[217,1277,246],{"class":227},[217,1279,440],{"class":414},[217,1281,263],{"class":227},[217,1283,1284],{"class":219,"line":312},[217,1285,475],{"class":227},[217,1287,1288],{"class":219,"line":341},[217,1289,992],{"class":227},[208,1291,1293],{"className":210,"code":1292,"language":212,"meta":213,"style":213},"\u002F\u002F handlers\u002FReadingHandler.ts — pose la question, n'épelle jamais le stockage\nexport default class ReadingHandler {\n  constructor(private readings: ReadingRepository) {}\n  async loud() { return this.readings.loud(); }\n}\n",[203,1294,1295,1300,1313,1335,1367],{"__ignoreMap":213},[217,1296,1297],{"class":219,"line":220},[217,1298,1299],{"class":344},"\u002F\u002F handlers\u002FReadingHandler.ts — pose la question, n'épelle jamais le stockage\n",[217,1301,1302,1304,1306,1308,1311],{"class":219,"line":266},[217,1303,315],{"class":223},[217,1305,318],{"class":223},[217,1307,322],{"class":321},[217,1309,1310],{"class":325}," ReadingHandler",[217,1312,380],{"class":227},[217,1314,1315,1318,1320,1323,1326,1328,1330,1332],{"class":219,"line":286},[217,1316,1317],{"class":321},"  constructor",[217,1319,432],{"class":227},[217,1321,1322],{"class":321},"private",[217,1324,1325],{"class":437}," readings",[217,1327,362],{"class":361},[217,1329,1220],{"class":325},[217,1331,440],{"class":227},[217,1333,1334],{"class":227}," {}\n",[217,1336,1337,1339,1341,1343,1345,1348,1350,1352,1355,1357,1360,1362,1364],{"class":219,"line":305},[217,1338,351],{"class":321},[217,1340,1269],{"class":354},[217,1342,358],{"class":227},[217,1344,228],{"class":227},[217,1346,1347],{"class":223}," return",[217,1349,400],{"class":399},[217,1351,403],{"class":227},[217,1353,1354],{"class":231},"readings",[217,1356,403],{"class":227},[217,1358,1359],{"class":332},"loud",[217,1361,358],{"class":414},[217,1363,1072],{"class":227},[217,1365,1366],{"class":227}," }\n",[217,1368,1369],{"class":219,"line":312},[217,1370,992],{"class":227},[199,1372,1373,1374,1378,1379,403],{},"Chaque entité en a un, que vous ayez écrit le fichier ou non, et ce n'est ",[1375,1376,1377],"strong",{},"pas une porte","\n— donc le juge reste ici, dans le handler. Voir\n",[1380,1381,120],"a",{"href":121},[1187,1383,1385],{"id":1384},"les-quatre-règles-de-binding","Les quatre règles de binding",[199,1387,1388],{},"Fougere lit votre signature dans le programme TypeScript du projet au boot et lie chaque paramètre depuis\nl'invocation — dans cet ordre :",[1390,1391,1392,1408],"table",{},[1393,1394,1395],"thead",{},[1396,1397,1398,1402,1405],"tr",{},[1399,1400,1401],"th",{},"#",[1399,1403,1404],{},"Le paramètre ressemble à",[1399,1406,1407],{},"Lié depuis",[1409,1410,1411,1437,1450,1474],"tbody",{},[1396,1412,1413,1417,1430],{},[1414,1415,1416],"td",{},"1",[1414,1418,1419,1420,1423,1424,540,1427,440],{},"un type qui a un ",[1380,1421,1422],{"href":97},"Collector"," ",[1375,1425,1426],{},"déclaré dans cette Frond",[203,1428,1429],{},"user?: User",[1414,1431,1432,1433,1436],{},"le ",[203,1434,1435],{},"collect(ctx)"," du collector",[1396,1438,1439,1442,1447],{},[1414,1440,1441],{},"2",[1414,1443,1444],{},[203,1445,1446],{},"ctx: InvocationContext",[1414,1448,1449],{},"l'invocation entière",[1396,1451,1452,1455,1464],{},[1414,1453,1454],{},"3",[1414,1456,1457,1458,1013,1461,440],{},"un primitif (",[203,1459,1460],{},"id: string",[203,1462,1463],{},"page: number",[1414,1465,1466,1469,1470,1473],{},[203,1467,1468],{},"params[nom]",", puis ",[203,1471,1472],{},"query[nom]"," — coercé en number\u002Fboolean",[1396,1475,1476,1479,1485],{},[1414,1477,1478],{},"4",[1414,1480,1481,1482,440],{},"tout le reste (",[203,1483,1484],{},"input: PostDraft",[1414,1486,1432,1487,1490],{},[203,1488,1489],{},"body"," de la requête",[199,1492,1493],{},"Le scanner garde l'AST pour savoir ce que vous avez déclaré et utilise le type checker\npour comprendre ce que cette déclaration signifie. En particulier :",[1495,1496,1497,1508,1520],"ul",{},[1498,1499,1500,1503,1504,403],"li",{},[1375,1501,1502],{},"Un collector ne se lie qu'à l'intérieur de sa propre Frond."," La règle 1 lit les\ncollectors de cette Frond, donc un type qu'elle ne sait pas résoudre tombe en règle 4 et\nle paramètre porte le corps de la requête — voir\n",[1380,1505,1507],{"href":1506},"\u002Ffr\u002Fdocs\u002Fbusiness\u002Fcollectors#un-collector-ne-franchit-pas-la-fronti%C3%A8re-dune-frond","Un collector ne franchit pas la frontière d'une Frond",[1498,1509,1510,1423,1513,1515,1516,1519],{},[1375,1511,1512],{},"Les alias sont résolus.",[203,1514,1429],{}," se lie directement, et\n",[203,1517,1518],{},"type CurrentUser = User | undefined; user: CurrentUser"," se lie au même collector.",[1498,1521,1522,1525,1526,1528,1529,1532,1533,1013,1536,1539,1540,1543],{},[1375,1523,1524],{},"La porte est ce que vous déclarez public."," Une méthode ",[203,1527,1322],{}," ou ",[203,1530,1531],{},"protected"," n'est\npas une opération : le scan la saute, parce que TypeScript a déjà le mot pour ça. Un\nhelper que le handler nomme par intention (",[203,1534,1535],{},"mustOwn",[203,1537,1538],{},"refuse",") reste donc un helper, à\nl'intérieur de la classe. ",[203,1541,1542],{},"#nom"," fonctionne aussi.",[1545,1546,1548],"h3",{"id":1547},"absence-canonique","Absence canonique",[199,1550,1551],{},"La façade normalise chaque porte avant d'appeler le handler. L'absence suit TypeScript,\npas le transport :",[208,1553,1555],{"className":210,"code":1554,"language":212,"meta":213,"style":213},"foo?: T          \u002F\u002F omis → undefined\nfoo: T | null    \u002F\u002F null est une valeur métier explicite et reste null\nfoo?: T | null   \u002F\u002F omis → undefined ; null explicite → null\n",[203,1556,1557,1570,1587],{"__ignoreMap":213},[217,1558,1559,1562,1564,1567],{"class":219,"line":220},[217,1560,1561],{"class":231},"foo",[217,1563,513],{"class":361},[217,1565,1566],{"class":389}," T",[217,1568,1569],{"class":344},"          \u002F\u002F omis → undefined\n",[217,1571,1572,1574,1576,1578,1581,1584],{"class":219,"line":266},[217,1573,1561],{"class":325},[217,1575,362],{"class":227},[217,1577,1566],{"class":389},[217,1579,1580],{"class":361}," |",[217,1582,1583],{"class":399}," null",[217,1585,1586],{"class":344},"    \u002F\u002F null est une valeur métier explicite et reste null\n",[217,1588,1589,1591,1593,1595,1597,1599],{"class":219,"line":286},[217,1590,1561],{"class":231},[217,1592,513],{"class":361},[217,1594,1566],{"class":389},[217,1596,1580],{"class":361},[217,1598,1583],{"class":399},[217,1600,1601],{"class":344},"   \u002F\u002F omis → undefined ; null explicite → null\n",[199,1603,1604,1605,1608,1609,1612,1613,1616,1617,1619,1620,1622,1623,1626,1627,1629],{},"JSON ne sait pas transporter une propriété ",[203,1606,1607],{},"undefined",", donc un appel distant ou navigateur\nl'omet. Le chemin local applique la même règle : ",[203,1610,1611],{},"{ foo: undefined }"," est canonisé en ",[203,1614,1615],{},"{}","\net lire ",[203,1618,1561],{}," renvoie toujours ",[203,1621,1607],{},". Aucun adapter ne peut transformer un ",[203,1624,1625],{},"null","\nexplicite en ",[203,1628,1607],{},". La règle vaut pour les bindings primitifs comme pour les champs\noptionnels d'un body.",[1187,1631,1633],{"id":1632},"validation-des-entrées","Validation des entrées",[199,1635,1636,1637,1640,1641,1644],{},"Quand le type d'un paramètre est une classe de schéma (entité ou vue), la façade valide\nle body avant d'appeler la méthode. Une entrée invalide produit ",[203,1638,1639],{},"VALIDATION_FAILED"," avec\nles détails par champ. Une vue ",[203,1642,1643],{},"partial()"," utilise le mode patch.",[1545,1646,1648],{"id":1647},"les-clés-inconnues-sont-refusées","Les clés inconnues sont refusées",[199,1650,1651,1652,1655,1656,1658,1659,540,1661,1664,1665,1668],{},"Une clé hors contrat produit une erreur au lieu d'être retirée silencieusement : un body\n",[203,1653,1654],{},"{ …, status: 'published' }"," contre une vue qui ne déclare pas ",[203,1657,451],{}," ressort en\n",[203,1660,1639],{},[203,1662,1663],{},"status: Unknown field",") avant votre méthode. Une entrée acceptée peut\ndonc être transmise à le storage sans projection préalable. ",[203,1666,1667],{},"useFormFor"," applique la même\nvalidation dans le navigateur avant l'appel réseau.",[1187,1670,1672],{"id":1671},"un-état-change-par-une-opération-jamais-par-une-écriture-de-champ","Un état change par une opération, jamais par une écriture de champ",[199,1674,1675,1678],{},[203,1676,1677],{},"publish()"," illustre une transition d'état explicite pour une commande, un abonnement ou\nun ticket. Elle combine les règles suivantes :",[208,1680,1682],{"className":210,"code":1681,"language":212,"meta":213,"style":213},"\u002F\u002F l'entité — le jeu de valeurs, et celle avec laquelle elle naît\nstatus: readOnly(oneOf('draft', 'published', { default: 'draft' })),\n",[203,1683,1684,1689],{"__ignoreMap":213},[217,1685,1686],{"class":219,"line":220},[217,1687,1688],{"class":344},"\u002F\u002F l'entité — le jeu de valeurs, et celle avec laquelle elle naît\n",[217,1690,1691,1693,1695,1698,1700,1703,1705,1707,1710,1712,1714,1716,1718,1720,1722,1724,1726,1728,1730,1732,1734,1736,1739],{"class":219,"line":266},[217,1692,451],{"class":325},[217,1694,362],{"class":227},[217,1696,1697],{"class":332}," readOnly",[217,1699,432],{"class":231},[217,1701,1702],{"class":332},"oneOf",[217,1704,432],{"class":231},[217,1706,260],{"class":252},[217,1708,1709],{"class":256},"draft",[217,1711,260],{"class":252},[217,1713,235],{"class":227},[217,1715,253],{"class":252},[217,1717,459],{"class":256},[217,1719,260],{"class":252},[217,1721,235],{"class":227},[217,1723,228],{"class":227},[217,1725,318],{"class":414},[217,1727,362],{"class":227},[217,1729,253],{"class":252},[217,1731,1709],{"class":256},[217,1733,260],{"class":252},[217,1735,246],{"class":227},[217,1737,1738],{"class":231},"))",[217,1740,1741],{"class":227},",\n",[208,1743,1745],{"className":210,"code":1744,"language":212,"meta":213,"style":213},"\u002F\u002F le handler — le passage, et le refus\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",[203,1746,1747,1752,1773,1796,1816,1854,1885,1889,1913],{"__ignoreMap":213},[217,1748,1749],{"class":219,"line":220},[217,1750,1751],{"class":344},"\u002F\u002F le handler — le passage, et le refus\n",[217,1753,1754,1757,1759,1762,1765,1767,1769,1771],{"class":219,"line":266},[217,1755,1756],{"class":231},"async ",[217,1758,613],{"class":332},[217,1760,1761],{"class":231},"(id: string): ",[217,1763,1764],{"class":504},"Promise",[217,1766,368],{"class":361},[217,1768,527],{"class":231},[217,1770,377],{"class":361},[217,1772,380],{"class":227},[217,1774,1775,1778,1781,1783,1785,1787,1789,1791,1793],{"class":219,"line":286},[217,1776,1777],{"class":231},"  const post ",[217,1779,1780],{"class":361},"=",[217,1782,396],{"class":223},[217,1784,400],{"class":399},[217,1786,403],{"class":227},[217,1788,406],{"class":231},[217,1790,403],{"class":227},[217,1792,644],{"class":332},[217,1794,1795],{"class":231},"(id);\n",[217,1797,1798,1801,1803,1806,1808,1810,1812,1814],{"class":219,"line":305},[217,1799,1800],{"class":354},"  if",[217,1802,540],{"class":227},[217,1804,1805],{"class":231},"post.status === ",[217,1807,260],{"class":252},[217,1809,459],{"class":256},[217,1811,260],{"class":252},[217,1813,440],{"class":227},[217,1815,380],{"class":227},[217,1817,1818,1821,1823,1825,1827,1829,1831,1833,1835,1837,1839,1841,1843,1845,1847,1850,1852],{"class":219,"line":312},[217,1819,1820],{"class":223},"    throw",[217,1822,554],{"class":361},[217,1824,238],{"class":332},[217,1826,432],{"class":414},[217,1828,561],{"class":227},[217,1830,564],{"class":414},[217,1832,362],{"class":227},[217,1834,243],{"class":231},[217,1836,403],{"class":227},[217,1838,875],{"class":389},[217,1840,235],{"class":227},[217,1842,578],{"class":414},[217,1844,362],{"class":227},[217,1846,253],{"class":252},[217,1848,1849],{"class":256},"Already published",[217,1851,260],{"class":252},[217,1853,1741],{"class":227},[217,1855,1856,1859,1861,1863,1865,1867,1869,1871,1873,1875,1877,1879,1881,1883],{"class":219,"line":341},[217,1857,1858],{"class":414},"      entity",[217,1860,362],{"class":227},[217,1862,253],{"class":252},[217,1864,599],{"class":256},[217,1866,260],{"class":252},[217,1868,235],{"class":227},[217,1870,606],{"class":414},[217,1872,362],{"class":227},[217,1874,253],{"class":252},[217,1876,613],{"class":256},[217,1878,260],{"class":252},[217,1880,246],{"class":227},[217,1882,440],{"class":414},[217,1884,263],{"class":227},[217,1886,1887],{"class":219,"line":348},[217,1888,475],{"class":227},[217,1890,1891,1894,1896,1898,1900,1902,1904,1906,1908,1910],{"class":219,"line":383},[217,1892,1893],{"class":231},"  return this.storage.update(id",[217,1895,235],{"class":227},[217,1897,228],{"class":227},[217,1899,945],{"class":325},[217,1901,362],{"class":227},[217,1903,253],{"class":252},[217,1905,459],{"class":256},[217,1907,260],{"class":252},[217,1909,246],{"class":227},[217,1911,1912],{"class":231},");\n",[217,1914,1915],{"class":219,"line":419},[217,1916,992],{"class":227},[1495,1918,1919,1934,1946,1965],{},[1498,1920,1921,1925,1926,1929,1930,1933],{},[1375,1922,1923],{},[203,1924,1702],{}," limite les valeurs. Le formulaire peut produire un ",[203,1927,1928],{},"\u003Cselect>",", le DDL émet\n",[203,1931,1932],{},"CHECK status in (…)"," et GraphQL déclare un type enum.",[1498,1935,1936,1941,1942,1945],{},[1375,1937,1938],{},[203,1939,1940],{},"{ default: 'draft' }"," est l'état initial — une règle de création sur\n",[1380,1943,1944],{"href":60},"l'axe lifecycle",", donc personne ne le fournit.",[1498,1947,1948,1953,1954,1957,1958,1961,1962,1964],{},[1375,1949,1950],{},[203,1951,1952],{},"readOnly"," interdit l'écriture du champ par les clients : ",[203,1955,1956],{},"boundary.in"," vaut ",[203,1959,1960],{},"closed",", donc ",[203,1963,451],{},"\nest absent de toute vue d'entrée. Un corps qui le porte est refusé en clé inconnue.",[1498,1966,1967,1970,1971,1973,1974,1977],{},[1375,1968,1969],{},"L'opération nommée"," porte la transition et renvoie ",[203,1972,875],{},", projeté en\n",[1380,1975,1976],{"href":101},"409",", lorsque l'état courant l'interdit.",[199,1979,1980,1981,1984,1985,1988],{},"La méthode est seule responsable de l'ordre des transitions. Une mise à jour SQL directe,\nou un autre handler qui appelle ",[203,1982,1983],{},"this.storage.update(id, { status })",", peut la contourner tant\nque la valeur respecte le ",[203,1986,1987],{},"CHECK",". Fougere ne fournit pas de graphe d'états : le champ\ndéclare les valeurs possibles et l'opération contrôle le passage de l'une à l'autre.",[1187,1990,1992],{"id":1991},"vue-de-sortie","Vue de sortie",[199,1994,1995,1996,1999],{},"Le deuxième argument de ",[203,1997,1998],{},"Crud"," sélectionne la vue de sortie. Deux formes sont disponibles :",[208,2001,2003],{"className":210,"code":2002,"language":212,"meta":213,"style":213},"Crud(Post, { list: PostCard })   \u002F\u002F list rend des cartes, le reste rend Post\nCrud(Post, PostPublic)           \u002F\u002F tout le handler rend PostPublic\n",[203,2004,2005,2031],{"__ignoreMap":213},[217,2006,2007,2009,2012,2014,2016,2018,2020,2023,2025,2028],{"class":219,"line":220},[217,2008,1998],{"class":332},[217,2010,2011],{"class":231},"(Post",[217,2013,235],{"class":227},[217,2015,228],{"class":227},[217,2017,355],{"class":414},[217,2019,362],{"class":227},[217,2021,2022],{"class":231}," PostCard ",[217,2024,706],{"class":227},[217,2026,2027],{"class":231},")   ",[217,2029,2030],{"class":344},"\u002F\u002F list rend des cartes, le reste rend Post\n",[217,2032,2033,2035,2037,2039,2042],{"class":219,"line":266},[217,2034,1998],{"class":332},[217,2036,2011],{"class":231},[217,2038,235],{"class":227},[217,2040,2041],{"class":231}," PostPublic)           ",[217,2043,2044],{"class":344},"\u002F\u002F tout le handler rend PostPublic\n",[199,2046,2047],{},"Avec une configuration par opération, le storage renvoie la ligne entière et la façade applique\nla vue à la sortie. Les autres opérations conservent leur propre vue.",[199,2049,2050,2051,2054,2055,2058],{},"Avec une vue pour tout le handler, le storage injecté est lui-même restreint. Un second fichier\nde handler peut ainsi définir une autre audience : ",[203,2052,2053],{},"handlers\u002FPostHandler.ts"," (complet) et\n",[203,2056,2057],{},"handlers\u002Fpublic\u002FPostHandler.ts"," (restreint).",[1187,2060,2062],{"id":2061},"atteindre-une-autre-frond","Atteindre une autre Frond",[199,2064,2065,2066,2069,2070,2073],{},"Une Frond n'importe pas les fichiers d'une autre : elle passe par sa porte. ",[203,2067,2068],{},"Facade\u003CT>"," est\nle deuxième port du framework, résolu par type comme un repository (",[203,2071,2072],{},"RepositoryOf\u003CPost>",") :",[208,2075,2077],{"className":210,"code":2076,"language":212,"meta":213,"style":213},"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** Cette commande est-elle servable depuis l'étagère ? *\u002F\n  async servable(): Promise\u003Cboolean> {\n    const onHand = await this.articleFacade.onHand();\n    return onHand > 0;\n  }\n}\n",[203,2078,2079,2103,2123,2127,2140,2165,2169,2174,2196,2223,2237,2241],{"__ignoreMap":213},[217,2080,2081,2083,2086,2088,2091,2093,2095,2097,2099,2101],{"class":219,"line":220},[217,2082,224],{"class":223},[217,2084,2085],{"class":223}," type",[217,2087,228],{"class":227},[217,2089,2090],{"class":231}," Facade",[217,2092,246],{"class":227},[217,2094,249],{"class":223},[217,2096,253],{"class":252},[217,2098,257],{"class":256},[217,2100,260],{"class":252},[217,2102,263],{"class":227},[217,2104,2105,2107,2109,2112,2114,2116,2119,2121],{"class":219,"line":266},[217,2106,224],{"class":223},[217,2108,2085],{"class":223},[217,2110,2111],{"class":231}," ArticleHandler ",[217,2113,274],{"class":223},[217,2115,253],{"class":252},[217,2117,2118],{"class":256},"@fronds\u002Fstock\u002Fhandlers\u002FArticleHandler",[217,2120,260],{"class":252},[217,2122,263],{"class":227},[217,2124,2125],{"class":219,"line":286},[217,2126,309],{"emptyLinePlaceholder":308},[217,2128,2129,2131,2133,2135,2138],{"class":219,"line":305},[217,2130,315],{"class":223},[217,2132,318],{"class":223},[217,2134,322],{"class":321},[217,2136,2137],{"class":325}," CommandeHandler",[217,2139,380],{"class":227},[217,2141,2142,2144,2146,2148,2151,2153,2155,2157,2160,2163],{"class":219,"line":312},[217,2143,1317],{"class":321},[217,2145,432],{"class":227},[217,2147,1322],{"class":321},[217,2149,2150],{"class":437}," articleFacade",[217,2152,362],{"class":361},[217,2154,2090],{"class":325},[217,2156,368],{"class":227},[217,2158,2159],{"class":325},"ArticleHandler",[217,2161,2162],{"class":227},">)",[217,2164,1334],{"class":227},[217,2166,2167],{"class":219,"line":341},[217,2168,309],{"emptyLinePlaceholder":308},[217,2170,2171],{"class":219,"line":348},[217,2172,2173],{"class":344},"  \u002F** Cette commande est-elle servable depuis l'étagère ? *\u002F\n",[217,2175,2176,2178,2181,2183,2185,2187,2189,2192,2194],{"class":219,"line":383},[217,2177,351],{"class":321},[217,2179,2180],{"class":354}," servable",[217,2182,358],{"class":227},[217,2184,362],{"class":361},[217,2186,365],{"class":325},[217,2188,368],{"class":227},[217,2190,2191],{"class":504},"boolean",[217,2193,377],{"class":227},[217,2195,380],{"class":227},[217,2197,2198,2200,2203,2205,2207,2209,2211,2214,2216,2219,2221],{"class":219,"line":419},[217,2199,386],{"class":321},[217,2201,2202],{"class":389}," onHand",[217,2204,393],{"class":361},[217,2206,396],{"class":223},[217,2208,400],{"class":399},[217,2210,403],{"class":227},[217,2212,2213],{"class":231},"articleFacade",[217,2215,403],{"class":227},[217,2217,2218],{"class":332},"onHand",[217,2220,358],{"class":414},[217,2222,263],{"class":227},[217,2224,2225,2227,2229,2232,2235],{"class":219,"line":472},[217,2226,422],{"class":223},[217,2228,2202],{"class":231},[217,2230,2231],{"class":361}," >",[217,2233,2234],{"class":1161}," 0",[217,2236,263],{"class":227},[217,2238,2239],{"class":219,"line":478},[217,2240,475],{"class":227},[217,2242,2243],{"class":219,"line":483},[217,2244,992],{"class":227},[199,2246,2247],{},"Trois choses à voir :",[1495,2249,2250,2262,2286],{},[1498,2251,2252,2255,2256,2258,2259,403],{},[1375,2253,2254],{},"C'est la porte, pas le handler."," Personne n'injecte un handler — ses méthodes prennent\ndes arguments positionnels. La façade prend l'invocation : chaque opération de\n",[203,2257,2068],{}," a la signature ",[203,2260,2261],{},"(invocation?) => Promise\u003C…>",[1498,2263,2264,2270,2271,2273,2274,2276,2277,2281,2282,2285],{},[1375,2265,2266,2269],{},[203,2267,2268],{},"keyof T"," est exactement la bonne liste."," Le scan saute ",[203,2272,1322],{}," et ",[203,2275,1531],{},", donc\nles méthodes publiques d'un handler ",[2278,2279,2280],"em",{},"sont"," ses opérations — et ",[203,2283,2284],{},"keyof"," exclut le reste\npour la même raison.",[1498,2287,2288,2291,2292,2295,2296,2299],{},[1375,2289,2290],{},"La signature ne dit pas où l'autre Frond tourne."," Le même type résout la façade locale\nou une doublure. Déclarer ",[203,2293,2294],{},"stock"," dans ",[203,2297,2298],{},"remotes:"," ne change pas cette ligne.",[199,2301,2302,2303,2306,2307,2309],{},"Ce qui ne voyage pas tout seul, c'est l'",[1375,2304,2305],{},"identité",". L'opération appelée reçoit\nl'invocation que vous lui passez, et rien d'autre : pour qu'un collector d'en face voie le\nmême utilisateur, déclarez ",[203,2308,1446],{}," (règle de binding 2) et transmettez-le.",[1545,2311,2313],{"id":2312},"annoncer-un-fait","Annoncer un fait",[199,2315,2316,2317,1033,2320,2323,2324,2327],{},"Un appel nomme un destinataire ; une émission nomme un ",[1375,2318,2319],{},"sujet",[203,2321,2322],{},"Emit\u003CT>"," est une\ndépendance de constructeur comme une autre, et accepter un ",[203,2325,2326],{},"Fact\u003CT>"," EST l'abonnement —\npas de topic, pas d'appel d'inscription.",[199,2329,2330,2331,2333],{},"Voir ",[1380,2332,108],{"href":109}," — le résolveur, ce qui traverse un processus, et\nquoi faire quand la forme d'un fait bouge.",[1545,2335,2337],{"id":2336},"écrire-plusieurs-entités-comme-une-seule","Écrire plusieurs entités comme une seule",[199,2339,2340,2341,2344,2345,2348],{},"L'unité d'",[203,2342,2343],{},"Storage\u003CT>"," est une instruction. Quand deux écritures doivent toutes deux\naboutir ou aucune, ",[203,2346,2347],{},"Together\u003C[Account, Ledger]>"," est le quatrième port, lu comme les trois\nautres — son unité est un bloc :",[208,2350,2352],{"className":210,"code":2351,"language":212,"meta":213,"style":213},"constructor(private together: Together\u003C[Account, Ledger]>) {}\n\nawait this.together.run(async ([accounts, ledger]) => { … });\n",[203,2353,2354,2379,2383],{"__ignoreMap":213},[217,2355,2356,2359,2362,2364,2367,2369,2372,2374,2376],{"class":219,"line":220},[217,2357,2358],{"class":332},"constructor",[217,2360,2361],{"class":231},"(private together: Together",[217,2363,368],{"class":361},[217,2365,2366],{"class":231},"[Account",[217,2368,235],{"class":227},[217,2370,2371],{"class":231}," Ledger]",[217,2373,377],{"class":361},[217,2375,464],{"class":231},[217,2377,2378],{"class":227},"{}\n",[217,2380,2381],{"class":219,"line":266},[217,2382,309],{"emptyLinePlaceholder":308},[217,2384,2385,2387,2389,2391,2394,2396,2399,2401,2404,2407,2410,2412,2415,2418,2420,2422,2425,2427,2429],{"class":219,"line":286},[217,2386,1047],{"class":223},[217,2388,400],{"class":399},[217,2390,403],{"class":227},[217,2392,2393],{"class":231},"together",[217,2395,403],{"class":227},[217,2397,2398],{"class":332},"run",[217,2400,432],{"class":231},[217,2402,2403],{"class":321},"async",[217,2405,2406],{"class":227}," ([",[217,2408,2409],{"class":437},"accounts",[217,2411,235],{"class":227},[217,2413,2414],{"class":437}," ledger",[217,2416,2417],{"class":227},"])",[217,2419,443],{"class":321},[217,2421,228],{"class":227},[217,2423,2424],{"class":414}," … ",[217,2426,706],{"class":227},[217,2428,440],{"class":231},[217,2430,263],{"class":227},[199,2432,2330,2433,2435],{},[1380,2434,116],{"href":117}," — les deux\nréalisations, et celle que vous obtenez.",[199,2437,2438,2439,2441],{},"Suite : ",[1380,2440,92],{"href":93}," — les champs calculés ajoutés à la sortie d'une entité.",[2443,2444,2445],"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":213,"searchDepth":266,"depth":266,"links":2447},[2448,2449,2452,2455,2456,2457],{"id":1189,"depth":266,"text":1190},{"id":1384,"depth":266,"text":1385,"children":2450},[2451],{"id":1547,"depth":286,"text":1548},{"id":1632,"depth":266,"text":1633,"children":2453},[2454],{"id":1647,"depth":286,"text":1648},{"id":1671,"depth":266,"text":1672},{"id":1991,"depth":266,"text":1992},{"id":2061,"depth":266,"text":2062,"children":2458},[2459,2460],{"id":2312,"depth":286,"text":2313},{"id":2336,"depth":286,"text":2337},"Crud(Entity), les opérations, les quatre règles de binding, la vue de sortie.","md",{},{"title":84,"description":2461},"P9HxcTxiREzkxbcOncquJKibVJeSk3ZnbMtCzBc6rj4",1788598419072]