[{"data":1,"prerenderedAt":1361},["ShallowReactive",2],{"docs-sections":3,"docs-page-\u002Fdocs\u002Fcatalogs":123},[4,9,13,17,22,26,30,34,38,42,46,50,55,59,63,67,72,76,80,85,89,93,97,102,106,110,114,119],{"path":5,"title":6,"description":7,"group":8},"\u002Fdocs","Introduction","What tunables is, the problem it solves, and the principles it holds to.","Get started",{"path":10,"title":11,"description":12,"group":8},"\u002Fdocs\u002Fquickstart","Quickstart","Take one group of settings from constants to a screen with a history, in a Nuxt app on Postgres.",{"path":14,"title":15,"description":16,"group":8},"\u002Fdocs\u002Fpatterns","What it is for","The kinds of value tunables keeps — live limits, plans, per-workspace and per-person values, model choices, values that bound each other, costly switches — and the feature that does each.",{"path":18,"title":19,"description":20,"group":21},"\u002Fdocs\u002Fsettings-and-groups","Settings and groups","Declare each setting once, in a group edited beside what it affects, and read it with types.","Concepts",{"path":23,"title":24,"description":25,"group":21},"\u002Fdocs\u002Fkinds-and-units","Kinds and units","The eight kinds of setting, the built-in units, your own units, and how values are checked and read.",{"path":27,"title":28,"description":29,"group":21},"\u002Fdocs\u002Fchecks-and-guards","Checks and guards","Rules across settings, guards for what your app keeps, within for presets, confirmations for costly changes, and notes.",{"path":31,"title":32,"description":33,"group":21},"\u002Fdocs\u002Fhistory-and-restore","History and restore","Every change is a revision with who, when, why and each value before and after; restoring one runs the same checks as any save.",{"path":35,"title":36,"description":37,"group":21},"\u002Fdocs\u002Fpresets","Presets","Named, reusable configurations with one default per slot, assigned by reference and bounded by the settings.",{"path":39,"title":40,"description":41,"group":21},"\u002Fdocs\u002Fscopes","Scopes","Values per workspace and per person, through layers — the system, a plan, the workspace, the person — each bounded by what it inherits.",{"path":43,"title":44,"description":45,"group":21},"\u002Fdocs\u002Fcatalogs","Catalogs","Lists of allowed choices, such as permitted models, whose entries are retired rather than deleted and cannot be retired while in use.",{"path":47,"title":48,"description":49,"group":21},"\u002Fdocs\u002Fdeployment-values","Deployment values","Values a deployment sets in its environment and nobody tunes while the app runs, declared once and listed in .env.example.",{"path":51,"title":52,"description":53,"group":54},"\u002Fdocs\u002Fstorage","Storage","Postgres through your own client, the four tables and how to create them, joining your transactions, and the storage contract behind it.","Integrate",{"path":56,"title":57,"description":58,"group":54},"\u002Fdocs\u002Fserver","Server and access","Mount tunables' API as a fetch handler in Nitro, Hono, Bun or Deno, as Express middleware or as an oRPC router, with access decided by your app.",{"path":60,"title":61,"description":62,"group":54},"\u002Fdocs\u002Faccess","Who can do what","End users bound by values they never see, staff who tune groups, owners and members who change only their own scope, and the terminal.",{"path":64,"title":65,"description":66,"group":54},"\u002Fdocs\u002Ftesting","Testing","Your app's tunables in memory for its own tests, with values set where a test needs them, and how to test rules, guards and confirmations.",{"path":68,"title":69,"description":70,"group":71},"\u002Fdocs\u002Fnuxt-layer","The Nuxt layer","Ready screens for a Nuxt UI app — a group's form, All settings, history, presets, assignments and catalogs — placed beside what they affect.","Screens",{"path":73,"title":74,"description":75,"group":71},"\u002Fdocs\u002Fvue-composables","Vue composables","Headless composables for building tunables' screens in any Vue app, on any design system.",{"path":77,"title":78,"description":79,"group":71},"\u002Fdocs\u002Ftheming","Theming","The screens bring no colors, fonts or CSS of their own; they take your Nuxt UI theme.",{"path":81,"title":82,"description":83,"group":84},"\u002Fdocs\u002Fcli","The CLI","The tunables command, tunables.config.ts, and which commands need a database.","CLI",{"path":86,"title":87,"description":88,"group":84},"\u002Fdocs\u002Fcli-check","Check and generate","The commands that need no database — checking definitions, writing .env.example, and keeping explanations and defaults in step with a pdd-kit Product Model.",{"path":90,"title":91,"description":92,"group":84},"\u002Fdocs\u002Fcli-values","Read and change values","The commands that read and change stored values, through the same checks and history as the screens.",{"path":94,"title":95,"description":96,"group":84},"\u002Fdocs\u002Fcli-skills","Install skills","Install tunables' agent skills for Claude Code, Codex CLI, Cursor, Gemini CLI and GitHub Copilot, and keep them current.",{"path":98,"title":99,"description":100,"group":101},"\u002Fdocs\u002Fskills","Agent skills","Three skills that let a coding agent adopt tunables, define settings the right way and verify that every value has one home.","Skills",{"path":103,"title":104,"description":105,"group":101},"\u002Fdocs\u002Fskill-adopt","tunables-adopt","Bring tunables into an app that has none yet, from an inventory of every value to a wired, synced app with the old homes removed.",{"path":107,"title":108,"description":109,"group":101},"\u002Fdocs\u002Fskill-define","tunables-define","Add, change, rename or remove one value in an app that uses tunables, with its home, its checks, its reads and what happens to stored values.",{"path":111,"title":112,"description":113,"group":101},"\u002Fdocs\u002Fskill-verify","tunables-verify","Audit an app's tunables against their rules, report every finding with where it is and the rule it breaks, and fix only the findings you choose.",{"path":115,"title":116,"description":117,"group":118},"\u002Fdocs\u002Fapi","API reference","Every export of the tunables package, by the subpath it is imported from.","Reference",{"path":120,"title":121,"description":122,"group":118},"\u002Fdocs\u002Ftasklane-example","Tasklane, the example app","A todo app with workspaces and people as tunables — two groups, plans as a layer, a catalog of models, scopes, a rule, a confirmation, and the playground that runs it.",{"id":124,"title":44,"body":125,"description":45,"extension":1356,"group":21,"meta":1357,"navigation":213,"order":365,"path":43,"seo":1358,"stem":1359,"__hash__":1360},"docs\u002Fdocs\u002Fcatalogs.md",{"type":126,"value":127,"toc":1349},"minimark",[128,132,149,154,648,736,739,743,749,804,811,815,1075,1093,1097,1102,1110,1131,1332,1336,1345],[129,130,44],"h1",{"id":131},"catalogs",[133,134,135,136,140,141,144,145,148],"p",{},"A ",[137,138,139],"strong",{},"catalog"," is a list of allowed choices that settings, presets and your\napp pick from: the models that may draft suggestions, the regions a deployment\nmay serve, the add-ons a sale may offer. Each entry has an id, the same fields as every other entry,\nand a state: ",[137,142,143],{},"Available"," or ",[137,146,147],{},"Retired",". Entries are never deleted. An entry\nsomething still uses cannot be retired.",[150,151,153],"h2",{"id":152},"declaring-a-catalog","Declaring a catalog",[155,156,161],"pre",{"className":157,"code":158,"language":159,"meta":160,"style":160},"language-ts shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","import { defineCatalog, number, text } from 'tunables'\n\nexport const models = defineCatalog({\n  id: 'models',\n  title: 'Models',\n  singular: 'Model',\n  description: 'The models that may draft task suggestions, and what one suggestion costs.',\n  place: { label: 'Admin › Models', href: '\u002Fadmin\u002Fmodels' },\n  idLabel: 'Model id',\n  labelField: 'label',\n  fields: {\n    label: text({ label: 'Label', default: '', optional: false, maxLength: 80 }),\n    centsPerSuggestion: number({ label: 'Cost a suggestion', unit: 'count', default: 1, affects: ['cost'] })\n  },\n  seed: [\n    { id: 'small', values: { label: 'Small and quick', centsPerSuggestion: 1 } },\n    { id: 'large', values: { label: 'Large and careful', centsPerSuggestion: 5 } }\n  ]\n})\n","ts","",[162,163,164,208,215,236,256,273,290,307,346,363,380,391,449,518,524,535,587,636,642],"code",{"__ignoreMap":160},[165,166,169,173,177,181,184,187,189,192,195,198,201,205],"span",{"class":167,"line":168},"line",1,[165,170,172],{"class":171},"sZ4mS","import",[165,174,176],{"class":175},"sp9ci"," {",[165,178,180],{"class":179},"sVz71"," defineCatalog",[165,182,183],{"class":175},",",[165,185,186],{"class":179}," number",[165,188,183],{"class":175},[165,190,191],{"class":179}," text ",[165,193,194],{"class":175},"}",[165,196,197],{"class":171}," from",[165,199,200],{"class":175}," '",[165,202,204],{"class":203},"sjCzm","tunables",[165,206,207],{"class":175},"'\n",[165,209,211],{"class":167,"line":210},2,[165,212,214],{"emptyLinePlaceholder":213},true,"\n",[165,216,218,221,224,227,230,233],{"class":167,"line":217},3,[165,219,220],{"class":171},"export",[165,222,223],{"class":171}," const",[165,225,226],{"class":179}," models ",[165,228,229],{"class":175},"=",[165,231,180],{"class":232},"sOThN",[165,234,235],{"class":175},"({\n",[165,237,239,242,245,247,250,253],{"class":167,"line":238},4,[165,240,241],{"class":179},"  id",[165,243,244],{"class":175},":",[165,246,200],{"class":175},[165,248,249],{"class":203},"models",[165,251,252],{"class":175},"'",[165,254,255],{"class":175},",\n",[165,257,259,262,264,266,269,271],{"class":167,"line":258},5,[165,260,261],{"class":179},"  title",[165,263,244],{"class":175},[165,265,200],{"class":175},[165,267,268],{"class":203},"Models",[165,270,252],{"class":175},[165,272,255],{"class":175},[165,274,276,279,281,283,286,288],{"class":167,"line":275},6,[165,277,278],{"class":179},"  singular",[165,280,244],{"class":175},[165,282,200],{"class":175},[165,284,285],{"class":203},"Model",[165,287,252],{"class":175},[165,289,255],{"class":175},[165,291,293,296,298,300,303,305],{"class":167,"line":292},7,[165,294,295],{"class":179},"  description",[165,297,244],{"class":175},[165,299,200],{"class":175},[165,301,302],{"class":203},"The models that may draft task suggestions, and what one suggestion costs.",[165,304,252],{"class":175},[165,306,255],{"class":175},[165,308,310,313,315,317,320,322,324,327,329,331,334,336,338,341,343],{"class":167,"line":309},8,[165,311,312],{"class":179},"  place",[165,314,244],{"class":175},[165,316,176],{"class":175},[165,318,319],{"class":179}," label",[165,321,244],{"class":175},[165,323,200],{"class":175},[165,325,326],{"class":203},"Admin › Models",[165,328,252],{"class":175},[165,330,183],{"class":175},[165,332,333],{"class":179}," href",[165,335,244],{"class":175},[165,337,200],{"class":175},[165,339,340],{"class":203},"\u002Fadmin\u002Fmodels",[165,342,252],{"class":175},[165,344,345],{"class":175}," },\n",[165,347,349,352,354,356,359,361],{"class":167,"line":348},9,[165,350,351],{"class":179},"  idLabel",[165,353,244],{"class":175},[165,355,200],{"class":175},[165,357,358],{"class":203},"Model id",[165,360,252],{"class":175},[165,362,255],{"class":175},[165,364,366,369,371,373,376,378],{"class":167,"line":365},10,[165,367,368],{"class":179},"  labelField",[165,370,244],{"class":175},[165,372,200],{"class":175},[165,374,375],{"class":203},"label",[165,377,252],{"class":175},[165,379,255],{"class":175},[165,381,383,386,388],{"class":167,"line":382},11,[165,384,385],{"class":179},"  fields",[165,387,244],{"class":175},[165,389,390],{"class":175}," {\n",[165,392,394,397,399,402,405,407,409,411,414,416,418,421,423,426,428,431,433,436,438,441,443,446],{"class":167,"line":393},12,[165,395,396],{"class":179},"    label",[165,398,244],{"class":175},[165,400,401],{"class":232}," text",[165,403,404],{"class":175},"({",[165,406,319],{"class":179},[165,408,244],{"class":175},[165,410,200],{"class":175},[165,412,413],{"class":203},"Label",[165,415,252],{"class":175},[165,417,183],{"class":175},[165,419,420],{"class":179}," default",[165,422,244],{"class":175},[165,424,425],{"class":175}," ''",[165,427,183],{"class":175},[165,429,430],{"class":179}," optional",[165,432,244],{"class":175},[165,434,435],{"class":171}," false",[165,437,183],{"class":175},[165,439,440],{"class":179}," maxLength",[165,442,244],{"class":175},[165,444,445],{"class":203}," 80",[165,447,448],{"class":175}," }),\n",[165,450,452,455,457,459,461,463,465,467,470,472,474,477,479,481,484,486,488,490,492,495,497,500,502,505,507,510,512,515],{"class":167,"line":451},13,[165,453,454],{"class":179},"    centsPerSuggestion",[165,456,244],{"class":175},[165,458,186],{"class":232},[165,460,404],{"class":175},[165,462,319],{"class":179},[165,464,244],{"class":175},[165,466,200],{"class":175},[165,468,469],{"class":203},"Cost a suggestion",[165,471,252],{"class":175},[165,473,183],{"class":175},[165,475,476],{"class":179}," unit",[165,478,244],{"class":175},[165,480,200],{"class":175},[165,482,483],{"class":203},"count",[165,485,252],{"class":175},[165,487,183],{"class":175},[165,489,420],{"class":179},[165,491,244],{"class":175},[165,493,494],{"class":203}," 1",[165,496,183],{"class":175},[165,498,499],{"class":179}," affects",[165,501,244],{"class":175},[165,503,504],{"class":175}," [",[165,506,252],{"class":175},[165,508,509],{"class":203},"cost",[165,511,252],{"class":175},[165,513,514],{"class":175},"]",[165,516,517],{"class":175}," })\n",[165,519,521],{"class":167,"line":520},14,[165,522,523],{"class":175},"  },\n",[165,525,527,530,532],{"class":167,"line":526},15,[165,528,529],{"class":179},"  seed",[165,531,244],{"class":175},[165,533,534],{"class":175}," [\n",[165,536,538,541,544,546,548,551,553,555,558,560,562,564,566,568,571,573,575,578,580,582,585],{"class":167,"line":537},16,[165,539,540],{"class":175},"    {",[165,542,543],{"class":179}," id",[165,545,244],{"class":175},[165,547,200],{"class":175},[165,549,550],{"class":203},"small",[165,552,252],{"class":175},[165,554,183],{"class":175},[165,556,557],{"class":179}," values",[165,559,244],{"class":175},[165,561,176],{"class":175},[165,563,319],{"class":179},[165,565,244],{"class":175},[165,567,200],{"class":175},[165,569,570],{"class":203},"Small and quick",[165,572,252],{"class":175},[165,574,183],{"class":175},[165,576,577],{"class":179}," centsPerSuggestion",[165,579,244],{"class":175},[165,581,494],{"class":203},[165,583,584],{"class":175}," }",[165,586,345],{"class":175},[165,588,590,592,594,596,598,601,603,605,607,609,611,613,615,617,620,622,624,626,628,631,633],{"class":167,"line":589},17,[165,591,540],{"class":175},[165,593,543],{"class":179},[165,595,244],{"class":175},[165,597,200],{"class":175},[165,599,600],{"class":203},"large",[165,602,252],{"class":175},[165,604,183],{"class":175},[165,606,557],{"class":179},[165,608,244],{"class":175},[165,610,176],{"class":175},[165,612,319],{"class":179},[165,614,244],{"class":175},[165,616,200],{"class":175},[165,618,619],{"class":203},"Large and careful",[165,621,252],{"class":175},[165,623,183],{"class":175},[165,625,577],{"class":179},[165,627,244],{"class":175},[165,629,630],{"class":203}," 5",[165,632,584],{"class":175},[165,634,635],{"class":175}," }\n",[165,637,639],{"class":167,"line":638},18,[165,640,641],{"class":175},"  ]\n",[165,643,645],{"class":167,"line":644},19,[165,646,647],{"class":175},"})\n",[649,650,651,664],"table",{},[652,653,654],"thead",{},[655,656,657,661],"tr",{},[658,659,660],"th",{},"Option",[658,662,663],{},"What it is",[665,666,667,682,692,702,712,722],"tbody",{},[655,668,669,679],{},[670,671,672,675,676],"td",{},[162,673,674],{},"title",", ",[162,677,678],{},"singular",[670,680,681],{},"The catalog and one of its entries: \"Models\", \"Model\".",[655,683,684,689],{},[670,685,686],{},[162,687,688],{},"idLabel",[670,690,691],{},"What an entry's id is called on the screens. Defaults to \"Id\".",[655,693,694,699],{},[670,695,696],{},[162,697,698],{},"labelField",[670,700,701],{},"The field that names an entry to a person. Defaults to its id.",[655,703,704,709],{},[670,705,706],{},[162,707,708],{},"fields",[670,710,711],{},"Each entry's fields.",[655,713,714,719],{},[670,715,716],{},[162,717,718],{},"rules",[670,720,721],{},"Checks between one entry's fields.",[655,723,724,729],{},[670,725,726],{},[162,727,728],{},"seed",[670,730,731,732,735],{},"The entries ",[162,733,734],{},"sync"," adds when the catalog is empty.",[133,737,738],{},"A catalog's field defaults are what a new entry's form starts from; unlike a\ngroup's, they need not pass on their own, because an entry is always given its\nvalues.",[150,740,742],{"id":741},"picking-from-a-catalog","Picking from a catalog",[133,744,135,745,748],{},[162,746,747],{},"pick"," field, on a group or a preset, names a catalog. It may hold the id of\nany of that catalog's entries, and the screens offer only the Available ones.",[155,750,752],{"className":157,"code":751,"language":159,"meta":160,"style":160},"suggestionModel: pick({ label: 'Suggestion model', catalog: 'models', default: 'small' })\n",[162,753,754],{"__ignoreMap":160},[165,755,756,759,761,764,766,768,770,772,775,777,779,782,784,786,788,790,792,794,796,798,800,802],{"class":167,"line":168},[165,757,758],{"class":179},"suggestionModel",[165,760,244],{"class":175},[165,762,763],{"class":232}," pick",[165,765,404],{"class":175},[165,767,319],{"class":179},[165,769,244],{"class":175},[165,771,200],{"class":175},[165,773,774],{"class":203},"Suggestion model",[165,776,252],{"class":175},[165,778,183],{"class":175},[165,780,781],{"class":179}," catalog",[165,783,244],{"class":175},[165,785,200],{"class":175},[165,787,249],{"class":203},[165,789,252],{"class":175},[165,791,183],{"class":175},[165,793,420],{"class":179},[165,795,244],{"class":175},[165,797,200],{"class":175},[165,799,550],{"class":203},[165,801,252],{"class":175},[165,803,517],{"class":175},[133,805,806,807],{},"A setting or preset that already picks an entry keeps it when the entry is\nretired, and can still be saved with it. It cannot newly pick a Retired entry:\n",[808,809,810],"em",{},"\"Suggestion model: \"large\" is retired; choose an Available model.\"",[150,812,814],{"id":813},"working-with-entries","Working with entries",[155,816,818],{"className":157,"code":817,"language":159,"meta":160,"style":160},"const models = tunables.catalog('models')\n\nawait models.add({ id: 'medium', values: { label: 'Medium', centsPerSuggestion: 2 } }, { actor })\nawait models.edit('medium', { centsPerSuggestion: 3 }, { actor, reason: 'New price' })\nawait models.retire('medium', { actor })\nawait models.reinstate('medium', { actor })\n\nawait models.available() \u002F\u002F Available entries only\nawait models.usedBy('small') \u002F\u002F ['Workspace › Suggestion model', …]\n",[162,819,820,851,855,921,974,1001,1028,1032,1050],{"__ignoreMap":160},[165,821,822,825,827,829,833,837,839,842,844,846,848],{"class":167,"line":168},[165,823,824],{"class":171},"const",[165,826,226],{"class":179},[165,828,229],{"class":175},[165,830,832],{"class":831},"sq0K3"," tunables",[165,834,836],{"class":835},"sBd0f",".",[165,838,139],{"class":232},[165,840,841],{"class":175},"(",[165,843,252],{"class":175},[165,845,249],{"class":203},[165,847,252],{"class":175},[165,849,850],{"class":175},")\n",[165,852,853],{"class":167,"line":210},[165,854,214],{"emptyLinePlaceholder":213},[165,856,857,860,863,865,868,870,872,874,876,879,881,883,885,887,889,891,893,895,898,900,902,904,906,909,911,914,916,919],{"class":167,"line":217},[165,858,859],{"class":171},"await",[165,861,862],{"class":831}," models",[165,864,836],{"class":835},[165,866,867],{"class":232},"add",[165,869,404],{"class":175},[165,871,543],{"class":179},[165,873,244],{"class":175},[165,875,200],{"class":175},[165,877,878],{"class":203},"medium",[165,880,252],{"class":175},[165,882,183],{"class":175},[165,884,557],{"class":179},[165,886,244],{"class":175},[165,888,176],{"class":175},[165,890,319],{"class":179},[165,892,244],{"class":175},[165,894,200],{"class":175},[165,896,897],{"class":203},"Medium",[165,899,252],{"class":175},[165,901,183],{"class":175},[165,903,577],{"class":179},[165,905,244],{"class":175},[165,907,908],{"class":203}," 2",[165,910,584],{"class":175},[165,912,913],{"class":175}," },",[165,915,176],{"class":175},[165,917,918],{"class":179}," actor ",[165,920,647],{"class":175},[165,922,923,925,927,929,932,934,936,938,940,942,944,946,948,951,953,955,958,960,963,965,967,970,972],{"class":167,"line":238},[165,924,859],{"class":171},[165,926,862],{"class":831},[165,928,836],{"class":835},[165,930,931],{"class":232},"edit",[165,933,841],{"class":175},[165,935,252],{"class":175},[165,937,878],{"class":203},[165,939,252],{"class":175},[165,941,183],{"class":175},[165,943,176],{"class":175},[165,945,577],{"class":179},[165,947,244],{"class":175},[165,949,950],{"class":203}," 3",[165,952,913],{"class":175},[165,954,176],{"class":175},[165,956,957],{"class":179}," actor",[165,959,183],{"class":175},[165,961,962],{"class":179}," reason",[165,964,244],{"class":175},[165,966,200],{"class":175},[165,968,969],{"class":203},"New price",[165,971,252],{"class":175},[165,973,517],{"class":175},[165,975,976,978,980,982,985,987,989,991,993,995,997,999],{"class":167,"line":258},[165,977,859],{"class":171},[165,979,862],{"class":831},[165,981,836],{"class":835},[165,983,984],{"class":232},"retire",[165,986,841],{"class":175},[165,988,252],{"class":175},[165,990,878],{"class":203},[165,992,252],{"class":175},[165,994,183],{"class":175},[165,996,176],{"class":175},[165,998,918],{"class":179},[165,1000,647],{"class":175},[165,1002,1003,1005,1007,1009,1012,1014,1016,1018,1020,1022,1024,1026],{"class":167,"line":275},[165,1004,859],{"class":171},[165,1006,862],{"class":831},[165,1008,836],{"class":835},[165,1010,1011],{"class":232},"reinstate",[165,1013,841],{"class":175},[165,1015,252],{"class":175},[165,1017,878],{"class":203},[165,1019,252],{"class":175},[165,1021,183],{"class":175},[165,1023,176],{"class":175},[165,1025,918],{"class":179},[165,1027,647],{"class":175},[165,1029,1030],{"class":167,"line":292},[165,1031,214],{"emptyLinePlaceholder":213},[165,1033,1034,1036,1038,1040,1043,1046],{"class":167,"line":309},[165,1035,859],{"class":171},[165,1037,862],{"class":831},[165,1039,836],{"class":835},[165,1041,1042],{"class":232},"available",[165,1044,1045],{"class":175},"()",[165,1047,1049],{"class":1048},"sKllz"," \u002F\u002F Available entries only\n",[165,1051,1052,1054,1056,1058,1061,1063,1065,1067,1069,1072],{"class":167,"line":348},[165,1053,859],{"class":171},[165,1055,862],{"class":831},[165,1057,836],{"class":835},[165,1059,1060],{"class":232},"usedBy",[165,1062,841],{"class":175},[165,1064,252],{"class":175},[165,1066,550],{"class":203},[165,1068,252],{"class":175},[165,1070,1071],{"class":175},")",[165,1073,1074],{"class":1048}," \u002F\u002F ['Workspace › Suggestion model', …]\n",[1076,1077,1078,1082,1090],"ul",{},[1079,1080,1081],"li",{},"An id is required and has no spaces. It never changes.",[1079,1083,1084,1086,1087,1089],{},[162,1085,931],{}," is refused when the change would make any preset fail its own\n",[162,1088,718],{},": an entry cannot change under a preset that relies on it.",[1079,1091,1092],{},"Every add, edit, retire and reinstate is recorded as a revision of the entry.",[150,1094,1096],{"id":1095},"what-uses-an-entry","What uses an entry",[133,1098,1099,1101],{},[162,1100,984],{}," is refused while anything uses the entry, and says what:",[155,1103,1108],{"className":1104,"code":1106,"language":1107},[1105],"language-text","Small and quick is in use by Workspace › Suggestion model; move them to another model first.\n","text",[162,1109,1106],{"__ignoreMap":160},[133,1111,1112,1113,1118,1119,1122,1123,1126,1127,1130],{},"What tunables keeps is found on its own: a setting that picks the entry, for\nthe whole system or at any ",[1114,1115,1117],"a",{"href":1116},".\u002Fscopes","scope"," (",[808,1120,1121],{},"\"Workspace › Suggestion\nmodel for Acme\"","), and a preset that picks it (",[808,1124,1125],{},"Plan \"Team\"","). For what your\napp keeps, such as a project's own choice of model, register ",[162,1128,1129],{},"uses",": a\nfunction per catalog that returns the names of whatever uses an entry.",[155,1132,1134],{"className":157,"code":1133,"language":159,"meta":160,"style":160},"import type { UsesProvider } from 'tunables'\n\nconst projectModels: UsesProvider = async ({ entry }) => {\n  const rows = await sql`select name from projects where suggestion_model = ${entry}`\n  return rows.map(row => `Project \"${row.name}\"`)\n}\n\ncreateTunables({ store, groups, presets, catalogs: [models], uses: { models: projectModels } })\n",[162,1135,1136,1158,1162,1194,1225,1271,1276,1280],{"__ignoreMap":160},[165,1137,1138,1140,1143,1145,1148,1150,1152,1154,1156],{"class":167,"line":168},[165,1139,172],{"class":171},[165,1141,1142],{"class":171}," type",[165,1144,176],{"class":175},[165,1146,1147],{"class":179}," UsesProvider ",[165,1149,194],{"class":175},[165,1151,197],{"class":171},[165,1153,200],{"class":175},[165,1155,204],{"class":203},[165,1157,207],{"class":175},[165,1159,1160],{"class":167,"line":210},[165,1161,214],{"emptyLinePlaceholder":213},[165,1163,1164,1166,1169,1171,1174,1177,1180,1183,1186,1189,1192],{"class":167,"line":217},[165,1165,824],{"class":171},[165,1167,1168],{"class":232}," projectModels",[165,1170,244],{"class":175},[165,1172,1173],{"class":232}," UsesProvider",[165,1175,1176],{"class":175}," =",[165,1178,1179],{"class":171}," async",[165,1181,1182],{"class":175}," ({",[165,1184,1185],{"class":179}," entry ",[165,1187,1188],{"class":175},"})",[165,1190,1191],{"class":171}," =>",[165,1193,390],{"class":175},[165,1195,1196,1199,1202,1204,1207,1210,1213,1216,1219,1222],{"class":167,"line":238},[165,1197,1198],{"class":171},"  const",[165,1200,1201],{"class":179}," rows ",[165,1203,229],{"class":175},[165,1205,1206],{"class":171}," await",[165,1208,1209],{"class":232}," sql",[165,1211,1212],{"class":175},"`",[165,1214,1215],{"class":203},"select name from projects where suggestion_model = ",[165,1217,1218],{"class":175},"${",[165,1220,1221],{"class":179},"entry",[165,1223,1224],{"class":175},"}`\n",[165,1226,1227,1230,1233,1235,1238,1240,1243,1246,1249,1252,1254,1257,1259,1262,1264,1267,1269],{"class":167,"line":258},[165,1228,1229],{"class":171},"  return",[165,1231,1232],{"class":831}," rows",[165,1234,836],{"class":835},[165,1236,1237],{"class":232},"map",[165,1239,841],{"class":175},[165,1241,1242],{"class":179},"row ",[165,1244,1245],{"class":171},"=>",[165,1247,1248],{"class":175}," `",[165,1250,1251],{"class":203},"Project \"",[165,1253,1218],{"class":175},[165,1255,1256],{"class":179},"row",[165,1258,836],{"class":175},[165,1260,1261],{"class":179},"name",[165,1263,194],{"class":175},[165,1265,1266],{"class":203},"\"",[165,1268,1212],{"class":175},[165,1270,850],{"class":175},[165,1272,1273],{"class":167,"line":275},[165,1274,1275],{"class":175},"}\n",[165,1277,1278],{"class":167,"line":292},[165,1279,214],{"emptyLinePlaceholder":213},[165,1281,1282,1285,1287,1290,1292,1295,1297,1300,1302,1305,1307,1309,1311,1314,1317,1319,1321,1323,1325,1328,1330],{"class":167,"line":309},[165,1283,1284],{"class":232},"createTunables",[165,1286,404],{"class":175},[165,1288,1289],{"class":179}," store",[165,1291,183],{"class":175},[165,1293,1294],{"class":179}," groups",[165,1296,183],{"class":175},[165,1298,1299],{"class":179}," presets",[165,1301,183],{"class":175},[165,1303,1304],{"class":179}," catalogs",[165,1306,244],{"class":175},[165,1308,504],{"class":175},[165,1310,249],{"class":179},[165,1312,1313],{"class":175},"],",[165,1315,1316],{"class":179}," uses",[165,1318,244],{"class":175},[165,1320,176],{"class":175},[165,1322,862],{"class":179},[165,1324,244],{"class":175},[165,1326,1327],{"class":179}," projectModels ",[165,1329,194],{"class":175},[165,1331,517],{"class":175},[150,1333,1335],{"id":1334},"on-the-screens","On the screens",[133,1337,1338,1341,1342,836],{},[162,1339,1340],{},"TunablesCatalog"," lists a catalog's entries with what uses each, and adds,\nedits, retires and reinstates them. See ",[1114,1343,69],{"href":1344},".\u002Fnuxt-layer",[1346,1347,1348],"style",{},"html pre.shiki code .sZ4mS, html code.shiki .sZ4mS{--shiki-light:#115E59;--shiki-default:#115E59;--shiki-dark:#5EEAD4}html pre.shiki code .sp9ci, html code.shiki .sp9ci{--shiki-light:#4F5D63;--shiki-default:#4F5D63;--shiki-dark:#A3AFB4}html pre.shiki code .sVz71, html code.shiki .sVz71{--shiki-light:#182025;--shiki-default:#182025;--shiki-dark:#E6EBEC}html pre.shiki code .sjCzm, html code.shiki .sjCzm{--shiki-light:#92400E;--shiki-default:#92400E;--shiki-dark:#FCD34D}html pre.shiki code .sOThN, html code.shiki .sOThN{--shiki-light:#2A3439;--shiki-light-font-weight:bold;--shiki-default:#2A3439;--shiki-default-font-weight:bold;--shiki-dark:#F6F8F8;--shiki-dark-font-weight:bold}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 .sq0K3, html code.shiki .sq0K3{--shiki-light:#182025;--shiki-light-font-weight:bold;--shiki-default:#182025;--shiki-default-font-weight:bold;--shiki-dark:#E6EBEC;--shiki-dark-font-weight:bold}html pre.shiki code .sBd0f, html code.shiki .sBd0f{--shiki-light:#4F5D63;--shiki-light-font-weight:bold;--shiki-default:#4F5D63;--shiki-default-font-weight:bold;--shiki-dark:#A3AFB4;--shiki-dark-font-weight:bold}html pre.shiki code .sKllz, html code.shiki .sKllz{--shiki-light:#4F5D63;--shiki-light-font-style:italic;--shiki-default:#4F5D63;--shiki-default-font-style:italic;--shiki-dark:#A3AFB4;--shiki-dark-font-style:italic}",{"title":160,"searchDepth":210,"depth":210,"links":1350},[1351,1352,1353,1354,1355],{"id":152,"depth":210,"text":153},{"id":741,"depth":210,"text":742},{"id":813,"depth":210,"text":814},{"id":1095,"depth":210,"text":1096},{"id":1334,"depth":210,"text":1335},"md",{},{"title":44,"description":45},"docs\u002Fcatalogs","gdr6gJ3a4muHhpdTUyIPmiH0sTOuD7h6oCk2hIm8Wck",1792845117977]