[{"data":1,"prerenderedAt":1532},["ShallowReactive",2],{"docs-nav:en":3,"docs:\u002Fdocs\u002Fschema\u002Fentities":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":56,"body":188,"description":1527,"extension":1528,"meta":1529,"navigation":316,"path":57,"seo":1530,"stem":58,"__hash__":1531},"docs_en\u002Fdocs\u002F3.schema\u002F1.entities.md",{"type":189,"value":190,"toc":1520},"minimark",[191,195,204,581,595,600,603,774,777,859,880,887,994,1001,1004,1043,1046,1133,1140,1152,1157,1167,1182,1185,1314,1317,1324,1328,1331,1429,1451,1465,1469,1476,1504,1510,1516],[192,193,56],"h1",{"id":194},"entities",[196,197,198,199,203],"p",{},"An entity is a class extending the ",[200,201,202],"code",{},"entity()"," factory. It provides the TypeScript type,\nvalidation, and the metadata read by adapters.",[205,206,211],"pre",{"className":207,"code":208,"language":209,"meta":210,"style":210},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","import { entity, primary, text, ref, created, oneOf, date, readOnly, optional } from '@fougere\u002Fschema';\nimport Author from '.\u002FAuthor.js';\n\nexport default class Post extends entity({\n  id: primary(),\n  slug: text({ min: 1, max: 80 }),\n  title: text({ min: 1, max: 160 }),\n  body: optional(text()),\n  authorId: ref(Author),\n  createdAt: created(),\n  status: readOnly(oneOf('draft', 'published', { default: 'draft' })),\n  publishedAt: readOnly(optional(date())),\n}) {}\n","ts","",[200,212,213,291,311,318,347,365,406,441,461,476,490,544,569],{"__ignoreMap":210},[214,215,218,222,226,230,233,236,238,241,243,246,248,251,253,256,258,261,263,266,268,271,274,277,281,285,288],"span",{"class":216,"line":217},"line",1,[214,219,221],{"class":220},"sVHd0","import",[214,223,225],{"class":224},"sP7_E"," {",[214,227,229],{"class":228},"su5hD"," entity",[214,231,232],{"class":224},",",[214,234,235],{"class":228}," primary",[214,237,232],{"class":224},[214,239,240],{"class":228}," text",[214,242,232],{"class":224},[214,244,245],{"class":228}," ref",[214,247,232],{"class":224},[214,249,250],{"class":228}," created",[214,252,232],{"class":224},[214,254,255],{"class":228}," oneOf",[214,257,232],{"class":224},[214,259,260],{"class":228}," date",[214,262,232],{"class":224},[214,264,265],{"class":228}," readOnly",[214,267,232],{"class":224},[214,269,270],{"class":228}," optional",[214,272,273],{"class":224}," }",[214,275,276],{"class":220}," from",[214,278,280],{"class":279},"sjJ54"," '",[214,282,284],{"class":283},"s_sjI","@fougere\u002Fschema",[214,286,287],{"class":279},"'",[214,289,290],{"class":224},";\n",[214,292,294,296,299,302,304,307,309],{"class":216,"line":293},2,[214,295,221],{"class":220},[214,297,298],{"class":228}," Author ",[214,300,301],{"class":220},"from",[214,303,280],{"class":279},[214,305,306],{"class":283},".\u002FAuthor.js",[214,308,287],{"class":279},[214,310,290],{"class":224},[214,312,314],{"class":216,"line":313},3,[214,315,317],{"emptyLinePlaceholder":316},true,"\n",[214,319,321,324,327,331,335,338,341,344],{"class":216,"line":320},4,[214,322,323],{"class":220},"export",[214,325,326],{"class":220}," default",[214,328,330],{"class":329},"sbsja"," class",[214,332,334],{"class":333},"sbgvK"," Post",[214,336,337],{"class":329}," extends",[214,339,229],{"class":340},"sGLFI",[214,342,343],{"class":228},"(",[214,345,346],{"class":224},"{\n",[214,348,350,354,357,359,362],{"class":216,"line":349},5,[214,351,353],{"class":352},"skxfh","  id",[214,355,356],{"class":224},":",[214,358,235],{"class":340},[214,360,361],{"class":228},"()",[214,363,364],{"class":224},",\n",[214,366,368,371,373,375,377,380,383,385,389,391,394,396,399,401,404],{"class":216,"line":367},6,[214,369,370],{"class":352},"  slug",[214,372,356],{"class":224},[214,374,240],{"class":340},[214,376,343],{"class":228},[214,378,379],{"class":224},"{",[214,381,382],{"class":352}," min",[214,384,356],{"class":224},[214,386,388],{"class":387},"srdBf"," 1",[214,390,232],{"class":224},[214,392,393],{"class":352}," max",[214,395,356],{"class":224},[214,397,398],{"class":387}," 80",[214,400,273],{"class":224},[214,402,403],{"class":228},")",[214,405,364],{"class":224},[214,407,409,412,414,416,418,420,422,424,426,428,430,432,435,437,439],{"class":216,"line":408},7,[214,410,411],{"class":352},"  title",[214,413,356],{"class":224},[214,415,240],{"class":340},[214,417,343],{"class":228},[214,419,379],{"class":224},[214,421,382],{"class":352},[214,423,356],{"class":224},[214,425,388],{"class":387},[214,427,232],{"class":224},[214,429,393],{"class":352},[214,431,356],{"class":224},[214,433,434],{"class":387}," 160",[214,436,273],{"class":224},[214,438,403],{"class":228},[214,440,364],{"class":224},[214,442,444,447,449,451,453,456,459],{"class":216,"line":443},8,[214,445,446],{"class":352},"  body",[214,448,356],{"class":224},[214,450,270],{"class":340},[214,452,343],{"class":228},[214,454,455],{"class":340},"text",[214,457,458],{"class":228},"())",[214,460,364],{"class":224},[214,462,464,467,469,471,474],{"class":216,"line":463},9,[214,465,466],{"class":352},"  authorId",[214,468,356],{"class":224},[214,470,245],{"class":340},[214,472,473],{"class":228},"(Author)",[214,475,364],{"class":224},[214,477,479,482,484,486,488],{"class":216,"line":478},10,[214,480,481],{"class":352},"  createdAt",[214,483,356],{"class":224},[214,485,250],{"class":340},[214,487,361],{"class":228},[214,489,364],{"class":224},[214,491,493,496,498,500,502,505,507,509,512,514,516,518,521,523,525,527,529,531,533,535,537,539,542],{"class":216,"line":492},11,[214,494,495],{"class":352},"  status",[214,497,356],{"class":224},[214,499,265],{"class":340},[214,501,343],{"class":228},[214,503,504],{"class":340},"oneOf",[214,506,343],{"class":228},[214,508,287],{"class":279},[214,510,511],{"class":283},"draft",[214,513,287],{"class":279},[214,515,232],{"class":224},[214,517,280],{"class":279},[214,519,520],{"class":283},"published",[214,522,287],{"class":279},[214,524,232],{"class":224},[214,526,225],{"class":224},[214,528,326],{"class":352},[214,530,356],{"class":224},[214,532,280],{"class":279},[214,534,511],{"class":283},[214,536,287],{"class":279},[214,538,273],{"class":224},[214,540,541],{"class":228},"))",[214,543,364],{"class":224},[214,545,547,550,552,554,556,559,561,564,567],{"class":216,"line":546},12,[214,548,549],{"class":352},"  publishedAt",[214,551,356],{"class":224},[214,553,265],{"class":340},[214,555,343],{"class":228},[214,557,558],{"class":340},"optional",[214,560,343],{"class":228},[214,562,563],{"class":340},"date",[214,565,566],{"class":228},"()))",[214,568,364],{"class":224},[214,570,572,575,578],{"class":216,"line":571},13,[214,573,574],{"class":224},"}",[214,576,577],{"class":228},") ",[214,579,580],{"class":224},"{}\n",[196,582,583,584,587,588,591,592,594],{},"The class name is used as the identity: ",[200,585,586],{},"Post.name"," names the SQLite table, the GraphQL\ntype, the registration key (",[200,589,590],{},"post","), and the DI match. ",[200,593,202],{}," takes no separate name.",[596,597,599],"h2",{"id":598},"field-vocabulary","Field vocabulary",[196,601,602],{},"Value fields:",[604,605,606,622],"table",{},[607,608,609],"thead",{},[610,611,612,616,619],"tr",{},[613,614,615],"th",{},"Helper",[613,617,618],{},"Type",[613,620,621],{},"Options",[623,624,625,656,674,691,714,730,745,759],"tbody",{},[610,626,627,633,638],{},[628,629,630],"td",{},[200,631,632],{},"text(opts?)",[628,634,635],{},[200,636,637],{},"string",[628,639,640,643,644,643,647,643,650,643,653],{},[200,641,642],{},"min",", ",[200,645,646],{},"max",[200,648,649],{},"pattern",[200,651,652],{},"format",[200,654,655],{},"default",[610,657,658,663,667],{},[628,659,660],{},[200,661,662],{},"email(opts?)",[628,664,665],{},[200,666,637],{},[628,668,669,671,672],{},[200,670,455],{}," options minus ",[200,673,652],{},[610,675,676,681,685],{},[628,677,678],{},[200,679,680],{},"url(opts?)",[628,682,683],{},[200,684,637],{},[628,686,687,671,689],{},[200,688,455],{},[200,690,652],{},[610,692,693,698,703],{},[628,694,695],{},[200,696,697],{},"number(opts?)",[628,699,700],{},[200,701,702],{},"number",[628,704,705,643,707,643,709,643,712],{},[200,706,642],{},[200,708,646],{},[200,710,711],{},"integer",[200,713,655],{},[610,715,716,721,726],{},[628,717,718],{},[200,719,720],{},"bool(opts?)",[628,722,723],{},[200,724,725],{},"boolean",[628,727,728],{},[200,729,655],{},[610,731,732,737,742],{},[628,733,734],{},[200,735,736],{},"date()",[628,738,739],{},[200,740,741],{},"Date",[628,743,744],{},"—",[610,746,747,752,755],{},[628,748,749],{},[200,750,751],{},"oneOf(...values, opts?)",[628,753,754],{},"union of literals",[628,756,757],{},[200,758,655],{},[610,760,761,766,771],{},[628,762,763],{},[200,764,765],{},"list(item, opts?)",[628,767,768],{},[200,769,770],{},"T[]",[628,772,773],{},"item is any field",[196,775,776],{},"Role fields:",[604,778,779,788],{},[607,780,781],{},[610,782,783,785],{},[613,784,615],{},[613,786,787],{},"Meaning",[623,789,790,803,820,830,845],{},[610,791,792,797],{},[628,793,794],{},[200,795,796],{},"primary()",[628,798,799,800],{},"primary key, generated — also wraps a field: ",[200,801,802],{},"primary(text())",[610,804,805,810],{},[628,806,807],{},[200,808,809],{},"ref(Entity)",[628,811,812,813,815,816,819],{},"foreign key (",[200,814,637],{},"); accepts ",[200,817,818],{},"() => Entity"," for cycles",[610,821,822,827],{},[628,823,824],{},[200,825,826],{},"many(Entity)",[628,828,829],{},"one-to-many — role only, no column",[610,831,832,837],{},[628,833,834],{},[200,835,836],{},"unique(f)",[628,838,839,840,844],{},"no two rows carry the same value — a constraint the ",[841,842,843],"strong",{},"database"," enforces",[610,846,847,852],{},[628,848,849],{},[200,850,851],{},"indexed(f)",[628,853,854,855,858],{},"reads filter on this often — emits ",[200,856,857],{},"CREATE INDEX",", changes no answer",[196,860,861,862,643,864,866,867,869,870,873,874,876,877,879],{},"The ",[200,863,504],{},[200,865,642],{},", and ",[200,868,646],{}," rules are also emitted as ",[200,871,872],{},"CHECK"," constraints. They therefore\napply to writes that bypass the façade, such as direct SQL or another process. ",[200,875,649],{},"\nand ",[200,878,652],{}," remain façade validations because regular-expression dialects differ across\ndatabases.",[196,881,882,883,886],{},"Some facts are about a pair, not a field. \"A book appears once in a list\" is true of\n",[200,884,885],{},"(listId, docId)"," and of neither alone, so it is declared on the entity:",[205,888,890],{"className":207,"code":889,"language":209,"meta":210,"style":210},"class ListBook extends entity({\n  id: primary(),\n  listId: ref(List),\n  docId: text(),\n}, {\n  unique: [['listId', 'docId']],\n}) {}\n",[200,891,892,908,920,934,947,955,986],{"__ignoreMap":210},[214,893,894,897,900,902,904,906],{"class":216,"line":217},[214,895,896],{"class":329},"class",[214,898,899],{"class":333}," ListBook",[214,901,337],{"class":329},[214,903,229],{"class":340},[214,905,343],{"class":228},[214,907,346],{"class":224},[214,909,910,912,914,916,918],{"class":216,"line":293},[214,911,353],{"class":352},[214,913,356],{"class":224},[214,915,235],{"class":340},[214,917,361],{"class":228},[214,919,364],{"class":224},[214,921,922,925,927,929,932],{"class":216,"line":313},[214,923,924],{"class":352},"  listId",[214,926,356],{"class":224},[214,928,245],{"class":340},[214,930,931],{"class":228},"(List)",[214,933,364],{"class":224},[214,935,936,939,941,943,945],{"class":216,"line":320},[214,937,938],{"class":352},"  docId",[214,940,356],{"class":224},[214,942,240],{"class":340},[214,944,361],{"class":228},[214,946,364],{"class":224},[214,948,949,952],{"class":216,"line":349},[214,950,951],{"class":224},"},",[214,953,954],{"class":224}," {\n",[214,956,957,960,962,965,967,970,972,974,976,979,981,984],{"class":216,"line":367},[214,958,959],{"class":352},"  unique",[214,961,356],{"class":224},[214,963,964],{"class":228}," [[",[214,966,287],{"class":279},[214,968,969],{"class":283},"listId",[214,971,287],{"class":279},[214,973,232],{"class":224},[214,975,280],{"class":279},[214,977,978],{"class":283},"docId",[214,980,287],{"class":279},[214,982,983],{"class":228},"]]",[214,985,364],{"class":224},[214,987,988,990,992],{"class":216,"line":408},[214,989,574],{"class":224},[214,991,577],{"class":228},[214,993,580],{"class":224},[196,995,996,997,1000],{},"The database enforces this constraint so that concurrent writes are covered as well. A\nderivation that removes one group member also removes the group: keeping only ",[200,998,999],{},"(listId)","\nwould change the declared rule.",[196,1002,1003],{},"Lifecycle fields:",[604,1005,1006,1014],{},[607,1007,1008],{},[610,1009,1010,1012],{},[613,1011,615],{},[613,1013,787],{},[623,1015,1016,1030],{},[610,1017,1018,1023],{},[628,1019,1020],{},[200,1021,1022],{},"created()",[628,1024,1025,1026,1029],{},"stamped at create (",[200,1027,1028],{},"createdAt",") — never client-written",[610,1031,1032,1037],{},[628,1033,1034],{},[200,1035,1036],{},"updated()",[628,1038,1039,1040,403],{},"re-stamped at every update (",[200,1041,1042],{},"updatedAt",[196,1044,1045],{},"Wrappers (compose around any field):",[604,1047,1048,1061],{},[607,1049,1050],{},[610,1051,1052,1055,1058],{},[613,1053,1054],{},"Wrapper",[613,1056,1057],{},"Axis",[613,1059,1060],{},"Effect",[623,1062,1063,1079,1095,1108,1121],{},[610,1064,1065,1070,1073],{},[628,1066,1067],{},[200,1068,1069],{},"optional(f)",[628,1071,1072],{},"shape",[628,1074,1075,1076],{},"may be absent; ",[200,1077,1078],{},"T | null",[610,1080,1081,1086,1088],{},[628,1082,1083],{},[200,1084,1085],{},"nullable(f)",[628,1087,1072],{},[628,1089,1090,1091,1094],{},"may be ",[200,1092,1093],{},"null",", must be present",[610,1096,1097,1102,1105],{},[628,1098,1099],{},[200,1100,1101],{},"immutable(f)",[628,1103,1104],{},"lifecycle",[628,1106,1107],{},"writable at create, forbidden on update",[610,1109,1110,1115,1118],{},[628,1111,1112],{},[200,1113,1114],{},"readOnly(f)",[628,1116,1117],{},"boundary",[628,1119,1120],{},"never crosses inward — output only",[610,1122,1123,1128,1130],{},[628,1124,1125],{},[200,1126,1127],{},"writeOnly(f)",[628,1129,1117],{},[628,1131,1132],{},"never crosses outward — input only (passwords)",[196,1134,1135,1136,1139],{},"Wrappers nest: ",[200,1137,1138],{},"readOnly(optional(date()))"," is a server-owned, possibly-absent date.",[1141,1142,1143],"blockquote",{},[196,1144,1145,1148,1149,1151],{},[200,1146,1147],{},"Post.validate()"," only sees the current input and therefore cannot check uniqueness\nagainst existing rows. The database enforces that rule and returns a driver error rather\nthan a field error. A ",[200,1150,796],{}," field is already unique.",[1153,1154,1156],"h3",{"id":1155},"two-ways-to-write-it-two-owners","Two ways to write it, two owners",[196,1158,1159,1162,1163,1166],{},[200,1160,1161],{},"unique(slug)"," declares a constraint on one field; ",[200,1164,1165],{},"unique: [['listId','docId']]"," declares\none across several. They are not the same statement, so they do not live in the same\nplace: a constraint on one field is that field's, and a constraint spanning several is the\nschema's — it belongs to none of them alone.",[196,1168,1169,1170,1173,1174,1177,1178,1181],{},"At runtime a field carries ",[200,1171,1172],{},"role.unique",", a boolean, read through ",[200,1175,1176],{},"Role.of(field).isUnique",".\nA group spanning several fields is held once by the schema and answered by ",[200,1179,1180],{},"getUnique()",".",[196,1183,1184],{},"The card flattens both onto the members, because a reader in another language sees one\nfield at a time and must be able to rebuild the constraint from what that field carries:",[205,1186,1190],{"className":1187,"code":1188,"language":1189,"meta":210,"style":210},"language-jsonc shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F describe(ListBook) — the `role.unique` of each member\n\"listId\": { \"role\": { \"unique\": [[\"listId\", \"docId\"]] } },\n\"docId\":  { \"role\": { \"unique\": [[\"listId\", \"docId\"]] } }\n","jsonc",[200,1191,1192,1198,1260],{"__ignoreMap":210},[214,1193,1194],{"class":216,"line":217},[214,1195,1197],{"class":1196},"sutJx","\u002F\u002F describe(ListBook) — the `role.unique` of each member\n",[214,1199,1200,1203,1205,1207,1210,1212,1216,1220,1222,1224,1226,1228,1232,1234,1236,1238,1240,1242,1244,1246,1248,1250,1252,1254,1256,1258],{"class":216,"line":293},[214,1201,1202],{"class":279},"\"",[214,1204,969],{"class":283},[214,1206,1202],{"class":279},[214,1208,1209],{"class":228},": ",[214,1211,379],{"class":224},[214,1213,1215],{"class":1214},"s39Yj"," \"",[214,1217,1219],{"class":1218},"sseR_","role",[214,1221,1202],{"class":1214},[214,1223,356],{"class":224},[214,1225,225],{"class":224},[214,1227,1215],{"class":1214},[214,1229,1231],{"class":1230},"sZMiF","unique",[214,1233,1202],{"class":1214},[214,1235,356],{"class":224},[214,1237,964],{"class":224},[214,1239,1202],{"class":279},[214,1241,969],{"class":283},[214,1243,1202],{"class":279},[214,1245,232],{"class":224},[214,1247,1215],{"class":279},[214,1249,978],{"class":283},[214,1251,1202],{"class":279},[214,1253,983],{"class":224},[214,1255,273],{"class":224},[214,1257,273],{"class":224},[214,1259,364],{"class":228},[214,1261,1262,1264,1266,1268,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1301,1303,1305,1307,1309,1311],{"class":216,"line":313},[214,1263,1202],{"class":279},[214,1265,978],{"class":283},[214,1267,1202],{"class":279},[214,1269,1270],{"class":228},":  ",[214,1272,379],{"class":224},[214,1274,1215],{"class":1214},[214,1276,1219],{"class":1218},[214,1278,1202],{"class":1214},[214,1280,356],{"class":224},[214,1282,225],{"class":224},[214,1284,1215],{"class":1214},[214,1286,1231],{"class":1230},[214,1288,1202],{"class":1214},[214,1290,356],{"class":224},[214,1292,964],{"class":224},[214,1294,1202],{"class":279},[214,1296,969],{"class":283},[214,1298,1202],{"class":279},[214,1300,232],{"class":224},[214,1302,1215],{"class":279},[214,1304,978],{"class":283},[214,1306,1202],{"class":279},[214,1308,983],{"class":224},[214,1310,273],{"class":224},[214,1312,1313],{"class":224}," }\n",[196,1315,1316],{},"A field belonging to two constraints carries two entries. Each member carries the whole\ngroup, which is what allows a compound constraint to be rebuilt from the card alone.",[196,1318,1319,1320,1323],{},"A derivation that removes a member removes the group: ",[200,1321,1322],{},"Post.pick('listId')"," does not retain\nthe compound constraint.",[596,1325,1327],{"id":1326},"the-four-axes","The four axes",[196,1329,1330],{},"Every field carries four orthogonal axes — this is the model all adapters read:",[604,1332,1333,1345],{},[607,1334,1335],{},[610,1336,1337,1339,1342],{},[613,1338,1057],{},[613,1340,1341],{},"Question",[613,1343,1344],{},"Read by",[623,1346,1347,1364,1387,1405],{},[610,1348,1349,1353,1361],{},[628,1350,1351],{},[841,1352,1072],{},[628,1354,1355,1356,1360],{},"what values are valid? (the shape ",[1357,1358,1359],"em",{},"is"," JSON Schema)",[628,1362,1363],{},"validation, forms, SQL column, GraphQL scalar",[610,1365,1366,1370,1384],{},[628,1367,1368],{},[841,1369,1219],{},[628,1371,1372,1373,643,1376,643,1379,643,1382,403],{},"what part does it play? (",[200,1374,1375],{},"primary",[200,1377,1378],{},"ref",[200,1380,1381],{},"many",[200,1383,1231],{},[628,1385,1386],{},"DDL (PK\u002FFK, constraints), relations, the card",[610,1388,1389,1393,1402],{},[628,1390,1391],{},[841,1392,1104],{},[628,1394,1395,1396,643,1399,403],{},"who writes it, when? (",[200,1397,1398],{},"create: 'now' | 'optional' | {value} | {generate}",[200,1400,1401],{},"update: 'now' | 'forbidden'",[628,1403,1404],{},"storage realization, constructor relaxation",[610,1406,1407,1411,1420],{},[628,1408,1409],{},[841,1410,1117],{},[628,1412,1413,1414,643,1417,403],{},"which way does it cross the API? (",[200,1415,1416],{},"readOnly",[200,1418,1419],{},"writeOnly",[628,1421,1422,1425,1426],{},[200,1423,1424],{},"Visibility.input"," \u002F ",[200,1427,1428],{},"Visibility.output",[196,1430,1431,1432,1434,1435,1437,1438,1440,1441,1444,1445,1447,1448,1450],{},"The façade validates client input against the ",[200,1433,1072],{}," and ",[200,1436,1117],{}," axes: writing a\n",[200,1439,1416],{}," field such as ",[200,1442,1443],{},"status"," is rejected. The storage then applies the ",[200,1446,1104],{}," axis,\nfor example by setting ",[200,1449,1028],{}," and default values.",[1141,1452,1453],{},[196,1454,1455,1456,1459,1460,1464],{},"Validation rejects unknown keys with ",[200,1457,1458],{},"Unknown field"," instead of silently dropping them.\nAn accepted input therefore matches the contract exactly (see\n",[1461,1462,81],"a",{"href":1463},"\u002Fdocs\u002Fbusiness\u002Fhandlers#unknown-keys-are-refused",").",[596,1466,1468],{"id":1467},"entityvalidate","Entity.validate",[196,1470,1471,1472,1475],{},"The validation engine is edge-safe (",[200,1473,1474],{},"@cfworker\u002Fjson-schema","). The same function can run\nin the browser and in the handler:",[205,1477,1479],{"className":207,"code":1478,"language":209,"meta":210,"style":210},"Post.validate(input)\n\u002F\u002F → { success: true, data }                                    valid\n\u002F\u002F → { success: false, errors: [{ path: 'title', message }] }   invalid, per-field\n",[200,1480,1481,1494,1499],{"__ignoreMap":210},[214,1482,1483,1486,1488,1491],{"class":216,"line":217},[214,1484,1485],{"class":228},"Post",[214,1487,1181],{"class":224},[214,1489,1490],{"class":340},"validate",[214,1492,1493],{"class":228},"(input)\n",[214,1495,1496],{"class":216,"line":293},[214,1497,1498],{"class":1196},"\u002F\u002F → { success: true, data }                                    valid\n",[214,1500,1501],{"class":216,"line":313},[214,1502,1503],{"class":1196},"\u002F\u002F → { success: false, errors: [{ path: 'title', message }] }   invalid, per-field\n",[196,1505,1506,1509],{},[200,1507,1508],{},"new Post(data)"," builds an instance without validation. Generated and automatic fields are\noptional for the constructor but remain present on the resulting type.",[196,1511,1512,1513,1515],{},"Next: ",[1461,1514,60],{"href":61}," — deriving contracts from the schema.",[1517,1518,1519],"style",{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .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 .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":210,"searchDepth":293,"depth":293,"links":1521},[1522,1525,1526],{"id":598,"depth":293,"text":599,"children":1523},[1524],{"id":1155,"depth":313,"text":1156},{"id":1326,"depth":293,"text":1327},{"id":1467,"depth":293,"text":1468},"The field vocabulary, the four axes, and Entity.validate.","md",{},{"title":56,"description":1527},"5UxnOz3Df7USAOTv44z18ZbxZsfmTelUqGKhJ_iHNfE",1788560230169]