[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-navigation":3,"docs-page-feature-flags":166},[4,38,57,65,114,138,157],{"id":5,"group":6,"pages":7},"get-started","Get started",[8,13,18,23,28,33],{"path":9,"title":10,"description":11,"navTitle":10,"group":6,"order":12},"\u002Fdocs\u002Fgetting-started","Getting started","Create your app from the kit, run it locally with no credentials, make yourself superadmin and find where your own code goes.",1,{"path":14,"title":15,"description":16,"navTitle":15,"group":6,"order":17},"\u002Fdocs\u002Flayout","Project structure","Where code lives by where it runs, and which server\u002F directories turn a new file into a route, a job or a provider.",2,{"path":19,"title":20,"description":21,"navTitle":20,"group":6,"order":22},"\u002Fdocs\u002Fconfiguration","Configuration","starter.config.ts switches each part of the kit and its providers, environment variables hold the secrets: every key and what it changes.",3,{"path":24,"title":25,"description":26,"navTitle":25,"group":6,"order":27},"\u002Fdocs\u002Ftemplate","Updating from the kit","Keep the kit as a git remote and take each update as one commit you review, with pnpm template:update, then resolve what conflicts.",4,{"path":29,"title":30,"description":31,"navTitle":30,"group":6,"order":32},"\u002Fdocs\u002Fdeployment","Deployment","Ship the app as one Cloudflare Worker: create the resources, set the secrets, deploy main from GitHub Actions or your machine.",5,{"path":34,"title":35,"description":36,"navTitle":35,"group":6,"order":37},"\u002Fdocs\u002Fmcp","MCP server","Connect Claude Code, Cursor or VS Code to https:\u002F\u002Fnuxter.app\u002Fmcp, so your agent searches and reads these docs and their design notes.",6,{"id":39,"group":40,"pages":41},"accounts","Accounts",[42,47,52],{"path":43,"title":44,"description":45,"navTitle":44,"group":40,"order":46},"\u002Fdocs\u002Fauth","Auth","Pick the sign-in methods, add an OAuth provider, protect a route, and handle sessions, second factors and account deletion.",7,{"path":48,"title":49,"description":50,"navTitle":49,"group":40,"order":51},"\u002Fdocs\u002Ftenancy","Organizations","Scope routes and pages to an organization, and work with its members, roles, seats, invitations and a second level such as branches.",8,{"path":53,"title":54,"description":55,"navTitle":54,"group":40,"order":56},"\u002Fdocs\u002Fpermissions","Roles and permissions","Routes check permissions that an account’s role or its organization’s plan grants: checking one, adding a role, and the staff tools.",9,{"id":58,"group":59,"pages":60},"payments","Payments",[61],{"path":62,"title":59,"description":63,"navTitle":59,"group":59,"order":64},"\u002Fdocs\u002Fpayments","Sell plans through Stripe, Polar, Lemon Squeezy or Creem: define plans, connect a provider, gate features, trials, coupons and refunds.",10,{"id":66,"group":67,"pages":68},"building-blocks","Building blocks",[69,74,79,84,89,94,99,104,109],{"path":70,"title":71,"description":72,"navTitle":71,"group":67,"order":73},"\u002Fdocs\u002Fdatabase","Database","Postgres through Drizzle, on Hyperdrive in production and PGlite locally: add a table, query, transactions, migrations and seed data.",11,{"path":75,"title":76,"description":77,"navTitle":76,"group":67,"order":78},"\u002Fdocs\u002Fbackground-jobs","Background jobs","Run work after the response on Cloudflare Queues with retries: add and enqueue a job, run jobs locally, and schedule cron tasks.",12,{"path":80,"title":81,"description":82,"navTitle":81,"group":67,"order":83},"\u002Fdocs\u002Fevents","Events","React to what the kit does by adding a file to server\u002Flisteners\u002F, fire events of your own, and look up every event’s payload.",13,{"path":85,"title":86,"description":87,"navTitle":86,"group":67,"order":88},"\u002Fdocs\u002Femail","Email","Set up sending, send your own mail with deliverEmail, change the templates and wording, handle unsubscribes and add a provider.",14,{"path":90,"title":91,"description":92,"navTitle":91,"group":67,"order":93},"\u002Fdocs\u002Fcontact","Contact","A \u002Fcontact page that mails you a message or shows your address after a Turnstile check: choose the channels, set the address, know the limits.",15,{"path":95,"title":96,"description":97,"navTitle":96,"group":67,"order":98},"\u002Fdocs\u002Fnotifications","Notifications","The dashboard bell: add a notification type, send one with notify(), and how categories, opt-outs and retention apply.",16,{"path":100,"title":101,"description":102,"navTitle":101,"group":67,"order":103},"\u002Fdocs\u002Fstorage","Storage","Uploads in R2 with one policy per purpose: add a purpose, accept an upload, replace or delete a file, and serve it to its owner.",17,{"path":105,"title":106,"description":107,"navTitle":106,"group":67,"order":108},"\u002Fdocs\u002Ffeature-flags","Feature flags","Declare typed flags in starter.config.ts, read them with flag() or useFlag(), roll them out by rule or percentage, and add a provider.",18,{"path":110,"title":111,"description":112,"navTitle":111,"group":67,"order":113},"\u002Fdocs\u002Fai","AI","Call models from server code by role: text, images, speech, transcription, embeddings and video, with provider fallbacks.",19,{"id":115,"group":116,"pages":117},"web","Web",[118,123,128,133],{"path":119,"title":120,"description":121,"navTitle":120,"group":116,"order":122},"\u002Fdocs\u002Fsecurity-headers","Security headers","The Content-Security-Policy and hardening headers on every response: allow another origin, keep tokens out of Referer, accept cross-site posts.",20,{"path":124,"title":125,"description":126,"navTitle":125,"group":116,"order":127},"\u002Fdocs\u002Frate-limiting","Rate limiting","Cap how often a caller hits a route with requireRateLimit, count only failures, space out sends, and protect the zone.",21,{"path":129,"title":130,"description":131,"navTitle":130,"group":116,"order":132},"\u002Fdocs\u002Fanalytics","Analytics","Product analytics and ad conversions sent from the server with no browser script: add an event, connect a provider, and handle consent.",22,{"path":134,"title":135,"description":136,"navTitle":135,"group":116,"order":137},"\u002Fdocs\u002Fobservability","Observability","Find a failed request by its id, send faults to Sentry or Better Stack, report an error you caught, and change the error page.",23,{"id":139,"group":140,"pages":141},"content-and-look","Content and look",[142,147,152],{"path":143,"title":144,"description":145,"navTitle":144,"group":140,"order":146},"\u002Fdocs\u002Fblog","Blog","Markdown posts with an RSS feed per language, and workflows that draft posts with an AI model and open each one as a pull request.",24,{"path":148,"title":149,"description":150,"navTitle":149,"group":140,"order":151},"\u002Fdocs\u002Flanguages","Languages","Add or remove a language, write copy in the locale files, and write code, refusals and links that follow the reader’s language.",25,{"path":153,"title":154,"description":155,"navTitle":154,"group":140,"order":156},"\u002Fdocs\u002Ftheme","Theme","Change the brand colour and font, and build dashboard sections, list pages, loading placeholders and charts that match the kit.",26,{"id":158,"group":159,"pages":160},"quality","Quality",[161],{"path":162,"title":163,"description":164,"navTitle":163,"group":159,"order":165},"\u002Fdocs\u002Ftesting","Testing","Unit tests in Node, component tests in a real Nuxt app and Playwright end to end: test a route or a component, and what CI runs.",27,{"page":167,"surround":1959,"linksToKit":351},{"path":105,"title":106,"description":107,"navTitle":106,"group":67,"seo":168,"body":170},{"title":169,"description":107},"Feature flags in Nuxt by rule or percentage",{"type":171,"value":172,"toc":1947},"minimark",[173,177,201,206,249,253,260,424,439,443,460,549,571,575,585,713,722,728,842,847,879,885,948,985,989,994,1124,1127,1162,1199,1213,1228,1272,1275,1297,1328,1332,1349,1601,1662,1690,1693,1900,1904,1943],[174,175,106],"h1",{"id":176},"feature-flags",[178,179,180,181,192,193,196,197,200],"p",{},"Flags are typed values you declare in ",[182,183,188],"a",{"href":184,"rel":185,"title":187},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Fstarter.config.ts",[186],"nofollow","Licensees only",[189,190,191],"code",{},"starter.config.ts","\nand read with ",[189,194,195],{},"flag()"," on the server or ",[189,198,199],{},"useFlag()"," in a component. Use one as a\nkill switch, or to show a feature to staff or a share of visitors first. Flags\nship off.",[202,203,205],"h2",{"id":204},"how-it-works","How it works",[207,208,209,217,228,243],"ol",{},[210,211,212,213,216],"li",{},"Each flag is declared under ",[189,214,215],{},"flags.declarations"," with a type and a default.\nThe default is what the app gets when nothing else answers.",[210,218,219,220,223,224,227],{},"Providers answer in the order of ",[189,221,222],{},"flags.providers",". The kit ships\n",[189,225,226],{},"selfhosted",", which evaluates a JSON document stored in KV or R2. A provider\nthat doesn't know a flag passes it to the next one, and the declared default\ncomes last.",[210,229,230,231,238,239,242],{},"On server-rendered pages,\n",[182,232,235],{"href":233,"rel":234,"title":187},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Fserver\u002Fmiddleware\u002Fflags.ts",[186],[189,236,237],{},"server\u002Fmiddleware\u002Fflags.ts"," resolves every\ndeclared flag once and puts the values in the payload. ",[189,240,241],{},"useFlag"," reads them\nfrom there; nothing is evaluated in the browser.",[210,244,245,248],{},[189,246,247],{},"flag(event, key)"," evaluates one flag on demand, in API routes, jobs and\nanywhere else you have an event.",[202,250,252],{"id":251},"turn-flags-on","Turn flags on",[178,254,255,256,259],{},"Set ",[189,257,258],{},"flags.enabled: true",":",[261,262,267],"pre",{"className":263,"code":264,"language":265,"meta":266,"style":266},"language-ts shiki shiki-themes github-light github-light github-dark","flags: {\n  enabled: true,\n  providers: ['selfhosted'],\n  driver: 'kv',\n  key: 'flags.json',\n  purpose: 'avatars',\n\n  declarations: {\n    maintenance: { type: 'boolean', default: false },\n    newPricing: { type: 'boolean', default: false },\n  },\n},\n","ts","",[189,268,269,281,296,311,323,335,347,353,360,391,414,419],{"__ignoreMap":266},[270,271,273,277],"span",{"class":272,"line":12},"line",[270,274,276],{"class":275},"snPdu","flags",[270,278,280],{"class":279},"sxrX7",": {\n",[270,282,283,286,289,293],{"class":272,"line":17},[270,284,285],{"class":275},"  enabled",[270,287,288],{"class":279},": ",[270,290,292],{"class":291},"sBjJW","true",[270,294,295],{"class":279},",\n",[270,297,298,301,304,308],{"class":272,"line":22},[270,299,300],{"class":275},"  providers",[270,302,303],{"class":279},": [",[270,305,307],{"class":306},"sIIMD","'selfhosted'",[270,309,310],{"class":279},"],\n",[270,312,313,316,318,321],{"class":272,"line":27},[270,314,315],{"class":275},"  driver",[270,317,288],{"class":279},[270,319,320],{"class":306},"'kv'",[270,322,295],{"class":279},[270,324,325,328,330,333],{"class":272,"line":32},[270,326,327],{"class":275},"  key",[270,329,288],{"class":279},[270,331,332],{"class":306},"'flags.json'",[270,334,295],{"class":279},[270,336,337,340,342,345],{"class":272,"line":37},[270,338,339],{"class":275},"  purpose",[270,341,288],{"class":279},[270,343,344],{"class":306},"'avatars'",[270,346,295],{"class":279},[270,348,349],{"class":272,"line":46},[270,350,352],{"emptyLinePlaceholder":351},true,"\n",[270,354,355,358],{"class":272,"line":51},[270,356,357],{"class":275},"  declarations",[270,359,280],{"class":279},[270,361,362,365,368,371,373,376,379,383,385,388],{"class":272,"line":56},[270,363,364],{"class":275},"    maintenance",[270,366,367],{"class":279},": { ",[270,369,370],{"class":275},"type",[270,372,288],{"class":279},[270,374,375],{"class":306},"'boolean'",[270,377,378],{"class":279},", ",[270,380,382],{"class":381},"s8jYJ","default",[270,384,288],{"class":279},[270,386,387],{"class":291},"false",[270,389,390],{"class":279}," },\n",[270,392,393,396,398,400,402,404,406,408,410,412],{"class":272,"line":64},[270,394,395],{"class":275},"    newPricing",[270,397,367],{"class":279},[270,399,370],{"class":275},[270,401,288],{"class":279},[270,403,375],{"class":306},[270,405,378],{"class":279},[270,407,382],{"class":381},[270,409,288],{"class":279},[270,411,387],{"class":291},[270,413,390],{"class":279},[270,415,416],{"class":272,"line":73},[270,417,418],{"class":279},"  },\n",[270,420,421],{"class":272,"line":78},[270,422,423],{"class":279},"},\n",[178,425,426,427,430,431,434,435,438],{},"The ",[189,428,429],{},"kv"," driver reads the key ",[189,432,433],{},"flags.json"," from the ",[189,436,437],{},"CACHE"," namespace that\nNitro's cache already binds, so there is nothing new to create. Until you write\na document, every flag reads its default.",[202,440,442],{"id":441},"declare-a-flag","Declare a flag",[178,444,445,446,448,449,378,452,455,456,459],{},"Give it a ",[189,447,370],{}," of ",[189,450,451],{},"boolean",[189,453,454],{},"string"," or ",[189,457,458],{},"number"," and a default of that type:",[261,461,463],{"className":263,"code":462,"language":265,"meta":266,"style":266},"declarations: {\n  maintenance: { type: 'boolean', default: false },\n  checkoutCopy: { type: 'string', default: 'control' },\n  freeSeats: { type: 'number', default: 3 },\n},\n",[189,464,465,472,495,520,545],{"__ignoreMap":266},[270,466,467,470],{"class":272,"line":12},[270,468,469],{"class":275},"declarations",[270,471,280],{"class":279},[270,473,474,477,479,481,483,485,487,489,491,493],{"class":272,"line":17},[270,475,476],{"class":275},"  maintenance",[270,478,367],{"class":279},[270,480,370],{"class":275},[270,482,288],{"class":279},[270,484,375],{"class":306},[270,486,378],{"class":279},[270,488,382],{"class":381},[270,490,288],{"class":279},[270,492,387],{"class":291},[270,494,390],{"class":279},[270,496,497,500,502,504,506,509,511,513,515,518],{"class":272,"line":22},[270,498,499],{"class":275},"  checkoutCopy",[270,501,367],{"class":279},[270,503,370],{"class":275},[270,505,288],{"class":279},[270,507,508],{"class":306},"'string'",[270,510,378],{"class":279},[270,512,382],{"class":381},[270,514,288],{"class":279},[270,516,517],{"class":306},"'control'",[270,519,390],{"class":279},[270,521,522,525,527,529,531,534,536,538,540,543],{"class":272,"line":27},[270,523,524],{"class":275},"  freeSeats",[270,526,367],{"class":279},[270,528,370],{"class":275},[270,530,288],{"class":279},[270,532,533],{"class":306},"'number'",[270,535,378],{"class":279},[270,537,382],{"class":381},[270,539,288],{"class":279},[270,541,542],{"class":291},"3",[270,544,390],{"class":279},[270,546,547],{"class":272,"line":32},[270,548,423],{"class":279},[178,550,551,552,555,556,559,560,288,563,566,567,570],{},"The return type comes from the declaration, so ",[189,553,554],{},"flag(event, 'freeSeats')"," is a\n",[189,557,558],{},"Promise\u003Cnumber>"," and an undeclared key fails type checking. Ids must match\n",[189,561,562],{},"\u002F^[a-z][a-zA-Z0-9]*$\u002F",[189,564,565],{},"newPricing"," works, ",[189,568,569],{},"new-pricing"," fails the build. The\nbuild also refuses a default of the wrong type, and flags turned on with none\ndeclared.",[202,572,574],{"id":573},"read-a-flag","Read a flag",[178,576,577,578,581,582,259],{},"On the server, import ",[189,579,580],{},"flag"," from ",[189,583,584],{},"#server\u002Fflags",[261,586,588],{"className":263,"code":587,"language":265,"meta":266,"style":266},"import { flag } from '#server\u002Fflags'\n\nexport default defineEventHandler(async (event) => {\n  if (await flag(event, 'maintenance')) {\n    throw createError({ statusCode: 503, statusMessage: 'Back in a minute.' })\n  }\n\n  return { ok: true }\n})\n",[189,589,590,604,608,641,663,686,691,695,708],{"__ignoreMap":266},[270,591,592,595,598,601],{"class":272,"line":12},[270,593,594],{"class":381},"import",[270,596,597],{"class":279}," { flag } ",[270,599,600],{"class":381},"from",[270,602,603],{"class":306}," '#server\u002Fflags'\n",[270,605,606],{"class":272,"line":17},[270,607,352],{"emptyLinePlaceholder":351},[270,609,610,613,616,619,622,625,628,632,635,638],{"class":272,"line":22},[270,611,612],{"class":381},"export",[270,614,615],{"class":381}," default",[270,617,618],{"class":275}," defineEventHandler",[270,620,621],{"class":279},"(",[270,623,624],{"class":381},"async",[270,626,627],{"class":279}," (",[270,629,631],{"class":630},"sP4rz","event",[270,633,634],{"class":279},") ",[270,636,637],{"class":381},"=>",[270,639,640],{"class":279}," {\n",[270,642,643,646,648,651,654,657,660],{"class":272,"line":27},[270,644,645],{"class":381},"  if",[270,647,627],{"class":279},[270,649,650],{"class":381},"await",[270,652,653],{"class":275}," flag",[270,655,656],{"class":279},"(event, ",[270,658,659],{"class":306},"'maintenance'",[270,661,662],{"class":279},")) {\n",[270,664,665,668,671,674,677,680,683],{"class":272,"line":32},[270,666,667],{"class":381},"    throw",[270,669,670],{"class":275}," createError",[270,672,673],{"class":279},"({ statusCode: ",[270,675,676],{"class":291},"503",[270,678,679],{"class":279},", statusMessage: ",[270,681,682],{"class":306},"'Back in a minute.'",[270,684,685],{"class":279}," })\n",[270,687,688],{"class":272,"line":37},[270,689,690],{"class":279},"  }\n",[270,692,693],{"class":272,"line":46},[270,694,352],{"emptyLinePlaceholder":351},[270,696,697,700,703,705],{"class":272,"line":51},[270,698,699],{"class":381},"  return",[270,701,702],{"class":279}," { ok: ",[270,704,292],{"class":291},[270,706,707],{"class":279}," }\n",[270,709,710],{"class":272,"line":56},[270,711,712],{"class":279},"})\n",[178,714,715,717,718,721],{},[189,716,580],{}," never throws and always resolves to a value. A provider that throws or\nanswers with the wrong type is skipped. A document that fails to read or parse\nis logged as ",[189,719,720],{},"[flags] reading the document failed"," and treated as absent.",[178,723,724,725,727],{},"In a component, ",[189,726,241],{}," returns a computed ref:",[261,729,733],{"className":730,"code":731,"language":732,"meta":266,"style":266},"language-vue shiki shiki-themes github-light github-light github-dark","\u003Cscript setup lang=\"ts\">\nconst newPricing = useFlag('newPricing')\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUBadge v-if=\"newPricing\" label=\"New\" \u002F>\n\u003C\u002Ftemplate>\n","vue",[189,734,735,759,781,790,794,803,834],{"__ignoreMap":266},[270,736,737,740,744,747,750,753,756],{"class":272,"line":12},[270,738,739],{"class":279},"\u003C",[270,741,743],{"class":742},"sovSZ","script",[270,745,746],{"class":275}," setup",[270,748,749],{"class":275}," lang",[270,751,752],{"class":279},"=",[270,754,755],{"class":306},"\"ts\"",[270,757,758],{"class":279},">\n",[270,760,761,764,767,770,773,775,778],{"class":272,"line":17},[270,762,763],{"class":381},"const",[270,765,766],{"class":291}," newPricing",[270,768,769],{"class":381}," =",[270,771,772],{"class":275}," useFlag",[270,774,621],{"class":279},[270,776,777],{"class":306},"'newPricing'",[270,779,780],{"class":279},")\n",[270,782,783,786,788],{"class":272,"line":22},[270,784,785],{"class":279},"\u003C\u002F",[270,787,743],{"class":742},[270,789,758],{"class":279},[270,791,792],{"class":272,"line":27},[270,793,352],{"emptyLinePlaceholder":351},[270,795,796,798,801],{"class":272,"line":32},[270,797,739],{"class":279},[270,799,800],{"class":742},"template",[270,802,758],{"class":279},[270,804,805,808,811,814,816,819,821,823,826,828,831],{"class":272,"line":37},[270,806,807],{"class":279},"  \u003C",[270,809,810],{"class":742},"UBadge",[270,812,813],{"class":381}," v-if",[270,815,752],{"class":279},[270,817,818],{"class":306},"\"",[270,820,565],{"class":279},[270,822,818],{"class":306},[270,824,825],{"class":275}," label",[270,827,752],{"class":279},[270,829,830],{"class":306},"\"New\"",[270,832,833],{"class":279}," \u002F>\n",[270,835,836,838,840],{"class":272,"line":46},[270,837,785],{"class":279},[270,839,800],{"class":742},[270,841,758],{"class":279},[843,844,846],"h3",{"id":845},"read-a-flag-on-a-client-rendered-page","Read a flag on a client-rendered page",[178,848,849,850,853,854,455,857,860,861,863,864,378,867,870,871,874,875,878],{},"The middleware skips ",[189,851,852],{},"\u002Fapi\u002F"," paths, non-GET requests, and routes whose rule sets\n",[189,855,856],{},"cache",[189,858,859],{},"ssr: false",". On those pages the payload has no flags and ",[189,862,241],{},"\nreturns the declared default. As shipped that covers ",[189,865,866],{},"\u002Fblog",[189,868,869],{},"\u002Flegal",", the\ndashboard, the organization pages under ",[189,872,873],{},"\u002Fo"," and ",[189,876,877],{},"\u002Finvite",".",[178,880,881,882,259],{},"There, fetch the values from a route of your own, such as\n",[189,883,884],{},"server\u002Fapi\u002Fflags.get.ts",[261,886,888],{"className":263,"code":887,"language":265,"meta":266,"style":266},"import { flag } from '#server\u002Fflags'\n\nexport default defineEventHandler(async (event) => ({\n  newPricing: await flag(event, 'newPricing'),\n}))\n",[189,889,890,900,904,927,943],{"__ignoreMap":266},[270,891,892,894,896,898],{"class":272,"line":12},[270,893,594],{"class":381},[270,895,597],{"class":279},[270,897,600],{"class":381},[270,899,603],{"class":306},[270,901,902],{"class":272,"line":17},[270,903,352],{"emptyLinePlaceholder":351},[270,905,906,908,910,912,914,916,918,920,922,924],{"class":272,"line":22},[270,907,612],{"class":381},[270,909,615],{"class":381},[270,911,618],{"class":275},[270,913,621],{"class":279},[270,915,624],{"class":381},[270,917,627],{"class":279},[270,919,631],{"class":630},[270,921,634],{"class":279},[270,923,637],{"class":381},[270,925,926],{"class":279}," ({\n",[270,928,929,932,934,936,938,940],{"class":272,"line":27},[270,930,931],{"class":279},"  newPricing: ",[270,933,650],{"class":381},[270,935,653],{"class":275},[270,937,656],{"class":279},[270,939,777],{"class":306},[270,941,942],{"class":279},"),\n",[270,944,945],{"class":272,"line":32},[270,946,947],{"class":279},"}))\n",[261,949,951],{"className":263,"code":950,"language":265,"meta":266,"style":266},"const { data: flags } = await useFetch('\u002Fapi\u002Fflags')\n",[189,952,953],{"__ignoreMap":266},[270,954,955,957,960,963,965,967,970,972,975,978,980,983],{"class":272,"line":12},[270,956,763],{"class":381},[270,958,959],{"class":279}," { ",[270,961,962],{"class":630},"data",[270,964,288],{"class":279},[270,966,276],{"class":291},[270,968,969],{"class":279}," } ",[270,971,752],{"class":381},[270,973,974],{"class":381}," await",[270,976,977],{"class":275}," useFetch",[270,979,621],{"class":279},[270,981,982],{"class":306},"'\u002Fapi\u002Fflags'",[270,984,780],{"class":279},[202,986,988],{"id":987},"write-the-flag-document","Write the flag document",[178,990,426,991,993],{},[189,992,226],{}," provider reads one JSON document:",[261,995,999],{"className":996,"code":997,"language":998,"meta":266,"style":266},"language-json shiki shiki-themes github-light github-light github-dark","{\n  \"flags\": {\n    \"maintenance\": { \"value\": false },\n    \"newPricing\": {\n      \"value\": false,\n      \"rules\": [{ \"when\": { \"role\": [\"staff\", \"superadmin\"] }, \"value\": true }],\n      \"rollout\": { \"percent\": 10, \"value\": true }\n    }\n  }\n}\n","json",[189,1000,1001,1006,1013,1029,1036,1047,1085,1110,1115,1119],{"__ignoreMap":266},[270,1002,1003],{"class":272,"line":12},[270,1004,1005],{"class":279},"{\n",[270,1007,1008,1011],{"class":272,"line":17},[270,1009,1010],{"class":291},"  \"flags\"",[270,1012,280],{"class":279},[270,1014,1015,1018,1020,1023,1025,1027],{"class":272,"line":22},[270,1016,1017],{"class":291},"    \"maintenance\"",[270,1019,367],{"class":279},[270,1021,1022],{"class":291},"\"value\"",[270,1024,288],{"class":279},[270,1026,387],{"class":291},[270,1028,390],{"class":279},[270,1030,1031,1034],{"class":272,"line":27},[270,1032,1033],{"class":291},"    \"newPricing\"",[270,1035,280],{"class":279},[270,1037,1038,1041,1043,1045],{"class":272,"line":32},[270,1039,1040],{"class":291},"      \"value\"",[270,1042,288],{"class":279},[270,1044,387],{"class":291},[270,1046,295],{"class":279},[270,1048,1049,1052,1055,1058,1060,1063,1065,1068,1070,1073,1076,1078,1080,1082],{"class":272,"line":37},[270,1050,1051],{"class":291},"      \"rules\"",[270,1053,1054],{"class":279},": [{ ",[270,1056,1057],{"class":291},"\"when\"",[270,1059,367],{"class":279},[270,1061,1062],{"class":291},"\"role\"",[270,1064,303],{"class":279},[270,1066,1067],{"class":306},"\"staff\"",[270,1069,378],{"class":279},[270,1071,1072],{"class":306},"\"superadmin\"",[270,1074,1075],{"class":279},"] }, ",[270,1077,1022],{"class":291},[270,1079,288],{"class":279},[270,1081,292],{"class":291},[270,1083,1084],{"class":279}," }],\n",[270,1086,1087,1090,1092,1095,1097,1100,1102,1104,1106,1108],{"class":272,"line":46},[270,1088,1089],{"class":291},"      \"rollout\"",[270,1091,367],{"class":279},[270,1093,1094],{"class":291},"\"percent\"",[270,1096,288],{"class":279},[270,1098,1099],{"class":291},"10",[270,1101,378],{"class":279},[270,1103,1022],{"class":291},[270,1105,288],{"class":279},[270,1107,292],{"class":291},[270,1109,707],{"class":279},[270,1111,1112],{"class":272,"line":51},[270,1113,1114],{"class":279},"    }\n",[270,1116,1117],{"class":272,"line":56},[270,1118,690],{"class":279},[270,1120,1121],{"class":272,"line":64},[270,1122,1123],{"class":279},"}\n",[178,1125,1126],{},"For each flag it checks, in order:",[207,1128,1129,1143,1155,1159],{},[210,1130,1131,1134,1135,1138,1139,1142],{},[189,1132,1133],{},"rules",", top to bottom. The first rule whose every ",[189,1136,1137],{},"when"," condition matches\nreturns its ",[189,1140,1141],{},"value",". An array matches any of its entries, a string matches\nexactly.",[210,1144,1145,1148,1149,1152,1153,878],{},[189,1146,1147],{},"rollout",". A subject whose bucket, 0 to 99, is below ",[189,1150,1151],{},"percent"," gets the\nrollout's ",[189,1154,1141],{},[210,1156,1157,878],{},[189,1158,1141],{},[210,1160,1161],{},"Nothing: the next provider answers, then the declared default.",[178,1163,1164,1165,1168,1169,1172,1173,455,1176,1179,1180,1182,1183,627,1186,378,1189,378,1192,1195,1196,1198],{},"A condition can name ",[189,1166,1167],{},"locale"," (the locale code, such as ",[189,1170,1171],{},"sr",") for any visitor,\nand ",[189,1174,1175],{},"role",[189,1177,1178],{},"userId"," for a signed-in one. ",[189,1181,1175],{}," takes the ids in\n",[189,1184,1185],{},"access.rolePermissions",[189,1187,1188],{},"user",[189,1190,1191],{},"staff",[189,1193,1194],{},"superadmin","), and ",[189,1197,1178],{}," is the\naccount id as a string. A condition on something the subject doesn't have never\nmatches.",[178,1200,1201,1202,1205,1206,1209,1210,1212],{},"The subject is ",[189,1203,1204],{},"user:\u003Cid>"," when signed in. Anyone else is ",[189,1207,1208],{},"visitor:\u003Cid>",", the\ncookieless visitor id analytics uses (",[182,1211,130],{"href":129},"). The bucket\nhashes the flag id with the subject, so each flag splits visitors differently,\nand a signed-in user keeps their bucket across deploys.",[178,1214,1215,1216,1219,1220,1223,1224,1227],{},"Upload the document to KV with Wrangler. ",[189,1217,1218],{},"--local"," writes to the simulator\n",[189,1221,1222],{},"pnpm dev"," reads, ",[189,1225,1226],{},"--remote"," to the deployed namespace:",[261,1229,1233],{"className":1230,"code":1231,"language":1232,"meta":266,"style":266},"language-bash shiki shiki-themes github-light github-light github-dark","pnpm exec wrangler kv key put flags.json --path flags.json --binding CACHE --remote\n","bash",[189,1234,1235],{"__ignoreMap":266},[270,1236,1237,1240,1243,1246,1249,1252,1255,1258,1261,1263,1266,1269],{"class":272,"line":12},[270,1238,1239],{"class":275},"pnpm",[270,1241,1242],{"class":306}," exec",[270,1244,1245],{"class":306}," wrangler",[270,1247,1248],{"class":306}," kv",[270,1250,1251],{"class":306}," key",[270,1253,1254],{"class":306}," put",[270,1256,1257],{"class":306}," flags.json",[270,1259,1260],{"class":291}," --path",[270,1262,1257],{"class":306},[270,1264,1265],{"class":291}," --binding",[270,1267,1268],{"class":306}," CACHE",[270,1270,1271],{"class":291}," --remote\n",[178,1273,1274],{},"A KV write can take up to a minute to reach every Cloudflare location. The\ndocument is read once per request.",[178,1276,1277,1278,1281,1282,1285,1286,1289,1290,1293,1294,1296],{},"To keep it in R2 instead, set ",[189,1279,1280],{},"driver: 'r2'",". The driver reads ",[189,1283,1284],{},"flags.key"," from\nthe bucket of the storage purpose that ",[189,1287,1288],{},"flags.purpose"," names, ",[189,1291,1292],{},"AVATARS"," as\nshipped (",[182,1295,101],{"href":100},"):",[261,1298,1300],{"className":1230,"code":1299,"language":1232,"meta":266,"style":266},"pnpm exec wrangler r2 object put nuxter-avatars\u002Fflags.json --file flags.json --remote\n",[189,1301,1302],{"__ignoreMap":266},[270,1303,1304,1306,1308,1310,1313,1316,1318,1321,1324,1326],{"class":272,"line":12},[270,1305,1239],{"class":275},[270,1307,1242],{"class":306},[270,1309,1245],{"class":306},[270,1311,1312],{"class":306}," r2",[270,1314,1315],{"class":306}," object",[270,1317,1254],{"class":306},[270,1319,1320],{"class":306}," nuxter-avatars\u002Fflags.json",[270,1322,1323],{"class":291}," --file",[270,1325,1257],{"class":306},[270,1327,1271],{"class":291},[202,1329,1331],{"id":1330},"add-a-provider","Add a provider",[178,1333,1334,1335,1338,1339,1342,1343,1345,1346,259],{},"A hosted flag service plugs in as one file. Create\n",[189,1336,1337],{},"server\u002Fflags\u002Fadapters\u002Fcustom\u002F\u003Cid>.ts",", default-export a ",[189,1340,1341],{},"FlagAdapter",", and add\nthe id to ",[189,1344,222],{},". The file name is the id, so this is\n",[189,1347,1348],{},"server\u002Fflags\u002Fadapters\u002Fcustom\u002Fremote.ts",[261,1350,1352],{"className":263,"code":1351,"language":265,"meta":266,"style":266},"import type { FlagAdapter } from '#server\u002Fflags\u002Ftypes'\n\nconst remote: FlagAdapter = {\n  id: 'remote',\n\n  async evaluate(key, _fallback, subject, { credentials }) {\n    try {\n      const answer = await $fetch\u003C{ value?: boolean | string | number }>(\n        `https:\u002F\u002Fflags.example.com\u002Fv1\u002Fflags\u002F${key}`,\n        {\n          query: { subject: subject.key, ...subject.attributes },\n          headers: { authorization: `Bearer ${String(credentials.apiKey)}` },\n        },\n      )\n\n      return answer.value\n    } catch {\n      return undefined\n    }\n  },\n}\n\nexport default remote\n",[189,1353,1354,1369,1373,1389,1399,1403,1435,1442,1482,1494,1499,1510,1537,1542,1547,1551,1559,1569,1576,1580,1584,1588,1592],{"__ignoreMap":266},[270,1355,1356,1358,1361,1364,1366],{"class":272,"line":12},[270,1357,594],{"class":381},[270,1359,1360],{"class":381}," type",[270,1362,1363],{"class":279}," { FlagAdapter } ",[270,1365,600],{"class":381},[270,1367,1368],{"class":306}," '#server\u002Fflags\u002Ftypes'\n",[270,1370,1371],{"class":272,"line":17},[270,1372,352],{"emptyLinePlaceholder":351},[270,1374,1375,1377,1380,1382,1385,1387],{"class":272,"line":22},[270,1376,763],{"class":381},[270,1378,1379],{"class":291}," remote",[270,1381,259],{"class":381},[270,1383,1384],{"class":275}," FlagAdapter",[270,1386,769],{"class":381},[270,1388,640],{"class":279},[270,1390,1391,1394,1397],{"class":272,"line":27},[270,1392,1393],{"class":279},"  id: ",[270,1395,1396],{"class":306},"'remote'",[270,1398,295],{"class":279},[270,1400,1401],{"class":272,"line":32},[270,1402,352],{"emptyLinePlaceholder":351},[270,1404,1405,1408,1411,1413,1416,1418,1421,1423,1426,1429,1432],{"class":272,"line":37},[270,1406,1407],{"class":381},"  async",[270,1409,1410],{"class":275}," evaluate",[270,1412,621],{"class":279},[270,1414,1415],{"class":630},"key",[270,1417,378],{"class":279},[270,1419,1420],{"class":630},"_fallback",[270,1422,378],{"class":279},[270,1424,1425],{"class":630},"subject",[270,1427,1428],{"class":279},", { ",[270,1430,1431],{"class":630},"credentials",[270,1433,1434],{"class":279}," }) {\n",[270,1436,1437,1440],{"class":272,"line":46},[270,1438,1439],{"class":381},"    try",[270,1441,640],{"class":279},[270,1443,1444,1447,1450,1452,1454,1457,1460,1462,1465,1468,1471,1474,1476,1479],{"class":272,"line":51},[270,1445,1446],{"class":381},"      const",[270,1448,1449],{"class":291}," answer",[270,1451,769],{"class":381},[270,1453,974],{"class":381},[270,1455,1456],{"class":275}," $fetch",[270,1458,1459],{"class":279},"\u003C{ ",[270,1461,1141],{"class":630},[270,1463,1464],{"class":381},"?:",[270,1466,1467],{"class":291}," boolean",[270,1469,1470],{"class":381}," |",[270,1472,1473],{"class":291}," string",[270,1475,1470],{"class":381},[270,1477,1478],{"class":291}," number",[270,1480,1481],{"class":279}," }>(\n",[270,1483,1484,1487,1489,1492],{"class":272,"line":56},[270,1485,1486],{"class":306},"        `https:\u002F\u002Fflags.example.com\u002Fv1\u002Fflags\u002F${",[270,1488,1415],{"class":279},[270,1490,1491],{"class":306},"}`",[270,1493,295],{"class":279},[270,1495,1496],{"class":272,"line":64},[270,1497,1498],{"class":279},"        {\n",[270,1500,1501,1504,1507],{"class":272,"line":73},[270,1502,1503],{"class":279},"          query: { subject: subject.key, ",[270,1505,1506],{"class":381},"...",[270,1508,1509],{"class":279},"subject.attributes },\n",[270,1511,1512,1515,1518,1521,1523,1525,1527,1530,1533,1535],{"class":272,"line":78},[270,1513,1514],{"class":279},"          headers: { authorization: ",[270,1516,1517],{"class":306},"`Bearer ${",[270,1519,1520],{"class":275},"String",[270,1522,621],{"class":306},[270,1524,1431],{"class":279},[270,1526,878],{"class":306},[270,1528,1529],{"class":279},"apiKey",[270,1531,1532],{"class":306},")",[270,1534,1491],{"class":306},[270,1536,390],{"class":279},[270,1538,1539],{"class":272,"line":83},[270,1540,1541],{"class":279},"        },\n",[270,1543,1544],{"class":272,"line":88},[270,1545,1546],{"class":279},"      )\n",[270,1548,1549],{"class":272,"line":93},[270,1550,352],{"emptyLinePlaceholder":351},[270,1552,1553,1556],{"class":272,"line":98},[270,1554,1555],{"class":381},"      return",[270,1557,1558],{"class":279}," answer.value\n",[270,1560,1561,1564,1567],{"class":272,"line":103},[270,1562,1563],{"class":279},"    } ",[270,1565,1566],{"class":381},"catch",[270,1568,640],{"class":279},[270,1570,1571,1573],{"class":272,"line":108},[270,1572,1555],{"class":381},[270,1574,1575],{"class":291}," undefined\n",[270,1577,1578],{"class":272,"line":113},[270,1579,1114],{"class":279},[270,1581,1582],{"class":272,"line":122},[270,1583,418],{"class":279},[270,1585,1586],{"class":272,"line":127},[270,1587,1123],{"class":279},[270,1589,1590],{"class":272,"line":132},[270,1591,352],{"emptyLinePlaceholder":351},[270,1593,1594,1596,1598],{"class":272,"line":137},[270,1595,612],{"class":381},[270,1597,615],{"class":381},[270,1599,1600],{"class":279}," remote\n",[261,1602,1604],{"className":263,"code":1603,"language":265,"meta":266,"style":266},"flags: {\n  enabled: true,\n  providers: ['remote', 'selfhosted'],\n  adapters: { remote: { credentials: ['apiKey'] } },\n},\n",[189,1605,1606,1612,1622,1636,1658],{"__ignoreMap":266},[270,1607,1608,1610],{"class":272,"line":12},[270,1609,276],{"class":275},[270,1611,280],{"class":279},[270,1613,1614,1616,1618,1620],{"class":272,"line":17},[270,1615,285],{"class":275},[270,1617,288],{"class":279},[270,1619,292],{"class":291},[270,1621,295],{"class":279},[270,1623,1624,1626,1628,1630,1632,1634],{"class":272,"line":22},[270,1625,300],{"class":275},[270,1627,303],{"class":279},[270,1629,1396],{"class":306},[270,1631,378],{"class":279},[270,1633,307],{"class":306},[270,1635,310],{"class":279},[270,1637,1638,1641,1643,1646,1648,1650,1652,1655],{"class":272,"line":27},[270,1639,1640],{"class":275},"  adapters",[270,1642,367],{"class":279},[270,1644,1645],{"class":275},"remote",[270,1647,367],{"class":279},[270,1649,1431],{"class":275},[270,1651,303],{"class":279},[270,1653,1654],{"class":306},"'apiKey'",[270,1656,1657],{"class":279},"] } },\n",[270,1659,1660],{"class":272,"line":32},[270,1661,423],{"class":279},[178,1663,1664,1665,1667,1668,1671,1672,1675,1676,1678,1679,1682,1683,1686,1687,1689],{},"Each key in ",[189,1666,1431],{}," becomes one variable, here\n",[189,1669,1670],{},"NUXT_FLAGS_REMOTE_API_KEY",", and reaches the adapter as\n",[189,1673,1674],{},"context.credentials.apiKey",". Without a ",[189,1677,1431],{}," list, the whole slot is\none JSON variable, ",[189,1680,1681],{},"NUXT_FLAGS_REMOTE",". Answer ",[189,1684,1685],{},"undefined"," for a flag the\nservice doesn't know, and don't throw. Ids must be one lowercase word. Restart\n",[189,1688,1222],{}," after adding the file so the build registers the id.",[202,1691,20],{"id":1692},"configuration",[1694,1695,1696,1715],"table",{},[1697,1698,1699],"thead",{},[1700,1701,1702,1706,1709,1712],"tr",{},[1703,1704,1705],"th",{},"Key",[1703,1707,1708],{},"Where",[1703,1710,1711],{},"Default",[1703,1713,1714],{},"What it does",[1716,1717,1718,1742,1760,1786,1803,1824,1844,1864,1884],"tbody",{},[1700,1719,1720,1726,1730,1734],{},[1721,1722,1723],"td",{},[189,1724,1725],{},"flags.enabled",[1721,1727,1728],{},[189,1729,191],{},[1721,1731,1732],{},[189,1733,387],{},[1721,1735,1736,1737,874,1739,1741],{},"Off, ",[189,1738,195],{},[189,1740,199],{}," return the declared defaults.",[1700,1743,1744,1748,1752,1757],{},[1721,1745,1746],{},[189,1747,222],{},[1721,1749,1750],{},[189,1751,191],{},[1721,1753,1754],{},[189,1755,1756],{},"['selfhosted']",[1721,1758,1759],{},"Providers to bundle, asked in this order.",[1700,1761,1762,1767,1771,1775],{},[1721,1763,1764],{},[189,1765,1766],{},"flags.driver",[1721,1768,1769],{},[189,1770,191],{},[1721,1772,1773],{},[189,1774,320],{},[1721,1776,1777,1778,1780,1781,455,1783,878],{},"Where ",[189,1779,226],{}," reads its document: ",[189,1782,320],{},[189,1784,1785],{},"'r2'",[1700,1787,1788,1792,1796,1800],{},[1721,1789,1790],{},[189,1791,1284],{},[1721,1793,1794],{},[189,1795,191],{},[1721,1797,1798],{},[189,1799,332],{},[1721,1801,1802],{},"The KV key or R2 object key of the document.",[1700,1804,1805,1809,1813,1817],{},[1721,1806,1807],{},[189,1808,1288],{},[1721,1810,1811],{},[189,1812,191],{},[1721,1814,1815],{},[189,1816,344],{},[1721,1818,1819,1820,1823],{},"The storage purpose whose bucket the ",[189,1821,1822],{},"r2"," driver reads. Must exist.",[1700,1825,1826,1830,1834,1841],{},[1721,1827,1828],{},[189,1829,215],{},[1721,1831,1832],{},[189,1833,191],{},[1721,1835,1836,378,1839],{},[189,1837,1838],{},"maintenance",[189,1840,565],{},[1721,1842,1843],{},"Every flag, with its type and default.",[1700,1845,1846,1851,1855,1858],{},[1721,1847,1848],{},[189,1849,1850],{},"flags.adapters",[1721,1852,1853],{},[189,1854,191],{},[1721,1856,1857],{},"none",[1721,1859,1860,1861,878],{},"Credential keys of a provider you added in ",[189,1862,1863],{},"server\u002Fflags\u002Fadapters\u002Fcustom\u002F",[1700,1865,1866,1870,1875,1878],{},[1721,1867,1868],{},[189,1869,437],{},[1721,1871,1872],{},[189,1873,1874],{},"wrangler.jsonc",[1721,1876,1877],{},"KV namespace",[1721,1879,1880,1881,1883],{},"The namespace the ",[189,1882,429],{}," driver reads.",[1700,1885,1886,1891,1894,1897],{},[1721,1887,1888],{},[189,1889,1890],{},"NUXT_ANALYTICS_SALT",[1721,1892,1893],{},"environment",[1721,1895,1896],{},"the session password",[1721,1898,1899],{},"Seeds the anonymous visitor id that rollouts bucket on.",[202,1901,1903],{"id":1902},"good-to-know","Good to know",[1905,1906,1907,1916,1923,1933,1936],"ul",{},[210,1908,1909,1910,1912,1913,1915],{},"Values are fixed for a full page load. Sign-in finishes without a reload, so\nafter it ",[189,1911,241],{}," returns defaults until the next full load, while ",[189,1914,195],{},"\non the server already sees the user. Sign-out and impersonation reload the\npage.",[210,1917,1918,1919,1922],{},"Anonymous visitors get a new id every UTC day and land in new buckets. A\nrollout that has to hold across days belongs behind sign-in. Requests with no\nIP and no user agent all share the subject ",[189,1920,1921],{},"visitor:anonymous",", one bucket.",[210,1924,1925,1926,1929,1930,1932],{},"A document value counts only when its type matches the declaration: ",[189,1927,1928],{},"\"true\"","\non a boolean flag is ignored. A ",[189,1931,1151],{}," that isn't a number counts as 0.",[210,1934,1935],{},"The middleware evaluates every declared flag on every server-rendered page.\nA provider that calls a remote API makes one call per flag per page.",[210,1937,1938,1939,1942],{},"Flag calls can stay in your code with ",[189,1940,1941],{},"flags.enabled: false",". They return the\ndefaults.",[1944,1945,1946],"style",{},"html pre.shiki code .snPdu, html code.shiki .snPdu{--shiki-light:#6F42C1;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sxrX7, html code.shiki .sxrX7{--shiki-light:#24292E;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sBjJW, html code.shiki .sBjJW{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sIIMD, html code.shiki .sIIMD{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s8jYJ, html code.shiki .s8jYJ{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583}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 .sP4rz, html code.shiki .sP4rz{--shiki-light:#E36209;--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sovSZ, html code.shiki .sovSZ{--shiki-light:#22863A;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":266,"searchDepth":17,"depth":17,"links":1948},[1949,1950,1951,1952,1955,1956,1957,1958],{"id":204,"depth":17,"text":205},{"id":251,"depth":17,"text":252},{"id":441,"depth":17,"text":442},{"id":573,"depth":17,"text":574,"children":1953},[1954],{"id":845,"depth":22,"text":846},{"id":987,"depth":17,"text":988},{"id":1330,"depth":17,"text":1331},{"id":1692,"depth":17,"text":20},{"id":1902,"depth":17,"text":1903},[1960,1961],{"path":100,"title":101,"description":102,"navTitle":101,"group":67,"order":103},{"path":110,"title":111,"description":112,"navTitle":111,"group":67,"order":113}]