[{"data":1,"prerenderedAt":2103},["ShallowReactive",2],{"docs-sections":3,"docs-page-\u002Fdocs\u002Fchecks-and-guards":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":28,"body":125,"description":29,"extension":2098,"group":21,"meta":2099,"navigation":746,"order":515,"path":27,"seo":2100,"stem":2101,"__hash__":2102},"docs\u002Fdocs\u002Fchecks-and-guards.md",{"type":126,"value":127,"toc":2084},"minimark",[128,132,136,141,157,230,233,237,248,337,340,344,349,674,688,694,698,705,1070,1073,1169,1179,1184,1191,1426,1437,1441,1447,1455,1461,1465,1472,1478,1483,1487,1493,1616,1629,1812,1822,1826,1833,2043,2050,2054,2064,2068,2080],[129,130,28],"h1",{"id":131},"checks-and-guards",[133,134,135],"p",{},"A save is checked whole before anything is written. When any check refuses\nit, nothing is stored, and the outcome lists each problem in words. A check is\na function you write in plain TypeScript; it returns its problems, and\nreturning nothing means it passes.",[137,138,140],"h2",{"id":139},"the-order-of-a-save","The order of a save",[133,142,143,144,148,149,152,153,156],{},"When ",[145,146,147],"code",{},"save",", ",[145,150,151],{},"reset"," or ",[145,154,155],{},"restore"," changes a group, tunables:",[158,159,160,164,167,176,185,198,207,210,222],"ol",{},[161,162,163],"li",{},"takes the tunables lock, so no other change runs at the same time;",[161,165,166],{},"refuses keys the group does not declare;",[161,168,169,170,175],{},"checks each field on its own, from its ",[171,172,174],"a",{"href":173},".\u002Fkinds-and-units","kind",";",[161,177,178,179,175],{},"runs the group's ",[180,181,182],"strong",{},[145,183,184],{},"rules",[161,186,187,188,193,194,175],{},"runs every preset set's ",[180,189,190],{},[145,191,192],{},"within"," against the settings as they would be\nafter the change, and refuses a change that would leave a workspace's or a\nperson's own value outside its ",[171,195,197],{"href":196},".\u002Fscopes#bounds","bound",[161,199,178,200,203,204,175],{},[180,201,202],{},"guards",", with your transaction or request as ",[145,205,206],{},"host",[161,208,209],{},"stops if nothing changed: the save succeeds without a revision;",[161,211,212,213,218,219,175],{},"asks the group's ",[180,214,215],{},[145,216,217],{},"confirm"," question, unless the change is ",[145,220,221],{},"confirmed",[161,223,224,225,229],{},"writes the group and appends a ",[171,226,228],{"href":227},".\u002Fhistory-and-restore","revision",", in one\ntransaction.",[133,231,232],{},"Field problems are all reported together; later steps run only once the\nearlier ones pass, so a rule never sees a value of the wrong type.",[137,234,236],{"id":235},"what-a-check-returns","What a check returns",[133,238,239,240,243,244,247],{},"Every check returns a ",[145,241,242],{},"RuleResult",": an array of problems, or nothing. A problem\nis a sentence, or ",[145,245,246],{},"{ key, message }"," when it is about one field, so the screens\nshow it beside that field.",[249,250,255],"pre",{"className":251,"code":252,"language":253,"meta":254,"style":254},"language-ts shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","type RuleResult = readonly (Problem | string)[] | undefined | void\ninterface Problem { key?: string, message: string }\n","ts","",[145,256,257,302],{"__ignoreMap":254},[258,259,262,266,270,274,277,280,283,286,289,292,294,297,299],"span",{"class":260,"line":261},"line",1,[258,263,265],{"class":264},"sZ4mS","type",[258,267,269],{"class":268},"sOThN"," RuleResult",[258,271,273],{"class":272},"sp9ci"," =",[258,275,276],{"class":264}," readonly",[258,278,279],{"class":272}," (",[258,281,282],{"class":268},"Problem",[258,284,285],{"class":272}," |",[258,287,288],{"class":268}," string",[258,290,291],{"class":272},")[]",[258,293,285],{"class":272},[258,295,296],{"class":268}," undefined",[258,298,285],{"class":272},[258,300,301],{"class":268}," void\n",[258,303,305,308,311,314,318,321,323,326,329,332,334],{"class":260,"line":304},2,[258,306,307],{"class":264},"interface",[258,309,310],{"class":268}," Problem",[258,312,313],{"class":272}," {",[258,315,317],{"class":316},"sVz71"," key",[258,319,320],{"class":272},"?:",[258,322,288],{"class":268},[258,324,325],{"class":272},",",[258,327,328],{"class":316}," message",[258,330,331],{"class":272},":",[258,333,288],{"class":268},[258,335,336],{"class":272}," }\n",[133,338,339],{},"Write problems the way the setting reads to the person changing it: name the\nfields by their labels, say the limit, and say what is in the way.",[137,341,343],{"id":342},"rules-across-settings","Rules across settings",[133,345,346,348],{},[145,347,184],{}," checks the fields of one group against each other. It receives the\nwhole group as it would be after the change, with its types.",[249,350,352],{"className":251,"code":351,"language":253,"meta":254,"style":254},"export const workspace = defineGroup({\n  id: 'workspace',\n  title: 'Workspace',\n  fields: {\n    dueSoonDays: number({ label: 'Due soon', unit: 'days', default: 2, max: 30, override: ['workspace', 'user'] }),\n    archiveAfterDays: number({ label: 'Archive done tasks after', unit: 'days', default: 30, max: 365, override: ['workspace'] })\n  },\n  rules: values => values.dueSoonDays > values.archiveAfterDays\n    ? [{ key: 'dueSoonDays', message: 'Due soon may not be longer than the time before done tasks are archived.' }]\n    : []\n})\n",[145,353,354,374,394,411,422,513,584,590,623,659,668],{"__ignoreMap":254},[258,355,356,359,362,365,368,371],{"class":260,"line":261},[258,357,358],{"class":264},"export",[258,360,361],{"class":264}," const",[258,363,364],{"class":316}," workspace ",[258,366,367],{"class":272},"=",[258,369,370],{"class":268}," defineGroup",[258,372,373],{"class":272},"({\n",[258,375,376,379,381,384,388,391],{"class":260,"line":304},[258,377,378],{"class":316},"  id",[258,380,331],{"class":272},[258,382,383],{"class":272}," '",[258,385,387],{"class":386},"sjCzm","workspace",[258,389,390],{"class":272},"'",[258,392,393],{"class":272},",\n",[258,395,397,400,402,404,407,409],{"class":260,"line":396},3,[258,398,399],{"class":316},"  title",[258,401,331],{"class":272},[258,403,383],{"class":272},[258,405,406],{"class":386},"Workspace",[258,408,390],{"class":272},[258,410,393],{"class":272},[258,412,414,417,419],{"class":260,"line":413},4,[258,415,416],{"class":316},"  fields",[258,418,331],{"class":272},[258,420,421],{"class":272}," {\n",[258,423,425,428,430,433,436,439,441,443,446,448,450,453,455,457,460,462,464,467,469,472,474,477,479,482,484,487,489,492,494,496,498,500,502,505,507,510],{"class":260,"line":424},5,[258,426,427],{"class":316},"    dueSoonDays",[258,429,331],{"class":272},[258,431,432],{"class":268}," number",[258,434,435],{"class":272},"({",[258,437,438],{"class":316}," label",[258,440,331],{"class":272},[258,442,383],{"class":272},[258,444,445],{"class":386},"Due soon",[258,447,390],{"class":272},[258,449,325],{"class":272},[258,451,452],{"class":316}," unit",[258,454,331],{"class":272},[258,456,383],{"class":272},[258,458,459],{"class":386},"days",[258,461,390],{"class":272},[258,463,325],{"class":272},[258,465,466],{"class":316}," default",[258,468,331],{"class":272},[258,470,471],{"class":386}," 2",[258,473,325],{"class":272},[258,475,476],{"class":316}," max",[258,478,331],{"class":272},[258,480,481],{"class":386}," 30",[258,483,325],{"class":272},[258,485,486],{"class":316}," override",[258,488,331],{"class":272},[258,490,491],{"class":272}," [",[258,493,390],{"class":272},[258,495,387],{"class":386},[258,497,390],{"class":272},[258,499,325],{"class":272},[258,501,383],{"class":272},[258,503,504],{"class":386},"user",[258,506,390],{"class":272},[258,508,509],{"class":272},"]",[258,511,512],{"class":272}," }),\n",[258,514,516,519,521,523,525,527,529,531,534,536,538,540,542,544,546,548,550,552,554,556,558,560,562,565,567,569,571,573,575,577,579,581],{"class":260,"line":515},6,[258,517,518],{"class":316},"    archiveAfterDays",[258,520,331],{"class":272},[258,522,432],{"class":268},[258,524,435],{"class":272},[258,526,438],{"class":316},[258,528,331],{"class":272},[258,530,383],{"class":272},[258,532,533],{"class":386},"Archive done tasks after",[258,535,390],{"class":272},[258,537,325],{"class":272},[258,539,452],{"class":316},[258,541,331],{"class":272},[258,543,383],{"class":272},[258,545,459],{"class":386},[258,547,390],{"class":272},[258,549,325],{"class":272},[258,551,466],{"class":316},[258,553,331],{"class":272},[258,555,481],{"class":386},[258,557,325],{"class":272},[258,559,476],{"class":316},[258,561,331],{"class":272},[258,563,564],{"class":386}," 365",[258,566,325],{"class":272},[258,568,486],{"class":316},[258,570,331],{"class":272},[258,572,491],{"class":272},[258,574,390],{"class":272},[258,576,387],{"class":386},[258,578,390],{"class":272},[258,580,509],{"class":272},[258,582,583],{"class":272}," })\n",[258,585,587],{"class":260,"line":586},7,[258,588,589],{"class":272},"  },\n",[258,591,593,596,598,601,604,607,610,613,616,618,620],{"class":260,"line":592},8,[258,594,595],{"class":268},"  rules",[258,597,331],{"class":272},[258,599,600],{"class":316}," values ",[258,602,603],{"class":264},"=>",[258,605,606],{"class":316}," values",[258,608,609],{"class":272},".",[258,611,612],{"class":316},"dueSoonDays ",[258,614,615],{"class":272},">",[258,617,606],{"class":316},[258,619,609],{"class":272},[258,621,622],{"class":316},"archiveAfterDays\n",[258,624,626,629,632,634,636,638,641,643,645,647,649,651,654,656],{"class":260,"line":625},9,[258,627,628],{"class":272},"    ?",[258,630,631],{"class":272}," [{",[258,633,317],{"class":316},[258,635,331],{"class":272},[258,637,383],{"class":272},[258,639,640],{"class":386},"dueSoonDays",[258,642,390],{"class":272},[258,644,325],{"class":272},[258,646,328],{"class":316},[258,648,331],{"class":272},[258,650,383],{"class":272},[258,652,653],{"class":386},"Due soon may not be longer than the time before done tasks are archived.",[258,655,390],{"class":272},[258,657,658],{"class":272}," }]\n",[258,660,662,665],{"class":260,"line":661},10,[258,663,664],{"class":272},"    :",[258,666,667],{"class":272}," []\n",[258,669,671],{"class":260,"line":670},11,[258,672,673],{"class":272},"})\n",[133,675,676,677,148,681,683,684,687],{},"At a ",[171,678,680],{"href":679},".\u002Fscopes","scope",[145,682,184],{}," runs on the values as read there after the\nchange: Dana's ",[685,686,445],"em",{}," is checked against the archive time Acme sets.",[133,689,690,691,693],{},"Because ",[145,692,184],{}," sees only its own group, settings that bound one another\nbelong in one group.",[137,695,697],{"id":696},"guards-for-what-your-app-keeps","Guards for what your app keeps",[133,699,700,701,704],{},"A guard checks the records your app keeps that depend on a group: lowering\n",[685,702,703],{},"Tasks per project"," below what a project already holds would strand that\nproject. Guards are registered per group when you create tunables, so\nthey can reach your database.",[249,706,708],{"className":251,"code":707,"language":253,"meta":254,"style":254},"import { createTunables, type Guard } from 'tunables'\n\nconst projectsWithinLimit: Guard = async ({ next, scope, host }) => {\n  const db = (host as typeof sql | undefined) ?? sql \u002F\u002F the CLI passes no host\n  const limit = Number(next.maxTasksPerProject)\n  const over = scope.workspace\n    ? await db`select name, open_tasks from projects where workspace = ${scope.workspace} and open_tasks > ${limit}`\n    : await db`select name, open_tasks from projects where open_tasks > ${limit}`\n  return over.map(project => `Project \"${project.name}\" has ${project.open_tasks} open tasks, above the ${limit} allowed.`)\n}\n\nexport const tunables = createTunables({\n  store,\n  groups,\n  guards: { limits: [projectsWithinLimit] }\n})\n",[145,709,710,742,748,790,831,857,873,911,930,1000,1005,1009,1025,1033,1041,1065],{"__ignoreMap":254},[258,711,712,715,717,720,722,725,728,731,734,736,739],{"class":260,"line":261},[258,713,714],{"class":264},"import",[258,716,313],{"class":272},[258,718,719],{"class":316}," createTunables",[258,721,325],{"class":272},[258,723,724],{"class":264}," type",[258,726,727],{"class":316}," Guard ",[258,729,730],{"class":272},"}",[258,732,733],{"class":264}," from",[258,735,383],{"class":272},[258,737,738],{"class":386},"tunables",[258,740,741],{"class":272},"'\n",[258,743,744],{"class":260,"line":304},[258,745,747],{"emptyLinePlaceholder":746},true,"\n",[258,749,750,753,756,758,761,763,766,769,772,774,777,779,782,785,788],{"class":260,"line":396},[258,751,752],{"class":264},"const",[258,754,755],{"class":268}," projectsWithinLimit",[258,757,331],{"class":272},[258,759,760],{"class":268}," Guard",[258,762,273],{"class":272},[258,764,765],{"class":264}," async",[258,767,768],{"class":272}," ({",[258,770,771],{"class":316}," next",[258,773,325],{"class":272},[258,775,776],{"class":316}," scope",[258,778,325],{"class":272},[258,780,781],{"class":316}," host ",[258,783,784],{"class":272},"})",[258,786,787],{"class":264}," =>",[258,789,421],{"class":272},[258,791,792,795,798,800,802,805,808,811,814,817,819,822,825,827],{"class":260,"line":413},[258,793,794],{"class":264},"  const",[258,796,797],{"class":316}," db ",[258,799,367],{"class":272},[258,801,279],{"class":272},[258,803,804],{"class":316},"host ",[258,806,807],{"class":264},"as",[258,809,810],{"class":272}," typeof",[258,812,813],{"class":316}," sql ",[258,815,816],{"class":272},"|",[258,818,296],{"class":268},[258,820,821],{"class":272},")",[258,823,824],{"class":272}," ??",[258,826,813],{"class":316},[258,828,830],{"class":829},"sKllz","\u002F\u002F the CLI passes no host\n",[258,832,833,835,838,840,843,846,849,851,854],{"class":260,"line":424},[258,834,794],{"class":264},[258,836,837],{"class":316}," limit ",[258,839,367],{"class":272},[258,841,842],{"class":268}," Number",[258,844,845],{"class":272},"(",[258,847,848],{"class":316},"next",[258,850,609],{"class":272},[258,852,853],{"class":316},"maxTasksPerProject",[258,855,856],{"class":272},")\n",[258,858,859,861,864,866,868,870],{"class":260,"line":515},[258,860,794],{"class":264},[258,862,863],{"class":316}," over ",[258,865,367],{"class":272},[258,867,776],{"class":316},[258,869,609],{"class":272},[258,871,872],{"class":316},"workspace\n",[258,874,875,877,880,883,886,889,892,894,896,898,900,903,905,908],{"class":260,"line":586},[258,876,628],{"class":272},[258,878,879],{"class":264}," await",[258,881,882],{"class":268}," db",[258,884,885],{"class":272},"`",[258,887,888],{"class":386},"select name, open_tasks from projects where workspace = ",[258,890,891],{"class":272},"${",[258,893,680],{"class":316},[258,895,609],{"class":272},[258,897,387],{"class":316},[258,899,730],{"class":272},[258,901,902],{"class":386}," and open_tasks > ",[258,904,891],{"class":272},[258,906,907],{"class":316},"limit",[258,909,910],{"class":272},"}`\n",[258,912,913,915,917,919,921,924,926,928],{"class":260,"line":592},[258,914,664],{"class":272},[258,916,879],{"class":264},[258,918,882],{"class":268},[258,920,885],{"class":272},[258,922,923],{"class":386},"select name, open_tasks from projects where open_tasks > ",[258,925,891],{"class":272},[258,927,907],{"class":316},[258,929,910],{"class":272},[258,931,932,935,939,942,945,947,950,952,955,958,960,963,965,968,970,973,975,977,979,982,984,987,989,991,993,996,998],{"class":260,"line":625},[258,933,934],{"class":264},"  return",[258,936,938],{"class":937},"sq0K3"," over",[258,940,609],{"class":941},"sBd0f",[258,943,944],{"class":268},"map",[258,946,845],{"class":272},[258,948,949],{"class":316},"project ",[258,951,603],{"class":264},[258,953,954],{"class":272}," `",[258,956,957],{"class":386},"Project \"",[258,959,891],{"class":272},[258,961,962],{"class":316},"project",[258,964,609],{"class":272},[258,966,967],{"class":316},"name",[258,969,730],{"class":272},[258,971,972],{"class":386},"\" has ",[258,974,891],{"class":272},[258,976,962],{"class":316},[258,978,609],{"class":272},[258,980,981],{"class":316},"open_tasks",[258,983,730],{"class":272},[258,985,986],{"class":386}," open tasks, above the ",[258,988,891],{"class":272},[258,990,907],{"class":316},[258,992,730],{"class":272},[258,994,995],{"class":386}," allowed.",[258,997,885],{"class":272},[258,999,856],{"class":272},[258,1001,1002],{"class":260,"line":661},[258,1003,1004],{"class":272},"}\n",[258,1006,1007],{"class":260,"line":670},[258,1008,747],{"emptyLinePlaceholder":746},[258,1010,1012,1014,1016,1019,1021,1023],{"class":260,"line":1011},12,[258,1013,358],{"class":264},[258,1015,361],{"class":264},[258,1017,1018],{"class":316}," tunables ",[258,1020,367],{"class":272},[258,1022,719],{"class":268},[258,1024,373],{"class":272},[258,1026,1028,1031],{"class":260,"line":1027},13,[258,1029,1030],{"class":316},"  store",[258,1032,393],{"class":272},[258,1034,1036,1039],{"class":260,"line":1035},14,[258,1037,1038],{"class":316},"  groups",[258,1040,393],{"class":272},[258,1042,1044,1047,1049,1051,1054,1056,1058,1061,1063],{"class":260,"line":1043},15,[258,1045,1046],{"class":316},"  guards",[258,1048,331],{"class":272},[258,1050,313],{"class":272},[258,1052,1053],{"class":316}," limits",[258,1055,331],{"class":272},[258,1057,491],{"class":272},[258,1059,1060],{"class":316},"projectsWithinLimit",[258,1062,509],{"class":272},[258,1064,336],{"class":272},[258,1066,1068],{"class":260,"line":1067},16,[258,1069,673],{"class":272},[133,1071,1072],{},"A guard receives:",[1074,1075,1076,1089],"table",{},[1077,1078,1079],"thead",{},[1080,1081,1082,1086],"tr",{},[1083,1084,1085],"th",{},"Argument",[1083,1087,1088],{},"What it is",[1090,1091,1092,1103,1119,1130,1142,1152],"tbody",{},[1080,1093,1094,1100],{},[1095,1096,1097],"td",{},[145,1098,1099],{},"group",[1095,1101,1102],{},"The group's id.",[1080,1104,1105,1109],{},[1095,1106,1107],{},[145,1108,680],{},[1095,1110,1111,1112,1115,1116,609],{},"Where the change is made: ",[145,1113,1114],{},"{}"," for the whole system, or a path such as ",[145,1117,1118],{},"{ workspace: 'acme' }",[1080,1120,1121,1125],{},[1095,1122,1123],{},[145,1124,848],{},[1095,1126,1127,1128,609],{},"The group's values as they would be after the change, as read at ",[145,1129,680],{},[1080,1131,1132,1137],{},[1095,1133,1134],{},[145,1135,1136],{},"previous",[1095,1138,1139,1140,609],{},"The values now, as read at ",[145,1141,680],{},[1080,1143,1144,1149],{},[1095,1145,1146],{},[145,1147,1148],{},"session",[1095,1150,1151],{},"The store session the save runs in.",[1080,1153,1154,1158],{},[1095,1155,1156],{},[145,1157,206],{},[1095,1159,1160,1161,1163,1164,1166,1167,609],{},"What the save was given as ",[145,1162,206],{},": from the server's ",[145,1165,206],{}," option, or from the code that called ",[145,1168,147],{},[133,1170,1171,1172,1174,1175,1178],{},"Write a guard so it works without ",[145,1173,206],{},", because ",[145,1176,1177],{},"tunables set"," from a\nterminal passes none. Guards run in the order given; the first that returns\nproblems ends the check.",[1180,1181,1183],"h3",{"id":1182},"holding-the-settings-while-you-write","Holding the settings while you write",[133,1185,1186,1187,1190],{},"A guard reads your records at the moment of the save. If your app is adding\na task at that same moment, against the old limit, the guard cannot see it\nyet. ",[145,1188,1189],{},"hold"," closes that gap: inside your own transaction, take tunables' lock\nin shared mode before you read the limit and write the record. A change to\ntunables takes the same lock exclusively, so a lowering waits until your write\ncommits, and its guard then sees the new task.",[249,1192,1194],{"className":251,"code":1193,"language":253,"meta":254,"style":254},"await sql.begin(async (tx) => {\n  const scoped = tunables.within(tx)\n  await scoped.hold()\n  const { maxTasksPerProject } = await scoped.get('limits', { scope: { workspace: project.workspace } })\n  if (project.openTasks >= maxTasksPerProject) {\n    throw new Error(`A project may hold ${maxTasksPerProject} open tasks.`)\n  }\n  await tx`insert into tasks (project_id, title) values (${project.id}, ${input.title})`\n})\n",[145,1195,1196,1225,1247,1262,1320,1344,1375,1380,1422],{"__ignoreMap":254},[258,1197,1198,1201,1204,1206,1209,1211,1214,1216,1219,1221,1223],{"class":260,"line":261},[258,1199,1200],{"class":264},"await",[258,1202,1203],{"class":937}," sql",[258,1205,609],{"class":941},[258,1207,1208],{"class":268},"begin",[258,1210,845],{"class":272},[258,1212,1213],{"class":264},"async",[258,1215,279],{"class":272},[258,1217,1218],{"class":316},"tx",[258,1220,821],{"class":272},[258,1222,787],{"class":264},[258,1224,421],{"class":272},[258,1226,1227,1229,1232,1234,1237,1239,1241,1243,1245],{"class":260,"line":304},[258,1228,794],{"class":264},[258,1230,1231],{"class":316}," scoped ",[258,1233,367],{"class":272},[258,1235,1236],{"class":937}," tunables",[258,1238,609],{"class":941},[258,1240,192],{"class":268},[258,1242,845],{"class":272},[258,1244,1218],{"class":316},[258,1246,856],{"class":272},[258,1248,1249,1252,1255,1257,1259],{"class":260,"line":396},[258,1250,1251],{"class":264},"  await",[258,1253,1254],{"class":937}," scoped",[258,1256,609],{"class":941},[258,1258,1189],{"class":268},[258,1260,1261],{"class":272},"()\n",[258,1263,1264,1266,1268,1271,1273,1275,1277,1279,1281,1284,1286,1288,1291,1293,1295,1297,1299,1301,1303,1306,1308,1311,1313,1316,1318],{"class":260,"line":413},[258,1265,794],{"class":264},[258,1267,313],{"class":272},[258,1269,1270],{"class":316}," maxTasksPerProject ",[258,1272,730],{"class":272},[258,1274,273],{"class":272},[258,1276,879],{"class":264},[258,1278,1254],{"class":937},[258,1280,609],{"class":941},[258,1282,1283],{"class":268},"get",[258,1285,845],{"class":272},[258,1287,390],{"class":272},[258,1289,1290],{"class":386},"limits",[258,1292,390],{"class":272},[258,1294,325],{"class":272},[258,1296,313],{"class":272},[258,1298,776],{"class":316},[258,1300,331],{"class":272},[258,1302,313],{"class":272},[258,1304,1305],{"class":316}," workspace",[258,1307,331],{"class":272},[258,1309,1310],{"class":316}," project",[258,1312,609],{"class":272},[258,1314,1315],{"class":316},"workspace ",[258,1317,730],{"class":272},[258,1319,583],{"class":272},[258,1321,1322,1325,1327,1329,1331,1334,1337,1340,1342],{"class":260,"line":424},[258,1323,1324],{"class":264},"  if",[258,1326,279],{"class":272},[258,1328,962],{"class":316},[258,1330,609],{"class":272},[258,1332,1333],{"class":316},"openTasks ",[258,1335,1336],{"class":272},">=",[258,1338,1339],{"class":316}," maxTasksPerProject",[258,1341,821],{"class":272},[258,1343,421],{"class":272},[258,1345,1346,1349,1352,1355,1357,1359,1362,1364,1366,1368,1371,1373],{"class":260,"line":515},[258,1347,1348],{"class":264},"    throw",[258,1350,1351],{"class":264}," new",[258,1353,1354],{"class":268}," Error",[258,1356,845],{"class":272},[258,1358,885],{"class":272},[258,1360,1361],{"class":386},"A project may hold ",[258,1363,891],{"class":272},[258,1365,853],{"class":316},[258,1367,730],{"class":272},[258,1369,1370],{"class":386}," open tasks.",[258,1372,885],{"class":272},[258,1374,856],{"class":272},[258,1376,1377],{"class":260,"line":586},[258,1378,1379],{"class":272},"  }\n",[258,1381,1382,1384,1387,1389,1392,1394,1396,1398,1401,1403,1405,1407,1410,1412,1415,1417,1419],{"class":260,"line":592},[258,1383,1251],{"class":264},[258,1385,1386],{"class":268}," tx",[258,1388,885],{"class":272},[258,1390,1391],{"class":386},"insert into tasks (project_id, title) values (",[258,1393,891],{"class":272},[258,1395,962],{"class":316},[258,1397,609],{"class":272},[258,1399,1400],{"class":316},"id",[258,1402,730],{"class":272},[258,1404,148],{"class":386},[258,1406,891],{"class":272},[258,1408,1409],{"class":316},"input",[258,1411,609],{"class":272},[258,1413,1414],{"class":316},"title",[258,1416,730],{"class":272},[258,1418,821],{"class":386},[258,1420,1421],{"class":272},"`\n",[258,1423,1424],{"class":260,"line":625},[258,1425,673],{"class":272},[133,1427,1428,1430,1431,1433,1434,609],{},[145,1429,192],{}," and ",[145,1432,1189],{}," are described in ",[171,1435,52],{"href":1436},".\u002Fstorage#transactions",[137,1438,1440],{"id":1439},"presets-stay-within-the-settings","Presets stay within the settings",[133,1442,1443,1444,1446],{},"A preset set's ",[145,1445,192],{}," says how the settings bound each preset. It runs when\na preset is saved, and on every group save, against every stored preset. So\nlowering a limit below a preset is refused, naming the preset:",[249,1448,1453],{"className":1449,"code":1451,"language":1452},[1450],"language-text","Plan \"Team\": its Projects, 500, is above the ceiling of 400.\n","text",[145,1454,1451],{"__ignoreMap":254},[133,1456,1457,1458,609],{},"See ",[171,1459,36],{"href":1460},".\u002Fpresets#within",[137,1462,1464],{"id":1463},"overrides-stay-within-their-bounds","Overrides stay within their bounds",[133,1466,1467,1468,1471],{},"A workspace's or a person's own value is checked against what it inherits,\nand every broader change is checked against the values below it. Raising\nAcme's ",[685,1469,1470],{},"Projects"," above its plan, or editing the plan below what Acme keeps,\nis refused, naming the scope:",[249,1473,1476],{"className":1474,"code":1475,"language":1452},[1450],"Projects may be at most 50 for Acme, the value it inherits.\nPlan \"Pro\": Acme keeps Projects at 20, above 10.\n",[145,1477,1475],{"__ignoreMap":254},[133,1479,1457,1480,609],{},[171,1481,40],{"href":1482},".\u002Fscopes#refusals",[137,1484,1486],{"id":1485},"confirmations-for-costly-changes","Confirmations for costly changes",[133,1488,1489,1490,1492],{},"Some changes are allowed but have consequences: they cost money, redo work,\nor show something to more people.\n",[145,1491,217],{}," compares the change with what is stored and returns the question to\nanswer, or nothing.",[249,1494,1496],{"className":251,"code":1495,"language":253,"meta":254,"style":254},"export const workspace = defineGroup({\n  id: 'workspace',\n  title: 'Workspace',\n  fields: { \u002F* … *\u002F },\n  confirm: (next, previous) => next.guestAccess && !previous.guestAccess\n    ? 'Guests will see every task in the project they are invited to, including its comments and attachments.'\n    : undefined\n})\n",[145,1497,1498,1512,1526,1540,1554,1594,1605,1612],{"__ignoreMap":254},[258,1499,1500,1502,1504,1506,1508,1510],{"class":260,"line":261},[258,1501,358],{"class":264},[258,1503,361],{"class":264},[258,1505,364],{"class":316},[258,1507,367],{"class":272},[258,1509,370],{"class":268},[258,1511,373],{"class":272},[258,1513,1514,1516,1518,1520,1522,1524],{"class":260,"line":304},[258,1515,378],{"class":316},[258,1517,331],{"class":272},[258,1519,383],{"class":272},[258,1521,387],{"class":386},[258,1523,390],{"class":272},[258,1525,393],{"class":272},[258,1527,1528,1530,1532,1534,1536,1538],{"class":260,"line":396},[258,1529,399],{"class":316},[258,1531,331],{"class":272},[258,1533,383],{"class":272},[258,1535,406],{"class":386},[258,1537,390],{"class":272},[258,1539,393],{"class":272},[258,1541,1542,1544,1546,1548,1551],{"class":260,"line":413},[258,1543,416],{"class":316},[258,1545,331],{"class":272},[258,1547,313],{"class":272},[258,1549,1550],{"class":829}," \u002F* … *\u002F",[258,1552,1553],{"class":272}," },\n",[258,1555,1556,1559,1561,1563,1565,1567,1570,1572,1574,1576,1578,1581,1584,1587,1589,1591],{"class":260,"line":424},[258,1557,1558],{"class":268},"  confirm",[258,1560,331],{"class":272},[258,1562,279],{"class":272},[258,1564,848],{"class":316},[258,1566,325],{"class":272},[258,1568,1569],{"class":316}," previous",[258,1571,821],{"class":272},[258,1573,787],{"class":264},[258,1575,771],{"class":316},[258,1577,609],{"class":272},[258,1579,1580],{"class":316},"guestAccess ",[258,1582,1583],{"class":272},"&&",[258,1585,1586],{"class":272}," !",[258,1588,1136],{"class":316},[258,1590,609],{"class":272},[258,1592,1593],{"class":316},"guestAccess\n",[258,1595,1596,1598,1600,1603],{"class":260,"line":515},[258,1597,628],{"class":272},[258,1599,383],{"class":272},[258,1601,1602],{"class":386},"Guests will see every task in the project they are invited to, including its comments and attachments.",[258,1604,741],{"class":272},[258,1606,1607,1609],{"class":260,"line":586},[258,1608,664],{"class":272},[258,1610,1611],{"class":264}," undefined\n",[258,1613,1614],{"class":260,"line":592},[258,1615,673],{"class":272},[133,1617,1618,1619,1430,1622,1624,1625,1628],{},"A change that asks comes back with ",[145,1620,1621],{},"saved: false",[145,1623,217],{}," set, and\nnothing is stored. Send the same change again with ",[145,1626,1627],{},"confirmed: true"," to save it:",[249,1630,1632],{"className":251,"code":1631,"language":253,"meta":254,"style":254},"const acme = { workspace: 'acme' }\nlet outcome = await tunables.save('workspace', { guestAccess: true }, { actor, scope: acme })\nif (outcome.confirm && await askPerson(outcome.confirm)) {\n  outcome = await tunables.save('workspace', { guestAccess: true }, { actor, scope: acme, confirmed: true })\n}\n",[145,1633,1634,1658,1714,1749,1808],{"__ignoreMap":254},[258,1635,1636,1638,1641,1643,1645,1647,1649,1651,1654,1656],{"class":260,"line":261},[258,1637,752],{"class":264},[258,1639,1640],{"class":316}," acme ",[258,1642,367],{"class":272},[258,1644,313],{"class":272},[258,1646,1305],{"class":316},[258,1648,331],{"class":272},[258,1650,383],{"class":272},[258,1652,1653],{"class":386},"acme",[258,1655,390],{"class":272},[258,1657,336],{"class":272},[258,1659,1660,1663,1666,1668,1670,1672,1674,1676,1678,1680,1682,1684,1686,1688,1691,1693,1696,1699,1701,1704,1706,1708,1710,1712],{"class":260,"line":304},[258,1661,1662],{"class":264},"let",[258,1664,1665],{"class":316}," outcome ",[258,1667,367],{"class":272},[258,1669,879],{"class":264},[258,1671,1236],{"class":937},[258,1673,609],{"class":941},[258,1675,147],{"class":268},[258,1677,845],{"class":272},[258,1679,390],{"class":272},[258,1681,387],{"class":386},[258,1683,390],{"class":272},[258,1685,325],{"class":272},[258,1687,313],{"class":272},[258,1689,1690],{"class":316}," guestAccess",[258,1692,331],{"class":272},[258,1694,1695],{"class":264}," true",[258,1697,1698],{"class":272}," },",[258,1700,313],{"class":272},[258,1702,1703],{"class":316}," actor",[258,1705,325],{"class":272},[258,1707,776],{"class":316},[258,1709,331],{"class":272},[258,1711,1640],{"class":316},[258,1713,673],{"class":272},[258,1715,1716,1719,1721,1724,1726,1729,1731,1733,1736,1738,1740,1742,1744,1747],{"class":260,"line":396},[258,1717,1718],{"class":264},"if",[258,1720,279],{"class":272},[258,1722,1723],{"class":316},"outcome",[258,1725,609],{"class":272},[258,1727,1728],{"class":316},"confirm ",[258,1730,1583],{"class":272},[258,1732,879],{"class":264},[258,1734,1735],{"class":268}," askPerson",[258,1737,845],{"class":272},[258,1739,1723],{"class":316},[258,1741,609],{"class":272},[258,1743,217],{"class":316},[258,1745,1746],{"class":272},"))",[258,1748,421],{"class":272},[258,1750,1751,1754,1756,1758,1760,1762,1764,1766,1768,1770,1772,1774,1776,1778,1780,1782,1784,1786,1788,1790,1792,1794,1797,1799,1802,1804,1806],{"class":260,"line":413},[258,1752,1753],{"class":316},"  outcome ",[258,1755,367],{"class":272},[258,1757,879],{"class":264},[258,1759,1236],{"class":937},[258,1761,609],{"class":941},[258,1763,147],{"class":268},[258,1765,845],{"class":272},[258,1767,390],{"class":272},[258,1769,387],{"class":386},[258,1771,390],{"class":272},[258,1773,325],{"class":272},[258,1775,313],{"class":272},[258,1777,1690],{"class":316},[258,1779,331],{"class":272},[258,1781,1695],{"class":264},[258,1783,1698],{"class":272},[258,1785,313],{"class":272},[258,1787,1703],{"class":316},[258,1789,325],{"class":272},[258,1791,776],{"class":316},[258,1793,331],{"class":272},[258,1795,1796],{"class":316}," acme",[258,1798,325],{"class":272},[258,1800,1801],{"class":316}," confirmed",[258,1803,331],{"class":272},[258,1805,1695],{"class":264},[258,1807,583],{"class":272},[258,1809,1810],{"class":260,"line":424},[258,1811,1004],{"class":272},[133,1813,1814,1815,1817,1818,1821],{},"The form shows the question in a dialog; ",[145,1816,1177],{}," asks at the terminal,\nor takes ",[145,1819,1820],{},"--yes",". The question is asked only once every check has passed, so\na person never confirms a change that would be refused anyway.",[137,1823,1825],{"id":1824},"notes","Notes",[133,1827,1828,1829,1832],{},"A note says what a value means here and now, under the field: ",[685,1830,1831],{},"\"312 done\ntasks would be archived tonight.\""," Notes are read-only; they never refuse\nanything. Register a notes provider per group:",[249,1834,1836],{"className":251,"code":1835,"language":253,"meta":254,"style":254},"import type { NotesProvider } from 'tunables'\n\nconst workspaceNotes: NotesProvider = async ({ values, host }) => {\n  const db = (host as typeof sql | undefined) ?? sql\n  const [{ count }] = await db`select count(*)::int as count from tasks\n    where done_at \u003C now() - make_interval(days => ${Number(values.archiveAfterDays)}) and not archived`\n  return { archiveAfterDays: `${count} done tasks would be archived tonight.` }\n}\n\ncreateTunables({ store, groups, notes: { workspace: workspaceNotes } })\n",[145,1837,1838,1859,1863,1893,1922,1945,1974,2000,2004,2008],{"__ignoreMap":254},[258,1839,1840,1842,1844,1846,1849,1851,1853,1855,1857],{"class":260,"line":261},[258,1841,714],{"class":264},[258,1843,724],{"class":264},[258,1845,313],{"class":272},[258,1847,1848],{"class":316}," NotesProvider ",[258,1850,730],{"class":272},[258,1852,733],{"class":264},[258,1854,383],{"class":272},[258,1856,738],{"class":386},[258,1858,741],{"class":272},[258,1860,1861],{"class":260,"line":304},[258,1862,747],{"emptyLinePlaceholder":746},[258,1864,1865,1867,1870,1872,1875,1877,1879,1881,1883,1885,1887,1889,1891],{"class":260,"line":396},[258,1866,752],{"class":264},[258,1868,1869],{"class":268}," workspaceNotes",[258,1871,331],{"class":272},[258,1873,1874],{"class":268}," NotesProvider",[258,1876,273],{"class":272},[258,1878,765],{"class":264},[258,1880,768],{"class":272},[258,1882,606],{"class":316},[258,1884,325],{"class":272},[258,1886,781],{"class":316},[258,1888,784],{"class":272},[258,1890,787],{"class":264},[258,1892,421],{"class":272},[258,1894,1895,1897,1899,1901,1903,1905,1907,1909,1911,1913,1915,1917,1919],{"class":260,"line":413},[258,1896,794],{"class":264},[258,1898,797],{"class":316},[258,1900,367],{"class":272},[258,1902,279],{"class":272},[258,1904,804],{"class":316},[258,1906,807],{"class":264},[258,1908,810],{"class":272},[258,1910,813],{"class":316},[258,1912,816],{"class":272},[258,1914,296],{"class":268},[258,1916,821],{"class":272},[258,1918,824],{"class":272},[258,1920,1921],{"class":316}," sql\n",[258,1923,1924,1926,1928,1931,1934,1936,1938,1940,1942],{"class":260,"line":424},[258,1925,794],{"class":264},[258,1927,631],{"class":272},[258,1929,1930],{"class":316}," count ",[258,1932,1933],{"class":272},"}]",[258,1935,273],{"class":272},[258,1937,879],{"class":264},[258,1939,882],{"class":268},[258,1941,885],{"class":272},[258,1943,1944],{"class":386},"select count(*)::int as count from tasks\n",[258,1946,1947,1950,1952,1955,1957,1960,1962,1965,1967,1969,1972],{"class":260,"line":515},[258,1948,1949],{"class":386},"    where done_at \u003C now() - make_interval(days => ",[258,1951,891],{"class":272},[258,1953,1954],{"class":268},"Number",[258,1956,845],{"class":272},[258,1958,1959],{"class":316},"values",[258,1961,609],{"class":272},[258,1963,1964],{"class":316},"archiveAfterDays",[258,1966,821],{"class":272},[258,1968,730],{"class":272},[258,1970,1971],{"class":386},") and not archived",[258,1973,1421],{"class":272},[258,1975,1976,1978,1980,1983,1985,1988,1991,1993,1996,1998],{"class":260,"line":586},[258,1977,934],{"class":264},[258,1979,313],{"class":272},[258,1981,1982],{"class":316}," archiveAfterDays",[258,1984,331],{"class":272},[258,1986,1987],{"class":272}," `${",[258,1989,1990],{"class":316},"count",[258,1992,730],{"class":272},[258,1994,1995],{"class":386}," done tasks would be archived tonight.",[258,1997,885],{"class":272},[258,1999,336],{"class":272},[258,2001,2002],{"class":260,"line":592},[258,2003,1004],{"class":272},[258,2005,2006],{"class":260,"line":625},[258,2007,747],{"emptyLinePlaceholder":746},[258,2009,2010,2013,2015,2018,2020,2023,2025,2028,2030,2032,2034,2036,2039,2041],{"class":260,"line":661},[258,2011,2012],{"class":268},"createTunables",[258,2014,435],{"class":272},[258,2016,2017],{"class":316}," store",[258,2019,325],{"class":272},[258,2021,2022],{"class":316}," groups",[258,2024,325],{"class":272},[258,2026,2027],{"class":316}," notes",[258,2029,331],{"class":272},[258,2031,313],{"class":272},[258,2033,1305],{"class":316},[258,2035,331],{"class":272},[258,2037,2038],{"class":316}," workspaceNotes ",[258,2040,730],{"class":272},[258,2042,583],{"class":272},[133,2044,2045,2046,609],{},"It returns, by field key, a note or a list of notes. They reach the screens in\nthe group's ",[171,2047,2049],{"href":2048},".\u002Fsettings-and-groups#the-entry","entry",[137,2051,2053],{"id":2052},"catalog-entries-in-use","Catalog entries in use",[133,2055,2056,2057,2060,2061,609],{},"A catalog's entries cannot be retired while something uses them. Settings and\npresets that pick an entry count on their own; register ",[145,2058,2059],{},"uses"," to add what\nyour app keeps.\nSee ",[171,2062,44],{"href":2063},".\u002Fcatalogs#what-uses-an-entry",[137,2065,2067],{"id":2066},"testing-your-checks","Testing your checks",[133,2069,2070,2071,2073,2074,2076,2077,609],{},"Cover each rule, guard and confirmation through ",[145,2072,147],{}," in your tests: refused\nwith its problem, asked, then saved with ",[145,2075,1627],{},". See\n",[171,2078,65],{"href":2079},".\u002Ftesting",[2081,2082,2083],"style",{},"html pre.shiki code .sZ4mS, html code.shiki .sZ4mS{--shiki-light:#115E59;--shiki-default:#115E59;--shiki-dark:#5EEAD4}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 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 .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 .sjCzm, html code.shiki .sjCzm{--shiki-light:#92400E;--shiki-default:#92400E;--shiki-dark:#FCD34D}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}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}",{"title":254,"searchDepth":304,"depth":304,"links":2085},[2086,2087,2088,2089,2092,2093,2094,2095,2096,2097],{"id":139,"depth":304,"text":140},{"id":235,"depth":304,"text":236},{"id":342,"depth":304,"text":343},{"id":696,"depth":304,"text":697,"children":2090},[2091],{"id":1182,"depth":396,"text":1183},{"id":1439,"depth":304,"text":1440},{"id":1463,"depth":304,"text":1464},{"id":1485,"depth":304,"text":1486},{"id":1824,"depth":304,"text":1825},{"id":2052,"depth":304,"text":2053},{"id":2066,"depth":304,"text":2067},"md",{},{"title":28,"description":29},"docs\u002Fchecks-and-guards","99w8hhLvF2MnwASzxafriVS7OiGXDPV_fPL6dVzdegA",1792845117972]