[{"data":1,"prerenderedAt":403},["ShallowReactive",2],{"navigation":3,"docs_contributing":56},[4],{"title":5,"path":6,"stem":7,"children":8},"Docs","\u002Fdocs","docs",[9,12,16,20,24,28,32,36,40,44,48,52],{"title":10,"path":6,"stem":11},"Getting started","docs\u002Findex",{"title":13,"path":14,"stem":15},"Adjust Colors","\u002Fdocs\u002Fadjust-colors","docs\u002Fadjust-colors",{"title":17,"path":18,"stem":19},"Areas","\u002Fdocs\u002Fareas","docs\u002Fareas",{"title":21,"path":22,"stem":23},"Boards","\u002Fdocs\u002Fboards","docs\u002Fboards",{"title":25,"path":26,"stem":27},"Cards","\u002Fdocs\u002Fcards","docs\u002Fcards",{"title":29,"path":30,"stem":31},"Comments","\u002Fdocs\u002Fcomments","docs\u002Fcomments",{"title":33,"path":34,"stem":35},"Contributing","\u002Fdocs\u002Fcontributing","docs\u002Fcontributing",{"title":37,"path":38,"stem":39},"Disable Signup","\u002Fdocs\u002Fdisable-signup","docs\u002Fdisable-signup",{"title":41,"path":42,"stem":43},"Health Check","\u002Fdocs\u002Fhealth-check","docs\u002Fhealth-check",{"title":45,"path":46,"stem":47},"MCP Server","\u002Fdocs\u002Fmcp-server","docs\u002Fmcp-server",{"title":49,"path":50,"stem":51},"Migration","\u002Fdocs\u002Fmigration","docs\u002Fmigration",{"title":53,"path":54,"stem":55},"Users","\u002Fdocs\u002Fusers","docs\u002Fusers",{"id":57,"title":33,"body":58,"description":397,"extension":398,"meta":399,"navigation":400,"path":34,"seo":401,"stem":35,"__hash__":402},"docs\u002Fdocs\u002Fcontributing.md",{"type":59,"value":60,"toc":388},"minimark",[61,65,80,85,103,106,150,164,186,190,193,203,215,221,277,280,284,299,303,326,330,360,367,371,384],[62,63,33],"h1",{"id":64},"contributing",[66,67,68,69,79],"p",{},"LokalBoards is open source (MIT) and contributions are very welcome — bug reports, fixes, features, and documentation. This page explains how to set up a development environment and what's expected of a pull request. The same information lives in ",[70,71,75],"a",{"href":72,"rel":73},"https:\u002F\u002Fgithub.com\u002Fflorian-strasser\u002FLokalBoards\u002Fblob\u002Fmaster\u002FCONTRIBUTING.md",[74],"nofollow",[76,77,78],"code",{},"CONTRIBUTING.md"," in the repository.",[81,82,84],"h2",{"id":83},"prerequisites","Prerequisites",[86,87,88,96],"ul",{},[89,90,91,95],"li",{},[92,93,94],"strong",{},"Node.js 22"," (the toolchain requires Node 20+; CI runs on 22).",[89,97,98,99,102],{},"A reachable ",[92,100,101],{},"MySQL 8+"," database for local development. The required tables are created automatically on first start, so an empty database is enough.",[81,104,10],{"id":105},"getting-started",[107,108,113],"pre",{"className":109,"code":110,"language":111,"meta":112,"style":112},"language-bash shiki shiki-themes github-light github-dark","git clone https:\u002F\u002Fgithub.com\u002Fflorian-strasser\u002FLokalBoards\ncd LokalBoards\nnpm install\n","bash","",[76,114,115,131,141],{"__ignoreMap":112},[116,117,120,124,128],"span",{"class":118,"line":119},"line",1,[116,121,123],{"class":122},"sScJk","git",[116,125,127],{"class":126},"sZZnC"," clone",[116,129,130],{"class":126}," https:\u002F\u002Fgithub.com\u002Fflorian-strasser\u002FLokalBoards\n",[116,132,134,138],{"class":118,"line":133},2,[116,135,137],{"class":136},"sj4cs","cd",[116,139,140],{"class":126}," LokalBoards\n",[116,142,144,147],{"class":118,"line":143},3,[116,145,146],{"class":122},"npm",[116,148,149],{"class":126}," install\n",[66,151,152,153,156,157,159,160,163],{},"Create a ",[76,154,155],{},".env.local"," for local development (see ",[70,158,10],{"href":6}," for the available environment variables) pointing ",[76,161,162],{},"NUXT_MYSQL_*"," at your local database, then run the dev server:",[107,165,167],{"className":109,"code":166,"language":111,"meta":112,"style":112},"npx nuxt dev --dotenv .env.local\n",[76,168,169],{"__ignoreMap":112},[116,170,171,174,177,180,183],{"class":118,"line":119},[116,172,173],{"class":122},"npx",[116,175,176],{"class":126}," nuxt",[116,178,179],{"class":126}," dev",[116,181,182],{"class":136}," --dotenv",[116,184,185],{"class":126}," .env.local\n",[81,187,189],{"id":188},"tests","Tests",[66,191,192],{},"There are two test suites.",[66,194,195,198,199,202],{},[92,196,197],{},"Unit tests"," are fast and need no database. This is what ",[76,200,201],{},"npm test"," runs and what gates every pull request:",[107,204,206],{"className":109,"code":205,"language":111,"meta":112,"style":112},"npm test\n",[76,207,208],{"__ignoreMap":112},[116,209,210,212],{"class":118,"line":119},[116,211,146],{"class":122},[116,213,214],{"class":126}," test\n",[66,216,217,220],{},[92,218,219],{},"Integration tests"," run the real code against a real MySQL. They use a throwaway database (they truncate tables, so never point them at real data). Create an empty database, then:",[107,222,224],{"className":109,"code":223,"language":111,"meta":112,"style":112},"TEST_MYSQL_HOST=127.0.0.1 \\\nTEST_MYSQL_USER=root \\\nTEST_MYSQL_PASSWORD=yourpassword \\\nTEST_MYSQL_DATABASE=lokalboards_test \\\nnpm run test:integration\n",[76,225,226,242,250,257,265],{"__ignoreMap":112},[116,227,228,232,236,239],{"class":118,"line":119},[116,229,231],{"class":230},"sVt8B","TEST_MYSQL_HOST",[116,233,235],{"class":234},"szBVR","=",[116,237,238],{"class":126},"127.0.0.1",[116,240,241],{"class":122}," \\\n",[116,243,244,247],{"class":118,"line":133},[116,245,246],{"class":230},"TEST_MYSQL_USER=root ",[116,248,249],{"class":136},"\\\n",[116,251,252,255],{"class":118,"line":143},[116,253,254],{"class":230},"TEST_MYSQL_PASSWORD=yourpassword ",[116,256,249],{"class":136},[116,258,260,263],{"class":118,"line":259},4,[116,261,262],{"class":230},"TEST_MYSQL_DATABASE=lokalboards_test ",[116,264,249],{"class":136},[116,266,268,271,274],{"class":118,"line":267},5,[116,269,270],{"class":230},"npm ",[116,272,273],{"class":126},"run",[116,275,276],{"class":126}," test:integration\n",[66,278,279],{},"Please add or update tests for any behaviour you change. Security-sensitive logic (authorization, sessions, API keys) should keep its test coverage.",[81,281,283],{"id":282},"building","Building",[107,285,287],{"className":109,"code":286,"language":111,"meta":112,"style":112},"npm run build\n",[76,288,289],{"__ignoreMap":112},[116,290,291,293,296],{"class":118,"line":119},[116,292,146],{"class":122},[116,294,295],{"class":126}," run",[116,297,298],{"class":126}," build\n",[81,300,302],{"id":301},"database-schema-changes","Database schema changes",[66,304,305,306,309,310,313,314,317,318,321,322,325],{},"The schema is managed by a small migration runner in ",[76,307,308],{},"app\u002Flib\u002FdatabaseSetup.ts"," and applied once at startup. To change the schema, ",[92,311,312],{},"append a new migration"," to the ",[76,315,316],{},"migrations"," array (for example ",[76,319,320],{},"0002_add_x",") with an ",[76,323,324],{},"up(db)"," function — never edit or remove an existing migration, as it may already be applied on other installations.",[81,327,329],{"id":328},"pull-request-checklist","Pull request checklist",[86,331,332,341,347,354,357],{},[89,333,334,336,337,340],{},[76,335,201],{}," passes (and ",[76,338,339],{},"npm run test:integration"," if you touched database code).",[89,342,343,346],{},[76,344,345],{},"npm run build"," succeeds.",[89,348,349,350,353],{},"A ",[76,351,352],{},"CHANGELOG.md"," entry is added under the current unreleased version, describing the change and the why.",[89,355,356],{},"New or changed behaviour is covered by tests where practical.",[89,358,359],{},"Code matches the style of the surrounding files.",[66,361,362,363,366],{},"Continuous integration runs the unit suite, a production build, the integration suite (against a MySQL service container), ",[76,364,365],{},"npm audit",", and CodeQL on every push and pull request.",[81,368,370],{"id":369},"reporting-security-issues","Reporting security issues",[66,372,373,374,377,378,383],{},"Please do ",[92,375,376],{},"not"," open a public issue for security vulnerabilities. Follow the process described in the project's ",[70,379,382],{"href":380,"rel":381},"https:\u002F\u002Fgithub.com\u002Fflorian-strasser\u002FLokalBoards\u002Fblob\u002Fmaster\u002FSECURITY.md",[74],"security policy",".",[385,386,387],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":112,"searchDepth":133,"depth":133,"links":389},[390,391,392,393,394,395,396],{"id":83,"depth":133,"text":84},{"id":105,"depth":133,"text":10},{"id":188,"depth":133,"text":189},{"id":282,"depth":133,"text":283},{"id":301,"depth":133,"text":302},{"id":328,"depth":133,"text":329},{"id":369,"depth":133,"text":370},"LokalBoards is open source (MIT) and contributions are very welcome — bug reports, fixes, features, and documentation. This page explains how to set up a development environment and what's expected of a pull request. The same information lives in CONTRIBUTING.md in the repository.","md",{},true,{"title":33,"description":397},"r5nijHuh0QUaONud0UCrYe-yAu7rN9Lb0rDqJvDLi-s",1786546244248]