[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"docs-navigation":3,"docs-page-testing":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":3052,"linksToKit":638},{"path":162,"title":163,"description":164,"navTitle":163,"group":159,"seo":168,"body":170},{"title":169,"description":164},"Testing Nuxt: Vitest, components and Playwright",{"type":171,"value":172,"toc":3041},"minimark",[173,177,208,310,320,325,408,411,424,489,542,563,668,672,682,1343,1396,1400,1407,1693,1765,1769,1780,2002,2008,2154,2160,2167,2230,2254,2276,2280,2311,2336,2362,2631,2641,2679,2683,2747,2762,2765,2961,2965,3037],[174,175,163],"h1",{"id":176},"testing",[178,179,180,181,185,186,189,190,193,194,197,198,201,202,207],"p",{},"The kit has three suites. ",[182,183,184],"code",{},"unit"," runs server and shared code in plain Node,\n",[182,187,188],{},"nuxt"," mounts components inside a real Nuxt app, and ",[182,191,192],{},"e2e"," drives a browser\nthrough ",[182,195,196],{},"nuxt dev"," with Playwright. You work here when you test your own code.\nChanging ",[182,199,200],{},"starter.config.ts"," fails none of the kit's own suites:\n",[203,204,206],"a",{"href":205},"#your-config-and-the-kits-suites","Your config and the kit's suites"," says why.",[209,210,211,224],"table",{},[212,213,214],"thead",{},[215,216,217,221],"tr",{},[218,219,220],"th",{},"Command",[218,222,223],{},"What it runs",[225,226,227,242,254,266,276,286,300],"tbody",{},[215,228,229,235],{},[230,231,232],"td",{},[182,233,234],{},"pnpm test --run",[230,236,237,239,240],{},[182,238,184],{}," and ",[182,241,188],{},[215,243,244,249],{},[230,245,246],{},[182,247,248],{},"pnpm test:unit --run",[230,250,251,253],{},[182,252,184],{}," only, in seconds",[215,255,256,261],{},[230,257,258],{},[182,259,260],{},"pnpm test:nuxt --run",[230,262,263,265],{},[182,264,188],{}," only",[215,267,268,273],{},[230,269,270],{},[182,271,272],{},"pnpm test:coverage",[230,274,275],{},"Both, with v8 coverage",[215,277,278,283],{},[230,279,280],{},[182,281,282],{},"pnpm test:e2e",[230,284,285],{},"Every Playwright spec, about three minutes",[215,287,288,293],{},[230,289,290],{},[182,291,292],{},"pnpm test:e2e:smoke",[230,294,295,296,299],{},"The specs tagged ",[182,297,298],{},"@smoke",": sign-in and payments paths",[215,301,302,307],{},[230,303,304],{},[182,305,306],{},"pnpm test:e2e:ui",[230,308,309],{},"Playwright's UI mode",[178,311,312,315,316,319],{},[182,313,314],{},"pnpm test"," without ",[182,317,318],{},"--run"," starts Vitest in watch mode.",[321,322,324],"h2",{"id":323},"how-it-works","How it works",[326,327,328,375,390,401],"ul",{},[329,330,331,333,334,337,338,341,342,351,352,355,356,359,360,239,363,366,367,370,371,374],"li",{},[182,332,184],{}," runs ",[182,335,336],{},"test\u002Funit\u002F*.test.ts"," and your suites in ",[182,339,340],{},"test\u002Funit\u002Fcustom\u002F",", in\nNode.\n",[203,343,348],{"href":344,"rel":345,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Fvitest.config.ts",[346],"nofollow","Licensees only",[182,349,350],{},"vitest.config.ts"," gives it the ",[182,353,354],{},"#server",", ",[182,357,358],{},"#shared",",\n",[182,361,362],{},"~",[182,364,365],{},"~~"," aliases, and points every build-generated ",[182,368,369],{},"#*-registry"," module\nat a stub in ",[182,372,373],{},"test\u002Funit\u002F"," with no adapters, so dispatch does nothing unless a\ntest supplies an adapter. Its global setup migrates one PGlite database per\nrun.",[329,376,377,333,379,337,382,385,386,389],{},[182,378,188],{},[182,380,381],{},"test\u002Fnuxt\u002F*.test.ts",[182,383,384],{},"test\u002Fnuxt\u002Fcustom\u002F","\nthrough ",[182,387,388],{},"@nuxt\u002Ftest-utils"," in happy-dom.\nThe first mount in a file boots the app, so tests and hooks get 20 seconds.",[329,391,392,333,394,397,398,400],{},[182,393,192],{},[182,395,396],{},"test\u002Fe2e\u002F*.spec.ts"," against ",[182,399,196],{}," in Chromium, one worker\nat a time.",[329,402,403,404,407],{},"A passing test prints nothing. Console output shows only for tests that fail\n(",[182,405,406],{},"silent: 'passed-only'",").",[321,409,206],{"id":410},"your-config-and-the-kits-suites",[178,412,413,414,416,417,419,420,423],{},"The kit's suites test the configuration the kit ships, whatever your\n",[182,415,200],{}," says, so turning payments off or taking a permission from a\nrole fails none of them. In both vitest projects, every import of\n",[182,418,200],{}," resolves to ",[182,421,422],{},"test\u002Fhelpers\u002Fsuite-starter-config.ts",", which\nanswers according to the test file that is running:",[209,425,426,436],{},[212,427,428],{},[215,429,430,433],{},[218,431,432],{},"Test file",[218,434,435],{},"Reads",[225,437,438,458,476],{},[215,439,440,449],{},[230,441,442,443,445,446],{},"A kit suite at the top of ",[182,444,373],{}," or ",[182,447,448],{},"test\u002Fnuxt\u002F",[230,450,451,454,455],{},[182,452,453],{},"test\u002Fshipped\u002Fstarter.config.ts",", the kit's config byte for byte, with your ",[182,456,457],{},"i18n",[215,459,460,471],{},[230,461,462,463,466,467,470],{},"A kit suite named in ",[182,464,465],{},"LIVE_CONFIG_SUITES"," (",[182,468,469],{},"test\u002Fhelpers\u002Flive-config-suites.ts",")",[230,472,473,474],{},"Your ",[182,475,200],{},[215,477,478,485],{},[230,479,480,481,445,483],{},"Yours, anywhere under ",[182,482,340],{},[182,484,384],{},[230,486,473,487],{},[182,488,200],{},[326,490,491,502,507,518,530],{},[329,492,493,494,239,496,498,499,501],{},"Put your own suites under ",[182,495,340],{},[182,497,384],{},", which\nthe template never writes. At the top of ",[182,500,373],{}," a suite tests your code\nagainst the kit's config instead of yours.",[329,503,504,506],{},[182,505,457],{}," always comes from your config, because your locale files and the\nbuilt app follow it.",[329,508,509,510,513,514,517],{},"The kit suites that read your config check it against your own files: that\neach locale file has copy for every plan, role and organization level you\ndeclare, and that ",[182,511,512],{},"nuxt.config.ts"," builds with your ",[182,515,516],{},"wrangler.jsonc",".",[329,519,520,521,523,524,526,527,517],{},"In the ",[182,522,188],{}," project only what your code imports is pinned. Runtime config,\nthe ",[182,525,369],{}," modules and the i18n setup are built from your config, and\nso are the exclusions under ",[203,528,20],{"href":529},"#configuration",[329,531,532,355,535,239,538,541],{},[182,533,534],{},"withStarterConfig",[182,536,537],{},"vi.mock('~~\u002Fstarter.config', …)",[182,539,540],{},"vi.importActual","\nwork as before, on whichever config the file reads.",[178,543,544,547,548,551,552,554,555,558,559,562],{},[182,545,546],{},"test\u002Funit\u002Fschema-cascades.test.ts"," sweeps every column that references\n",[182,549,550],{},"users",", in your tables too. Erasing an account deletes its ",[182,553,550],{}," row, so a\nkey that neither cascades nor sets null makes the erasure fail. A column you\nset null on purpose, or one that keeps no key so a record outlives the account,\ngoes in ",[182,556,557],{},"test\u002Fhelpers\u002Fcustom\u002Fschema-cascades.ts",", keyed ",[182,560,561],{},"table.column",":",[564,565,570],"pre",{"className":566,"code":567,"language":568,"meta":569,"style":569},"language-ts shiki shiki-themes github-light github-light github-dark","export const USER_COLUMNS_WITHOUT_FOREIGN_KEY: Record\u003Cstring, string> = {\n  'admin_audit.actor_user_id':\n    'the staff audit trail outlives the staff account, as activity_log does',\n}\n\nexport const USER_COLUMNS_SET_NULL: string[] = ['creators.user_id']\n","ts","",[182,571,572,613,622,629,634,640],{"__ignoreMap":569},[573,574,576,580,583,587,589,593,597,600,602,604,607,610],"span",{"class":575,"line":12},"line",[573,577,579],{"class":578},"s8jYJ","export",[573,581,582],{"class":578}," const",[573,584,586],{"class":585},"sBjJW"," USER_COLUMNS_WITHOUT_FOREIGN_KEY",[573,588,562],{"class":578},[573,590,592],{"class":591},"snPdu"," Record",[573,594,596],{"class":595},"sxrX7","\u003C",[573,598,599],{"class":585},"string",[573,601,355],{"class":595},[573,603,599],{"class":585},[573,605,606],{"class":595},"> ",[573,608,609],{"class":578},"=",[573,611,612],{"class":595}," {\n",[573,614,615,619],{"class":575,"line":17},[573,616,618],{"class":617},"sIIMD","  'admin_audit.actor_user_id'",[573,620,621],{"class":595},":\n",[573,623,624,627],{"class":575,"line":22},[573,625,626],{"class":617},"    'the staff audit trail outlives the staff account, as activity_log does'",[573,628,359],{"class":595},[573,630,631],{"class":575,"line":27},[573,632,633],{"class":595},"}\n",[573,635,636],{"class":575,"line":32},[573,637,639],{"emptyLinePlaceholder":638},true,"\n",[573,641,642,644,646,649,651,654,657,659,662,665],{"class":575,"line":37},[573,643,579],{"class":578},[573,645,582],{"class":578},[573,647,648],{"class":585}," USER_COLUMNS_SET_NULL",[573,650,562],{"class":578},[573,652,653],{"class":585}," string",[573,655,656],{"class":595},"[] ",[573,658,609],{"class":578},[573,660,661],{"class":595}," [",[573,663,664],{"class":617},"'creators.user_id'",[573,666,667],{"class":595},"]\n",[321,669,671],{"id":670},"test-a-server-route","Test a server route",[178,673,674,675,562],{},"Import the route's default export and call it with a fake event. The database\nis real Postgres (PGlite), migrated and emptied before every test. This is a\ntrimmed ",[203,676,679],{"href":677,"rel":678,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Ftest\u002Funit\u002Fadmin-ban.test.ts",[346],[182,680,681],{},"test\u002Funit\u002Fadmin-ban.test.ts",[564,683,685],{"className":566,"code":684,"language":568,"meta":569,"style":569},"import { eq } from 'drizzle-orm'\nimport { afterEach, beforeEach, expect, it, vi } from 'vitest'\nimport { z } from 'zod'\n\nimport * as schema from '#server\u002Fdatabase\u002Fschema'\n\nimport { stubDatabaseGlobals } from '..\u002Fhelpers\u002Fdatabase-globals'\nimport { fakeH3Event } from '..\u002Fhelpers\u002Ffake-h3-event'\nimport { useTestDatabase } from '..\u002Fhelpers\u002Ftest-database'\n\nvi.stubGlobal('defineEventHandler', (handler: unknown) => handler)\nvi.stubGlobal('z', z)\n\nconst { default: banRoute } = await import('#server\u002Fapi\u002Fadmin\u002Fusers\u002Fban.post')\n\nconst database = useTestDatabase()\n\nbeforeEach(() => {\n  stubDatabaseGlobals(database.orm)\n  vi.stubGlobal('requirePermission', () => Promise.resolve({ user: { id: 1 } }))\n  vi.stubGlobal(\n    'readValidatedBody',\n    (event: { body: unknown }, parse: (input: unknown) => unknown) =>\n      Promise.resolve(parse(event.body)),\n  )\n  vi.stubGlobal('recordActivity', vi.fn())\n})\n\nafterEach(() => {\n  vi.unstubAllGlobals()\n})\n\nit('suspends a customer', async () => {\n  const [, customer] = await database.orm\n    .insert(schema.users)\n    .values([\n      { email: 'admin@example.test', role: 'superadmin' },\n      { email: 'customer@example.test', role: 'user' },\n    ])\n    .returning({ id: schema.users.id })\n\n  await banRoute(fakeH3Event({ body: { userId: customer!.id, banned: true } }))\n\n  const [row] = await database.orm\n    .select({ bannedAt: schema.users.bannedAt })\n    .from(schema.users)\n    .where(eq(schema.users.id, customer!.id))\n\n  expect(row?.bannedAt).not.toBeNull()\n})\n",[182,686,687,701,713,725,729,747,751,763,775,787,791,826,840,844,880,884,900,904,916,924,958,967,974,1021,1037,1042,1062,1067,1072,1084,1094,1099,1104,1127,1149,1161,1172,1190,1205,1211,1222,1227,1255,1260,1278,1289,1298,1319,1324,1338],{"__ignoreMap":569},[573,688,689,692,695,698],{"class":575,"line":12},[573,690,691],{"class":578},"import",[573,693,694],{"class":595}," { eq } ",[573,696,697],{"class":578},"from",[573,699,700],{"class":617}," 'drizzle-orm'\n",[573,702,703,705,708,710],{"class":575,"line":17},[573,704,691],{"class":578},[573,706,707],{"class":595}," { afterEach, beforeEach, expect, it, vi } ",[573,709,697],{"class":578},[573,711,712],{"class":617}," 'vitest'\n",[573,714,715,717,720,722],{"class":575,"line":22},[573,716,691],{"class":578},[573,718,719],{"class":595}," { z } ",[573,721,697],{"class":578},[573,723,724],{"class":617}," 'zod'\n",[573,726,727],{"class":575,"line":27},[573,728,639],{"emptyLinePlaceholder":638},[573,730,731,733,736,739,742,744],{"class":575,"line":32},[573,732,691],{"class":578},[573,734,735],{"class":585}," *",[573,737,738],{"class":578}," as",[573,740,741],{"class":595}," schema ",[573,743,697],{"class":578},[573,745,746],{"class":617}," '#server\u002Fdatabase\u002Fschema'\n",[573,748,749],{"class":575,"line":37},[573,750,639],{"emptyLinePlaceholder":638},[573,752,753,755,758,760],{"class":575,"line":46},[573,754,691],{"class":578},[573,756,757],{"class":595}," { stubDatabaseGlobals } ",[573,759,697],{"class":578},[573,761,762],{"class":617}," '..\u002Fhelpers\u002Fdatabase-globals'\n",[573,764,765,767,770,772],{"class":575,"line":51},[573,766,691],{"class":578},[573,768,769],{"class":595}," { fakeH3Event } ",[573,771,697],{"class":578},[573,773,774],{"class":617}," '..\u002Fhelpers\u002Ffake-h3-event'\n",[573,776,777,779,782,784],{"class":575,"line":56},[573,778,691],{"class":578},[573,780,781],{"class":595}," { useTestDatabase } ",[573,783,697],{"class":578},[573,785,786],{"class":617}," '..\u002Fhelpers\u002Ftest-database'\n",[573,788,789],{"class":575,"line":64},[573,790,639],{"emptyLinePlaceholder":638},[573,792,793,796,799,802,805,808,812,814,817,820,823],{"class":575,"line":73},[573,794,795],{"class":595},"vi.",[573,797,798],{"class":591},"stubGlobal",[573,800,801],{"class":595},"(",[573,803,804],{"class":617},"'defineEventHandler'",[573,806,807],{"class":595},", (",[573,809,811],{"class":810},"sP4rz","handler",[573,813,562],{"class":578},[573,815,816],{"class":585}," unknown",[573,818,819],{"class":595},") ",[573,821,822],{"class":578},"=>",[573,824,825],{"class":595}," handler)\n",[573,827,828,830,832,834,837],{"class":575,"line":78},[573,829,795],{"class":595},[573,831,798],{"class":591},[573,833,801],{"class":595},[573,835,836],{"class":617},"'z'",[573,838,839],{"class":595},", z)\n",[573,841,842],{"class":575,"line":83},[573,843,639],{"emptyLinePlaceholder":638},[573,845,846,849,852,855,858,861,864,866,869,872,874,877],{"class":575,"line":88},[573,847,848],{"class":578},"const",[573,850,851],{"class":595}," { ",[573,853,854],{"class":810},"default",[573,856,857],{"class":595},": ",[573,859,860],{"class":585},"banRoute",[573,862,863],{"class":595}," } ",[573,865,609],{"class":578},[573,867,868],{"class":578}," await",[573,870,871],{"class":578}," import",[573,873,801],{"class":595},[573,875,876],{"class":617},"'#server\u002Fapi\u002Fadmin\u002Fusers\u002Fban.post'",[573,878,879],{"class":595},")\n",[573,881,882],{"class":575,"line":93},[573,883,639],{"emptyLinePlaceholder":638},[573,885,886,888,891,894,897],{"class":575,"line":98},[573,887,848],{"class":578},[573,889,890],{"class":585}," database",[573,892,893],{"class":578}," =",[573,895,896],{"class":591}," useTestDatabase",[573,898,899],{"class":595},"()\n",[573,901,902],{"class":575,"line":103},[573,903,639],{"emptyLinePlaceholder":638},[573,905,906,909,912,914],{"class":575,"line":108},[573,907,908],{"class":591},"beforeEach",[573,910,911],{"class":595},"(() ",[573,913,822],{"class":578},[573,915,612],{"class":595},[573,917,918,921],{"class":575,"line":113},[573,919,920],{"class":591},"  stubDatabaseGlobals",[573,922,923],{"class":595},"(database.orm)\n",[573,925,926,929,931,933,936,939,941,944,946,949,952,955],{"class":575,"line":122},[573,927,928],{"class":595},"  vi.",[573,930,798],{"class":591},[573,932,801],{"class":595},[573,934,935],{"class":617},"'requirePermission'",[573,937,938],{"class":595},", () ",[573,940,822],{"class":578},[573,942,943],{"class":585}," Promise",[573,945,517],{"class":595},[573,947,948],{"class":591},"resolve",[573,950,951],{"class":595},"({ user: { id: ",[573,953,954],{"class":585},"1",[573,956,957],{"class":595}," } }))\n",[573,959,960,962,964],{"class":575,"line":127},[573,961,928],{"class":595},[573,963,798],{"class":591},[573,965,966],{"class":595},"(\n",[573,968,969,972],{"class":575,"line":132},[573,970,971],{"class":617},"    'readValidatedBody'",[573,973,359],{"class":595},[573,975,976,979,982,984,986,989,991,993,996,999,1001,1003,1006,1008,1010,1012,1014,1016,1018],{"class":575,"line":137},[573,977,978],{"class":595},"    (",[573,980,981],{"class":810},"event",[573,983,562],{"class":578},[573,985,851],{"class":595},[573,987,988],{"class":810},"body",[573,990,562],{"class":578},[573,992,816],{"class":585},[573,994,995],{"class":595}," }, ",[573,997,998],{"class":591},"parse",[573,1000,562],{"class":578},[573,1002,466],{"class":595},[573,1004,1005],{"class":810},"input",[573,1007,562],{"class":578},[573,1009,816],{"class":585},[573,1011,819],{"class":595},[573,1013,822],{"class":578},[573,1015,816],{"class":585},[573,1017,819],{"class":595},[573,1019,1020],{"class":578},"=>\n",[573,1022,1023,1026,1028,1030,1032,1034],{"class":575,"line":146},[573,1024,1025],{"class":585},"      Promise",[573,1027,517],{"class":595},[573,1029,948],{"class":591},[573,1031,801],{"class":595},[573,1033,998],{"class":591},[573,1035,1036],{"class":595},"(event.body)),\n",[573,1038,1039],{"class":575,"line":151},[573,1040,1041],{"class":595},"  )\n",[573,1043,1044,1046,1048,1050,1053,1056,1059],{"class":575,"line":156},[573,1045,928],{"class":595},[573,1047,798],{"class":591},[573,1049,801],{"class":595},[573,1051,1052],{"class":617},"'recordActivity'",[573,1054,1055],{"class":595},", vi.",[573,1057,1058],{"class":591},"fn",[573,1060,1061],{"class":595},"())\n",[573,1063,1064],{"class":575,"line":165},[573,1065,1066],{"class":595},"})\n",[573,1068,1070],{"class":575,"line":1069},28,[573,1071,639],{"emptyLinePlaceholder":638},[573,1073,1075,1078,1080,1082],{"class":575,"line":1074},29,[573,1076,1077],{"class":591},"afterEach",[573,1079,911],{"class":595},[573,1081,822],{"class":578},[573,1083,612],{"class":595},[573,1085,1087,1089,1092],{"class":575,"line":1086},30,[573,1088,928],{"class":595},[573,1090,1091],{"class":591},"unstubAllGlobals",[573,1093,899],{"class":595},[573,1095,1097],{"class":575,"line":1096},31,[573,1098,1066],{"class":595},[573,1100,1102],{"class":575,"line":1101},32,[573,1103,639],{"emptyLinePlaceholder":638},[573,1105,1107,1110,1112,1115,1117,1120,1123,1125],{"class":575,"line":1106},33,[573,1108,1109],{"class":591},"it",[573,1111,801],{"class":595},[573,1113,1114],{"class":617},"'suspends a customer'",[573,1116,355],{"class":595},[573,1118,1119],{"class":578},"async",[573,1121,1122],{"class":595}," () ",[573,1124,822],{"class":578},[573,1126,612],{"class":595},[573,1128,1130,1133,1136,1139,1142,1144,1146],{"class":575,"line":1129},34,[573,1131,1132],{"class":578},"  const",[573,1134,1135],{"class":595}," [, ",[573,1137,1138],{"class":585},"customer",[573,1140,1141],{"class":595},"] ",[573,1143,609],{"class":578},[573,1145,868],{"class":578},[573,1147,1148],{"class":595}," database.orm\n",[573,1150,1152,1155,1158],{"class":575,"line":1151},35,[573,1153,1154],{"class":595},"    .",[573,1156,1157],{"class":591},"insert",[573,1159,1160],{"class":595},"(schema.users)\n",[573,1162,1164,1166,1169],{"class":575,"line":1163},36,[573,1165,1154],{"class":595},[573,1167,1168],{"class":591},"values",[573,1170,1171],{"class":595},"([\n",[573,1173,1175,1178,1181,1184,1187],{"class":575,"line":1174},37,[573,1176,1177],{"class":595},"      { email: ",[573,1179,1180],{"class":617},"'admin@example.test'",[573,1182,1183],{"class":595},", role: ",[573,1185,1186],{"class":617},"'superadmin'",[573,1188,1189],{"class":595}," },\n",[573,1191,1193,1195,1198,1200,1203],{"class":575,"line":1192},38,[573,1194,1177],{"class":595},[573,1196,1197],{"class":617},"'customer@example.test'",[573,1199,1183],{"class":595},[573,1201,1202],{"class":617},"'user'",[573,1204,1189],{"class":595},[573,1206,1208],{"class":575,"line":1207},39,[573,1209,1210],{"class":595},"    ])\n",[573,1212,1214,1216,1219],{"class":575,"line":1213},40,[573,1215,1154],{"class":595},[573,1217,1218],{"class":591},"returning",[573,1220,1221],{"class":595},"({ id: schema.users.id })\n",[573,1223,1225],{"class":575,"line":1224},41,[573,1226,639],{"emptyLinePlaceholder":638},[573,1228,1230,1233,1236,1238,1241,1244,1247,1250,1253],{"class":575,"line":1229},42,[573,1231,1232],{"class":578},"  await",[573,1234,1235],{"class":591}," banRoute",[573,1237,801],{"class":595},[573,1239,1240],{"class":591},"fakeH3Event",[573,1242,1243],{"class":595},"({ body: { userId: customer",[573,1245,1246],{"class":578},"!",[573,1248,1249],{"class":595},".id, banned: ",[573,1251,1252],{"class":585},"true",[573,1254,957],{"class":595},[573,1256,1258],{"class":575,"line":1257},43,[573,1259,639],{"emptyLinePlaceholder":638},[573,1261,1263,1265,1267,1270,1272,1274,1276],{"class":575,"line":1262},44,[573,1264,1132],{"class":578},[573,1266,661],{"class":595},[573,1268,1269],{"class":585},"row",[573,1271,1141],{"class":595},[573,1273,609],{"class":578},[573,1275,868],{"class":578},[573,1277,1148],{"class":595},[573,1279,1281,1283,1286],{"class":575,"line":1280},45,[573,1282,1154],{"class":595},[573,1284,1285],{"class":591},"select",[573,1287,1288],{"class":595},"({ bannedAt: schema.users.bannedAt })\n",[573,1290,1292,1294,1296],{"class":575,"line":1291},46,[573,1293,1154],{"class":595},[573,1295,697],{"class":591},[573,1297,1160],{"class":595},[573,1299,1301,1303,1306,1308,1311,1314,1316],{"class":575,"line":1300},47,[573,1302,1154],{"class":595},[573,1304,1305],{"class":591},"where",[573,1307,801],{"class":595},[573,1309,1310],{"class":591},"eq",[573,1312,1313],{"class":595},"(schema.users.id, customer",[573,1315,1246],{"class":578},[573,1317,1318],{"class":595},".id))\n",[573,1320,1322],{"class":575,"line":1321},48,[573,1323,639],{"emptyLinePlaceholder":638},[573,1325,1327,1330,1333,1336],{"class":575,"line":1326},49,[573,1328,1329],{"class":591},"  expect",[573,1331,1332],{"class":595},"(row?.bannedAt).not.",[573,1334,1335],{"class":591},"toBeNull",[573,1337,899],{"class":595},[573,1339,1341],{"class":575,"line":1340},50,[573,1342,1066],{"class":595},[326,1344,1345,1358,1380,1386],{},[329,1346,1347,1348,1350,1351,1354,1355,517],{},"Nuxt's auto-imports don't exist in the ",[182,1349,184],{}," project. Stub each one the\ncode reads with ",[182,1352,1353],{},"vi.stubGlobal",", and import the module under test after the\nstubs, with ",[182,1356,1357],{},"await import()",[329,1359,1360,1363,1364,355,1367,355,1370,359,1373,355,1376,1379],{},[182,1361,1362],{},"stubDatabaseGlobals(orm)"," installs ",[182,1365,1366],{},"useDrizzle",[182,1368,1369],{},"tables",[182,1371,1372],{},"createError",[182,1374,1375],{},"setResponseHeader",[182,1377,1378],{},"isUniqueConstraintError"," and the query operators.",[329,1381,1382,1383,1385],{},"Seed without ids and use the ones ",[182,1384,1218],{}," hands back. Identities restart\nat 1 before every test, and an insert that names its id doesn't move the\nsequence, so the next generated id would collide with it.",[329,1387,1388,1391,1392,1395],{},[182,1389,1390],{},"database.client"," is the PGlite itself, for a raw ",[182,1393,1394],{},"query(text, params)","\nwhen SQL reads more plainly than the builder.",[321,1397,1399],{"id":1398},"test-a-component","Test a component",[178,1401,1402,1403,1406],{},"Mount through ",[182,1404,1405],{},"UApp"," when the component can show a toast or a tooltip, and pass\nthe timerless toaster:",[564,1408,1410],{"className":566,"code":1409,"language":568,"meta":569,"style":569},"import { mountSuspended, registerEndpoint } from '@nuxt\u002Ftest-utils\u002Fruntime'\nimport UApp from '@nuxt\u002Fui\u002Fcomponents\u002FApp.vue'\nimport { flushPromises } from '@vue\u002Ftest-utils'\nimport { expect, it } from 'vitest'\nimport { h } from 'vue'\n\nimport ReportCard from '~\u002Fcomponents\u002FReportCard.vue'\n\nimport { withStarterConfig } from '..\u002Fhelpers\u002Fstarter-config'\nimport { TIMERLESS_TOASTER } from '..\u002Fhelpers\u002Ftoaster'\n\nregisterEndpoint('\u002Fapi\u002Freports', () => ({ total: 3 }))\n\nit('shows the report count', () =>\n  withStarterConfig({ payments: { enabled: false } }, async () => {\n    const app = await mountSuspended(UApp, {\n      props: { toaster: TIMERLESS_TOASTER },\n      slots: { default: () => h(ReportCard) },\n    })\n\n    await flushPromises()\n\n    expect(app.text()).toContain('3')\n\n    app.unmount()\n  }))\n",[182,1411,1412,1424,1436,1448,1459,1471,1475,1487,1491,1503,1515,1519,1542,1546,1559,1581,1599,1609,1627,1632,1636,1646,1650,1674,1678,1688],{"__ignoreMap":569},[573,1413,1414,1416,1419,1421],{"class":575,"line":12},[573,1415,691],{"class":578},[573,1417,1418],{"class":595}," { mountSuspended, registerEndpoint } ",[573,1420,697],{"class":578},[573,1422,1423],{"class":617}," '@nuxt\u002Ftest-utils\u002Fruntime'\n",[573,1425,1426,1428,1431,1433],{"class":575,"line":17},[573,1427,691],{"class":578},[573,1429,1430],{"class":595}," UApp ",[573,1432,697],{"class":578},[573,1434,1435],{"class":617}," '@nuxt\u002Fui\u002Fcomponents\u002FApp.vue'\n",[573,1437,1438,1440,1443,1445],{"class":575,"line":22},[573,1439,691],{"class":578},[573,1441,1442],{"class":595}," { flushPromises } ",[573,1444,697],{"class":578},[573,1446,1447],{"class":617}," '@vue\u002Ftest-utils'\n",[573,1449,1450,1452,1455,1457],{"class":575,"line":27},[573,1451,691],{"class":578},[573,1453,1454],{"class":595}," { expect, it } ",[573,1456,697],{"class":578},[573,1458,712],{"class":617},[573,1460,1461,1463,1466,1468],{"class":575,"line":32},[573,1462,691],{"class":578},[573,1464,1465],{"class":595}," { h } ",[573,1467,697],{"class":578},[573,1469,1470],{"class":617}," 'vue'\n",[573,1472,1473],{"class":575,"line":37},[573,1474,639],{"emptyLinePlaceholder":638},[573,1476,1477,1479,1482,1484],{"class":575,"line":46},[573,1478,691],{"class":578},[573,1480,1481],{"class":595}," ReportCard ",[573,1483,697],{"class":578},[573,1485,1486],{"class":617}," '~\u002Fcomponents\u002FReportCard.vue'\n",[573,1488,1489],{"class":575,"line":51},[573,1490,639],{"emptyLinePlaceholder":638},[573,1492,1493,1495,1498,1500],{"class":575,"line":56},[573,1494,691],{"class":578},[573,1496,1497],{"class":595}," { withStarterConfig } ",[573,1499,697],{"class":578},[573,1501,1502],{"class":617}," '..\u002Fhelpers\u002Fstarter-config'\n",[573,1504,1505,1507,1510,1512],{"class":575,"line":64},[573,1506,691],{"class":578},[573,1508,1509],{"class":595}," { TIMERLESS_TOASTER } ",[573,1511,697],{"class":578},[573,1513,1514],{"class":617}," '..\u002Fhelpers\u002Ftoaster'\n",[573,1516,1517],{"class":575,"line":73},[573,1518,639],{"emptyLinePlaceholder":638},[573,1520,1521,1524,1526,1529,1531,1533,1536,1539],{"class":575,"line":78},[573,1522,1523],{"class":591},"registerEndpoint",[573,1525,801],{"class":595},[573,1527,1528],{"class":617},"'\u002Fapi\u002Freports'",[573,1530,938],{"class":595},[573,1532,822],{"class":578},[573,1534,1535],{"class":595}," ({ total: ",[573,1537,1538],{"class":585},"3",[573,1540,1541],{"class":595}," }))\n",[573,1543,1544],{"class":575,"line":83},[573,1545,639],{"emptyLinePlaceholder":638},[573,1547,1548,1550,1552,1555,1557],{"class":575,"line":88},[573,1549,1109],{"class":591},[573,1551,801],{"class":595},[573,1553,1554],{"class":617},"'shows the report count'",[573,1556,938],{"class":595},[573,1558,1020],{"class":578},[573,1560,1561,1564,1567,1570,1573,1575,1577,1579],{"class":575,"line":93},[573,1562,1563],{"class":591},"  withStarterConfig",[573,1565,1566],{"class":595},"({ payments: { enabled: ",[573,1568,1569],{"class":585},"false",[573,1571,1572],{"class":595}," } }, ",[573,1574,1119],{"class":578},[573,1576,1122],{"class":595},[573,1578,822],{"class":578},[573,1580,612],{"class":595},[573,1582,1583,1586,1589,1591,1593,1596],{"class":575,"line":98},[573,1584,1585],{"class":578},"    const",[573,1587,1588],{"class":585}," app",[573,1590,893],{"class":578},[573,1592,868],{"class":578},[573,1594,1595],{"class":591}," mountSuspended",[573,1597,1598],{"class":595},"(UApp, {\n",[573,1600,1601,1604,1607],{"class":575,"line":103},[573,1602,1603],{"class":595},"      props: { toaster: ",[573,1605,1606],{"class":585},"TIMERLESS_TOASTER",[573,1608,1189],{"class":595},[573,1610,1611,1614,1616,1619,1621,1624],{"class":575,"line":108},[573,1612,1613],{"class":595},"      slots: { ",[573,1615,854],{"class":591},[573,1617,1618],{"class":595},": () ",[573,1620,822],{"class":578},[573,1622,1623],{"class":591}," h",[573,1625,1626],{"class":595},"(ReportCard) },\n",[573,1628,1629],{"class":575,"line":113},[573,1630,1631],{"class":595},"    })\n",[573,1633,1634],{"class":575,"line":122},[573,1635,639],{"emptyLinePlaceholder":638},[573,1637,1638,1641,1644],{"class":575,"line":127},[573,1639,1640],{"class":578},"    await",[573,1642,1643],{"class":591}," flushPromises",[573,1645,899],{"class":595},[573,1647,1648],{"class":575,"line":132},[573,1649,639],{"emptyLinePlaceholder":638},[573,1651,1652,1655,1658,1661,1664,1667,1669,1672],{"class":575,"line":137},[573,1653,1654],{"class":591},"    expect",[573,1656,1657],{"class":595},"(app.",[573,1659,1660],{"class":591},"text",[573,1662,1663],{"class":595},"()).",[573,1665,1666],{"class":591},"toContain",[573,1668,801],{"class":595},[573,1670,1671],{"class":617},"'3'",[573,1673,879],{"class":595},[573,1675,1676],{"class":575,"line":146},[573,1677,639],{"emptyLinePlaceholder":638},[573,1679,1680,1683,1686],{"class":575,"line":151},[573,1681,1682],{"class":595},"    app.",[573,1684,1685],{"class":591},"unmount",[573,1687,899],{"class":595},[573,1689,1690],{"class":575,"line":156},[573,1691,1692],{"class":595},"  }))\n",[326,1694,1695,1708,1725,1728,1754],{},[329,1696,1697,1699,1700,1703,1704,1707],{},[182,1698,1405],{}," provides the tooltip context. A bare ",[182,1701,1702],{},"mountSuspended"," of anything\nthat renders a ",[182,1705,1706],{},"UTooltip"," throws from inside Reka UI.",[329,1709,1710,1711,1713,1714,1721,1722,1724],{},"A toast's close timer outlives the test and throws once the file's page is\ntorn down. ",[182,1712,1606],{}," stops that, and\n",[203,1715,1718],{"href":1716,"rel":1717,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Ftest\u002Funit\u002Fuapp-mount-toaster.test.ts",[346],[182,1719,1720],{},"test\u002Funit\u002Fuapp-mount-toaster.test.ts","\nfails on a ",[182,1723,1405],{}," mount without it.",[329,1726,1727],{},"Unmount what you mount. A leftover page keeps following the shared router,\nre-renders on every later test, and slows the file until it times out.",[329,1729,1730,1731,1733,1734,1737,1738,1741,1742,1749,1750,1753],{},"Answer requests with ",[182,1732,1523],{},". To make one fail, mock ",[182,1735,1736],{},"$fetch","\nwith ",[182,1739,1740],{},"mockNuxtImport",", as\n",[203,1743,1746],{"href":1744,"rel":1745,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Ftest\u002Fnuxt\u002Fcheckout-button.test.ts",[346],[182,1747,1748],{},"test\u002Fnuxt\u002Fcheckout-button.test.ts","\ndoes. ",[182,1751,1752],{},"vi.stubGlobal('$fetch', …)"," never reaches a component.",[329,1755,1756,1757,1760,1761,1764],{},"Every ",[182,1758,1759],{},"mockNuxtImport('useRoute', …)"," returns an object with a ",[182,1762,1763],{},"query",", or\nthe SEO module throws after the test passes.",[321,1766,1768],{"id":1767},"helpers-and-stubs","Helpers and stubs",[178,1770,1771,1772,1779],{},"The shared helpers live in ",[203,1773,1776],{"href":1774,"rel":1775,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Ftree\u002Fmain\u002Ftest\u002Fhelpers",[346],[182,1777,1778],{},"test\u002Fhelpers\u002F",". Tests import\nthem by relative path.",[209,1781,1782,1792],{},[212,1783,1784],{},[215,1785,1786,1789],{},[218,1787,1788],{},"Helper",[218,1790,1791],{},"Use it to",[225,1793,1794,1810,1822,1842,1858,1877,1897,1913,1931,1946,1973,1986],{},[215,1795,1796,1805],{},[230,1797,1798,1801,1802],{},[182,1799,1800],{},"useTestDatabase()"," in ",[182,1803,1804],{},"test-database.ts",[230,1806,1807,1808,265],{},"Get a migrated PGlite per file, emptied before each test. ",[182,1809,184],{},[215,1811,1812,1819],{},[230,1813,1814,1801,1816],{},[182,1815,1362],{},[182,1817,1818],{},"database-globals.ts",[230,1820,1821],{},"Install the auto-imports a database-backed module reads",[215,1823,1824,1832],{},[230,1825,1826,1801,1829],{},[182,1827,1828],{},"withStarterConfig(patch, fn)",[182,1830,1831],{},"starter-config.ts",[230,1833,1834,1835,1838,1839,1841],{},"Overlay sections of ",[182,1836,1837],{},"starter.config"," while ",[182,1840,1058],{}," runs, and restore them when it returns or settles",[215,1843,1844,1852],{},[230,1845,1846,1801,1849],{},[182,1847,1848],{},"withTenancy(fn, orgs?)",[182,1850,1851],{},"orgs-config.ts",[230,1853,1854,1855,1857],{},"Run ",[182,1856,1058],{}," with organizations and invitations on",[215,1859,1860,1868],{},[230,1861,1862,1801,1865],{},[182,1863,1864],{},"withPublicConfig(section, patch, fn)",[182,1866,1867],{},"runtime-config.ts",[230,1869,1870,1871,1874,1875,265],{},"Patch ",[182,1872,1873],{},"runtimeConfig.public"," the same way. ",[182,1876,188],{},[215,1878,1879,1887],{},[230,1880,1881,1801,1884],{},[182,1882,1883],{},"starterConfigFixture(patch)",[182,1885,1886],{},"starter-config-fixture.ts",[230,1888,1889,1890,1893,1894],{},"Build a complete ",[182,1891,1892],{},"StarterConfig"," to return from ",[182,1895,1896],{},"vi.mock",[215,1898,1899,1910],{},[230,1900,1901,355,1904,355,1907],{},[182,1902,1903],{},"MONTHLY_PLAN",[182,1905,1906],{},"TWO_INTERVAL_PLAN",[182,1908,1909],{},"ONE_TIME_PLAN",[230,1911,1912],{},"Priced plans to spread into a config patch",[215,1914,1915,1928],{},[230,1916,1917,355,1919,355,1922,355,1925],{},[182,1918,1240],{},[182,1920,1921],{},"fakeNitroApp",[182,1923,1924],{},"fakeRoute",[182,1926,1927],{},"fakeResponse",[230,1929,1930],{},"Type a partial fake of a large external type, with the cast in one place",[215,1932,1933,1940],{},[230,1934,1935,355,1937],{},[182,1936,1606],{},[182,1938,1939],{},"TIMERLESS_TOASTER_COLLAPSED",[230,1941,1942,1943,1945],{},"Mount ",[182,1944,1405],{}," with toasts that never close on their own",[215,1947,1948,1959],{},[230,1949,1950,355,1953,355,1956],{},[182,1951,1952],{},"rememberCoupon",[182,1954,1955],{},"forgetCoupon",[182,1957,1958],{},"rememberedCoupon",[230,1960,1961,1962,1965,1966,1801,1968,1970,1971,265],{},"Plant, clear and read the ",[182,1963,1964],{},"coupon"," cookie. Call ",[182,1967,1955],{},[182,1969,908],{},". ",[182,1972,188],{},[215,1974,1975,1983],{},[230,1976,1977,239,1980],{},[182,1978,1979],{},"inMemoryThrottles()",[182,1981,1982],{},"fake-durable-object.ts",[230,1984,1985],{},"Run the real rate limiter over in-memory storage",[215,1987,1988,1996],{},[230,1989,1990,1801,1993],{},[182,1991,1992],{},"installEvents()",[182,1994,1995],{},"events.ts",[230,1997,1998,1999],{},"Record what the event bus emits, and run deferred listeners with ",[182,2000,2001],{},"settled()",[178,2003,2004,2005,2007],{},"A module that reads ",[182,2006,1837],{}," when it is imported can't be patched\nafterwards. Mock the config before the import instead:",[564,2009,2011],{"className":566,"code":2010,"language":568,"meta":569,"style":569},"vi.mock('~~\u002Fstarter.config', async () => {\n  const { MONTHLY_PLAN, starterConfigFixture } =\n    await import('..\u002Fhelpers\u002Fstarter-config-fixture')\n\n  return {\n    default: starterConfigFixture({\n      payments: {\n        enabled: true,\n        modes: ['subscription'],\n        providers: ['stripe'],\n        plans: { pro: { ...MONTHLY_PLAN } },\n      },\n    }),\n  }\n})\n",[182,2012,2013,2035,2053,2066,2070,2077,2087,2092,2101,2112,2122,2135,2140,2145,2150],{"__ignoreMap":569},[573,2014,2015,2017,2020,2022,2025,2027,2029,2031,2033],{"class":575,"line":12},[573,2016,795],{"class":595},[573,2018,2019],{"class":591},"mock",[573,2021,801],{"class":595},[573,2023,2024],{"class":617},"'~~\u002Fstarter.config'",[573,2026,355],{"class":595},[573,2028,1119],{"class":578},[573,2030,1122],{"class":595},[573,2032,822],{"class":578},[573,2034,612],{"class":595},[573,2036,2037,2039,2041,2043,2045,2048,2050],{"class":575,"line":17},[573,2038,1132],{"class":578},[573,2040,851],{"class":595},[573,2042,1903],{"class":585},[573,2044,355],{"class":595},[573,2046,2047],{"class":585},"starterConfigFixture",[573,2049,863],{"class":595},[573,2051,2052],{"class":578},"=\n",[573,2054,2055,2057,2059,2061,2064],{"class":575,"line":22},[573,2056,1640],{"class":578},[573,2058,871],{"class":578},[573,2060,801],{"class":595},[573,2062,2063],{"class":617},"'..\u002Fhelpers\u002Fstarter-config-fixture'",[573,2065,879],{"class":595},[573,2067,2068],{"class":575,"line":27},[573,2069,639],{"emptyLinePlaceholder":638},[573,2071,2072,2075],{"class":575,"line":32},[573,2073,2074],{"class":578},"  return",[573,2076,612],{"class":595},[573,2078,2079,2082,2084],{"class":575,"line":37},[573,2080,2081],{"class":595},"    default: ",[573,2083,2047],{"class":591},[573,2085,2086],{"class":595},"({\n",[573,2088,2089],{"class":575,"line":46},[573,2090,2091],{"class":595},"      payments: {\n",[573,2093,2094,2097,2099],{"class":575,"line":51},[573,2095,2096],{"class":595},"        enabled: ",[573,2098,1252],{"class":585},[573,2100,359],{"class":595},[573,2102,2103,2106,2109],{"class":575,"line":56},[573,2104,2105],{"class":595},"        modes: [",[573,2107,2108],{"class":617},"'subscription'",[573,2110,2111],{"class":595},"],\n",[573,2113,2114,2117,2120],{"class":575,"line":64},[573,2115,2116],{"class":595},"        providers: [",[573,2118,2119],{"class":617},"'stripe'",[573,2121,2111],{"class":595},[573,2123,2124,2127,2130,2132],{"class":575,"line":73},[573,2125,2126],{"class":595},"        plans: { pro: { ",[573,2128,2129],{"class":578},"...",[573,2131,1903],{"class":585},[573,2133,2134],{"class":595}," } },\n",[573,2136,2137],{"class":575,"line":78},[573,2138,2139],{"class":595},"      },\n",[573,2141,2142],{"class":575,"line":83},[573,2143,2144],{"class":595},"    }),\n",[573,2146,2147],{"class":575,"line":88},[573,2148,2149],{"class":595},"  }\n",[573,2151,2152],{"class":575,"line":93},[573,2153,1066],{"class":595},[178,2155,2156,2157,2159],{},"The fixture's roles and grants are a fixed baseline, independent of your\n",[182,2158,200],{},", so editing your config doesn't change what these tests\nexercise.",[178,2161,2162,2163,2166],{},"For the rate limiter, mock ",[182,2164,2165],{},"cloudflare:workers"," with the fake and put the\nthrottles on the event's bindings:",[564,2168,2170],{"className":566,"code":2169,"language":568,"meta":569,"style":569},"vi.mock('cloudflare:workers', () => import('..\u002Fhelpers\u002Ffake-durable-object'))\n\nconst event = fakeH3Event({\n  context: { cloudflare: { env: { AUTH_THROTTLE: inMemoryThrottles() } } },\n})\n",[182,2171,2172,2197,2201,2215,2226],{"__ignoreMap":569},[573,2173,2174,2176,2178,2180,2183,2185,2187,2189,2191,2194],{"class":575,"line":12},[573,2175,795],{"class":595},[573,2177,2019],{"class":591},[573,2179,801],{"class":595},[573,2181,2182],{"class":617},"'cloudflare:workers'",[573,2184,938],{"class":595},[573,2186,822],{"class":578},[573,2188,871],{"class":578},[573,2190,801],{"class":595},[573,2192,2193],{"class":617},"'..\u002Fhelpers\u002Ffake-durable-object'",[573,2195,2196],{"class":595},"))\n",[573,2198,2199],{"class":575,"line":17},[573,2200,639],{"emptyLinePlaceholder":638},[573,2202,2203,2205,2208,2210,2213],{"class":575,"line":22},[573,2204,848],{"class":578},[573,2206,2207],{"class":585}," event",[573,2209,893],{"class":578},[573,2211,2212],{"class":591}," fakeH3Event",[573,2214,2086],{"class":595},[573,2216,2217,2220,2223],{"class":575,"line":27},[573,2218,2219],{"class":595},"  context: { cloudflare: { env: { AUTH_THROTTLE: ",[573,2221,2222],{"class":591},"inMemoryThrottles",[573,2224,2225],{"class":595},"() } } },\n",[573,2227,2228],{"class":575,"line":32},[573,2229,1066],{"class":595},[178,2231,520,2232,2234,2235,2238,2239,2242,2243,2238,2246,2249,2250,2253],{},[182,2233,184],{}," project, ",[182,2236,2237],{},"import.meta.dev"," reads ",[182,2240,2241],{},"globalThis.__IMPORT_META_DEV__","\nand ",[182,2244,2245],{},"import.meta.prerender",[182,2247,2248],{},"globalThis.__IMPORT_META_PRERENDER__",". Both\nare false until a test sets them, for example with\n",[182,2251,2252],{},"vi.stubGlobal('__IMPORT_META_DEV__', true)"," to run a dev-only branch.",[178,2255,2256,2257,2260,2261,2263,2264,1801,2267,2269,2270,1801,2273,2275],{},"A new build-generated ",[182,2258,2259],{},"#…-registry"," module needs a stub in ",[182,2262,373],{}," and an\nalias in two places: ",[182,2265,2266],{},"UNIT_ALIASES",[182,2268,350],{},", and\n",[182,2271,2272],{},"typescript.nodeTsConfig.compilerOptions.paths",[182,2274,512],{},", which\ntypechecks the same files.",[321,2277,2279],{"id":2278},"run-the-end-to-end-suite","Run the end-to-end suite",[564,2281,2285],{"className":2282,"code":2283,"language":2284,"meta":569,"style":569},"language-bash shiki shiki-themes github-light github-light github-dark","pnpm exec playwright install chromium\npnpm test:e2e\n","bash",[182,2286,2287,2304],{"__ignoreMap":569},[573,2288,2289,2292,2295,2298,2301],{"class":575,"line":12},[573,2290,2291],{"class":591},"pnpm",[573,2293,2294],{"class":617}," exec",[573,2296,2297],{"class":617}," playwright",[573,2299,2300],{"class":617}," install",[573,2302,2303],{"class":617}," chromium\n",[573,2305,2306,2308],{"class":575,"line":17},[573,2307,2291],{"class":591},[573,2309,2310],{"class":617}," test:e2e\n",[178,2312,2313,2320,2321,2323,2324,2327,2328,2331,2332,2335],{},[203,2314,2317],{"href":2315,"rel":2316,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Fplaywright.config.ts",[346],[182,2318,2319],{},"playwright.config.ts"," starts ",[182,2322,196],{}," itself and\nsupplies what a fresh clone lacks: a session password, an empty\n",[182,2325,2326],{},"NUXT_SITE_URL",", dummy Stripe keys with a price mapped to ",[182,2329,2330],{},"pro","'s monthly ref,\nand a wrangler config without the AI binding. The dev server migrates the\nPGlite it serves before it listens, so the suite runs on a fresh clone. Global\nsetup clears the local ",[182,2333,2334],{},"CACHE"," KV and Nuxt's dev payload cache before each run,\nand keeps the database.",[178,2337,2338,2339,239,2342,2345,2346,2349,2350,2353,2354,2361],{},"A spec imports ",[182,2340,2341],{},"test",[182,2343,2344],{},"expect"," from ",[182,2347,2348],{},".\u002Ffixtures",", never from\n",[182,2351,2352],{},"@nuxt\u002Ftest-utils\u002Fplaywright"," directly, and a unit test enforces it. Specs read\n",[203,2355,2358],{"href":2356,"rel":2357,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Ftest\u002Fe2e\u002Fstarter.ts",[346],[182,2359,2360],{},"test\u002Fe2e\u002Fstarter.ts"," to learn what the build ships\nand skip what it doesn't, so the suite passes on any configuration:",[564,2363,2365],{"className":566,"code":2364,"language":568,"meta":569,"style":569},"import { PASSWORD, seedAccount, uniqueEmail } from '.\u002Faccounts'\nimport { expectOnDashboard } from '.\u002Fdashboard'\nimport { test } from '.\u002Ffixtures'\nimport { shipped } from '.\u002Fstarter'\n\ntest.skip(!shipped.usesPassword, 'this build has no password registration')\n\ntest('a registered account signs in', async ({ page, goto, request }) => {\n  const email = uniqueEmail()\n  await seedAccount(request, email)\n\n  await goto('\u002Flogin', { waitUntil: 'hydration' })\n  await page.getByLabel('Email').fill(email)\n  await page.getByLabel('Password', { exact: true }).fill(PASSWORD)\n  await page.getByRole('button', { name: 'Sign in', exact: true }).click()\n\n  await expectOnDashboard(page)\n})\n",[182,2366,2367,2379,2391,2403,2415,2419,2439,2443,2479,2493,2503,2507,2528,2551,2581,2613,2617,2627],{"__ignoreMap":569},[573,2368,2369,2371,2374,2376],{"class":575,"line":12},[573,2370,691],{"class":578},[573,2372,2373],{"class":595}," { PASSWORD, seedAccount, uniqueEmail } ",[573,2375,697],{"class":578},[573,2377,2378],{"class":617}," '.\u002Faccounts'\n",[573,2380,2381,2383,2386,2388],{"class":575,"line":17},[573,2382,691],{"class":578},[573,2384,2385],{"class":595}," { expectOnDashboard } ",[573,2387,697],{"class":578},[573,2389,2390],{"class":617}," '.\u002Fdashboard'\n",[573,2392,2393,2395,2398,2400],{"class":575,"line":22},[573,2394,691],{"class":578},[573,2396,2397],{"class":595}," { test } ",[573,2399,697],{"class":578},[573,2401,2402],{"class":617}," '.\u002Ffixtures'\n",[573,2404,2405,2407,2410,2412],{"class":575,"line":27},[573,2406,691],{"class":578},[573,2408,2409],{"class":595}," { shipped } ",[573,2411,697],{"class":578},[573,2413,2414],{"class":617}," '.\u002Fstarter'\n",[573,2416,2417],{"class":575,"line":32},[573,2418,639],{"emptyLinePlaceholder":638},[573,2420,2421,2424,2427,2429,2431,2434,2437],{"class":575,"line":37},[573,2422,2423],{"class":595},"test.",[573,2425,2426],{"class":591},"skip",[573,2428,801],{"class":595},[573,2430,1246],{"class":578},[573,2432,2433],{"class":595},"shipped.usesPassword, ",[573,2435,2436],{"class":617},"'this build has no password registration'",[573,2438,879],{"class":595},[573,2440,2441],{"class":575,"line":46},[573,2442,639],{"emptyLinePlaceholder":638},[573,2444,2445,2447,2449,2452,2454,2456,2459,2462,2464,2467,2469,2472,2475,2477],{"class":575,"line":51},[573,2446,2341],{"class":591},[573,2448,801],{"class":595},[573,2450,2451],{"class":617},"'a registered account signs in'",[573,2453,355],{"class":595},[573,2455,1119],{"class":578},[573,2457,2458],{"class":595}," ({ ",[573,2460,2461],{"class":810},"page",[573,2463,355],{"class":595},[573,2465,2466],{"class":810},"goto",[573,2468,355],{"class":595},[573,2470,2471],{"class":810},"request",[573,2473,2474],{"class":595}," }) ",[573,2476,822],{"class":578},[573,2478,612],{"class":595},[573,2480,2481,2483,2486,2488,2491],{"class":575,"line":56},[573,2482,1132],{"class":578},[573,2484,2485],{"class":585}," email",[573,2487,893],{"class":578},[573,2489,2490],{"class":591}," uniqueEmail",[573,2492,899],{"class":595},[573,2494,2495,2497,2500],{"class":575,"line":64},[573,2496,1232],{"class":578},[573,2498,2499],{"class":591}," seedAccount",[573,2501,2502],{"class":595},"(request, email)\n",[573,2504,2505],{"class":575,"line":73},[573,2506,639],{"emptyLinePlaceholder":638},[573,2508,2509,2511,2514,2516,2519,2522,2525],{"class":575,"line":78},[573,2510,1232],{"class":578},[573,2512,2513],{"class":578}," goto",[573,2515,801],{"class":595},[573,2517,2518],{"class":617},"'\u002Flogin'",[573,2520,2521],{"class":595},", { waitUntil: ",[573,2523,2524],{"class":617},"'hydration'",[573,2526,2527],{"class":595}," })\n",[573,2529,2530,2532,2535,2538,2540,2543,2545,2548],{"class":575,"line":83},[573,2531,1232],{"class":578},[573,2533,2534],{"class":595}," page.",[573,2536,2537],{"class":591},"getByLabel",[573,2539,801],{"class":595},[573,2541,2542],{"class":617},"'Email'",[573,2544,407],{"class":595},[573,2546,2547],{"class":591},"fill",[573,2549,2550],{"class":595},"(email)\n",[573,2552,2553,2555,2557,2559,2561,2564,2567,2569,2572,2574,2576,2579],{"class":575,"line":88},[573,2554,1232],{"class":578},[573,2556,2534],{"class":595},[573,2558,2537],{"class":591},[573,2560,801],{"class":595},[573,2562,2563],{"class":617},"'Password'",[573,2565,2566],{"class":595},", { exact: ",[573,2568,1252],{"class":585},[573,2570,2571],{"class":595}," }).",[573,2573,2547],{"class":591},[573,2575,801],{"class":595},[573,2577,2578],{"class":585},"PASSWORD",[573,2580,879],{"class":595},[573,2582,2583,2585,2587,2590,2592,2595,2598,2601,2604,2606,2608,2611],{"class":575,"line":93},[573,2584,1232],{"class":578},[573,2586,2534],{"class":595},[573,2588,2589],{"class":591},"getByRole",[573,2591,801],{"class":595},[573,2593,2594],{"class":617},"'button'",[573,2596,2597],{"class":595},", { name: ",[573,2599,2600],{"class":617},"'Sign in'",[573,2602,2603],{"class":595},", exact: ",[573,2605,1252],{"class":585},[573,2607,2571],{"class":595},[573,2609,2610],{"class":591},"click",[573,2612,899],{"class":595},[573,2614,2615],{"class":575,"line":98},[573,2616,639],{"emptyLinePlaceholder":638},[573,2618,2619,2621,2624],{"class":575,"line":103},[573,2620,1232],{"class":578},[573,2622,2623],{"class":591}," expectOnDashboard",[573,2625,2626],{"class":595},"(page)\n",[573,2628,2629],{"class":575,"line":108},[573,2630,1066],{"class":595},[178,2632,2633,2634,2637,2638,517],{},"To add a spec to the set CI runs, pass ",[182,2635,2636],{},"{ tag: '@smoke' }"," as the second\nargument of ",[182,2639,2640],{},"test()",[178,2642,2643,2650,2651,355,2654,355,2657,239,2660,2663,2664,2667,2668,2671,2672,2674,2675,2678],{},[203,2644,2647],{"href":2645,"rel":2646,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002Ftest\u002Fe2e\u002Faccounts.ts",[346],[182,2648,2649],{},"test\u002Fe2e\u002Faccounts.ts"," holds the shared password,\n",[182,2652,2653],{},"uniqueEmail()",[182,2655,2656],{},"seedAccount()",[182,2658,2659],{},"registerInPage()",[182,2661,2662],{},"query(sql)",", which\nreads the dev server's database. Fresh addresses use the seed domain, so\n",[182,2665,2666],{},"pnpm db:seed --reset"," clears what the suite leaves behind. ",[182,2669,2670],{},"seedAccount","\nregisters through the standalone ",[182,2673,2471],{}," fixture; use ",[182,2676,2677],{},"registerInPage"," when\nthe page itself needs the session cookie.",[321,2680,2682],{"id":2681},"in-ci","In CI",[326,2684,2685,2721,2731],{},[329,2686,2687,2694,2695,239,2698,2701,2702,2705,2706,2709,2710,2713,2714,2716,2717,2720],{},[203,2688,2691],{"href":2689,"rel":2690,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002F.github\u002Fworkflows\u002Fci.yml",[346],[182,2692,2693],{},"ci.yml"," runs lint, format check, typecheck,\n",[182,2696,2697],{},"test:unit",[182,2699,2700],{},"test:nuxt"," on every pull request and every push to ",[182,2703,2704],{},"main",",\nand a ",[182,2707,2708],{},"build"," job beside it. A job that already passed on the exact same git\ntree skips its steps. Its ",[182,2711,2712],{},"changed-config"," job runs only in the kit's own\nrepository, so yours shows it skipped: it checks that\n",[182,2715,453],{}," still matches the kit's config, then reruns\ntypecheck and both projects under ",[182,2718,2719],{},"test\u002Fchanged\u002Fstarter.config.ts",", an app's\nconfig with nearly every switch flipped, and with the Serbian locale deleted.",[329,2722,2723,2730],{},[203,2724,2727],{"href":2725,"rel":2726,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002F.github\u002Fworkflows\u002Fcommits.yml",[346],[182,2728,2729],{},"commits.yml"," runs commitlint over the\npull request's title and every commit in it.",[329,2732,2733,333,2740,2743,2744,2746],{},[203,2734,2737],{"href":2735,"rel":2736,"title":347},"https:\u002F\u002Fgithub.com\u002Fnuxter-app\u002Fkit\u002Fblob\u002Fmain\u002F.github\u002Fworkflows\u002Fe2e.yml",[346],[182,2738,2739],{},"e2e.yml",[182,2741,2742],{},"test:e2e:smoke"," when you add\nthe ",[182,2745,192],{}," label to a pull request, on every push while the label stays, or\nby hand. It needs no secrets and uploads the Playwright report when it fails.",[178,2748,2749,2750,2752,2753,355,2756,239,2758,2761],{},"None of these blocks a merge until a ruleset on ",[182,2751,2704],{}," requires it. Rulesets\nare a repository setting, so your repository needs its own. Require\n",[182,2754,2755],{},"ci (ubuntu-latest, 22)",[182,2757,2708],{},[182,2759,2760],{},"commitlint",", with GitHub Actions as the\nsource. Renaming a job or changing the matrix changes the check name GitHub\nwaits for.",[321,2763,20],{"id":2764},"configuration",[209,2766,2767,2783],{},[212,2768,2769],{},[215,2770,2771,2774,2777,2780],{},[218,2772,2773],{},"Setting",[218,2775,2776],{},"Where",[218,2778,2779],{},"Default",[218,2781,2782],{},"What it does",[225,2784,2785,2808,2828,2847,2874,2894,2911,2941],{},[215,2786,2787,2792,2796,2802],{},[230,2788,2789],{},[182,2790,2791],{},"hookTimeout",[230,2793,2794],{},[182,2795,350],{},[230,2797,2798,2801],{},[182,2799,2800],{},"20_000"," in both projects",[230,2803,2804,2805],{},"Time for PGlite to open and Nuxt to boot in ",[182,2806,2807],{},"beforeAll",[215,2809,2810,2815,2819,2825],{},[230,2811,2812],{},[182,2813,2814],{},"testTimeout",[230,2816,2817],{},[182,2818,350],{},[230,2820,2821,1801,2823],{},[182,2822,2800],{},[182,2824,188],{},[230,2826,2827],{},"Time per component test, including the first boot",[215,2829,2830,2835,2839,2844],{},[230,2831,2832],{},[182,2833,2834],{},"silent",[230,2836,2837],{},[182,2838,350],{},[230,2840,2841],{},[182,2842,2843],{},"'passed-only'",[230,2845,2846],{},"Prints console output only for failing tests",[215,2848,2849,2860,2864,2867],{},[230,2850,2851,355,2854,355,2857],{},[182,2852,2853],{},"PASSKEY_TESTS",[182,2855,2856],{},"MULTI_LOCALE_TESTS",[182,2858,2859],{},"EMAIL_TEMPLATE_TESTS",[230,2861,2862],{},[182,2863,350],{},[230,2865,2866],{},"excluded when their feature is off",[230,2868,2869,2870,2873],{},"Suites skipped while ",[182,2871,2872],{},"auth.passkey"," is off, with one language, or with email off",[215,2875,2876,2881,2885,2889],{},[230,2877,2878],{},[182,2879,2880],{},"workers",[230,2882,2883],{},[182,2884,2319],{},[230,2886,2887],{},[182,2888,954],{},[230,2890,2891,2892],{},"Every worker would start its own ",[182,2893,196],{},[215,2895,2896,2901,2905,2908],{},[230,2897,2898],{},[182,2899,2900],{},"timeout",[230,2902,2903],{},[182,2904,2319],{},[230,2906,2907],{},"30 s, 90 s on CI",[230,2909,2910],{},"Time per spec",[215,2912,2913,2921,2925,2935],{},[230,2914,2915,355,2918],{},[182,2916,2917],{},"retries",[182,2919,2920],{},"maxFailures",[230,2922,2923],{},[182,2924,2319],{},[230,2926,2927,2930,2931,2934],{},[182,2928,2929],{},"0"," locally, ",[182,2932,2933],{},"2"," on CI",[230,2936,2937,2938,2940],{},"Retries per spec, and failed specs before the run stops (",[182,2939,2929],{}," is no limit)",[215,2942,2943,2948,2952,2955],{},[230,2944,2945],{},[182,2946,2947],{},"serverStartTimeout",[230,2949,2950],{},[182,2951,2319],{},[230,2953,2954],{},"60 s, 240 s on CI",[230,2956,2957,2958,2960],{},"Time for ",[182,2959,196],{}," to serve its first page",[321,2962,2964],{"id":2963},"good-to-know","Good to know",[326,2966,2967,2978,2989,3000,3019],{},[329,2968,2969,2970,2973,2974,2977],{},"A test that hangs inside a transaction is almost always calling\n",[182,2971,2972],{},"useDrizzle(event)"," from the transaction callback. PGlite has one\nconnection and the open transaction holds it. Use the ",[182,2975,2976],{},"tx"," you were given.",[329,2979,2980,2981,2984,2985,2988],{},"PGlite can't race two transactions. To test a race, spy on\n",[182,2982,2983],{},"database.orm.transaction"," and run the competing write just before the real\none opens, as ",[182,2986,2987],{},"admin-ban.test.ts"," does.",[329,2990,2991,2992,2995,2996,2999],{},"unovis charts never settle under happy-dom. Replace\n",[182,2993,2994],{},"DashboardStackedBarChart"," with ",[182,2997,2998],{},"mockComponent"," in any test that mounts a\ncard holding one.",[329,3001,3002,3003,3006,3007,3010,3011,3014,3015,3018],{},"happy-dom's ",[182,3004,3005],{},":has()"," ignores descendant combinators, so ",[182,3008,3009],{},"body:has(A B)","\nmatches whenever an ",[182,3012,3013],{},"A"," exists. Query ",[182,3016,3017],{},"A B"," directly instead.",[329,3020,3021,3022,3025,3026,1970,3029,3032,3033,3036],{},"A type-level test is a ",[182,3023,3024],{},"\u002F\u002F @ts-expect-error"," above a call in a closure that\nnever runs, with ",[182,3027,3028],{},"expect.assertions(0)",[182,3030,3031],{},"pnpm typecheck"," covers\n",[182,3034,3035],{},"test\u002Funit",", so the directive fails the build once the error goes away.",[3038,3039,3040],"style",{},"html pre.shiki code .s8jYJ, html code.shiki .s8jYJ{--shiki-light:#D73A49;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sBjJW, html code.shiki .sBjJW{--shiki-light:#005CC5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sIIMD, html code.shiki .sIIMD{--shiki-light:#032F62;--shiki-default:#032F62;--shiki-dark:#9ECBFF}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}",{"title":569,"searchDepth":17,"depth":17,"links":3042},[3043,3044,3045,3046,3047,3048,3049,3050,3051],{"id":323,"depth":17,"text":324},{"id":410,"depth":17,"text":206},{"id":670,"depth":17,"text":671},{"id":1398,"depth":17,"text":1399},{"id":1767,"depth":17,"text":1768},{"id":2278,"depth":17,"text":2279},{"id":2681,"depth":17,"text":2682},{"id":2764,"depth":17,"text":20},{"id":2963,"depth":17,"text":2964},[3053,3054],{"path":153,"title":154,"description":155,"navTitle":154,"group":140,"order":156},null]