[{"data":1,"prerenderedAt":967},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Fbusiness\u002Fports":186},[4],{"title":5,"path":6,"stem":7,"children":8},"Docs","\u002Fdocs","docs",[9,12,16,20,24,28,50,75,120,145],{"title":10,"path":6,"stem":11},"What is Fougere","docs\u002Findex",{"title":13,"path":14,"stem":15},"Getting started","\u002Fdocs\u002Fgetting-started","docs\u002F01.getting-started",{"title":17,"path":18,"stem":19},"An app you already have","\u002Fdocs\u002Fexisting-app","docs\u002F02.existing-app",{"title":21,"path":22,"stem":23},"Bring your schema","\u002Fdocs\u002Fbring-your-schema","docs\u002F03.bring-your-schema",{"title":25,"path":26,"stem":27},"The CLI","\u002Fdocs\u002Fcli","docs\u002F04.cli",{"title":29,"path":30,"stem":31,"children":32,"page":49},"Concepts","\u002Fdocs\u002Fconcepts","docs\u002F2.concepts",[33,37,41,45],{"title":34,"path":35,"stem":36},"Philosophy","\u002Fdocs\u002Fconcepts\u002Fphilosophy","docs\u002F2.concepts\u002F1.philosophy",{"title":38,"path":39,"stem":40},"The Frond","\u002Fdocs\u002Fconcepts\u002Ffrond","docs\u002F2.concepts\u002F2.frond",{"title":42,"path":43,"stem":44},"The shortest path","\u002Fdocs\u002Fconcepts\u002Fshortest-path","docs\u002F2.concepts\u002F3.shortest-path",{"title":46,"path":47,"stem":48},"The base","\u002Fdocs\u002Fconcepts\u002Fthe-base","docs\u002F2.concepts\u002F4.the-base",false,{"title":51,"path":52,"stem":53,"children":54,"page":49},"Schema","\u002Fdocs\u002Fschema","docs\u002F3.schema",[55,59,63,67,71],{"title":56,"path":57,"stem":58},"Entities","\u002Fdocs\u002Fschema\u002Fentities","docs\u002F3.schema\u002F1.entities",{"title":60,"path":61,"stem":62},"Views","\u002Fdocs\u002Fschema\u002Fviews","docs\u002F3.schema\u002F2.views",{"title":64,"path":65,"stem":66},"The identity card","\u002Fdocs\u002Fschema\u002Fcard","docs\u002F3.schema\u002F3.card",{"title":68,"path":69,"stem":70},"Standard Schema","\u002Fdocs\u002Fschema\u002Fstandard-schema","docs\u002F3.schema\u002F4.standard-schema",{"title":72,"path":73,"stem":74},"How a schema moves","\u002Fdocs\u002Fschema\u002Fevolution","docs\u002F3.schema\u002F5.evolution",{"title":76,"path":77,"stem":78,"children":79,"page":49},"Business","\u002Fdocs\u002Fbusiness","docs\u002F4.business",[80,84,88,92,96,100,104,108,112,116],{"title":81,"path":82,"stem":83},"Handlers","\u002Fdocs\u002Fbusiness\u002Fhandlers","docs\u002F4.business\u002F1.handlers",{"title":85,"path":86,"stem":87},"Ports","\u002Fdocs\u002Fbusiness\u002Fports","docs\u002F4.business\u002F10.ports",{"title":89,"path":90,"stem":91},"Presenters","\u002Fdocs\u002Fbusiness\u002Fpresenters","docs\u002F4.business\u002F2.presenters",{"title":93,"path":94,"stem":95},"Collectors","\u002Fdocs\u002Fbusiness\u002Fcollectors","docs\u002F4.business\u002F3.collectors",{"title":97,"path":98,"stem":99},"Errors","\u002Fdocs\u002Fbusiness\u002Ferrors","docs\u002F4.business\u002F4.errors",{"title":101,"path":102,"stem":103},"Seeds","\u002Fdocs\u002Fbusiness\u002Fseeds","docs\u002F4.business\u002F5.seeds",{"title":105,"path":106,"stem":107},"Facts","\u002Fdocs\u002Fbusiness\u002Ffacts","docs\u002F4.business\u002F6.facts",{"title":109,"path":110,"stem":111},"Storage","\u002Fdocs\u002Fbusiness\u002Fstorage","docs\u002F4.business\u002F7.storage",{"title":113,"path":114,"stem":115},"Writes that stand or fall as one","\u002Fdocs\u002Fbusiness\u002Ftogether","docs\u002F4.business\u002F8.together",{"title":117,"path":118,"stem":119},"Repositories","\u002Fdocs\u002Fbusiness\u002Frepositories","docs\u002F4.business\u002F9.repositories",{"title":121,"path":122,"stem":123,"children":124,"page":49},"Client","\u002Fdocs\u002Fclient","docs\u002F5.client",[125,129,133,137,141],{"title":126,"path":127,"stem":128},"Queries & commands","\u002Fdocs\u002Fclient\u002Fqueries-commands","docs\u002F5.client\u002F1.queries-commands",{"title":130,"path":131,"stem":132},"Forms","\u002Fdocs\u002Fclient\u002Fforms","docs\u002F5.client\u002F2.forms",{"title":134,"path":135,"stem":136},"Session","\u002Fdocs\u002Fclient\u002Fsession","docs\u002F5.client\u002F3.session",{"title":138,"path":139,"stem":140},"invoke","\u002Fdocs\u002Fclient\u002Finvoke","docs\u002F5.client\u002F4.invoke",{"title":142,"path":143,"stem":144},"Back-office","\u002Fdocs\u002Fclient\u002Fadmin","docs\u002F5.client\u002F5.admin",{"title":146,"path":147,"stem":148,"children":149,"page":49},"Infrastructure","\u002Fdocs\u002Finfra","docs\u002F6.infra",[150,154,158,162,166,170,174,178,182],{"title":151,"path":152,"stem":153},"The gradient","\u002Fdocs\u002Finfra\u002Fgradient","docs\u002F6.infra\u002F1.gradient",{"title":155,"path":156,"stem":157},"Surfaces — REST & GraphQL","\u002Fdocs\u002Finfra\u002Fsurfaces","docs\u002F6.infra\u002F2.surfaces",{"title":159,"path":160,"stem":161},"Deployment","\u002Fdocs\u002Finfra\u002Fdeployment","docs\u002F6.infra\u002F3.deployment",{"title":163,"path":164,"stem":165},"Hosts","\u002Fdocs\u002Finfra\u002Fhosts","docs\u002F6.infra\u002F4.hosts",{"title":167,"path":168,"stem":169},"Sources","\u002Fdocs\u002Finfra\u002Fsources","docs\u002F6.infra\u002F5.sources",{"title":171,"path":172,"stem":173},"Lifecycle","\u002Fdocs\u002Finfra\u002Flifecycle","docs\u002F6.infra\u002F6.lifecycle",{"title":175,"path":176,"stem":177},"Identity across Fronds","\u002Fdocs\u002Finfra\u002Fidentity","docs\u002F6.infra\u002F7.identity",{"title":179,"path":180,"stem":181},"Observability","\u002Fdocs\u002Finfra\u002Fobservability","docs\u002F6.infra\u002F8.observability",{"title":183,"path":184,"stem":185},"Testing","\u002Fdocs\u002Finfra\u002Ftesting","docs\u002F6.infra\u002F9.testing",{"id":187,"title":85,"body":188,"description":962,"extension":963,"meta":964,"navigation":297,"path":86,"seo":965,"stem":87,"__hash__":966},"docs_en\u002Fdocs\u002F4.business\u002F10.ports.md",{"type":189,"value":190,"toc":955},"minimark",[191,195,204,207,492,511,516,535,553,562,566,581,660,663,671,678,682,685,693,696,828,867,881,885,892,916,939,943,946,951],[192,193,85],"h1",{"id":194},"ports",[196,197,198,199,203],"p",{},"A handler that writes ",[200,201,202],"code",{},"constructor(private payment: StripePayment)"," has named its provider.\nEverything downstream of that line — the tests, the sandbox, the day you change PSP — pays\nfor it.",[196,205,206],{},"Declare the capability instead, and extend it:",[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","\u002F\u002F services\u002FPayment.ts — the port. It names no provider.\nexport default abstract class Payment {\n  abstract charge(amountCents: number): Charge;\n}\n\n\u002F\u002F services\u002FStripePayment.ts — the realization.\nexport default class StripePayment extends Payment {\n  charge(amountCents: number) { return { provider: 'stripe', amountCents }; }\n}\n\n\u002F\u002F handlers\u002FCheckoutHandler.ts — asks for the port.\nexport default class CheckoutHandler {\n  constructor(private payment: Payment) {}\n\n  \u002F** Charge the cart. *\u002F\n  async pay() { return this.payment.charge(4990); }\n}\n","ts","",[200,215,216,225,250,286,292,299,305,324,378,383,388,394,408,431,436,442,487],{"__ignoreMap":213},[217,218,221],"span",{"class":219,"line":220},"line",1,[217,222,224],{"class":223},"sutJx","\u002F\u002F services\u002FPayment.ts — the port. It names no provider.\n",[217,226,228,232,235,239,242,246],{"class":219,"line":227},2,[217,229,231],{"class":230},"sVHd0","export",[217,233,234],{"class":230}," default",[217,236,238],{"class":237},"sbsja"," abstract",[217,240,241],{"class":237}," class",[217,243,245],{"class":244},"sbgvK"," Payment",[217,247,249],{"class":248},"sP7_E"," {\n",[217,251,253,256,260,263,267,271,275,278,280,283],{"class":219,"line":252},3,[217,254,255],{"class":237},"  abstract",[217,257,259],{"class":258},"sVXei"," charge",[217,261,262],{"class":248},"(",[217,264,266],{"class":265},"s99_P","amountCents",[217,268,270],{"class":269},"smGrS",":",[217,272,274],{"class":273},"sZMiF"," number",[217,276,277],{"class":248},")",[217,279,270],{"class":269},[217,281,282],{"class":244}," Charge",[217,284,285],{"class":248},";\n",[217,287,289],{"class":219,"line":288},4,[217,290,291],{"class":248},"}\n",[217,293,295],{"class":219,"line":294},5,[217,296,298],{"emptyLinePlaceholder":297},true,"\n",[217,300,302],{"class":219,"line":301},6,[217,303,304],{"class":223},"\u002F\u002F services\u002FStripePayment.ts — the realization.\n",[217,306,308,310,312,314,317,320,322],{"class":219,"line":307},7,[217,309,231],{"class":230},[217,311,234],{"class":230},[217,313,241],{"class":237},[217,315,316],{"class":244}," StripePayment",[217,318,319],{"class":237}," extends",[217,321,245],{"class":244},[217,323,249],{"class":248},[217,325,327,330,332,334,336,338,340,343,346,348,352,354,358,362,365,368,372,375],{"class":219,"line":326},8,[217,328,329],{"class":258},"  charge",[217,331,262],{"class":248},[217,333,266],{"class":265},[217,335,270],{"class":269},[217,337,274],{"class":273},[217,339,277],{"class":248},[217,341,342],{"class":248}," {",[217,344,345],{"class":230}," return",[217,347,342],{"class":248},[217,349,351],{"class":350},"skxfh"," provider",[217,353,270],{"class":248},[217,355,357],{"class":356},"sjJ54"," '",[217,359,361],{"class":360},"s_sjI","stripe",[217,363,364],{"class":356},"'",[217,366,367],{"class":248},",",[217,369,371],{"class":370},"su5hD"," amountCents",[217,373,374],{"class":248}," };",[217,376,377],{"class":248}," }\n",[217,379,381],{"class":219,"line":380},9,[217,382,291],{"class":248},[217,384,386],{"class":219,"line":385},10,[217,387,298],{"emptyLinePlaceholder":297},[217,389,391],{"class":219,"line":390},11,[217,392,393],{"class":223},"\u002F\u002F handlers\u002FCheckoutHandler.ts — asks for the port.\n",[217,395,397,399,401,403,406],{"class":219,"line":396},12,[217,398,231],{"class":230},[217,400,234],{"class":230},[217,402,241],{"class":237},[217,404,405],{"class":244}," CheckoutHandler",[217,407,249],{"class":248},[217,409,411,414,416,419,422,424,426,428],{"class":219,"line":410},13,[217,412,413],{"class":237},"  constructor",[217,415,262],{"class":248},[217,417,418],{"class":237},"private",[217,420,421],{"class":265}," payment",[217,423,270],{"class":269},[217,425,245],{"class":244},[217,427,277],{"class":248},[217,429,430],{"class":248}," {}\n",[217,432,434],{"class":219,"line":433},14,[217,435,298],{"emptyLinePlaceholder":297},[217,437,439],{"class":219,"line":438},15,[217,440,441],{"class":223},"  \u002F** Charge the cart. *\u002F\n",[217,443,445,448,451,454,456,458,462,465,468,470,474,476,480,482,485],{"class":219,"line":444},16,[217,446,447],{"class":237},"  async",[217,449,450],{"class":258}," pay",[217,452,453],{"class":248},"()",[217,455,342],{"class":248},[217,457,345],{"class":230},[217,459,461],{"class":460},"s39Yj"," this",[217,463,464],{"class":248},".",[217,466,467],{"class":370},"payment",[217,469,464],{"class":248},[217,471,473],{"class":472},"sGLFI","charge",[217,475,262],{"class":350},[217,477,479],{"class":478},"srdBf","4990",[217,481,277],{"class":350},[217,483,484],{"class":248},";",[217,486,377],{"class":248},[217,488,490],{"class":219,"line":489},17,[217,491,291],{"class":248},[196,493,494,497,498,501,502,506,507,510],{},[200,495,496],{},"this.payment"," is a ",[200,499,500],{},"StripePayment",". ",[503,504,505],"strong",{},"Nothing declares that"," — ",[200,508,509],{},"extends"," is the whole\nregistration, and the boot reads the prototype chain.",[512,513,515],"h2",{"id":514},"a-port-is-not-a-kind-of-file","A port is not a kind of file",[196,517,518,519,522,523,526,527,530,531,534],{},"There is no ",[200,520,521],{},"ports\u002F"," directory and no ",[200,524,525],{},"Port(...)"," factory. A port is a provider class that\nanother provider extends: ",[200,528,529],{},"services\u002F"," and ",[200,532,533],{},"repositories\u002F"," are scanned as before, and the\nrelation between two of their classes is what makes one a port. Write the base alone and it\nis an ordinary service; write a class extending it and the base becomes the key the\nimplementation answers under.",[196,536,537,538,541,542,545,546,530,549,552],{},"This is the same reading the rest of the framework already applies — the type on the\nparameter names the ",[503,539,540],{},"subject",", the container holds the realization. It is how\n",[200,543,544],{},"RepositoryOf\u003CPost>",", ",[200,547,548],{},"Emit\u003CPostPublished>",[200,550,551],{},"Facade\u003CPostHandler>"," resolve. Providers were\nthe one case where the type named the realization instead.",[554,555,556],"note",{},[196,557,558,561],{},[200,559,560],{},"abstract"," is a TypeScript keyword, erased at runtime. It buys the compile-time guarantee\nthat an implementation exists — a base with no subclass is just a service, and the boot\ncannot tell you it was meant to be abstract.",[512,563,565],{"id":564},"a-framework-class-is-a-port-too","A framework class is a port too",[196,567,568,569,572,573,576,577,580],{},"The condition is one line: ",[503,570,571],{},"something already answers under that name",". Providers are\nregistered into the Frond's scope, and Fougere's own builtins sit in its parent — so\n",[200,574,575],{},"Logger"," is a port like ",[200,578,579],{},"Payment"," is, and extending it takes the key over:",[208,582,584],{"className":210,"code":583,"language":212,"meta":213,"style":213},"\u002F\u002F services\u002FAuditLogger.ts\nexport default class AuditLogger extends Logger {\n  info(msg: string) { \u002F* ship it somewhere *\u002F }\n}\n\n\u002F\u002F any handler, unchanged\nconstructor(private logger: Logger) {}   \u002F\u002F receives AuditLogger\n",[200,585,586,591,609,633,637,641,646],{"__ignoreMap":213},[217,587,588],{"class":219,"line":220},[217,589,590],{"class":223},"\u002F\u002F services\u002FAuditLogger.ts\n",[217,592,593,595,597,599,602,604,607],{"class":219,"line":227},[217,594,231],{"class":230},[217,596,234],{"class":230},[217,598,241],{"class":237},[217,600,601],{"class":244}," AuditLogger",[217,603,319],{"class":237},[217,605,606],{"class":244}," Logger",[217,608,249],{"class":248},[217,610,611,614,616,619,621,624,626,628,631],{"class":219,"line":252},[217,612,613],{"class":258},"  info",[217,615,262],{"class":248},[217,617,618],{"class":265},"msg",[217,620,270],{"class":269},[217,622,623],{"class":273}," string",[217,625,277],{"class":248},[217,627,342],{"class":248},[217,629,630],{"class":223}," \u002F* ship it somewhere *\u002F",[217,632,377],{"class":248},[217,634,635],{"class":219,"line":288},[217,636,291],{"class":248},[217,638,639],{"class":219,"line":294},[217,640,298],{"emptyLinePlaceholder":297},[217,642,643],{"class":219,"line":301},[217,644,645],{"class":223},"\u002F\u002F any handler, unchanged\n",[217,647,648,651,654,657],{"class":219,"line":307},[217,649,650],{"class":472},"constructor",[217,652,653],{"class":370},"(private logger: Logger) ",[217,655,656],{"class":248},"{}",[217,658,659],{"class":223},"   \u002F\u002F receives AuditLogger\n",[196,661,662],{},"The default stays for every Frond that declares no subclass — the override is per Frond,\nbecause a Frond's services are its own.",[196,664,665,666,670],{},"A logger built before a level change still obeys it, and so does a child: the level is\nconsulted rather than copied into each instance, so nothing is rebuilt when it moves. That\nbelongs to the ",[667,668,669],"a",{"href":172},"lifecycle",", not here.",[196,672,673,674,677],{},"The same condition is what keeps a prefab out: a repository extends the class\n",[200,675,676],{},"Repository(Post)"," returned, and nothing ever answers under that name.",[512,679,681],{"id":680},"two-implementations","Two implementations",[196,683,684],{},"Which realization a deployment uses is not a fact about the code — it changes between dev\nand production. So the boot refuses to guess:",[208,686,691],{"className":687,"code":689,"language":690},[688],"language-text","[ports] StripePayment and OgonePayment both extend Payment, and nothing says which one\nanswers it. Which realization a deployment uses is not a fact about the code — state it:\nports: { Payment: 'StripePayment' } in fougere.config.ts.\n","text",[200,692,689],{"__ignoreMap":213},[196,694,695],{},"Refusing rather than keeping one is deliberate: whichever won would depend on scan order,\nand the handler would charge the wrong provider without a word.",[208,697,699],{"className":210,"code":698,"language":212,"meta":213,"style":213},"\u002F\u002F fougere.config.ts\nexport default defineFougere({\n  db: 'sqlite',\n  remotes: { blog: 'http:\u002F\u002F127.0.0.1:4100' },\n  ports: { Payment: process.env.PSP === 'ogone' ? 'OgonePayment' : 'StripePayment' },\n});\n",[200,700,701,706,720,737,761,819],{"__ignoreMap":213},[217,702,703],{"class":219,"line":220},[217,704,705],{"class":223},"\u002F\u002F fougere.config.ts\n",[217,707,708,710,712,715,717],{"class":219,"line":227},[217,709,231],{"class":230},[217,711,234],{"class":230},[217,713,714],{"class":472}," defineFougere",[217,716,262],{"class":370},[217,718,719],{"class":248},"{\n",[217,721,722,725,727,729,732,734],{"class":219,"line":252},[217,723,724],{"class":350},"  db",[217,726,270],{"class":248},[217,728,357],{"class":356},[217,730,731],{"class":360},"sqlite",[217,733,364],{"class":356},[217,735,736],{"class":248},",\n",[217,738,739,742,744,746,749,751,753,756,758],{"class":219,"line":288},[217,740,741],{"class":350},"  remotes",[217,743,270],{"class":248},[217,745,342],{"class":248},[217,747,748],{"class":350}," blog",[217,750,270],{"class":248},[217,752,357],{"class":356},[217,754,755],{"class":360},"http:\u002F\u002F127.0.0.1:4100",[217,757,364],{"class":356},[217,759,760],{"class":248}," },\n",[217,762,763,766,768,770,772,774,777,779,782,784,788,791,793,796,798,801,803,806,808,811,813,815,817],{"class":219,"line":294},[217,764,765],{"class":350},"  ports",[217,767,270],{"class":248},[217,769,342],{"class":248},[217,771,245],{"class":350},[217,773,270],{"class":248},[217,775,776],{"class":370}," process",[217,778,464],{"class":248},[217,780,781],{"class":370},"env",[217,783,464],{"class":248},[217,785,787],{"class":786},"s_hVV","PSP",[217,789,790],{"class":269}," ===",[217,792,357],{"class":356},[217,794,795],{"class":360},"ogone",[217,797,364],{"class":356},[217,799,800],{"class":269}," ?",[217,802,357],{"class":356},[217,804,805],{"class":360},"OgonePayment",[217,807,364],{"class":356},[217,809,810],{"class":269}," :",[217,812,357],{"class":356},[217,814,500],{"class":360},[217,816,364],{"class":356},[217,818,760],{"class":248},[217,820,821,824,826],{"class":219,"line":301},[217,822,823],{"class":248},"}",[217,825,277],{"class":370},[217,827,285],{"class":248},[196,829,830,833,834,837,838,841,842,844,845,848,849,851,852,855,856,858,859,862,863,866],{},[200,831,832],{},"ports:"," sits beside ",[200,835,836],{},"remotes:"," for the same reason ",[200,839,840],{},"sources:"," does. ",[200,843,836],{}," says where\na ",[503,846,847],{},"call"," goes, ",[200,850,840],{}," says where a ",[503,853,854],{},"row"," is, ",[200,857,832],{}," says who performs an\n",[503,860,861],{},"action",". None of the three belongs inside the Frond, which describes itself and not its\ndeployment — a ",[200,864,865],{},"billing"," Frond naming Stripe would be the one thing Fougere refuses.",[196,868,869,870,873,874,876,877,880],{},"Delete ",[200,871,872],{},"OgonePayment.ts"," and the ",[200,875,832],{}," line disappears with it: one implementation is\nresolved by convention. ",[503,878,879],{},"The key only exists for the exception."," A key that matches no\nport anywhere is reported at boot rather than silently obeyed.",[512,882,884],{"id":883},"port-or-frond","Port or Frond?",[196,886,887,888,891],{},"A remote ",[667,889,890],{"href":39},"Frond"," also lets you swap what answers, and it swaps the\nprocess too. The line between them is checkable:",[893,894,895,906],"ul",{},[896,897,898,899,902,903,905],"li",{},"The collaborator ",[503,900,901],{},"owns data"," — entities, tables, seeds → it is a Frond, and ",[200,904,836],{},"\nis its statement.",[896,907,908,909,912,913,915],{},"It only ",[503,910,911],{},"acts on the outside world"," — a PSP, a mailer, a file store → it is a port,\nand ",[200,914,832],{}," is its statement.",[196,917,918,919,530,921,924,925,928,929,932,933,935,936,938],{},"Fougere's own ports follow the same rule from the other side: ",[200,920,109],{},[200,922,923],{},"HttpRouter","\nare declared by the framework because it ",[503,926,927],{},"derives"," things from their shape (",[200,930,931],{},"Crud",", the\nDDL, the identity card). Nothing is derived from ",[200,934,579],{},", so ",[200,937,579],{}," is yours.",[512,940,942],{"id":941},"what-it-does-not-do","What it does not do",[196,944,945],{},"A port lets you express a seam. It does not tell you where the seams are — cutting a domain\ninto collaborators is still your call, and nothing here decides it for you.",[196,947,948,949,464],{},"Next: ",[667,950,126],{"href":127},[952,953,954],"style",{},"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 .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 .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 .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--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 .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":213,"searchDepth":227,"depth":227,"links":956},[957,958,959,960,961],{"id":514,"depth":227,"text":515},{"id":564,"depth":227,"text":565},{"id":680,"depth":227,"text":681},{"id":883,"depth":227,"text":884},{"id":941,"depth":227,"text":942},"How a handler depends on a capability without naming who performs it — a class, its implementations, and the one line that only exists when there are two.","md",{},{"title":85,"description":962},"L-L_m5osnMoExOIbry8W7vFOF9C2LSQK_3j5gsvRQmM",1788560231009]