[{"data":1,"prerenderedAt":1510},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Finfra\u002Fsources":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":167,"body":188,"description":1505,"extension":1506,"meta":1507,"navigation":752,"path":168,"seo":1508,"stem":169,"__hash__":1509},"docs_en\u002Fdocs\u002F6.infra\u002F5.sources.md",{"type":189,"value":190,"toc":1496},"minimark",[191,195,208,213,227,373,390,401,405,427,580,596,599,657,667,675,678,683,698,1084,1107,1111,1129,1136,1196,1210,1352,1355,1395,1406,1410,1416,1422,1440,1449,1453,1492],[192,193,167],"h1",{"id":194},"sources",[196,197,198,199,203,204,207],"p",{},"An app usually has one database, and declares nothing. When it has several — a legacy\narchive, a partner's replica, a warehouse — ",[200,201,202],"code",{},"sources:"," says which entities live where, and\n",[200,205,206],{},"reads:"," says which Fronds may query across them.",[209,210,212],"h2",{"id":211},"where-rows-live","Where rows live",[196,214,215,217,218,222,223,226],{},[200,216,194],{}," names the places that are ",[219,220,221],"strong",{},"not"," the default one. An entity it does not name\nstays in ",[200,224,225],{},"db",", so an app with one database behaves exactly as before:",[228,229,234],"pre",{"className":230,"code":231,"language":232,"meta":233,"style":233},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F fougere.config.ts\nexport default defineFougere({\n  db: { path: '.data\u002Fapp.db' },\n  sources: {\n    archive: { path: '\u002Fmnt\u002Flegacy\u002Fcatalog.db', entities: ['Book'] },\n  },\n});\n","ts","",[200,235,236,245,267,299,310,355,361],{"__ignoreMap":233},[237,238,241],"span",{"class":239,"line":240},"line",1,[237,242,244],{"class":243},"sutJx","\u002F\u002F fougere.config.ts\n",[237,246,248,252,255,259,263],{"class":239,"line":247},2,[237,249,251],{"class":250},"sVHd0","export",[237,253,254],{"class":250}," default",[237,256,258],{"class":257},"sGLFI"," defineFougere",[237,260,262],{"class":261},"su5hD","(",[237,264,266],{"class":265},"sP7_E","{\n",[237,268,270,274,277,280,283,285,289,293,296],{"class":239,"line":269},3,[237,271,273],{"class":272},"skxfh","  db",[237,275,276],{"class":265},":",[237,278,279],{"class":265}," {",[237,281,282],{"class":272}," path",[237,284,276],{"class":265},[237,286,288],{"class":287},"sjJ54"," '",[237,290,292],{"class":291},"s_sjI",".data\u002Fapp.db",[237,294,295],{"class":287},"'",[237,297,298],{"class":265}," },\n",[237,300,302,305,307],{"class":239,"line":301},4,[237,303,304],{"class":272},"  sources",[237,306,276],{"class":265},[237,308,309],{"class":265}," {\n",[237,311,313,316,318,320,322,324,326,329,331,334,337,339,342,344,347,349,352],{"class":239,"line":312},5,[237,314,315],{"class":272},"    archive",[237,317,276],{"class":265},[237,319,279],{"class":265},[237,321,282],{"class":272},[237,323,276],{"class":265},[237,325,288],{"class":287},[237,327,328],{"class":291},"\u002Fmnt\u002Flegacy\u002Fcatalog.db",[237,330,295],{"class":287},[237,332,333],{"class":265},",",[237,335,336],{"class":272}," entities",[237,338,276],{"class":265},[237,340,341],{"class":261}," [",[237,343,295],{"class":287},[237,345,346],{"class":291},"Book",[237,348,295],{"class":287},[237,350,351],{"class":261},"] ",[237,353,354],{"class":265},"},\n",[237,356,358],{"class":239,"line":357},6,[237,359,360],{"class":265},"  },\n",[237,362,364,367,370],{"class":239,"line":363},7,[237,365,366],{"class":265},"}",[237,368,369],{"class":261},")",[237,371,372],{"class":265},";\n",[196,374,375,377,378,381,382,384,385,389],{},[200,376,346],{}," now reads and writes in ",[200,379,380],{},"archive","; everything else stays in ",[200,383,225],{},". The\n",[386,387,388],"a",{"href":110},"storage"," a handler receives is unchanged — placement is a fact about the\napp, not about the code that uses it. A source may also be an engine the caller built, so a\npool Fougere did not open is still a place rows can live.",[196,391,392,393,396,397,400],{},"A ",[386,394,395],{"href":61},"derivation"," makes no table of its own unless it says ",[200,398,399],{},".anchor()",". That\nis the entity's own word, never a key of this file: a frond stays mountable without its host\nknowing that one of its schemas holds rows.",[209,402,404],{"id":403},"what-realizes-a-source","What realizes a source",[196,406,407,410,411,414,415,418,419,422,423,426],{},[200,408,409],{},"source:"," names the ",[219,412,413],{},"adapter",", and what sits below it belongs to that adapter — the shape\n",[200,416,417],{},"adapters:"," already has on an entity. ",[200,420,421],{},"dialect"," is SQL's own property and stays there,\nbecause only ",[200,424,425],{},"@fougere\u002Fadapter-sql"," knows what it is worth.",[228,428,430],{"className":230,"code":429,"language":232,"meta":233,"style":233},"export default defineFougere({\n  db: { path: '.data\u002Fapp.db' },                             \u002F\u002F `source: 'sql'` by convention\n  sources: {\n    archive: { source: 'file', path: '.\u002Frows', entities: ['Snapshot'] },\n    cache:   { source: 'memory', entities: ['Draft'] },\n  },\n});\n",[200,431,432,444,468,476,528,568,572],{"__ignoreMap":233},[237,433,434,436,438,440,442],{"class":239,"line":240},[237,435,251],{"class":250},[237,437,254],{"class":250},[237,439,258],{"class":257},[237,441,262],{"class":261},[237,443,266],{"class":265},[237,445,446,448,450,452,454,456,458,460,462,465],{"class":239,"line":247},[237,447,273],{"class":272},[237,449,276],{"class":265},[237,451,279],{"class":265},[237,453,282],{"class":272},[237,455,276],{"class":265},[237,457,288],{"class":287},[237,459,292],{"class":291},[237,461,295],{"class":287},[237,463,464],{"class":265}," },",[237,466,467],{"class":243},"                             \u002F\u002F `source: 'sql'` by convention\n",[237,469,470,472,474],{"class":239,"line":269},[237,471,304],{"class":272},[237,473,276],{"class":265},[237,475,309],{"class":265},[237,477,478,480,482,484,487,489,491,494,496,498,500,502,504,507,509,511,513,515,517,519,522,524,526],{"class":239,"line":301},[237,479,315],{"class":272},[237,481,276],{"class":265},[237,483,279],{"class":265},[237,485,486],{"class":272}," source",[237,488,276],{"class":265},[237,490,288],{"class":287},[237,492,493],{"class":291},"file",[237,495,295],{"class":287},[237,497,333],{"class":265},[237,499,282],{"class":272},[237,501,276],{"class":265},[237,503,288],{"class":287},[237,505,506],{"class":291},".\u002Frows",[237,508,295],{"class":287},[237,510,333],{"class":265},[237,512,336],{"class":272},[237,514,276],{"class":265},[237,516,341],{"class":261},[237,518,295],{"class":287},[237,520,521],{"class":291},"Snapshot",[237,523,295],{"class":287},[237,525,351],{"class":261},[237,527,354],{"class":265},[237,529,530,533,535,538,540,542,544,547,549,551,553,555,557,559,562,564,566],{"class":239,"line":312},[237,531,532],{"class":272},"    cache",[237,534,276],{"class":265},[237,536,537],{"class":265},"   {",[237,539,486],{"class":272},[237,541,276],{"class":265},[237,543,288],{"class":287},[237,545,546],{"class":291},"memory",[237,548,295],{"class":287},[237,550,333],{"class":265},[237,552,336],{"class":272},[237,554,276],{"class":265},[237,556,341],{"class":261},[237,558,295],{"class":287},[237,560,561],{"class":291},"Draft",[237,563,295],{"class":287},[237,565,351],{"class":261},[237,567,354],{"class":265},[237,569,570],{"class":239,"line":357},[237,571,360],{"class":265},[237,573,574,576,578],{"class":239,"line":363},[237,575,366],{"class":265},[237,577,369],{"class":261},[237,579,372],{"class":265},[196,581,582,583,586,587,589,590,592,593,595],{},"An adapter answers a name by registering at import, so nothing central lists them and a name\nnothing answers is refused saying what this process does answer. Three ship today: ",[200,584,585],{},"sql","\n(SQLite from a name; another engine is built in code and handed in), ",[200,588,493],{}," (one JSON per row,\na directory per entity) and ",[200,591,546],{}," (a Map, which is also the fallback for an app with no\n",[200,594,225],{},").",[196,597,598],{},"A source states four gestures and only the first is required:",[600,601,602,613],"table",{},[603,604,605],"thead",{},[606,607,608,611],"tr",{},[609,610],"th",{},[609,612],{},[614,615,616,627,637,647],"tbody",{},[606,617,618,624],{},[619,620,621],"td",{},[200,622,623],{},"storageFactory",[619,625,626],{},"build the storage of an entity that lives here",[606,628,629,634],{},[619,630,631],{},[200,632,633],{},"migrate?",[619,635,636],{},"bring the shape of what lives here up to date",[606,638,639,644],{},[619,640,641],{},[200,642,643],{},"transacted?",[619,645,646],{},"run one unit of work",[606,648,649,654],{},[619,650,651],{},[200,652,653],{},"close?",[619,655,656],{},"release",[196,658,659,662,663,666],{},[219,660,661],{},"The absence answers too."," A source that hands out no transaction makes a\n",[386,664,665],{"href":114},"frame"," compensate instead of transacting, and the boot says which of\nthe two it built:",[228,668,673],{"className":669,"code":671,"language":672},[670],"language-text","RateCard+Ledger|RateMirrorTogether — compensated: rateCard in 'archive', ledger in 'db' — no isolation\nAccount+LedgerTogether            — transaction, source 'db'\n","text",[200,674,671],{"__ignoreMap":233},[196,676,677],{},"Two frames, one app, two guarantees — and the handlers are the same either way.",[679,680,682],"h3",{"id":681},"writing-one","Writing one",[196,684,685,686,689,690,693,694,697],{},"An adapter supplies ",[200,687,688],{},"Rows",", four gestures over a keyed collection, and ",[200,691,692],{},"storageOver"," derives\nthe thirteen of the ",[386,695,696],{"href":110},"storage port"," from them. Pages, criteria and\nlifecycle stamps are not written twice:",[228,699,701],{"className":230,"code":700,"language":232,"meta":233,"style":233},"import { Sources, storageOver, type Rows, type Source } from '@fougere\u002Fcore';\n\nconst mapRows = (): Rows => {\n  const store = new Map();\n  return {\n    client: store,\n    get: async (key) => store.get(key),\n    has: async (key) => store.has(key),\n    set: async (key, row) => { store.set(key, row); },\n    delete: async (key) => store.delete(key),\n    all: async () => [...store.values()],\n  };\n};\n\nSources.register('memory', (): Source => ({ storageFactory: storageOver(mapRows), name: 'memory' }));\n",[200,702,703,748,754,781,803,810,822,859,892,939,972,1004,1010,1016,1021],{"__ignoreMap":233},[237,704,705,708,710,713,715,718,720,723,726,728,730,733,736,739,741,744,746],{"class":239,"line":240},[237,706,707],{"class":250},"import",[237,709,279],{"class":265},[237,711,712],{"class":261}," Sources",[237,714,333],{"class":265},[237,716,717],{"class":261}," storageOver",[237,719,333],{"class":265},[237,721,722],{"class":250}," type",[237,724,725],{"class":261}," Rows",[237,727,333],{"class":265},[237,729,722],{"class":250},[237,731,732],{"class":261}," Source",[237,734,735],{"class":265}," }",[237,737,738],{"class":250}," from",[237,740,288],{"class":287},[237,742,743],{"class":291},"@fougere\u002Fcore",[237,745,295],{"class":287},[237,747,372],{"class":265},[237,749,750],{"class":239,"line":247},[237,751,753],{"emptyLinePlaceholder":752},true,"\n",[237,755,756,760,764,768,771,773,776,779],{"class":239,"line":269},[237,757,759],{"class":758},"sbsja","const",[237,761,763],{"class":762},"sfCm-"," mapRows",[237,765,767],{"class":766},"smGrS"," =",[237,769,770],{"class":265}," ()",[237,772,276],{"class":766},[237,774,725],{"class":775},"sbgvK",[237,777,778],{"class":758}," =>",[237,780,309],{"class":265},[237,782,783,786,790,792,795,798,801],{"class":239,"line":301},[237,784,785],{"class":758},"  const",[237,787,789],{"class":788},"s_hVV"," store",[237,791,767],{"class":766},[237,793,794],{"class":766}," new",[237,796,797],{"class":257}," Map",[237,799,800],{"class":272},"()",[237,802,372],{"class":265},[237,804,805,808],{"class":239,"line":312},[237,806,807],{"class":250},"  return",[237,809,309],{"class":265},[237,811,812,815,817,819],{"class":239,"line":357},[237,813,814],{"class":272},"    client",[237,816,276],{"class":265},[237,818,789],{"class":261},[237,820,821],{"class":265},",\n",[237,823,824,827,829,832,835,839,841,843,845,848,851,853,855,857],{"class":239,"line":363},[237,825,826],{"class":257},"    get",[237,828,276],{"class":265},[237,830,831],{"class":758}," async",[237,833,834],{"class":265}," (",[237,836,838],{"class":837},"s99_P","key",[237,840,369],{"class":265},[237,842,778],{"class":758},[237,844,789],{"class":261},[237,846,847],{"class":265},".",[237,849,850],{"class":257},"get",[237,852,262],{"class":272},[237,854,838],{"class":261},[237,856,369],{"class":272},[237,858,821],{"class":265},[237,860,862,865,867,869,871,873,875,877,879,881,884,886,888,890],{"class":239,"line":861},8,[237,863,864],{"class":257},"    has",[237,866,276],{"class":265},[237,868,831],{"class":758},[237,870,834],{"class":265},[237,872,838],{"class":837},[237,874,369],{"class":265},[237,876,778],{"class":758},[237,878,789],{"class":261},[237,880,847],{"class":265},[237,882,883],{"class":257},"has",[237,885,262],{"class":272},[237,887,838],{"class":261},[237,889,369],{"class":272},[237,891,821],{"class":265},[237,893,895,898,900,902,904,906,908,911,913,915,917,919,921,924,926,928,930,932,934,937],{"class":239,"line":894},9,[237,896,897],{"class":257},"    set",[237,899,276],{"class":265},[237,901,831],{"class":758},[237,903,834],{"class":265},[237,905,838],{"class":837},[237,907,333],{"class":265},[237,909,910],{"class":837}," row",[237,912,369],{"class":265},[237,914,778],{"class":758},[237,916,279],{"class":265},[237,918,789],{"class":261},[237,920,847],{"class":265},[237,922,923],{"class":257},"set",[237,925,262],{"class":272},[237,927,838],{"class":261},[237,929,333],{"class":265},[237,931,910],{"class":261},[237,933,369],{"class":272},[237,935,936],{"class":265},";",[237,938,298],{"class":265},[237,940,942,945,947,949,951,953,955,957,959,961,964,966,968,970],{"class":239,"line":941},10,[237,943,944],{"class":257},"    delete",[237,946,276],{"class":265},[237,948,831],{"class":758},[237,950,834],{"class":265},[237,952,838],{"class":837},[237,954,369],{"class":265},[237,956,778],{"class":758},[237,958,789],{"class":261},[237,960,847],{"class":265},[237,962,963],{"class":257},"delete",[237,965,262],{"class":272},[237,967,838],{"class":261},[237,969,369],{"class":272},[237,971,821],{"class":265},[237,973,975,978,980,982,984,986,988,991,994,996,999,1002],{"class":239,"line":974},11,[237,976,977],{"class":257},"    all",[237,979,276],{"class":265},[237,981,831],{"class":758},[237,983,770],{"class":265},[237,985,778],{"class":758},[237,987,341],{"class":272},[237,989,990],{"class":766},"...",[237,992,993],{"class":261},"store",[237,995,847],{"class":265},[237,997,998],{"class":257},"values",[237,1000,1001],{"class":272},"()]",[237,1003,821],{"class":265},[237,1005,1007],{"class":239,"line":1006},12,[237,1008,1009],{"class":265},"  };\n",[237,1011,1013],{"class":239,"line":1012},13,[237,1014,1015],{"class":265},"};\n",[237,1017,1019],{"class":239,"line":1018},14,[237,1020,753],{"emptyLinePlaceholder":752},[237,1022,1024,1026,1028,1031,1033,1035,1037,1039,1041,1043,1045,1047,1049,1051,1054,1057,1059,1061,1064,1066,1069,1071,1073,1075,1077,1079,1082],{"class":239,"line":1023},15,[237,1025,167],{"class":261},[237,1027,847],{"class":265},[237,1029,1030],{"class":257},"register",[237,1032,262],{"class":261},[237,1034,295],{"class":287},[237,1036,546],{"class":291},[237,1038,295],{"class":287},[237,1040,333],{"class":265},[237,1042,770],{"class":265},[237,1044,276],{"class":766},[237,1046,732],{"class":775},[237,1048,778],{"class":758},[237,1050,834],{"class":261},[237,1052,1053],{"class":265},"{",[237,1055,1056],{"class":272}," storageFactory",[237,1058,276],{"class":265},[237,1060,717],{"class":257},[237,1062,1063],{"class":261},"(mapRows)",[237,1065,333],{"class":265},[237,1067,1068],{"class":272}," name",[237,1070,276],{"class":265},[237,1072,288],{"class":287},[237,1074,546],{"class":291},[237,1076,295],{"class":287},[237,1078,735],{"class":265},[237,1080,1081],{"class":261},"))",[237,1083,372],{"class":265},[196,1085,1086,1087,1090,1091,1094,1095,1098,1099,1102,1103,1106],{},"That is ",[200,1088,1089],{},"@fougere\u002Fadapter-memory"," in full. ",[200,1092,1093],{},"all()"," reading everything is what bounds a\nfile-backed source: ",[200,1096,1097],{},"list"," with a ",[200,1100,1101],{},"where"," or an ",[200,1104,1105],{},"orderBy"," filters in memory, which is right\nfor rows held for their durability and wrong on a hot read path.",[209,1108,1110],{"id":1109},"reading-across-them","Reading across them",[196,1112,1113,1114,1120,1121,1124,1125,1128],{},"By-key reads (",[386,1115,1117],{"href":1116},"\u002Fdocs\u002Fbusiness\u002Fstorage#reading-a-set-not-a-row-at-a-time",[200,1118,1119],{},"findByKeys"," and its\ndual) ",[219,1122,1123],{},"enrich"," a page: \"I hold these rows, give me the related ones\". They cannot\n",[219,1126,1127],{},"select"," one. Filtering, sorting, paginating or counting on the other side collapses into\nreading that side whole — and \"my loans, newest book first\" crosses.",[196,1130,1131,1132,1135],{},"That is what ",[200,1133,1134],{},"@fougere\u002Fadapter-duckdb"," closes. A Frond declares what it reads:",[228,1137,1139],{"className":230,"code":1138,"language":232,"meta":233,"style":233},"\u002F\u002F fronds\u002Flibrary\u002Ffrond.config.ts\nexport default defineFrond({\n  reads: ['Loan', 'Book'],\n});\n",[200,1140,1141,1146,1159,1188],{"__ignoreMap":233},[237,1142,1143],{"class":239,"line":240},[237,1144,1145],{"class":243},"\u002F\u002F fronds\u002Flibrary\u002Ffrond.config.ts\n",[237,1147,1148,1150,1152,1155,1157],{"class":239,"line":247},[237,1149,251],{"class":250},[237,1151,254],{"class":250},[237,1153,1154],{"class":257}," defineFrond",[237,1156,262],{"class":261},[237,1158,266],{"class":265},[237,1160,1161,1164,1166,1168,1170,1173,1175,1177,1179,1181,1183,1186],{"class":239,"line":269},[237,1162,1163],{"class":272},"  reads",[237,1165,276],{"class":265},[237,1167,341],{"class":261},[237,1169,295],{"class":287},[237,1171,1172],{"class":291},"Loan",[237,1174,295],{"class":287},[237,1176,333],{"class":265},[237,1178,288],{"class":287},[237,1180,346],{"class":291},[237,1182,295],{"class":287},[237,1184,1185],{"class":261},"]",[237,1187,821],{"class":265},[237,1189,1190,1192,1194],{"class":239,"line":301},[237,1191,366],{"class":265},[237,1193,369],{"class":261},[237,1195,372],{"class":265},[196,1197,1198,1199,1202,1203,1206,1207,1209],{},"Declaring it is what makes ",[200,1200,1201],{},"Reads"," injectable, and it also ",[219,1204,1205],{},"bounds what gets attached",":\na source holding none of these is never opened, so its tables do not exist in the\nconnection. ",[200,1208,206],{}," may name any entity of the app, not only the Frond's own.",[228,1211,1213],{"className":230,"code":1212,"language":232,"meta":233,"style":233},"export default class LibraryHandler {\n  constructor(private reads: Reads) {}\n\n  async loansByLanguage() {\n    return this.reads.read(LoansByLanguage)`\n      select b.language, count(*) as loans\n      from ${Loan} l join ${Book} b on b.id = l.book_id\n      group by b.language\n      order by b.language`;\n  }\n}\n",[200,1214,1215,1229,1252,1256,1269,1298,1303,1327,1332,1342,1347],{"__ignoreMap":233},[237,1216,1217,1219,1221,1224,1227],{"class":239,"line":240},[237,1218,251],{"class":250},[237,1220,254],{"class":250},[237,1222,1223],{"class":758}," class",[237,1225,1226],{"class":775}," LibraryHandler",[237,1228,309],{"class":265},[237,1230,1231,1234,1236,1239,1242,1244,1247,1249],{"class":239,"line":247},[237,1232,1233],{"class":758},"  constructor",[237,1235,262],{"class":265},[237,1237,1238],{"class":758},"private",[237,1240,1241],{"class":837}," reads",[237,1243,276],{"class":766},[237,1245,1246],{"class":775}," Reads",[237,1248,369],{"class":265},[237,1250,1251],{"class":265}," {}\n",[237,1253,1254],{"class":239,"line":269},[237,1255,753],{"emptyLinePlaceholder":752},[237,1257,1258,1261,1265,1267],{"class":239,"line":301},[237,1259,1260],{"class":758},"  async",[237,1262,1264],{"class":1263},"sVXei"," loansByLanguage",[237,1266,800],{"class":265},[237,1268,309],{"class":265},[237,1270,1271,1274,1278,1280,1283,1285,1288,1290,1293,1295],{"class":239,"line":312},[237,1272,1273],{"class":250},"    return",[237,1275,1277],{"class":1276},"s39Yj"," this",[237,1279,847],{"class":265},[237,1281,1282],{"class":261},"reads",[237,1284,847],{"class":265},[237,1286,1287],{"class":257},"read",[237,1289,262],{"class":272},[237,1291,1292],{"class":261},"LoansByLanguage",[237,1294,369],{"class":272},[237,1296,1297],{"class":287},"`\n",[237,1299,1300],{"class":239,"line":357},[237,1301,1302],{"class":291},"      select b.language, count(*) as loans\n",[237,1304,1305,1308,1311,1313,1315,1318,1320,1322,1324],{"class":239,"line":363},[237,1306,1307],{"class":291},"      from ",[237,1309,1310],{"class":287},"${",[237,1312,1172],{"class":261},[237,1314,366],{"class":287},[237,1316,1317],{"class":291}," l join ",[237,1319,1310],{"class":287},[237,1321,346],{"class":261},[237,1323,366],{"class":287},[237,1325,1326],{"class":291}," b on b.id = l.book_id\n",[237,1328,1329],{"class":239,"line":861},[237,1330,1331],{"class":291},"      group by b.language\n",[237,1333,1334,1337,1340],{"class":239,"line":894},[237,1335,1336],{"class":291},"      order by b.language",[237,1338,1339],{"class":287},"`",[237,1341,372],{"class":265},[237,1343,1344],{"class":239,"line":941},[237,1345,1346],{"class":265},"  }\n",[237,1348,1349],{"class":239,"line":974},[237,1350,1351],{"class":265},"}\n",[196,1353,1354],{},"Two things are held by the types rather than asked for:",[1356,1357,1358,1378],"ul",{},[1359,1360,1361,1364,1365,1369,1370,1373,1374,1377],"li",{},[219,1362,1363],{},"The shape names the answer."," The tag is only reachable ",[1366,1367,1368],"em",{},"through"," a shape, so there is\nno query without a declared output. A raw ",[200,1371,1372],{},"select *"," would return the fields\n",[200,1375,1376],{},"boundary.out: 'closed'"," promises never leave; the projection is the fence. A column the\nshape does not name never comes out, and a shape declaring a field the query does not\nanswer fails by name rather than yielding nulls.",[1359,1379,1380,1383,1384,1387,1388,1391,1392,1394],{},[219,1381,1382],{},"Only an entity may be interpolated."," ",[200,1385,1386],{},"${Book}"," becomes ",[200,1389,1390],{},"\"archive\".\"books\""," — the alias\nand the table from one declaration. Naming an entity outside ",[200,1393,206],{}," is refused, because\nits table genuinely is not in this connection.",[196,1396,1397,1398,1401,1402,1405],{},"Every attach is ",[200,1399,1400],{},"READ_ONLY",", and it is the engine that enforces it. This door reads across\nthe app's whole storage, so it is strictly more reachable than ",[200,1403,1404],{},"storage.client"," — which at\nleast keeps the scope of its entity — and a write through it would meet no judge at all.",[209,1407,1409],{"id":1408},"what-it-is-not","What it is not",[196,1411,1412,1415],{},[219,1413,1414],{},"Not a query builder."," A cross-source builder would be a Calcite in TypeScript, and it\nwould promise a composability the sources have not got. The query stays SQL. What Fougere\ncontributes is the three derivations a hand-written one would duplicate: where each entity\nlives, what its table and columns are called, and the shape of the answer.",[196,1417,1418,1421],{},[219,1419,1420],{},"Not the ordinary read path."," Measured before any of it was written: at page size DuckDB\nis ~100× slower than two indexed reads — roughly 7 ms of floor per query. Use it where a\nquestion genuinely crosses, never to fetch a page.",[196,1423,1424,1425,1428,1429,1432,1433,1439],{},"Nothing is copied. Attaching Postgres pushes the filter down — ",[200,1426,1427],{},"Filters: lang='fr'"," reaches\nit over 100 000 rows — so a real database is queried where it is. For a source with ",[219,1430,1431],{},"no","\nalgebra at all (an HTTP API, a partner's catalog), the answer is\n",[386,1434,1436],{"href":1435},"\u002Fdocs\u002Fconcepts\u002Fthe-base#mirror--a-local-copy-of-rows-you-cannot-query",[200,1437,1438],{},"Mirror"," instead.",[1441,1442,1443],"warning",{},[196,1444,1445,1448],{},[219,1446,1447],{},"There is no parameter binding."," A value goes into the query text you write, so do not\nbuild that text from user input. Filter the result, or keep the varying part out of the\nstring.",[209,1450,1452],{"id":1451},"limits","Limits",[1356,1454,1455,1476,1487],{},[1359,1456,1457,1460,1461,1464,1465,1468,1469,1472,1473,847],{},[219,1458,1459],{},"SQL Server cannot be attached"," — no ",[200,1462,1463],{},"sqlserver"," extension exists for DuckDB. The\nattachable engines are ",[200,1466,1467],{},"sqlite",", ",[200,1470,1471],{},"postgres"," and ",[200,1474,1475],{},"mysql",[1359,1477,1478,1479,1482,1483,1486],{},"A source must say ",[200,1480,1481],{},"path"," or ",[200,1484,1485],{},"attach","; DuckDB is told what to open, and an in-memory\ndatabase of its own would hold none of your rows.",[1359,1488,392,1489,1491],{},[200,1490,206],{}," no boot can serve is reported rather than ignored.",[1493,1494,1495],"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 .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .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 .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--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 .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sVXei, html code.shiki .sVXei{--shiki-light:#E53935;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":233,"searchDepth":247,"depth":247,"links":1497},[1498,1499,1502,1503,1504],{"id":211,"depth":247,"text":212},{"id":403,"depth":247,"text":404,"children":1500},[1501],{"id":681,"depth":269,"text":682},{"id":1109,"depth":247,"text":1110},{"id":1408,"depth":247,"text":1409},{"id":1451,"depth":247,"text":1452},"Where an entity's rows live, and how one query reads across several places without copying any of them.","md",{},{"title":167,"description":1505},"vEDIaU2coS9-D9RNnz_hX63kPUNZWYV7YCWa6EH1Us4",1788560230913]