[{"data":1,"prerenderedAt":2403},["ShallowReactive",2],{"docs-sections":3,"docs-page-\u002Fdocs\u002Fstorage":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":52,"body":125,"description":53,"extension":2398,"group":54,"meta":2399,"navigation":306,"order":1314,"path":51,"seo":2400,"stem":2401,"__hash__":2402},"docs\u002Fdocs\u002Fstorage.md",{"type":126,"value":127,"toc":2383},"minimark",[128,132,136,210,215,438,444,481,485,553,567,666,669,673,676,797,806,810,813,955,966,970,1039,1042,1046,1053,1059,1341,1352,1367,1370,1378,1593,1608,1612,1615,1619,1626,1643,2248,2278,2282,2288,2361,2379],[129,130,52],"h1",{"id":131},"storage",[133,134,135],"p",{},"tunables stores its values in your own Postgres, through the client your app\nalready has. It never opens a connection of its own: you hand it a client, and\nit runs its statements through it, inside your transactions when you ask.",[137,138,139,152],"table",{},[140,141,142],"thead",{},[143,144,145,149],"tr",{},[146,147,148],"th",{},"You have",[146,150,151],{},"Use",[153,154,155,172,184,198],"tbody",{},[143,156,157,161],{},[158,159,160],"td",{},"Postgres, through node-postgres or postgres.js",[158,162,163,167,168,171],{},[164,165,166],"code",{},"postgresStore"," from ",[164,169,170],{},"tunables\u002Fpostgres",", below",[143,173,174,177],{},[158,175,176],{},"Drizzle",[158,178,179,180,183],{},"the same store, over the driver beneath Drizzle; ",[164,181,182],{},"tunables\u002Fdrizzle"," only adds the tables to your drizzle-kit migrations",[143,185,186,189],{},[158,187,188],{},"Tests, demos, a first try",[158,190,191],{},[192,193,195],"a",{"href":194},"#the-memory-store",[164,196,197],{},"memoryStore",[143,199,200,203],{},[158,201,202],{},"Another database",[158,204,205,206],{},"a store of your own, written to ",[192,207,209],{"href":208},"#the-storage-contract","the storage contract",[211,212,214],"h2",{"id":213},"postgres","Postgres",[216,217,222],"pre",{"className":218,"code":219,"language":220,"meta":221,"style":221},"language-ts shiki shiki-themes tunables-panel-light tunables-panel-light tunables-panel-dark","import pg from 'pg'\nimport { createTunables } from 'tunables'\nimport { fromPg, postgresStore } from 'tunables\u002Fpostgres'\n\nconst pool = new pg.Pool({ connectionString: process.env.DATABASE_URL })\n\nexport const tunables = createTunables({\n  store: postgresStore(fromPg(pool), { schema: 'tunables' }),\n  groups\n})\n","ts","",[164,223,224,251,275,301,308,360,365,385,427,433],{"__ignoreMap":221},[225,226,229,233,237,240,244,248],"span",{"class":227,"line":228},"line",1,[225,230,232],{"class":231},"sZ4mS","import",[225,234,236],{"class":235},"sVz71"," pg ",[225,238,239],{"class":231},"from",[225,241,243],{"class":242},"sp9ci"," '",[225,245,247],{"class":246},"sjCzm","pg",[225,249,250],{"class":242},"'\n",[225,252,254,256,259,262,265,268,270,273],{"class":227,"line":253},2,[225,255,232],{"class":231},[225,257,258],{"class":242}," {",[225,260,261],{"class":235}," createTunables ",[225,263,264],{"class":242},"}",[225,266,267],{"class":231}," from",[225,269,243],{"class":242},[225,271,272],{"class":246},"tunables",[225,274,250],{"class":242},[225,276,278,280,282,285,288,291,293,295,297,299],{"class":227,"line":277},3,[225,279,232],{"class":231},[225,281,258],{"class":242},[225,283,284],{"class":235}," fromPg",[225,286,287],{"class":242},",",[225,289,290],{"class":235}," postgresStore ",[225,292,264],{"class":242},[225,294,267],{"class":231},[225,296,243],{"class":242},[225,298,170],{"class":246},[225,300,250],{"class":242},[225,302,304],{"class":227,"line":303},4,[225,305,307],{"emptyLinePlaceholder":306},true,"\n",[225,309,311,314,317,320,323,327,331,335,338,341,344,347,349,352,354,357],{"class":227,"line":310},5,[225,312,313],{"class":231},"const",[225,315,316],{"class":235}," pool ",[225,318,319],{"class":242},"=",[225,321,322],{"class":231}," new",[225,324,326],{"class":325},"sq0K3"," pg",[225,328,330],{"class":329},"sBd0f",".",[225,332,334],{"class":333},"sOThN","Pool",[225,336,337],{"class":242},"({",[225,339,340],{"class":235}," connectionString",[225,342,343],{"class":242},":",[225,345,346],{"class":235}," process",[225,348,330],{"class":242},[225,350,351],{"class":235},"env",[225,353,330],{"class":242},[225,355,356],{"class":235},"DATABASE_URL ",[225,358,359],{"class":242},"})\n",[225,361,363],{"class":227,"line":362},6,[225,364,307],{"emptyLinePlaceholder":306},[225,366,368,371,374,377,379,382],{"class":227,"line":367},7,[225,369,370],{"class":231},"export",[225,372,373],{"class":231}," const",[225,375,376],{"class":235}," tunables ",[225,378,319],{"class":242},[225,380,381],{"class":333}," createTunables",[225,383,384],{"class":242},"({\n",[225,386,388,391,393,396,399,402,404,407,410,412,415,417,419,421,424],{"class":227,"line":387},8,[225,389,390],{"class":235},"  store",[225,392,343],{"class":242},[225,394,395],{"class":333}," postgresStore",[225,397,398],{"class":242},"(",[225,400,401],{"class":333},"fromPg",[225,403,398],{"class":242},[225,405,406],{"class":235},"pool",[225,408,409],{"class":242},"),",[225,411,258],{"class":242},[225,413,414],{"class":235}," schema",[225,416,343],{"class":242},[225,418,243],{"class":242},[225,420,272],{"class":246},[225,422,423],{"class":242},"'",[225,425,426],{"class":242}," }),\n",[225,428,430],{"class":227,"line":429},9,[225,431,432],{"class":235},"  groups\n",[225,434,436],{"class":227,"line":435},10,[225,437,359],{"class":242},[133,439,440,443],{},[164,441,442],{},"postgresStore(client, options)"," takes:",[137,445,446,456],{},[140,447,448],{},[143,449,450,453],{},[146,451,452],{},"Option",[146,454,455],{},"What it is",[153,457,458,471],{},[143,459,460,465],{},[158,461,462],{},[164,463,464],{},"schema",[158,466,467,468,470],{},"The schema its tables live in. Defaults to ",[164,469,272],{},". A plain lowercase name.",[143,472,473,478],{},[158,474,475],{},[164,476,477],{},"lockKey",[158,479,480],{},"The advisory lock key. Defaults to one derived from the schema name, so two apps in one database never share a lock.",[211,482,484],{"id":483},"drivers","Drivers",[137,486,487,497],{},[140,488,489],{},[143,490,491,494],{},[146,492,493],{},"Your client",[146,495,496],{},"Pass",[153,498,499,516,530,538],{},[143,500,501,511],{},[158,502,503,504,506,507,510],{},"node-postgres: a ",[164,505,334],{},", a ",[164,508,509],{},"Client"," or a pool client",[158,512,513],{},[164,514,515],{},"fromPg(pool)",[143,517,518,525],{},[158,519,520,521,524],{},"postgres.js: an ",[164,522,523],{},"sql"," instance",[158,526,527],{},[164,528,529],{},"fromPostgresJs(sql)",[143,531,532,535],{},[158,533,534],{},"Drizzle over either",[158,536,537],{},"the driver Drizzle was built on, through the same function",[143,539,540,543],{},[158,541,542],{},"any other Postgres client",[158,544,545,546,549,550],{},"an object with ",[164,547,548],{},"query(text, params)"," that resolves rows, and ",[164,551,552],{},"transaction(work)",[133,554,555,558,559,562,563,566],{},[164,556,557],{},"asExecutor(host)"," turns any of these, or an object with ",[164,560,561],{},"query",", into what\n",[164,564,565],{},"within"," runs statements through; you rarely call it yourself.",[216,568,570],{"className":218,"code":569,"language":220,"meta":221,"style":221},"import postgres from 'postgres'\nimport { fromPostgresJs, postgresStore } from 'tunables\u002Fpostgres'\n\nconst sql = postgres(process.env.DATABASE_URL!)\nconst store = postgresStore(fromPostgresJs(sql))\n",[164,571,572,587,610,614,643],{"__ignoreMap":221},[225,573,574,576,579,581,583,585],{"class":227,"line":228},[225,575,232],{"class":231},[225,577,578],{"class":235}," postgres ",[225,580,239],{"class":231},[225,582,243],{"class":242},[225,584,213],{"class":246},[225,586,250],{"class":242},[225,588,589,591,593,596,598,600,602,604,606,608],{"class":227,"line":253},[225,590,232],{"class":231},[225,592,258],{"class":242},[225,594,595],{"class":235}," fromPostgresJs",[225,597,287],{"class":242},[225,599,290],{"class":235},[225,601,264],{"class":242},[225,603,267],{"class":231},[225,605,243],{"class":242},[225,607,170],{"class":246},[225,609,250],{"class":242},[225,611,612],{"class":227,"line":277},[225,613,307],{"emptyLinePlaceholder":306},[225,615,616,618,621,623,626,628,631,633,635,637,640],{"class":227,"line":303},[225,617,313],{"class":231},[225,619,620],{"class":235}," sql ",[225,622,319],{"class":242},[225,624,625],{"class":333}," postgres",[225,627,398],{"class":242},[225,629,630],{"class":235},"process",[225,632,330],{"class":242},[225,634,351],{"class":235},[225,636,330],{"class":242},[225,638,639],{"class":235},"DATABASE_URL",[225,641,642],{"class":242},"!)\n",[225,644,645,647,650,652,654,656,659,661,663],{"class":227,"line":310},[225,646,313],{"class":231},[225,648,649],{"class":235}," store ",[225,651,319],{"class":242},[225,653,395],{"class":333},[225,655,398],{"class":242},[225,657,658],{"class":333},"fromPostgresJs",[225,660,398],{"class":242},[225,662,523],{"class":235},[225,664,665],{"class":242},"))\n",[133,667,668],{},"Both drivers open their first connection on their first query, so creating\ntunables at start never touches the database.",[211,670,672],{"id":671},"creating-the-tables","Creating the tables",[133,674,675],{},"tunables keeps four tables in its schema. Create them one of three ways, in\nyour migrate or deploy step, never at boot:",[677,678,679,782,792],"ul",{},[680,681,682,688,689,692,693,696,697,762,765,766,769,770,773,774,777,778,330],"li",{},[683,684,685],"strong",{},[164,686,687],{},"migrate"," creates the schema and tables if they are missing, and brings\ntables an earlier version created up to date, such as adding the ",[164,690,691],{},"scope","\ncolumn to ",[164,694,695],{},"tunables_groups",". It is safe to run on every deploy.",[216,698,700],{"className":218,"code":699,"language":220,"meta":221,"style":221},"import { fromPg, migrate } from 'tunables\u002Fpostgres'\n\nawait migrate(fromPg(pool), { schema: 'tunables' })\n",[164,701,702,725,729],{"__ignoreMap":221},[225,703,704,706,708,710,712,715,717,719,721,723],{"class":227,"line":228},[225,705,232],{"class":231},[225,707,258],{"class":242},[225,709,284],{"class":235},[225,711,287],{"class":242},[225,713,714],{"class":235}," migrate ",[225,716,264],{"class":242},[225,718,267],{"class":231},[225,720,243],{"class":242},[225,722,170],{"class":246},[225,724,250],{"class":242},[225,726,727],{"class":227,"line":253},[225,728,307],{"emptyLinePlaceholder":306},[225,730,731,734,737,739,741,743,745,747,749,751,753,755,757,759],{"class":227,"line":277},[225,732,733],{"class":231},"await",[225,735,736],{"class":333}," migrate",[225,738,398],{"class":242},[225,740,401],{"class":333},[225,742,398],{"class":242},[225,744,406],{"class":235},[225,746,409],{"class":242},[225,748,258],{"class":242},[225,750,414],{"class":235},[225,752,343],{"class":242},[225,754,243],{"class":242},[225,756,272],{"class":246},[225,758,423],{"class":242},[225,760,761],{"class":242}," })\n",[763,764],"br",{},"Calling it from ",[164,767,768],{},"connect"," in ",[164,771,772],{},"tunables.config.ts"," lets ",[164,775,776],{},"tunables sync","\ncreate and seed in one command; see the ",[192,779,781],{"href":780},".\u002Fquickstart","quickstart",[680,783,784,787,788,791],{},[683,785,786],{},"Your own SQL migrations."," ",[164,789,790],{},"tunablesSql({ schema })"," returns the DDL as a\nstring, to paste into a migration file.",[680,793,794,796],{},[683,795,176],{},", below.",[133,798,799,800,802,803,805],{},"Use the same ",[164,801,464],{}," everywhere: in the store, in ",[164,804,687],{}," and in your\nmigrations.",[807,808,176],"h3",{"id":809},"drizzle",[133,811,812],{},"Drizzle is optional: tunables never queries through it. An app that generates\nits migrations with drizzle-kit can include tunables' tables in its own schema\nfile, so its migrations create them:",[216,814,817],{"className":218,"code":815,"filename":816,"language":220,"meta":221,"style":221},"import { tunablesTables } from 'tunables\u002Fdrizzle'\n\nconst tunables = tunablesTables('tunables')\nexport const tunablesSchema = tunables.schema\nexport const tunablesGroups = tunables.groups\nexport const tunablesRevisions = tunables.revisions\nexport const tunablesRecords = tunables.records\nexport const tunablesAssignments = tunables.assignments\n","db\u002Fschema.ts",[164,818,819,838,842,864,883,901,919,937],{"__ignoreMap":221},[225,820,821,823,825,828,830,832,834,836],{"class":227,"line":228},[225,822,232],{"class":231},[225,824,258],{"class":242},[225,826,827],{"class":235}," tunablesTables ",[225,829,264],{"class":242},[225,831,267],{"class":231},[225,833,243],{"class":242},[225,835,182],{"class":246},[225,837,250],{"class":242},[225,839,840],{"class":227,"line":253},[225,841,307],{"emptyLinePlaceholder":306},[225,843,844,846,848,850,853,855,857,859,861],{"class":227,"line":277},[225,845,313],{"class":231},[225,847,376],{"class":235},[225,849,319],{"class":242},[225,851,852],{"class":333}," tunablesTables",[225,854,398],{"class":242},[225,856,423],{"class":242},[225,858,272],{"class":246},[225,860,423],{"class":242},[225,862,863],{"class":242},")\n",[225,865,866,868,870,873,875,878,880],{"class":227,"line":303},[225,867,370],{"class":231},[225,869,373],{"class":231},[225,871,872],{"class":235}," tunablesSchema ",[225,874,319],{"class":242},[225,876,877],{"class":235}," tunables",[225,879,330],{"class":242},[225,881,882],{"class":235},"schema\n",[225,884,885,887,889,892,894,896,898],{"class":227,"line":310},[225,886,370],{"class":231},[225,888,373],{"class":231},[225,890,891],{"class":235}," tunablesGroups ",[225,893,319],{"class":242},[225,895,877],{"class":235},[225,897,330],{"class":242},[225,899,900],{"class":235},"groups\n",[225,902,903,905,907,910,912,914,916],{"class":227,"line":362},[225,904,370],{"class":231},[225,906,373],{"class":231},[225,908,909],{"class":235}," tunablesRevisions ",[225,911,319],{"class":242},[225,913,877],{"class":235},[225,915,330],{"class":242},[225,917,918],{"class":235},"revisions\n",[225,920,921,923,925,928,930,932,934],{"class":227,"line":367},[225,922,370],{"class":231},[225,924,373],{"class":231},[225,926,927],{"class":235}," tunablesRecords ",[225,929,319],{"class":242},[225,931,877],{"class":235},[225,933,330],{"class":242},[225,935,936],{"class":235},"records\n",[225,938,939,941,943,946,948,950,952],{"class":227,"line":387},[225,940,370],{"class":231},[225,942,373],{"class":231},[225,944,945],{"class":235}," tunablesAssignments ",[225,947,319],{"class":242},[225,949,877],{"class":235},[225,951,330],{"class":242},[225,953,954],{"class":235},"assignments\n",[133,956,957,958,961,962,965],{},"The Drizzle tables match ",[164,959,960],{},"tunablesSql"," column for column. They are for\nmigrations only: read settings with ",[164,963,964],{},"tunables.get",", never with a query.",[807,967,969],{"id":968},"the-tables","The tables",[137,971,972,982],{},[140,973,974],{},[143,975,976,979],{},[146,977,978],{},"Table",[146,980,981],{},"Holds",[153,983,984,1009,1019,1029],{},[143,985,986,990],{},[158,987,988],{},[164,989,695],{},[158,991,992,993,996,997,1000,1001,1004,1005,1008],{},"A group's values as JSON, and who changed them last and when, keyed by ",[164,994,995],{},"(id, scope)",". The system row has scope ",[164,998,999],{},"''"," and holds every field; a ",[192,1002,691],{"href":1003},".\u002Fscopes","'s row, such as ",[164,1006,1007],{},"workspace=acme",", holds only the fields it overrides.",[143,1010,1011,1016],{},[158,1012,1013],{},[164,1014,1015],{},"tunables_revisions",[158,1017,1018],{},"Every revision, appended and never updated, indexed by target.",[143,1020,1021,1026],{},[158,1022,1023],{},[164,1024,1025],{},"tunables_records",[158,1027,1028],{},"Presets and catalog entries, as JSON by collection and id.",[143,1030,1031,1036],{},[158,1032,1033],{},[164,1034,1035],{},"tunables_assignments",[158,1037,1038],{},"Which preset each subject is assigned, by preset set and slot.",[133,1040,1041],{},"Only tunables writes these tables. A value moves in through a save, so the\nmove is recorded; never insert or update rows by hand.",[211,1043,1045],{"id":1044},"transactions","Transactions",[133,1047,1048,1049,1052],{},"Every change tunables makes runs in one transaction that also holds the\ntunables lock (",[164,1050,1051],{},"pg_advisory_xact_lock"," on the store's key), so changes never\ninterleave and a refused change writes nothing.",[133,1054,1055,1058],{},[164,1056,1057],{},"within(host)"," gives the same API inside a transaction your app already has\nopen. Its reads see your uncommitted writes, and its changes commit or roll\nback with yours.",[216,1060,1062],{"className":218,"code":1061,"language":220,"meta":221,"style":221},"const client = await pool.connect()\ntry {\n  await client.query('begin')\n  const scoped = tunables.within(client)\n  await scoped.save('limits', { maxProjects: 5 }, { actor, reason: 'More room on the free tier' })\n  await client.query('insert into audit_log (message) values ($1)', ['Raised the project limit'])\n  await client.query('commit')\n  scoped.committed()\n} catch (error) {\n  await client.query('rollback')\n  throw error\n} finally {\n  client.release()\n}\n",[164,1063,1064,1086,1094,1117,1140,1197,1231,1252,1264,1282,1303,1312,1322,1335],{"__ignoreMap":221},[225,1065,1066,1068,1071,1073,1076,1079,1081,1083],{"class":227,"line":228},[225,1067,313],{"class":231},[225,1069,1070],{"class":235}," client ",[225,1072,319],{"class":242},[225,1074,1075],{"class":231}," await",[225,1077,1078],{"class":325}," pool",[225,1080,330],{"class":329},[225,1082,768],{"class":333},[225,1084,1085],{"class":242},"()\n",[225,1087,1088,1091],{"class":227,"line":253},[225,1089,1090],{"class":231},"try",[225,1092,1093],{"class":242}," {\n",[225,1095,1096,1099,1102,1104,1106,1108,1110,1113,1115],{"class":227,"line":277},[225,1097,1098],{"class":231},"  await",[225,1100,1101],{"class":325}," client",[225,1103,330],{"class":329},[225,1105,561],{"class":333},[225,1107,398],{"class":242},[225,1109,423],{"class":242},[225,1111,1112],{"class":246},"begin",[225,1114,423],{"class":242},[225,1116,863],{"class":242},[225,1118,1119,1122,1125,1127,1129,1131,1133,1135,1138],{"class":227,"line":303},[225,1120,1121],{"class":231},"  const",[225,1123,1124],{"class":235}," scoped ",[225,1126,319],{"class":242},[225,1128,877],{"class":325},[225,1130,330],{"class":329},[225,1132,565],{"class":333},[225,1134,398],{"class":242},[225,1136,1137],{"class":235},"client",[225,1139,863],{"class":242},[225,1141,1142,1144,1147,1149,1152,1154,1156,1159,1161,1163,1165,1168,1170,1173,1176,1178,1181,1183,1186,1188,1190,1193,1195],{"class":227,"line":310},[225,1143,1098],{"class":231},[225,1145,1146],{"class":325}," scoped",[225,1148,330],{"class":329},[225,1150,1151],{"class":333},"save",[225,1153,398],{"class":242},[225,1155,423],{"class":242},[225,1157,1158],{"class":246},"limits",[225,1160,423],{"class":242},[225,1162,287],{"class":242},[225,1164,258],{"class":242},[225,1166,1167],{"class":235}," maxProjects",[225,1169,343],{"class":242},[225,1171,1172],{"class":246}," 5",[225,1174,1175],{"class":242}," },",[225,1177,258],{"class":242},[225,1179,1180],{"class":235}," actor",[225,1182,287],{"class":242},[225,1184,1185],{"class":235}," reason",[225,1187,343],{"class":242},[225,1189,243],{"class":242},[225,1191,1192],{"class":246},"More room on the free tier",[225,1194,423],{"class":242},[225,1196,761],{"class":242},[225,1198,1199,1201,1203,1205,1207,1209,1211,1214,1216,1218,1221,1223,1226,1228],{"class":227,"line":362},[225,1200,1098],{"class":231},[225,1202,1101],{"class":325},[225,1204,330],{"class":329},[225,1206,561],{"class":333},[225,1208,398],{"class":242},[225,1210,423],{"class":242},[225,1212,1213],{"class":246},"insert into audit_log (message) values ($1)",[225,1215,423],{"class":242},[225,1217,287],{"class":242},[225,1219,1220],{"class":242}," [",[225,1222,423],{"class":242},[225,1224,1225],{"class":246},"Raised the project limit",[225,1227,423],{"class":242},[225,1229,1230],{"class":242},"])\n",[225,1232,1233,1235,1237,1239,1241,1243,1245,1248,1250],{"class":227,"line":367},[225,1234,1098],{"class":231},[225,1236,1101],{"class":325},[225,1238,330],{"class":329},[225,1240,561],{"class":333},[225,1242,398],{"class":242},[225,1244,423],{"class":242},[225,1246,1247],{"class":246},"commit",[225,1249,423],{"class":242},[225,1251,863],{"class":242},[225,1253,1254,1257,1259,1262],{"class":227,"line":387},[225,1255,1256],{"class":325},"  scoped",[225,1258,330],{"class":329},[225,1260,1261],{"class":333},"committed",[225,1263,1085],{"class":242},[225,1265,1266,1268,1271,1274,1277,1280],{"class":227,"line":429},[225,1267,264],{"class":242},[225,1269,1270],{"class":231}," catch",[225,1272,1273],{"class":242}," (",[225,1275,1276],{"class":235},"error",[225,1278,1279],{"class":242},")",[225,1281,1093],{"class":242},[225,1283,1284,1286,1288,1290,1292,1294,1296,1299,1301],{"class":227,"line":435},[225,1285,1098],{"class":231},[225,1287,1101],{"class":325},[225,1289,330],{"class":329},[225,1291,561],{"class":333},[225,1293,398],{"class":242},[225,1295,423],{"class":242},[225,1297,1298],{"class":246},"rollback",[225,1300,423],{"class":242},[225,1302,863],{"class":242},[225,1304,1306,1309],{"class":227,"line":1305},11,[225,1307,1308],{"class":231},"  throw",[225,1310,1311],{"class":235}," error\n",[225,1313,1315,1317,1320],{"class":227,"line":1314},12,[225,1316,264],{"class":242},[225,1318,1319],{"class":231}," finally",[225,1321,1093],{"class":242},[225,1323,1325,1328,1330,1333],{"class":227,"line":1324},13,[225,1326,1327],{"class":325},"  client",[225,1329,330],{"class":329},[225,1331,1332],{"class":333},"release",[225,1334,1085],{"class":242},[225,1336,1338],{"class":227,"line":1337},14,[225,1339,1340],{"class":242},"}\n",[133,1342,1343,1344,1347,1348,1351],{},"Listeners registered with ",[164,1345,1346],{},"onChange",", and the last good values a worker reads,\nhear a change made inside your transaction only when you call ",[164,1349,1350],{},"committed()","\nafter your commit. tunables cannot see your commit; after a rollback, don't call it.",[133,1353,1354,1356,1357,1359,1360,1363,1364,1366],{},[164,1355,565],{}," accepts a node-postgres client inside ",[164,1358,1112],{},", a postgres.js\ntransaction from ",[164,1361,1362],{},"sql.begin",", or any object with ",[164,1365,548],{},". A\nDrizzle transaction object is not one; use the driver's transaction beneath\nit.",[807,1368,1369],{"id":1369},"hold",[133,1371,1372,1373,1377],{},"A write in your app that depends on a setting, such as adding a task to a\nproject that must stay under ",[1374,1375,1376],"em",{},"Tasks per project",", should hold the settings\nwhile it reads them and writes:",[216,1379,1381],{"className":218,"code":1380,"language":220,"meta":221,"style":221},"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) throw new Error('This project is full.')\n  await tx`insert into tasks (project_id, title) values (${project.id}, ${input.title})`\n})\n",[164,1382,1383,1411,1431,1443,1502,1544,1589],{"__ignoreMap":221},[225,1384,1385,1387,1390,1392,1394,1396,1399,1401,1404,1406,1409],{"class":227,"line":228},[225,1386,733],{"class":231},[225,1388,1389],{"class":325}," sql",[225,1391,330],{"class":329},[225,1393,1112],{"class":333},[225,1395,398],{"class":242},[225,1397,1398],{"class":231},"async",[225,1400,1273],{"class":242},[225,1402,1403],{"class":235},"tx",[225,1405,1279],{"class":242},[225,1407,1408],{"class":231}," =>",[225,1410,1093],{"class":242},[225,1412,1413,1415,1417,1419,1421,1423,1425,1427,1429],{"class":227,"line":253},[225,1414,1121],{"class":231},[225,1416,1124],{"class":235},[225,1418,319],{"class":242},[225,1420,877],{"class":325},[225,1422,330],{"class":329},[225,1424,565],{"class":333},[225,1426,398],{"class":242},[225,1428,1403],{"class":235},[225,1430,863],{"class":242},[225,1432,1433,1435,1437,1439,1441],{"class":227,"line":277},[225,1434,1098],{"class":231},[225,1436,1146],{"class":325},[225,1438,330],{"class":329},[225,1440,1369],{"class":333},[225,1442,1085],{"class":242},[225,1444,1445,1447,1449,1452,1454,1457,1459,1461,1463,1466,1468,1470,1472,1474,1476,1478,1481,1483,1485,1488,1490,1493,1495,1498,1500],{"class":227,"line":303},[225,1446,1121],{"class":231},[225,1448,258],{"class":242},[225,1450,1451],{"class":235}," maxTasksPerProject ",[225,1453,264],{"class":242},[225,1455,1456],{"class":242}," =",[225,1458,1075],{"class":231},[225,1460,1146],{"class":325},[225,1462,330],{"class":329},[225,1464,1465],{"class":333},"get",[225,1467,398],{"class":242},[225,1469,423],{"class":242},[225,1471,1158],{"class":246},[225,1473,423],{"class":242},[225,1475,287],{"class":242},[225,1477,258],{"class":242},[225,1479,1480],{"class":235}," scope",[225,1482,343],{"class":242},[225,1484,258],{"class":242},[225,1486,1487],{"class":235}," workspace",[225,1489,343],{"class":242},[225,1491,1492],{"class":235}," project",[225,1494,330],{"class":242},[225,1496,1497],{"class":235},"workspace ",[225,1499,264],{"class":242},[225,1501,761],{"class":242},[225,1503,1504,1507,1509,1512,1514,1517,1520,1523,1525,1528,1530,1533,1535,1537,1540,1542],{"class":227,"line":310},[225,1505,1506],{"class":231},"  if",[225,1508,1273],{"class":242},[225,1510,1511],{"class":235},"project",[225,1513,330],{"class":242},[225,1515,1516],{"class":235},"openTasks ",[225,1518,1519],{"class":242},">=",[225,1521,1522],{"class":235}," maxTasksPerProject",[225,1524,1279],{"class":242},[225,1526,1527],{"class":231}," throw",[225,1529,322],{"class":231},[225,1531,1532],{"class":333}," Error",[225,1534,398],{"class":242},[225,1536,423],{"class":242},[225,1538,1539],{"class":246},"This project is full.",[225,1541,423],{"class":242},[225,1543,863],{"class":242},[225,1545,1546,1548,1551,1554,1557,1560,1562,1564,1567,1569,1572,1574,1577,1579,1582,1584,1586],{"class":227,"line":362},[225,1547,1098],{"class":231},[225,1549,1550],{"class":333}," tx",[225,1552,1553],{"class":242},"`",[225,1555,1556],{"class":246},"insert into tasks (project_id, title) values (",[225,1558,1559],{"class":242},"${",[225,1561,1511],{"class":235},[225,1563,330],{"class":242},[225,1565,1566],{"class":235},"id",[225,1568,264],{"class":242},[225,1570,1571],{"class":246},", ",[225,1573,1559],{"class":242},[225,1575,1576],{"class":235},"input",[225,1578,330],{"class":242},[225,1580,1581],{"class":235},"title",[225,1583,264],{"class":242},[225,1585,1279],{"class":246},[225,1587,1588],{"class":242},"`\n",[225,1590,1591],{"class":227,"line":367},[225,1592,359],{"class":242},[133,1594,1595,1597,1598,1602,1603,1605,1606,330],{},[164,1596,1369],{}," takes the tunables lock in shared mode until your transaction ends.\nWrites that hold it run side by side; a change to tunables takes it\nexclusively, so a lowering waits for your write to commit, and its\n",[192,1599,1601],{"href":1600},".\u002Fchecks-and-guards#guards-for-what-your-app-keeps","guard"," then sees what\nyou wrote. ",[164,1604,1369],{}," only means something inside a transaction, so it is offered\nonly on ",[164,1607,565],{},[211,1609,1611],{"id":1610},"two-apps-in-one-database","Two apps in one database",[133,1613,1614],{},"Give each app its own schema. The lock key is derived from the schema name,\nso they never wait on each other.",[211,1616,1618],{"id":1617},"the-storage-contract","The storage contract",[133,1620,1621,1622,1625],{},"Postgres is one implementation of a small contract, ",[164,1623,1624],{},"TunablesStore",". Anything\nthat can keep four kinds of thing, in a transaction, behind a lock, can store\ntunables, so another database needs only a store of its own:",[677,1627,1628,1634,1637,1640],{},[680,1629,1630,1631,1633],{},"a group's values, one row per group and scope: the system's at scope ",[164,1632,999],{},",\nand one for each scope that overrides something;",[680,1635,1636],{},"revisions, appended and never changed, with increasing ids;",[680,1638,1639],{},"records, the presets and catalog entries, as JSON by collection and id;",[680,1641,1642],{},"assignments of presets to subjects.",[216,1644,1646],{"className":218,"code":1645,"language":220,"meta":221,"style":221},"interface StoreSession {\n  lock(mode: 'shared' | 'exclusive'): Promise\u003Cvoid>\n  readGroups(ids?: readonly string[], scope?: string): Promise\u003CStoredGroup[]> \u002F\u002F one scope, the system's ('') by default\n  readScopes(id: string): Promise\u003CStoredGroup[]> \u002F\u002F every scoped row of a group, in key order\n  writeGroup(group: StoredGroup): Promise\u003Cvoid> \u002F\u002F { id, scope, values, updatedAt, updatedBy }\n  deleteGroup(id: string, scope: string): Promise\u003Cvoid> \u002F\u002F a scoped row that overrides nothing any more\n  appendRevision(revision: NewRevision): Promise\u003CRevision>\n  listRevisions(target: RevisionTarget, query: RevisionQuery): Promise\u003CRevision[]>\n  getRevision(id: string): Promise\u003CRevision | undefined>\n  listRecords(collection: string): Promise\u003CStoredRecord[]>\n  putRecord(record: Omit\u003CStoredRecord, 'createdAt' | 'updatedAt'>): Promise\u003Cvoid>\n  deleteRecord(collection: string, id: string): Promise\u003Cvoid>\n  listAssignments(collection: string, filter?: { subject?: string, recordId?: string }): Promise\u003CAssignment[]>\n  putAssignment(assignment: Assignment): Promise\u003Cvoid>\n  deleteAssignment(collection: string, subject: string, slot: string): Promise\u003Cvoid>\n}\n\ninterface TunablesStore extends StoreSession {\n  transaction\u003CT>(work: (session: StoreSession) => Promise\u003CT>): Promise\u003CT>\n  within?(host: unknown): StoreSession\n}\n",[164,1647,1648,1658,1702,1746,1772,1801,1835,1861,1897,1925,1950,1998,2030,2080,2105,2146,2151,2156,2173,2221,2243],{"__ignoreMap":221},[225,1649,1650,1653,1656],{"class":227,"line":228},[225,1651,1652],{"class":231},"interface",[225,1654,1655],{"class":333}," StoreSession",[225,1657,1093],{"class":242},[225,1659,1660,1663,1665,1668,1670,1672,1675,1677,1680,1682,1685,1687,1690,1693,1696,1699],{"class":227,"line":253},[225,1661,1662],{"class":333},"  lock",[225,1664,398],{"class":242},[225,1666,1667],{"class":235},"mode",[225,1669,343],{"class":242},[225,1671,243],{"class":242},[225,1673,1674],{"class":246},"shared",[225,1676,423],{"class":242},[225,1678,1679],{"class":242}," |",[225,1681,243],{"class":242},[225,1683,1684],{"class":246},"exclusive",[225,1686,423],{"class":242},[225,1688,1689],{"class":242},"):",[225,1691,1692],{"class":333}," Promise",[225,1694,1695],{"class":242},"\u003C",[225,1697,1698],{"class":333},"void",[225,1700,1701],{"class":242},">\n",[225,1703,1704,1707,1709,1712,1715,1718,1721,1724,1726,1728,1730,1732,1734,1736,1739,1742],{"class":227,"line":277},[225,1705,1706],{"class":333},"  readGroups",[225,1708,398],{"class":242},[225,1710,1711],{"class":235},"ids",[225,1713,1714],{"class":242},"?:",[225,1716,1717],{"class":231}," readonly",[225,1719,1720],{"class":333}," string",[225,1722,1723],{"class":242},"[],",[225,1725,1480],{"class":235},[225,1727,1714],{"class":242},[225,1729,1720],{"class":333},[225,1731,1689],{"class":242},[225,1733,1692],{"class":333},[225,1735,1695],{"class":242},[225,1737,1738],{"class":333},"StoredGroup",[225,1740,1741],{"class":242},"[]>",[225,1743,1745],{"class":1744},"sKllz"," \u002F\u002F one scope, the system's ('') by default\n",[225,1747,1748,1751,1753,1755,1757,1759,1761,1763,1765,1767,1769],{"class":227,"line":303},[225,1749,1750],{"class":333},"  readScopes",[225,1752,398],{"class":242},[225,1754,1566],{"class":235},[225,1756,343],{"class":242},[225,1758,1720],{"class":333},[225,1760,1689],{"class":242},[225,1762,1692],{"class":333},[225,1764,1695],{"class":242},[225,1766,1738],{"class":333},[225,1768,1741],{"class":242},[225,1770,1771],{"class":1744}," \u002F\u002F every scoped row of a group, in key order\n",[225,1773,1774,1777,1779,1782,1784,1787,1789,1791,1793,1795,1798],{"class":227,"line":310},[225,1775,1776],{"class":333},"  writeGroup",[225,1778,398],{"class":242},[225,1780,1781],{"class":235},"group",[225,1783,343],{"class":242},[225,1785,1786],{"class":333}," StoredGroup",[225,1788,1689],{"class":242},[225,1790,1692],{"class":333},[225,1792,1695],{"class":242},[225,1794,1698],{"class":333},[225,1796,1797],{"class":242},">",[225,1799,1800],{"class":1744}," \u002F\u002F { id, scope, values, updatedAt, updatedBy }\n",[225,1802,1803,1806,1808,1810,1812,1814,1816,1818,1820,1822,1824,1826,1828,1830,1832],{"class":227,"line":362},[225,1804,1805],{"class":333},"  deleteGroup",[225,1807,398],{"class":242},[225,1809,1566],{"class":235},[225,1811,343],{"class":242},[225,1813,1720],{"class":333},[225,1815,287],{"class":242},[225,1817,1480],{"class":235},[225,1819,343],{"class":242},[225,1821,1720],{"class":333},[225,1823,1689],{"class":242},[225,1825,1692],{"class":333},[225,1827,1695],{"class":242},[225,1829,1698],{"class":333},[225,1831,1797],{"class":242},[225,1833,1834],{"class":1744}," \u002F\u002F a scoped row that overrides nothing any more\n",[225,1836,1837,1840,1842,1845,1847,1850,1852,1854,1856,1859],{"class":227,"line":367},[225,1838,1839],{"class":333},"  appendRevision",[225,1841,398],{"class":242},[225,1843,1844],{"class":235},"revision",[225,1846,343],{"class":242},[225,1848,1849],{"class":333}," NewRevision",[225,1851,1689],{"class":242},[225,1853,1692],{"class":333},[225,1855,1695],{"class":242},[225,1857,1858],{"class":333},"Revision",[225,1860,1701],{"class":242},[225,1862,1863,1866,1868,1871,1873,1876,1878,1881,1883,1886,1888,1890,1892,1894],{"class":227,"line":387},[225,1864,1865],{"class":333},"  listRevisions",[225,1867,398],{"class":242},[225,1869,1870],{"class":235},"target",[225,1872,343],{"class":242},[225,1874,1875],{"class":333}," RevisionTarget",[225,1877,287],{"class":242},[225,1879,1880],{"class":235}," query",[225,1882,343],{"class":242},[225,1884,1885],{"class":333}," RevisionQuery",[225,1887,1689],{"class":242},[225,1889,1692],{"class":333},[225,1891,1695],{"class":242},[225,1893,1858],{"class":333},[225,1895,1896],{"class":242},"[]>\n",[225,1898,1899,1902,1904,1906,1908,1910,1912,1914,1916,1918,1920,1923],{"class":227,"line":429},[225,1900,1901],{"class":333},"  getRevision",[225,1903,398],{"class":242},[225,1905,1566],{"class":235},[225,1907,343],{"class":242},[225,1909,1720],{"class":333},[225,1911,1689],{"class":242},[225,1913,1692],{"class":333},[225,1915,1695],{"class":242},[225,1917,1858],{"class":333},[225,1919,1679],{"class":242},[225,1921,1922],{"class":333}," undefined",[225,1924,1701],{"class":242},[225,1926,1927,1930,1932,1935,1937,1939,1941,1943,1945,1948],{"class":227,"line":435},[225,1928,1929],{"class":333},"  listRecords",[225,1931,398],{"class":242},[225,1933,1934],{"class":235},"collection",[225,1936,343],{"class":242},[225,1938,1720],{"class":333},[225,1940,1689],{"class":242},[225,1942,1692],{"class":333},[225,1944,1695],{"class":242},[225,1946,1947],{"class":333},"StoredRecord",[225,1949,1896],{"class":242},[225,1951,1952,1955,1957,1960,1962,1965,1967,1969,1971,1973,1976,1978,1980,1982,1985,1987,1990,1992,1994,1996],{"class":227,"line":1305},[225,1953,1954],{"class":333},"  putRecord",[225,1956,398],{"class":242},[225,1958,1959],{"class":235},"record",[225,1961,343],{"class":242},[225,1963,1964],{"class":333}," Omit",[225,1966,1695],{"class":242},[225,1968,1947],{"class":333},[225,1970,287],{"class":242},[225,1972,243],{"class":242},[225,1974,1975],{"class":246},"createdAt",[225,1977,423],{"class":242},[225,1979,1679],{"class":242},[225,1981,243],{"class":242},[225,1983,1984],{"class":246},"updatedAt",[225,1986,423],{"class":242},[225,1988,1989],{"class":242},">):",[225,1991,1692],{"class":333},[225,1993,1695],{"class":242},[225,1995,1698],{"class":333},[225,1997,1701],{"class":242},[225,1999,2000,2003,2005,2007,2009,2011,2013,2016,2018,2020,2022,2024,2026,2028],{"class":227,"line":1314},[225,2001,2002],{"class":333},"  deleteRecord",[225,2004,398],{"class":242},[225,2006,1934],{"class":235},[225,2008,343],{"class":242},[225,2010,1720],{"class":333},[225,2012,287],{"class":242},[225,2014,2015],{"class":235}," id",[225,2017,343],{"class":242},[225,2019,1720],{"class":333},[225,2021,1689],{"class":242},[225,2023,1692],{"class":333},[225,2025,1695],{"class":242},[225,2027,1698],{"class":333},[225,2029,1701],{"class":242},[225,2031,2032,2035,2037,2039,2041,2043,2045,2048,2050,2052,2055,2057,2059,2061,2064,2066,2068,2071,2073,2075,2078],{"class":227,"line":1324},[225,2033,2034],{"class":333},"  listAssignments",[225,2036,398],{"class":242},[225,2038,1934],{"class":235},[225,2040,343],{"class":242},[225,2042,1720],{"class":333},[225,2044,287],{"class":242},[225,2046,2047],{"class":235}," filter",[225,2049,1714],{"class":242},[225,2051,258],{"class":242},[225,2053,2054],{"class":235}," subject",[225,2056,1714],{"class":242},[225,2058,1720],{"class":333},[225,2060,287],{"class":242},[225,2062,2063],{"class":235}," recordId",[225,2065,1714],{"class":242},[225,2067,1720],{"class":333},[225,2069,2070],{"class":242}," }):",[225,2072,1692],{"class":333},[225,2074,1695],{"class":242},[225,2076,2077],{"class":333},"Assignment",[225,2079,1896],{"class":242},[225,2081,2082,2085,2087,2090,2092,2095,2097,2099,2101,2103],{"class":227,"line":1337},[225,2083,2084],{"class":333},"  putAssignment",[225,2086,398],{"class":242},[225,2088,2089],{"class":235},"assignment",[225,2091,343],{"class":242},[225,2093,2094],{"class":333}," Assignment",[225,2096,1689],{"class":242},[225,2098,1692],{"class":333},[225,2100,1695],{"class":242},[225,2102,1698],{"class":333},[225,2104,1701],{"class":242},[225,2106,2108,2111,2113,2115,2117,2119,2121,2123,2125,2127,2129,2132,2134,2136,2138,2140,2142,2144],{"class":227,"line":2107},15,[225,2109,2110],{"class":333},"  deleteAssignment",[225,2112,398],{"class":242},[225,2114,1934],{"class":235},[225,2116,343],{"class":242},[225,2118,1720],{"class":333},[225,2120,287],{"class":242},[225,2122,2054],{"class":235},[225,2124,343],{"class":242},[225,2126,1720],{"class":333},[225,2128,287],{"class":242},[225,2130,2131],{"class":235}," slot",[225,2133,343],{"class":242},[225,2135,1720],{"class":333},[225,2137,1689],{"class":242},[225,2139,1692],{"class":333},[225,2141,1695],{"class":242},[225,2143,1698],{"class":333},[225,2145,1701],{"class":242},[225,2147,2149],{"class":227,"line":2148},16,[225,2150,1340],{"class":242},[225,2152,2154],{"class":227,"line":2153},17,[225,2155,307],{"emptyLinePlaceholder":306},[225,2157,2159,2161,2164,2167,2170],{"class":227,"line":2158},18,[225,2160,1652],{"class":231},[225,2162,2163],{"class":333}," TunablesStore",[225,2165,2166],{"class":231}," extends",[225,2168,2169],{"class":235}," StoreSession ",[225,2171,2172],{"class":242},"{\n",[225,2174,2176,2179,2181,2184,2187,2190,2192,2194,2197,2199,2201,2203,2205,2207,2209,2211,2213,2215,2217,2219],{"class":227,"line":2175},19,[225,2177,2178],{"class":333},"  transaction",[225,2180,1695],{"class":242},[225,2182,2183],{"class":333},"T",[225,2185,2186],{"class":242},">(",[225,2188,2189],{"class":333},"work",[225,2191,343],{"class":242},[225,2193,1273],{"class":242},[225,2195,2196],{"class":235},"session",[225,2198,343],{"class":242},[225,2200,1655],{"class":333},[225,2202,1279],{"class":242},[225,2204,1408],{"class":231},[225,2206,1692],{"class":333},[225,2208,1695],{"class":242},[225,2210,2183],{"class":333},[225,2212,1989],{"class":242},[225,2214,1692],{"class":333},[225,2216,1695],{"class":242},[225,2218,2183],{"class":333},[225,2220,1701],{"class":242},[225,2222,2224,2227,2230,2233,2235,2238,2240],{"class":227,"line":2223},20,[225,2225,2226],{"class":333},"  within",[225,2228,2229],{"class":242},"?(",[225,2231,2232],{"class":235},"host",[225,2234,343],{"class":242},[225,2236,2237],{"class":333}," unknown",[225,2239,1689],{"class":242},[225,2241,2242],{"class":333}," StoreSession\n",[225,2244,2246],{"class":227,"line":2245},21,[225,2247,1340],{"class":242},[133,2249,2250,2253,2254,2257,2258,2261,2262,2264,2265,2268,2269,2277],{},[164,2251,2252],{},"listRevisions"," returns newest first, at most ",[164,2255,2256],{},"limit",", and only ids below\n",[164,2259,2260],{},"before"," when it is given. A store without ",[164,2263,565],{}," cannot join a host\ntransaction, and ",[164,2266,2267],{},"tunables.within"," says so. The full contract, including what\neach method must guarantee, is\n",[192,2270,2274],{"href":2271,"rel":2272},"https:\u002F\u002Fgithub.com\u002Fitai-gendler\u002Ftunables\u002Fblob\u002Fmain\u002Fspec\u002Fstorage.md",[2273],"nofollow",[164,2275,2276],{},"spec\u002Fstorage.md",";\na store passes when it runs the repository's engine suite unchanged.",[807,2279,2281],{"id":2280},"the-memory-store","The memory store",[133,2283,2284,2287],{},[164,2285,2286],{},"memoryStore()"," keeps everything in the process. Its transactions are\nserialized and roll back on error, so it behaves as a database would for one\nprocess. Use it for tests, demos and trying tunables out; it forgets\neverything when the process exits.",[216,2289,2291],{"className":218,"code":2290,"language":220,"meta":221,"style":221},"import { createTunables, memoryStore } from 'tunables'\n\nconst tunables = createTunables({ store: memoryStore(), groups })\nawait tunables.sync()\n",[164,2292,2293,2316,2320,2348],{"__ignoreMap":221},[225,2294,2295,2297,2299,2301,2303,2306,2308,2310,2312,2314],{"class":227,"line":228},[225,2296,232],{"class":231},[225,2298,258],{"class":242},[225,2300,381],{"class":235},[225,2302,287],{"class":242},[225,2304,2305],{"class":235}," memoryStore ",[225,2307,264],{"class":242},[225,2309,267],{"class":231},[225,2311,243],{"class":242},[225,2313,272],{"class":246},[225,2315,250],{"class":242},[225,2317,2318],{"class":227,"line":253},[225,2319,307],{"emptyLinePlaceholder":306},[225,2321,2322,2324,2326,2328,2330,2332,2335,2337,2340,2343,2346],{"class":227,"line":277},[225,2323,313],{"class":231},[225,2325,376],{"class":235},[225,2327,319],{"class":242},[225,2329,381],{"class":333},[225,2331,337],{"class":242},[225,2333,2334],{"class":235}," store",[225,2336,343],{"class":242},[225,2338,2339],{"class":333}," memoryStore",[225,2341,2342],{"class":242},"(),",[225,2344,2345],{"class":235}," groups ",[225,2347,359],{"class":242},[225,2349,2350,2352,2354,2356,2359],{"class":227,"line":303},[225,2351,733],{"class":231},[225,2353,877],{"class":325},[225,2355,330],{"class":329},[225,2357,2358],{"class":333},"sync",[225,2360,1085],{"class":242},[133,2362,2363,2364,2367,2368,2371,2372,2378],{},"It takes an optional ",[164,2365,2366],{},"now()"," for fixed timestamps, and has a ",[164,2369,2370],{},"reset()"," that\nempties it. For tests, ",[192,2373,2375],{"href":2374},".\u002Ftesting",[164,2376,2377],{},"tunables\u002Ftesting"," wraps it.",[2380,2381,2382],"style",{},"html pre.shiki code .sZ4mS, html code.shiki .sZ4mS{--shiki-light:#115E59;--shiki-default:#115E59;--shiki-dark:#5EEAD4}html pre.shiki code .sVz71, html code.shiki .sVz71{--shiki-light:#182025;--shiki-default:#182025;--shiki-dark:#E6EBEC}html pre.shiki code .sp9ci, html code.shiki .sp9ci{--shiki-light:#4F5D63;--shiki-default:#4F5D63;--shiki-dark:#A3AFB4}html pre.shiki code .sjCzm, html code.shiki .sjCzm{--shiki-light:#92400E;--shiki-default:#92400E;--shiki-dark:#FCD34D}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 .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 .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":221,"searchDepth":253,"depth":253,"links":2384},[2385,2386,2387,2391,2394,2395],{"id":213,"depth":253,"text":214},{"id":483,"depth":253,"text":484},{"id":671,"depth":253,"text":672,"children":2388},[2389,2390],{"id":809,"depth":277,"text":176},{"id":968,"depth":277,"text":969},{"id":1044,"depth":253,"text":1045,"children":2392},[2393],{"id":1369,"depth":277,"text":1369},{"id":1610,"depth":253,"text":1611},{"id":1617,"depth":253,"text":1618,"children":2396},[2397],{"id":2280,"depth":277,"text":2281},"md",{},{"title":52,"description":53},"docs\u002Fstorage","npmkzPkSYciOowwxeMDx3a0JGqVsDD4rFXKy3rc1Qog",1792845117974]