[{"data":1,"prerenderedAt":1643},["ShallowReactive",2],{"fougere:post.list:{}":3,"docs:\u002Fdocs\u002Fguides\u002Fbusiness-logic":5,"docs-nav":1444},{"items":4},[],{"id":6,"title":7,"body":8,"description":1437,"extension":1438,"meta":1439,"navigation":295,"path":1440,"seo":1441,"stem":1442,"__hash__":1443},"docs\u002Fdocs\u002F1.guides\u002F1.business-logic.md","Handle your business logic",{"type":9,"value":10,"toc":1431},"minimark",[11,15,24,29,36,128,134,182,185,249,256,373,380,384,395,566,590,610,616,620,627,634,702,803,941,962,970,981,1065,1071,1075,1082,1313,1330,1336,1412,1415,1420,1427],[12,13,7],"h1",{"id":14},"handle-your-business-logic",[16,17,18,19,23],"p",{},"This guide starts a shop with no data yet: an operation, a page that calls it, a payment\nprovider you can swap, and the errors a payment can return. Everything happens in\n",[20,21,22],"code",{},"fronds\u002Fshop\u002F",".",[25,26,28],"h2",{"id":27},"handlers","Handlers",[16,30,31,32,35],{},"A handler is a class in ",[20,33,34],{},"handlers\u002F",". Its public methods are what the rest of the app can\ncall.",[37,38,43],"pre",{"className":39,"code":40,"language":41,"meta":42,"style":42},"language-ts shiki shiki-themes material-theme-lighter fougere-pastel material-theme-palenight","\u002F\u002F fronds\u002Fshop\u002Fhandlers\u002FCartHandler.ts\nexport default class CartHandler {\n  async getTotal(): Promise\u003Cnumber> {\n    return 4990;\n  }\n}\n","ts","",[20,44,45,54,76,103,116,122],{"__ignoreMap":42},[46,47,50],"span",{"class":48,"line":49},"line",1,[46,51,53],{"class":52},"skXKP","\u002F\u002F fronds\u002Fshop\u002Fhandlers\u002FCartHandler.ts\n",[46,55,57,61,64,68,72],{"class":48,"line":56},2,[46,58,60],{"class":59},"sK6XW","export",[46,62,63],{"class":59}," default",[46,65,67],{"class":66},"s76I7"," class",[46,69,71],{"class":70},"sBZYL"," CartHandler",[46,73,75],{"class":74},"s15xx"," {\n",[46,77,79,82,86,89,92,95,98,101],{"class":48,"line":78},3,[46,80,81],{"class":66},"  async",[46,83,85],{"class":84},"shDkW"," getTotal",[46,87,88],{"class":74},"():",[46,90,91],{"class":70}," Promise",[46,93,94],{"class":74},"\u003C",[46,96,97],{"class":70},"number",[46,99,100],{"class":74},">",[46,102,75],{"class":74},[46,104,106,109,113],{"class":48,"line":105},4,[46,107,108],{"class":59},"    return",[46,110,112],{"class":111},"s3dPA"," 4990",[46,114,115],{"class":74},";\n",[46,117,119],{"class":48,"line":118},5,[46,120,121],{"class":74},"  }\n",[46,123,125],{"class":48,"line":124},6,[46,126,127],{"class":74},"}\n",[16,129,130,131,133],{},"Nothing else to write: no route, no registration. The file being in ",[20,132,34],{}," is the\ndeclaration.",[16,135,136,137,140,141,140,144,140,147,140,150,140,153,140,156,159,160,140,163,140,166,169,170,173,174,177,178,181],{},"The name tells Fougere whether the method reads or writes. A method reads when its name uses\nonly read verbs — ",[20,138,139],{},"get",", ",[20,142,143],{},"list",[20,145,146],{},"find",[20,148,149],{},"count",[20,151,152],{},"search",[20,154,155],{},"has",[20,157,158],{},"is","… — and every other\nmethod writes: ",[20,161,162],{},"create",[20,164,165],{},"pay",[20,167,168],{},"publish",", and also ",[20,171,172],{},"total"," or ",[20,175,176],{},"getOrCreate",". A write is a\ncommand: it answers over ",[20,179,180],{},"POST"," and is never cached. A read taken for a write still works,\nwhich is why the rule leans that way; ASP.NET Web API settles a method with no known verb\nthe same way.",[16,183,184],{},"To make a read of a method whose name does not say so, state it for that one operation,\nnaming it the way a call does — its address, then its name:",[37,186,188],{"className":39,"code":187,"language":41,"meta":42,"style":42},"\u002F\u002F fronds\u002Fshop\u002Ffrond.config.ts\nexport default { operations: { 'cart.total': { kind: 'query' } } };\n",[20,189,190,195],{"__ignoreMap":42},[46,191,192],{"class":48,"line":49},[46,193,194],{"class":52},"\u002F\u002F fronds\u002Fshop\u002Ffrond.config.ts\n",[46,196,197,199,201,204,208,211,213,217,221,224,226,228,231,233,235,239,241,244,246],{"class":48,"line":56},[46,198,60],{"class":59},[46,200,63],{"class":59},[46,202,203],{"class":74}," {",[46,205,207],{"class":206},"sHtqg"," operations",[46,209,210],{"class":74},":",[46,212,203],{"class":74},[46,214,216],{"class":215},"sDDC1"," '",[46,218,220],{"class":219},"sK7Y8","cart.total",[46,222,223],{"class":215},"'",[46,225,210],{"class":74},[46,227,203],{"class":74},[46,229,230],{"class":206}," kind",[46,232,210],{"class":74},[46,234,216],{"class":215},[46,236,238],{"class":237},"sN-8n","query",[46,240,223],{"class":215},[46,242,243],{"class":74}," }",[46,245,243],{"class":74},[46,247,248],{"class":74}," };\n",[16,250,251,252,255],{},"When a read verb of your domain starts many operations, teach it once instead.\nA file in the frond's ",[20,253,254],{},"vocabulary\u002F"," exports it, and the process lists it beside its fronds:",[37,257,259],{"className":39,"code":258,"language":41,"meta":42,"style":42},"\u002F\u002F fronds\u002Fshop\u002Fvocabulary\u002Fverbs.ts\nimport { Verbs } from '@fougere\u002Fcore';\n\nexport const total = Verbs.term('total', 'query');\n\n\u002F\u002F fronds.ts — written by the scan, or by hand\nexport const vocabulary = [total];\n",[20,260,261,266,291,297,342,346,351],{"__ignoreMap":42},[46,262,263],{"class":48,"line":49},[46,264,265],{"class":52},"\u002F\u002F fronds\u002Fshop\u002Fvocabulary\u002Fverbs.ts\n",[46,267,268,271,273,277,279,282,284,287,289],{"class":48,"line":56},[46,269,270],{"class":59},"import",[46,272,203],{"class":74},[46,274,276],{"class":275},"sPEhv"," Verbs",[46,278,243],{"class":74},[46,280,281],{"class":59}," from",[46,283,216],{"class":215},[46,285,286],{"class":237},"@fougere\u002Fcore",[46,288,223],{"class":215},[46,290,115],{"class":74},[46,292,293],{"class":48,"line":78},[46,294,296],{"emptyLinePlaceholder":295},true,"\n",[46,298,299,301,304,307,310,312,314,318,322,324,326,328,331,333,335,337,340],{"class":48,"line":105},[46,300,60],{"class":59},[46,302,303],{"class":66}," const",[46,305,306],{"class":275}," total ",[46,308,309],{"class":74},"=",[46,311,276],{"class":275},[46,313,23],{"class":74},[46,315,317],{"class":316},"s4mR9","term",[46,319,321],{"class":320},"srlTG","(",[46,323,223],{"class":215},[46,325,172],{"class":237},[46,327,223],{"class":215},[46,329,330],{"class":74},",",[46,332,216],{"class":215},[46,334,238],{"class":237},[46,336,223],{"class":215},[46,338,339],{"class":320},")",[46,341,115],{"class":74},[46,343,344],{"class":48,"line":118},[46,345,296],{"emptyLinePlaceholder":295},[46,347,348],{"class":48,"line":124},[46,349,350],{"class":52},"\u002F\u002F fronds.ts — written by the scan, or by hand\n",[46,352,354,356,358,361,363,366,368,371],{"class":48,"line":353},7,[46,355,60],{"class":59},[46,357,303],{"class":66},[46,359,360],{"class":275}," vocabulary ",[46,362,309],{"class":74},[46,364,365],{"class":320}," [",[46,367,172],{"class":275},[46,369,370],{"class":320},"]",[46,372,115],{"class":74},[16,374,375,376],{},"→ ",[377,378,28],"a",{"href":379},"\u002Fdocs\u002Fbusiness\u002Fhandlers",[25,381,383],{"id":382},"the-facade","The facade",[16,385,386,387,391,392,210],{},"A page does not import the handler. It imports its ",[388,389,390],"strong",{},"facade",", a constant Fougere writes\nfor each handler in ",[20,393,394],{},"@fronds\u002Ffacade",[37,396,400],{"className":397,"code":398,"language":399,"meta":42,"style":42},"language-vue shiki shiki-themes material-theme-lighter fougere-pastel material-theme-palenight","\u003C!-- app\u002Fpages\u002Findex.vue -->\n\u003Cscript setup lang=\"ts\">\nimport { cart } from '@fronds\u002Ffacade';\n\nconst { data: total } = await useQuery(cart, 'getTotal');\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp v-if=\"total !== null\">Total: {{ total \u002F 100 }} €\u003C\u002Fp>\n\u003C\u002Ftemplate>\n","vue",[20,401,402,407,434,455,459,503,512,516,526,557],{"__ignoreMap":42},[46,403,404],{"class":48,"line":49},[46,405,406],{"class":52},"\u003C!-- app\u002Fpages\u002Findex.vue -->\n",[46,408,409,411,415,419,422,424,427,429,431],{"class":48,"line":56},[46,410,94],{"class":74},[46,412,414],{"class":413},"s687E","script",[46,416,418],{"class":417},"s8ClO"," setup",[46,420,421],{"class":417}," lang",[46,423,309],{"class":74},[46,425,426],{"class":215},"\"",[46,428,41],{"class":237},[46,430,426],{"class":215},[46,432,433],{"class":74},">\n",[46,435,436,438,440,443,445,447,449,451,453],{"class":48,"line":78},[46,437,270],{"class":59},[46,439,203],{"class":74},[46,441,442],{"class":275}," cart",[46,444,243],{"class":74},[46,446,281],{"class":59},[46,448,216],{"class":215},[46,450,394],{"class":237},[46,452,223],{"class":215},[46,454,115],{"class":74},[46,456,457],{"class":48,"line":105},[46,458,296],{"emptyLinePlaceholder":295},[46,460,461,464,466,469,471,473,476,479,482,485,487,490,492,494,497,499,501],{"class":48,"line":118},[46,462,463],{"class":66},"const",[46,465,203],{"class":74},[46,467,468],{"class":206}," data",[46,470,210],{"class":74},[46,472,306],{"class":275},[46,474,475],{"class":74},"}",[46,477,478],{"class":74}," =",[46,480,481],{"class":59}," await",[46,483,484],{"class":316}," useQuery",[46,486,321],{"class":320},[46,488,489],{"class":275},"cart",[46,491,330],{"class":74},[46,493,216],{"class":215},[46,495,496],{"class":237},"getTotal",[46,498,223],{"class":215},[46,500,339],{"class":320},[46,502,115],{"class":74},[46,504,505,508,510],{"class":48,"line":124},[46,506,507],{"class":74},"\u003C\u002F",[46,509,414],{"class":413},[46,511,433],{"class":74},[46,513,514],{"class":48,"line":353},[46,515,296],{"emptyLinePlaceholder":295},[46,517,519,521,524],{"class":48,"line":518},8,[46,520,94],{"class":74},[46,522,523],{"class":413},"template",[46,525,433],{"class":74},[46,527,529,532,534,537,539,541,544,546,548,551,553,555],{"class":48,"line":528},9,[46,530,531],{"class":74},"  \u003C",[46,533,16],{"class":413},[46,535,536],{"class":417}," v-if",[46,538,309],{"class":74},[46,540,426],{"class":215},[46,542,543],{"class":237},"total !== null",[46,545,426],{"class":215},[46,547,100],{"class":74},[46,549,550],{"class":275},"Total: {{ total \u002F 100 }} €",[46,552,507],{"class":74},[46,554,16],{"class":413},[46,556,433],{"class":74},[46,558,560,562,564],{"class":48,"line":559},10,[46,561,507],{"class":74},[46,563,523],{"class":413},[46,565,433],{"class":74},[16,567,568,570,571,574,575,578,579,581,582,585,586,589],{},[20,569,489],{}," carries the type of ",[20,572,573],{},"CartHandler",": ",[20,576,577],{},"'getTotal'"," is checked, and ",[20,580,172],{}," is a\n",[20,583,584],{},"number | null"," — ",[20,587,588],{},"null"," until the query has answered.",[16,591,592,593,140,596,140,599,602,603,173,606,609],{},"Fougere's plugin for your host — ",[20,594,595],{},"@fougere\u002Fnuxt",[20,597,598],{},"@fougere\u002Fvite",[20,600,601],{},"withFougere()"," for\nNext — writes this module from your handlers each time ",[20,604,605],{},"dev",[20,607,608],{},"build"," starts.",[16,611,375,612],{},[377,613,615],{"href":614},"\u002Fdocs\u002Fclient\u002Fqueries-commands","Queries & commands",[25,617,619],{"id":618},"services","Services",[16,621,622,623,626],{},"A service is a class in ",[20,624,625],{},"services\u002F"," that a handler asks for in its constructor. Fougere\nreads the type and hands an instance over.",[16,628,629,630,633],{},"A payment provider is the typical case. The handler asks for ",[20,631,632],{},"Payment",", and never names\nStripe:",[37,635,637],{"className":39,"code":636,"language":41,"meta":42,"style":42},"\u002F\u002F fronds\u002Fshop\u002Fservices\u002FPayment.ts\nexport default abstract class Payment {\n  abstract charge(cents: number): Promise\u003C{ reference: string }>;\n}\n",[20,638,639,644,660,698],{"__ignoreMap":42},[46,640,641],{"class":48,"line":49},[46,642,643],{"class":52},"\u002F\u002F fronds\u002Fshop\u002Fservices\u002FPayment.ts\n",[46,645,646,648,650,653,655,658],{"class":48,"line":56},[46,647,60],{"class":59},[46,649,63],{"class":59},[46,651,652],{"class":66}," abstract",[46,654,67],{"class":66},[46,656,657],{"class":70}," Payment",[46,659,75],{"class":74},[46,661,662,665,668,670,674,676,679,682,684,687,690,692,695],{"class":48,"line":78},[46,663,664],{"class":66},"  abstract",[46,666,667],{"class":84}," charge",[46,669,321],{"class":74},[46,671,673],{"class":672},"shDUQ","cents",[46,675,210],{"class":74},[46,677,678],{"class":70}," number",[46,680,681],{"class":74},"):",[46,683,91],{"class":70},[46,685,686],{"class":74},"\u003C{",[46,688,689],{"class":206}," reference",[46,691,210],{"class":74},[46,693,694],{"class":70}," string",[46,696,697],{"class":74}," }>;\n",[46,699,700],{"class":48,"line":105},[46,701,127],{"class":74},[37,703,705],{"className":39,"code":704,"language":41,"meta":42,"style":42},"\u002F\u002F fronds\u002Fshop\u002Fservices\u002FStripePayment.ts\nimport Payment from '.\u002FPayment.js';\n\nexport default class StripePayment extends Payment {\n  async charge(cents: number) {\n    \u002F\u002F call Stripe here\n    return { reference: 'ch_123' };\n  }\n}\n",[20,706,707,712,731,735,753,771,776,795,799],{"__ignoreMap":42},[46,708,709],{"class":48,"line":49},[46,710,711],{"class":52},"\u002F\u002F fronds\u002Fshop\u002Fservices\u002FStripePayment.ts\n",[46,713,714,716,719,722,724,727,729],{"class":48,"line":56},[46,715,270],{"class":59},[46,717,718],{"class":275}," Payment ",[46,720,721],{"class":59},"from",[46,723,216],{"class":215},[46,725,726],{"class":237},".\u002FPayment.js",[46,728,223],{"class":215},[46,730,115],{"class":74},[46,732,733],{"class":48,"line":78},[46,734,296],{"emptyLinePlaceholder":295},[46,736,737,739,741,743,746,749,751],{"class":48,"line":105},[46,738,60],{"class":59},[46,740,63],{"class":59},[46,742,67],{"class":66},[46,744,745],{"class":70}," StripePayment",[46,747,748],{"class":66}," extends",[46,750,657],{"class":70},[46,752,75],{"class":74},[46,754,755,757,759,761,763,765,767,769],{"class":48,"line":118},[46,756,81],{"class":66},[46,758,667],{"class":84},[46,760,321],{"class":74},[46,762,673],{"class":672},[46,764,210],{"class":74},[46,766,678],{"class":70},[46,768,339],{"class":74},[46,770,75],{"class":74},[46,772,773],{"class":48,"line":124},[46,774,775],{"class":52},"    \u002F\u002F call Stripe here\n",[46,777,778,780,782,784,786,788,791,793],{"class":48,"line":353},[46,779,108],{"class":59},[46,781,203],{"class":74},[46,783,689],{"class":206},[46,785,210],{"class":74},[46,787,216],{"class":215},[46,789,790],{"class":237},"ch_123",[46,792,223],{"class":215},[46,794,248],{"class":74},[46,796,797],{"class":48,"line":518},[46,798,121],{"class":74},[46,800,801],{"class":48,"line":528},[46,802,127],{"class":74},[37,804,806],{"className":39,"code":805,"language":41,"meta":42,"style":42},"\u002F\u002F fronds\u002Fshop\u002Fhandlers\u002FCheckoutHandler.ts\nimport Payment from '..\u002Fservices\u002FPayment.js';\n\nexport default class CheckoutHandler {\n  constructor(private payment: Payment) {}\n\n  async pay(cents: number): Promise\u003C{ reference: string }> {\n    return this.payment.charge(cents);\n  }\n}\n",[20,807,808,813,830,834,847,869,873,905,933,937],{"__ignoreMap":42},[46,809,810],{"class":48,"line":49},[46,811,812],{"class":52},"\u002F\u002F fronds\u002Fshop\u002Fhandlers\u002FCheckoutHandler.ts\n",[46,814,815,817,819,821,823,826,828],{"class":48,"line":56},[46,816,270],{"class":59},[46,818,718],{"class":275},[46,820,721],{"class":59},[46,822,216],{"class":215},[46,824,825],{"class":237},"..\u002Fservices\u002FPayment.js",[46,827,223],{"class":215},[46,829,115],{"class":74},[46,831,832],{"class":48,"line":78},[46,833,296],{"emptyLinePlaceholder":295},[46,835,836,838,840,842,845],{"class":48,"line":105},[46,837,60],{"class":59},[46,839,63],{"class":59},[46,841,67],{"class":66},[46,843,844],{"class":70}," CheckoutHandler",[46,846,75],{"class":74},[46,848,849,852,854,857,860,862,864,866],{"class":48,"line":118},[46,850,851],{"class":66},"  constructor",[46,853,321],{"class":74},[46,855,856],{"class":66},"private",[46,858,859],{"class":672}," payment",[46,861,210],{"class":74},[46,863,657],{"class":70},[46,865,339],{"class":74},[46,867,868],{"class":74}," {}\n",[46,870,871],{"class":48,"line":124},[46,872,296],{"emptyLinePlaceholder":295},[46,874,875,877,880,882,884,886,888,890,892,894,896,898,900,903],{"class":48,"line":353},[46,876,81],{"class":66},[46,878,879],{"class":84}," pay",[46,881,321],{"class":74},[46,883,673],{"class":672},[46,885,210],{"class":74},[46,887,678],{"class":70},[46,889,681],{"class":74},[46,891,91],{"class":70},[46,893,686],{"class":74},[46,895,689],{"class":206},[46,897,210],{"class":74},[46,899,694],{"class":70},[46,901,902],{"class":74}," }>",[46,904,75],{"class":74},[46,906,907,909,913,915,919,921,924,927,929,931],{"class":48,"line":518},[46,908,108],{"class":59},[46,910,912],{"class":911},"sxG60"," this",[46,914,23],{"class":74},[46,916,918],{"class":917},"sy9f4","payment",[46,920,23],{"class":74},[46,922,923],{"class":316},"charge",[46,925,321],{"class":926},"spAFX",[46,928,673],{"class":275},[46,930,339],{"class":926},[46,932,115],{"class":74},[46,934,935],{"class":48,"line":528},[46,936,121],{"class":74},[46,938,939],{"class":48,"line":559},[46,940,127],{"class":74},[16,942,943,946,947,949,950,953,954,957,958,961],{},[20,944,945],{},"StripePayment extends Payment"," is all it takes: ",[20,948,632],{}," is now a ",[388,951,952],{},"port",", and\n",[20,955,956],{},"StripePayment"," answers it. Add a second implementation — a ",[20,959,960],{},"FakePayment"," for\ndevelopment — and Fougere refuses to start rather than pick one:",[37,963,968],{"className":964,"code":966,"language":967},[965],"language-text","Fougere boot refused: 1 declaration(s) that do not hold:\n  [port-implemented-twice] Payment\n    FakePayment and StripePayment both extend Payment. Pick one in fougere.config.ts: ports: { Payment: 'FakePayment' }.\n    fronds\u002Fshop\u002Fservices\u002FFakePayment.ts\n","text",[20,969,966],{"__ignoreMap":42},[16,971,972,973,976,977,980],{},"The choice goes in ",[20,974,975],{},"fougere.config.ts",", and ",[20,978,979],{},"CheckoutHandler"," does not change:",[37,982,984],{"className":39,"code":983,"language":41,"meta":42,"style":42},"export default defineFougere({\n  ports: { Payment: process.env.NODE_ENV === 'production' ? 'StripePayment' : 'FakePayment' },\n});\n",[20,985,986,1000,1057],{"__ignoreMap":42},[46,987,988,990,992,995,997],{"class":48,"line":49},[46,989,60],{"class":59},[46,991,63],{"class":59},[46,993,994],{"class":316}," defineFougere",[46,996,321],{"class":320},[46,998,999],{"class":74},"{\n",[46,1001,1002,1005,1007,1009,1011,1013,1016,1018,1021,1023,1026,1029,1031,1034,1036,1039,1041,1043,1045,1048,1050,1052,1054],{"class":48,"line":56},[46,1003,1004],{"class":206},"  ports",[46,1006,210],{"class":74},[46,1008,203],{"class":74},[46,1010,657],{"class":206},[46,1012,210],{"class":74},[46,1014,1015],{"class":275}," process",[46,1017,23],{"class":74},[46,1019,1020],{"class":917},"env",[46,1022,23],{"class":74},[46,1024,1025],{"class":917},"NODE_ENV",[46,1027,1028],{"class":74}," ===",[46,1030,216],{"class":215},[46,1032,1033],{"class":237},"production",[46,1035,223],{"class":215},[46,1037,1038],{"class":74}," ?",[46,1040,216],{"class":215},[46,1042,956],{"class":237},[46,1044,223],{"class":215},[46,1046,1047],{"class":74}," :",[46,1049,216],{"class":215},[46,1051,960],{"class":237},[46,1053,223],{"class":215},[46,1055,1056],{"class":74}," },\n",[46,1058,1059,1061,1063],{"class":48,"line":78},[46,1060,475],{"class":74},[46,1062,339],{"class":320},[46,1064,115],{"class":74},[16,1066,375,1067],{},[377,1068,1070],{"href":1069},"\u002Fdocs\u002Fbusiness\u002Fports","Ports",[25,1072,1074],{"id":1073},"errors","Errors",[16,1076,1077,1078,1081],{},"An operation refuses with a ",[20,1079,1080],{},"FougereError"," and a code:",[37,1083,1085],{"className":39,"code":1084,"language":41,"meta":42,"style":42},"\u002F\u002F fronds\u002Fshop\u002Fhandlers\u002FCheckoutHandler.ts\nimport { FougereError, ErrorCode } from '@fougere\u002Fcore';\nimport Payment from '..\u002Fservices\u002FPayment.js';\n\nexport default class CheckoutHandler {\n  constructor(private payment: Payment) {}\n\n  async pay(cents: number): Promise\u003C{ reference: string }> {\n    if (cents \u003C= 0) {\n      throw new FougereError({ code: ErrorCode.UNPROCESSABLE_ENTITY, message: 'Nothing to pay' });\n    }\n\n    return this.payment.charge(cents);\n  }\n}\n",[20,1086,1087,1091,1117,1133,1137,1149,1167,1171,1201,1221,1269,1275,1280,1303,1308],{"__ignoreMap":42},[46,1088,1089],{"class":48,"line":49},[46,1090,812],{"class":52},[46,1092,1093,1095,1097,1100,1102,1105,1107,1109,1111,1113,1115],{"class":48,"line":56},[46,1094,270],{"class":59},[46,1096,203],{"class":74},[46,1098,1099],{"class":275}," FougereError",[46,1101,330],{"class":74},[46,1103,1104],{"class":275}," ErrorCode",[46,1106,243],{"class":74},[46,1108,281],{"class":59},[46,1110,216],{"class":215},[46,1112,286],{"class":237},[46,1114,223],{"class":215},[46,1116,115],{"class":74},[46,1118,1119,1121,1123,1125,1127,1129,1131],{"class":48,"line":78},[46,1120,270],{"class":59},[46,1122,718],{"class":275},[46,1124,721],{"class":59},[46,1126,216],{"class":215},[46,1128,825],{"class":237},[46,1130,223],{"class":215},[46,1132,115],{"class":74},[46,1134,1135],{"class":48,"line":105},[46,1136,296],{"emptyLinePlaceholder":295},[46,1138,1139,1141,1143,1145,1147],{"class":48,"line":118},[46,1140,60],{"class":59},[46,1142,63],{"class":59},[46,1144,67],{"class":66},[46,1146,844],{"class":70},[46,1148,75],{"class":74},[46,1150,1151,1153,1155,1157,1159,1161,1163,1165],{"class":48,"line":124},[46,1152,851],{"class":66},[46,1154,321],{"class":74},[46,1156,856],{"class":66},[46,1158,859],{"class":672},[46,1160,210],{"class":74},[46,1162,657],{"class":70},[46,1164,339],{"class":74},[46,1166,868],{"class":74},[46,1168,1169],{"class":48,"line":353},[46,1170,296],{"emptyLinePlaceholder":295},[46,1172,1173,1175,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199],{"class":48,"line":518},[46,1174,81],{"class":66},[46,1176,879],{"class":84},[46,1178,321],{"class":74},[46,1180,673],{"class":672},[46,1182,210],{"class":74},[46,1184,678],{"class":70},[46,1186,681],{"class":74},[46,1188,91],{"class":70},[46,1190,686],{"class":74},[46,1192,689],{"class":206},[46,1194,210],{"class":74},[46,1196,694],{"class":70},[46,1198,902],{"class":74},[46,1200,75],{"class":74},[46,1202,1203,1206,1209,1211,1214,1217,1219],{"class":48,"line":528},[46,1204,1205],{"class":59},"    if",[46,1207,1208],{"class":926}," (",[46,1210,673],{"class":275},[46,1212,1213],{"class":74}," \u003C=",[46,1215,1216],{"class":111}," 0",[46,1218,339],{"class":926},[46,1220,75],{"class":74},[46,1222,1223,1226,1229,1232,1234,1237,1240,1242,1244,1246,1249,1251,1254,1256,1258,1261,1263,1265,1267],{"class":48,"line":559},[46,1224,1225],{"class":59},"      throw",[46,1227,1228],{"class":74}," new",[46,1230,1099],{"class":1231},"sgjsB",[46,1233,321],{"class":926},[46,1235,1236],{"class":74},"{",[46,1238,1239],{"class":206}," code",[46,1241,210],{"class":74},[46,1243,1104],{"class":275},[46,1245,23],{"class":74},[46,1247,1248],{"class":917},"UNPROCESSABLE_ENTITY",[46,1250,330],{"class":74},[46,1252,1253],{"class":206}," message",[46,1255,210],{"class":74},[46,1257,216],{"class":215},[46,1259,1260],{"class":237},"Nothing to pay",[46,1262,223],{"class":215},[46,1264,243],{"class":74},[46,1266,339],{"class":926},[46,1268,115],{"class":74},[46,1270,1272],{"class":48,"line":1271},11,[46,1273,1274],{"class":74},"    }\n",[46,1276,1278],{"class":48,"line":1277},12,[46,1279,296],{"emptyLinePlaceholder":295},[46,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1301],{"class":48,"line":1282},13,[46,1284,108],{"class":59},[46,1286,912],{"class":911},[46,1288,23],{"class":74},[46,1290,918],{"class":917},[46,1292,23],{"class":74},[46,1294,923],{"class":316},[46,1296,321],{"class":926},[46,1298,673],{"class":275},[46,1300,339],{"class":926},[46,1302,115],{"class":74},[46,1304,1306],{"class":48,"line":1305},14,[46,1307,121],{"class":74},[46,1309,1311],{"class":48,"line":1310},15,[46,1312,127],{"class":74},[16,1314,1315,1316,1319,1320,140,1323,1326,1327,1329],{},"The facade adds where it was refused — ",[20,1317,1318],{},"error.address"," is ",[20,1321,1322],{},"checkout",[20,1324,1325],{},"error.operation","\nis ",[20,1328,165],{}," — so the handler writes neither.",[16,1331,1332,1333,1335],{},"The page knows which codes ",[20,1334,165],{}," can return — they are part of the facade's type:",[37,1337,1339],{"className":39,"code":1338,"language":41,"meta":42,"style":42},"const pay = useCommand(checkout, 'pay');\nawait pay.execute({ params: { cents: 0 } });\n\u002F\u002F pay.error.value?.code: ErrorCode.UNPROCESSABLE_ENTITY | …\n",[20,1340,1341,1369,1407],{"__ignoreMap":42},[46,1342,1343,1345,1348,1350,1353,1355,1357,1359,1361,1363,1365,1367],{"class":48,"line":49},[46,1344,463],{"class":66},[46,1346,1347],{"class":275}," pay ",[46,1349,309],{"class":74},[46,1351,1352],{"class":316}," useCommand",[46,1354,321],{"class":320},[46,1356,1322],{"class":275},[46,1358,330],{"class":74},[46,1360,216],{"class":215},[46,1362,165],{"class":237},[46,1364,223],{"class":215},[46,1366,339],{"class":320},[46,1368,115],{"class":74},[46,1370,1371,1374,1376,1378,1381,1383,1385,1388,1390,1392,1395,1397,1399,1401,1403,1405],{"class":48,"line":56},[46,1372,1373],{"class":59},"await",[46,1375,879],{"class":275},[46,1377,23],{"class":74},[46,1379,1380],{"class":316},"execute",[46,1382,321],{"class":320},[46,1384,1236],{"class":74},[46,1386,1387],{"class":206}," params",[46,1389,210],{"class":74},[46,1391,203],{"class":74},[46,1393,1394],{"class":206}," cents",[46,1396,210],{"class":74},[46,1398,1216],{"class":111},[46,1400,243],{"class":74},[46,1402,243],{"class":74},[46,1404,339],{"class":320},[46,1406,115],{"class":74},[46,1408,1409],{"class":48,"line":78},[46,1410,1411],{"class":52},"\u002F\u002F pay.error.value?.code: ErrorCode.UNPROCESSABLE_ENTITY | …\n",[16,1413,1414],{},"The error reaches the page with the same code and message whether the frond runs in the\napp's process or on another server.",[16,1416,375,1417],{},[377,1418,1074],{"href":1419},"\u002Fdocs\u002Fbusiness\u002Ferrors",[16,1421,1422,1423,23],{},"Next: ",[377,1424,1426],{"href":1425},"\u002Fdocs\u002Fguides\u002Fdata","Store your data",[1428,1429,1430],"style",{},"html pre.shiki code .skXKP, html code.shiki .skXKP{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#7D8C80;--shiki-default-font-style:inherit;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sK6XW, html code.shiki .sK6XW{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#8FCF86;--shiki-default-font-style:inherit;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s76I7, html code.shiki .s76I7{--shiki-light:#9C3EDA;--shiki-default:#8FCF86;--shiki-dark:#C792EA}html pre.shiki code .sBZYL, html code.shiki .sBZYL{--shiki-light:#E2931D;--shiki-default:#EFCD8F;--shiki-dark:#FFCB6B}html pre.shiki code .s15xx, html code.shiki .s15xx{--shiki-light:#39ADB5;--shiki-default:#A3B0A5;--shiki-dark:#89DDFF}html pre.shiki code .shDkW, html code.shiki .shDkW{--shiki-light:#E53935;--shiki-default:#C9B6EA;--shiki-dark:#F07178}html pre.shiki code .s3dPA, html code.shiki .s3dPA{--shiki-light:#F76D47;--shiki-default:#EE9F6B;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHtqg, html code.shiki .sHtqg{--shiki-light:#E53935;--shiki-default:#93CFC0;--shiki-dark:#F07178}html pre.shiki code .sDDC1, html code.shiki .sDDC1{--shiki-light:#39ADB5;--shiki-default:#EE9F6B;--shiki-dark:#89DDFF}html pre.shiki code .sK7Y8, html code.shiki .sK7Y8{--shiki-light:#E53935;--shiki-default:#EE9F6B;--shiki-dark:#F07178}html pre.shiki code .sN-8n, html code.shiki .sN-8n{--shiki-light:#91B859;--shiki-default:#EE9F6B;--shiki-dark:#C3E88D}html pre.shiki code .sPEhv, html code.shiki .sPEhv{--shiki-light:#90A4AE;--shiki-default:#DDE5DA;--shiki-dark:#BABED8}html pre.shiki code .s4mR9, html code.shiki .s4mR9{--shiki-light:#6182B8;--shiki-default:#C9B6EA;--shiki-dark:#82AAFF}html pre.shiki code .srlTG, html code.shiki .srlTG{--shiki-light:#90A4AE;--shiki-default:#A3B0A5;--shiki-dark:#BABED8}html pre.shiki code .s687E, html code.shiki .s687E{--shiki-light:#E53935;--shiki-default:#8FCF86;--shiki-dark:#F07178}html pre.shiki code .s8ClO, html code.shiki .s8ClO{--shiki-light:#9C3EDA;--shiki-default:#93CFC0;--shiki-dark:#C792EA}html pre.shiki code .shDUQ, html code.shiki .shDUQ{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#DDE5DA;--shiki-default-font-style:inherit;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sxG60, html code.shiki .sxG60{--shiki-light:#39ADB5;--shiki-light-font-style:inherit;--shiki-default:#8FCF86;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:inherit}html pre.shiki code .sy9f4, html code.shiki .sy9f4{--shiki-light:#90A4AE;--shiki-default:#93CFC0;--shiki-dark:#BABED8}html pre.shiki code .spAFX, html code.shiki .spAFX{--shiki-light:#E53935;--shiki-default:#A3B0A5;--shiki-dark:#F07178}html pre.shiki code .sgjsB, html code.shiki .sgjsB{--shiki-light:#6182B8;--shiki-default:#EFCD8F;--shiki-dark:#82AAFF}",{"title":42,"searchDepth":56,"depth":56,"links":1432},[1433,1434,1435,1436],{"id":27,"depth":56,"text":28},{"id":382,"depth":56,"text":383},{"id":618,"depth":56,"text":619},{"id":1073,"depth":56,"text":1074},"A handler, the page that calls it, a payment port behind it, and what it refuses.","md",{},"\u002Fdocs\u002Fguides\u002Fbusiness-logic",{"title":7,"description":1437},"docs\u002F1.guides\u002F1.business-logic","AZ9nw33kMLC6AV1LEQF7wnYyJ0-Hs6IWCkDAxmeNmOk",[1445],{"title":1446,"path":1447,"stem":1448,"children":1449,"page":-1},"Docs","\u002Fdocs","docs",[1450,1453,1457,1461,1465,1469,1473,1490,1511,1536,1579,1602],{"title":1451,"path":1447,"stem":1452},"What is Fougere","docs\u002Findex",{"title":1454,"path":1455,"stem":1456},"Getting started","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",{"title":1458,"path":1459,"stem":1460},"An app you already have","\u002Fdocs\u002Fexisting-app","docs\u002F02.existing-app",{"title":1462,"path":1463,"stem":1464},"Bring your schema","\u002Fdocs\u002Fbring-your-schema","docs\u002F03.bring-your-schema",{"title":1466,"path":1467,"stem":1468},"The CLI","\u002Fdocs\u002Fcli","docs\u002F04.cli",{"title":1470,"path":1471,"stem":1472},"Demos","\u002Fdocs\u002Fdemos","docs\u002F05.demos",{"title":1474,"path":1475,"stem":1476,"children":1477,"page":1489},"Guides","\u002Fdocs\u002Fguides","docs\u002F1.guides",[1478,1479,1481,1485],{"title":7,"path":1440,"stem":1442},{"title":1426,"path":1425,"stem":1480},"docs\u002F1.guides\u002F2.data",{"title":1482,"path":1483,"stem":1484},"Shape your topology","\u002Fdocs\u002Fguides\u002Ftopology","docs\u002F1.guides\u002F3.topology",{"title":1486,"path":1487,"stem":1488},"Choose how it runs","\u002Fdocs\u002Fguides\u002Fconfig","docs\u002F1.guides\u002F4.config",false,{"title":1491,"path":1492,"stem":1493,"children":1494,"page":1489},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[1495,1499,1503,1507],{"title":1496,"path":1497,"stem":1498},"Philosophy","\u002Fdocs\u002Fconcepts\u002Fphilosophy","docs\u002F2.concepts\u002F1.philosophy",{"title":1500,"path":1501,"stem":1502},"The Frond","\u002Fdocs\u002Fconcepts\u002Ffrond","docs\u002F2.concepts\u002F2.frond",{"title":1504,"path":1505,"stem":1506},"The shortest path","\u002Fdocs\u002Fconcepts\u002Fshortest-path","docs\u002F2.concepts\u002F3.shortest-path",{"title":1508,"path":1509,"stem":1510},"The base","\u002Fdocs\u002Fconcepts\u002Fthe-base","docs\u002F2.concepts\u002F4.the-base",{"title":1512,"path":1513,"stem":1514,"children":1515,"page":1489},"Schema","\u002Fdocs\u002Fschema","docs\u002F3.schema",[1516,1520,1524,1528,1532],{"title":1517,"path":1518,"stem":1519},"Entities","\u002Fdocs\u002Fschema\u002Fentities","docs\u002F3.schema\u002F1.entities",{"title":1521,"path":1522,"stem":1523},"Views","\u002Fdocs\u002Fschema\u002Fviews","docs\u002F3.schema\u002F2.views",{"title":1525,"path":1526,"stem":1527},"The identity card","\u002Fdocs\u002Fschema\u002Fcard","docs\u002F3.schema\u002F3.card",{"title":1529,"path":1530,"stem":1531},"Standard Schema","\u002Fdocs\u002Fschema\u002Fstandard-schema","docs\u002F3.schema\u002F4.standard-schema",{"title":1533,"path":1534,"stem":1535},"How a schema moves","\u002Fdocs\u002Fschema\u002Fevolution","docs\u002F3.schema\u002F5.evolution",{"title":1537,"path":1538,"stem":1539,"children":1540,"page":1489},"Business","\u002Fdocs\u002Fbusiness","docs\u002F4.business",[1541,1543,1545,1549,1553,1557,1559,1563,1567,1571,1575],{"title":28,"path":379,"stem":1542},"docs\u002F4.business\u002F1.handlers",{"title":1070,"path":1069,"stem":1544},"docs\u002F4.business\u002F10.ports",{"title":1546,"path":1547,"stem":1548},"Caller state","\u002Fdocs\u002Fbusiness\u002Fstate","docs\u002F4.business\u002F11.state",{"title":1550,"path":1551,"stem":1552},"Presenters","\u002Fdocs\u002Fbusiness\u002Fpresenters","docs\u002F4.business\u002F2.presenters",{"title":1554,"path":1555,"stem":1556},"Collectors","\u002Fdocs\u002Fbusiness\u002Fcollectors","docs\u002F4.business\u002F3.collectors",{"title":1074,"path":1419,"stem":1558},"docs\u002F4.business\u002F4.errors",{"title":1560,"path":1561,"stem":1562},"Seeds","\u002Fdocs\u002Fbusiness\u002Fseeds","docs\u002F4.business\u002F5.seeds",{"title":1564,"path":1565,"stem":1566},"Facts","\u002Fdocs\u002Fbusiness\u002Ffacts","docs\u002F4.business\u002F6.facts",{"title":1568,"path":1569,"stem":1570},"Storage","\u002Fdocs\u002Fbusiness\u002Fstorage","docs\u002F4.business\u002F7.storage",{"title":1572,"path":1573,"stem":1574},"Writes that stand or fall as one","\u002Fdocs\u002Fbusiness\u002Ftogether","docs\u002F4.business\u002F8.together",{"title":1576,"path":1577,"stem":1578},"Repositories","\u002Fdocs\u002Fbusiness\u002Frepositories","docs\u002F4.business\u002F9.repositories",{"title":1580,"path":1581,"stem":1582,"children":1583,"page":1489},"Client","\u002Fdocs\u002Fclient","docs\u002F5.client",[1584,1586,1590,1594,1598],{"title":615,"path":614,"stem":1585},"docs\u002F5.client\u002F1.queries-commands",{"title":1587,"path":1588,"stem":1589},"Forms","\u002Fdocs\u002Fclient\u002Fforms","docs\u002F5.client\u002F2.forms",{"title":1591,"path":1592,"stem":1593},"Session","\u002Fdocs\u002Fclient\u002Fsession","docs\u002F5.client\u002F3.session",{"title":1595,"path":1596,"stem":1597},"invoke","\u002Fdocs\u002Fclient\u002Finvoke","docs\u002F5.client\u002F4.invoke",{"title":1599,"path":1600,"stem":1601},"Back-office","\u002Fdocs\u002Fclient\u002Fadmin","docs\u002F5.client\u002F5.admin",{"title":1603,"path":1604,"stem":1605,"children":1606,"page":1489},"Infrastructure","\u002Fdocs\u002Finfra","docs\u002F6.infra",[1607,1611,1615,1619,1623,1627,1631,1635,1639],{"title":1608,"path":1609,"stem":1610},"The gradient","\u002Fdocs\u002Finfra\u002Fgradient","docs\u002F6.infra\u002F1.gradient",{"title":1612,"path":1613,"stem":1614},"Surfaces — REST & GraphQL","\u002Fdocs\u002Finfra\u002Fsurfaces","docs\u002F6.infra\u002F2.surfaces",{"title":1616,"path":1617,"stem":1618},"Deployment","\u002Fdocs\u002Finfra\u002Fdeployment","docs\u002F6.infra\u002F3.deployment",{"title":1620,"path":1621,"stem":1622},"Hosts","\u002Fdocs\u002Finfra\u002Fhosts","docs\u002F6.infra\u002F4.hosts",{"title":1624,"path":1625,"stem":1626},"Sources","\u002Fdocs\u002Finfra\u002Fsources","docs\u002F6.infra\u002F5.sources",{"title":1628,"path":1629,"stem":1630},"Lifecycle","\u002Fdocs\u002Finfra\u002Flifecycle","docs\u002F6.infra\u002F6.lifecycle",{"title":1632,"path":1633,"stem":1634},"Identity across Fronds","\u002Fdocs\u002Finfra\u002Fidentity","docs\u002F6.infra\u002F7.identity",{"title":1636,"path":1637,"stem":1638},"Observability","\u002Fdocs\u002Finfra\u002Fobservability","docs\u002F6.infra\u002F8.observability",{"title":1640,"path":1641,"stem":1642},"Testing","\u002Fdocs\u002Finfra\u002Ftesting","docs\u002F6.infra\u002F9.testing",1792704168333]