[{"data":1,"prerenderedAt":40549},["ShallowReactive",2],{"$flw0hv351bnn6":3,"cmd-posts":4,"learn-step-\u002Flearn\u002Fnuxt-4-3\u002F04-route-groups-in-page-meta":38634,"learn-siblings-nuxt-4-3":38945,"learn-landing-for-step-nuxt-4-3":40458},"\u003C!DOCTYPE html>\u003Chtml class=\"geist_mono_fe992356-module__VEU71W__className geistsans_d5a4f12f-module__7Z0MQa__className geist_mono_fe992356-module__VEU71W__variable geistsans_d5a4f12f-module__7Z0MQa__variable overflow-hidden antialiased bg-background-100\" lang=\"en\">\u003Chead>\u003Cmeta charSet=\"utf-8\"\u002F>\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\u002F>\u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2knmvlzwzsv42.css\" data-precedence=\"next\"\u002F>\u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F0tikn6gi9lee7.css\" data-precedence=\"next\"\u002F>\u003Clink rel=\"stylesheet\" href=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2vswdguc8nn24.css\" data-precedence=\"next\"\u002F>\u003Clink rel=\"preload\" as=\"script\" fetchPriority=\"low\" href=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F1-y-5f_3l2jwq.js\"\u002F>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F217a8hb2_z7wz.js\" async=\"\">\u003C\u002Fscript>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F1f0dx5-7_ge7a.js\" async=\"\">\u003C\u002Fscript>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F3nybxi3kies7g.js\" async=\"\">\u003C\u002Fscript>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002Fturbopack-05f9tr555viu-.js\" async=\"\">\u003C\u002Fscript>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\" async=\"\" crossorigin=\"\">\u003C\u002Fscript>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\" async=\"\" crossorigin=\"\">\u003C\u002Fscript>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2q_rxa5-03gh9.js\" async=\"\" crossorigin=\"\">\u003C\u002Fscript>\u003Cmeta name=\"next-size-adjust\" content=\"\"\u002F>\u003Ctitle>Deployment is building\u003C\u002Ftitle>\u003Clink href=\"https:\u002F\u002Fassets.vercel.com\u002Fimage\u002Fupload\u002Fq_auto\u002Ffront\u002Ffavicon\u002Fvercel\u002Fbuilding.ico\" rel=\"icon shortcut\" type=\"image\u002Fx-icon\"\u002F>\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F0c0hxoamwjsbw.js\" noModule=\"\">\u003C\u002Fscript>\u003C\u002Fhead>\u003Cbody>\u003Cdiv hidden=\"\">\u003C!--$-->\u003C!--\u002F$-->\u003C\u002Fdiv>\u003Cscript id=\"theme-script\">((ge,ke,et,F,ne,ce,xe,Ue)=>{let oe=document.documentElement,Ze=[\"light\",\"dark\"];function ve(Xe){var be;(Array.isArray(ge)?ge:[ge]).forEach(Ce=>{let mt=Ce===\"class\",Lt=mt&&ce?ne.map(At=>ce[At]||At):ne;mt?(oe.classList.remove(...Lt),oe.classList.add(ce?.[Xe]?ce[Xe]:Xe)):oe.setAttribute(Ce,Xe)}),be=Xe,Ue&&Ze.includes(be)&&(oe.style.colorScheme=be)}if(F)ve(F);else try{let Xe=localStorage.getItem(ke)||et,be=xe&&Xe===\"system\"?window.matchMedia(\"(prefers-color-scheme: dark)\").matches?\"dark\":\"light\":Xe;ve(be)}catch{}})(\"class\",\"geist-theme\",\"system\",null,[\"light\",\"dark\"],{\"light\":\"light-theme\",\"dark\":\"dark-theme\"},true,true)\u003C\u002Fscript>\u003C!--$-->\u003C!--\u002F$-->\u003Chead>\u003C\u002Fhead>\u003Cdiv class=\"index-module__g3_XOa__container\">\u003Cmain class=\"flex flex-col items-center justify-start gap-4 flex-initial index-module__g3_XOa__main\">\u003Cdiv class=\"flex flex-col items-center justify-center gap-4 flex-initial\">\u003Cdiv role=\"status\" aria-label=\"Loading\" data-testid=\"geistcn\u002Fspinner\" class=\"text-gray-700 relative inline-block aspect-square transform-gpu size-4 index-module__g3_XOa__spinner\" aria-hidden=\"false\" data-fade-out=\"false\">\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-900ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(0deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-800ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(36deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-700ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(72deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-600ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(108deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-500ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(144deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-400ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(180deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-300ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(216deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-200ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(252deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:-100ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(288deg) translate(146%)\">\u003C\u002Fdiv>\u003Cdiv aria-hidden=\"true\" class=\"absolute top-1\u002F2 left-1\u002F2 origin-center -translate-x-1\u002F2 -translate-y-1\u002F2 rounded-full bg-current text-current will-change-transform h-[1.5px] w-1\" style=\"--animation-delay:0ms;--animation-duration:1000ms;animation:spinner-opacity var(--animation-duration, 1.2s) linear infinite;animation-delay:var(--animation-delay, 0);transform:rotate(324deg) translate(146%)\">\u003C\u002Fdiv>\u003Cspan class=\"sr-only\">Loading...\u003C\u002Fspan>\u003C\u002Fdiv>\u003Ch1 aria-label=\"Deployment is building\" class=\"text-heading-24 index-module__g3_XOa__title\">\u003Cspan>Deployment is\u003C\u002Fspan> \u003Cspan class=\"index-module__g3_XOa__stack\">\u003Cspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.12s\">b\u003C\u002Fspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.135s\">u\u003C\u002Fspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.15s\">i\u003C\u002Fspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.16499999999999998s\">l\u003C\u002Fspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.18s\">d\u003C\u002Fspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.195s\">i\u003C\u002Fspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.21s\">n\u003C\u002Fspan>\u003Cspan class=\"index-module__g3_XOa__letter\" data-direction=\"exit\" data-animate=\"false\" style=\"animation-delay:0.22499999999999998s\">g\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fh1>\u003C\u002Fdiv>\u003Ca role=\"link\" href=\"https:\u002F\u002Fvercel.com\u002Fdeployments\u002Fvantolbennett-1xpfyfjx8-vantol-bennetts-projects-e2eca9a9.vercel.app?redirect=1\" target=\"_blank\" type=\"submit\" rel=\"noopener\" tabindex=\"0\" data-react-aria-pressable=\"true\" aria-hidden=\"false\" data-fade-out=\"false\" class=\"outline-none m-0 p-0 border-0 align-baseline no-underline group\u002Ftrigger relative cursor-pointer select-none transform translate-z-0 flex text-[var(--themed-fg,_var(--ds-background-100))] bg-[var(--themed-bg,_var(--ds-gray-1000))] font-medium !px-(--geist-gap-half) max-w-full items-center justify-center transition-[border-color, background,color,transform,box-shadow] duration-[time:150ms] ease-in-out [&amp;_svg]:shrink-0 disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:text-[var(--ds-gray-700)] disabled:bg-[var(--ds-gray-100)] aria-disabled:text-[var(--ds-gray-700)] aria-disabled:bg-[var(--ds-gray-100)] disabled:![--themed-border:_var(--ds-gray-400)] [--x-padding:6px] [--height:32px] !rounded-full [--themed-bg:_var(--ds-background-100)] shadow-[0_0_0_1px_var(--themed-border,_transparent)] [--themed-hover-bg:_var(--ds-gray-alpha-200)] [--themed-fg:_var(--ds-gray-1000)] [--themed-border:_var(--ds-gray-400)] shadow-[var(--ds-shadow-border-small)] data-[focus]:transition-none data-[focus]:shadow-[var(--ds-focus-ring)] text-(length:--geist-form-small-font) [--spinner-size:16px] h-[var(--height)] data-hover:bg-[var(--ds-gray-100)] dark-theme:data-hover:bg-[var(--ds-gray-200)] data-hover:text-[var(--themed-fg)] data-hover:disabled:bg-[var(--ds-gray-100)] index-module__g3_XOa__buildStatusLink !pl-1.5 !pr-2.5\" data-geist-button=\"\" data-prefix=\"true\" data-suffix=\"true\" data-version=\"v1\" style=\"--geist-icon-size:16px\">\u003Cspan class=\"mr-[3px] min-w-5 shrink-0 flex items-center justify-center [&amp;_svg[data-slot=geist-icon]]:size-(--ds-control-decoration-size)\">\u003Cdiv class=\"index-module__RzSyvq__dot index-module__RzSyvq__building\">\u003C\u002Fdiv>\u003C\u002Fspan>\u003Cspan class=\"truncate inline-block px-1.5\">\u003Cp>View Build\u003C\u002Fp>\u003C\u002Fspan>\u003Cspan class=\"ml-1 shrink-0 mr-0.5 flex items-center justify-center [&amp;_svg[data-slot=geist-icon]]:size-(--ds-control-decoration-size)\">\u003Csvg viewBox=\"0 0 16 16\" height=\"16\" width=\"16\" data-slot=\"geist-icon\" style=\"color:var(--ds-gray-900)\">\u003Cpath fill=\"currentColor\" fill-rule=\"evenodd\" d=\"M11.5 9.75v1.5q-.02.23-.25.25h-6.5a.25.25 0 0 1-.25-.25v-6.5c0-.14.11-.25.25-.25H7V3H4.75C3.78 3 3 3.78 3 4.75v6.5c0 .97.78 1.75 1.75 1.75h6.5c.97 0 1.75-.78 1.75-1.75V9h-1.5zM8.5 3h3.75c.41 0 .75.34.75.75V7.5h-1.5V5.56L8.53 8.53 8 9.06 6.94 8l.53-.53 2.97-2.97H8.5z\" clip-rule=\"evenodd\">\u003C\u002Fpath>\u003C\u002Fsvg>\u003C\u002Fspan>\u003C\u002Fa>\u003C\u002Fmain>\u003Cfooter class=\"index-module__g3_XOa__footer\" data-fade-out=\"false\" aria-hidden=\"false\">\u003Cp class=\"text-[14px] leading-[20px] font-medium text-gray-900\">This page will update once the build completes.\u003C\u002Fp>\u003C\u002Ffooter>\u003C\u002Fdiv>\u003C!--$-->\u003C!--\u002F$-->\u003C!--$-->\u003C!--\u002F$-->\u003Cscript src=\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F1-y-5f_3l2jwq.js\" id=\"_R_\" async=\"\">\u003C\u002Fscript>\u003Cscript>(self.__next_f=self.__next_f||[]).push([0])\u003C\u002Fscript>\u003Cscript>self.__next_f.push([1,\"1:\\\"$Sreact.fragment\\\"\\n2:I[77183,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"GeistProvider\\\"]\\n3:I[24251,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"default\\\"]\\n4:I[85174,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"default\\\"]\\n5:I[52957,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"Analytics\\\"]\\n7:I[13944,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"OutletBoundary\\\"]\\n8:\\\"$Sreact.suspense\\\"\\nb:I[13944,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"ViewportBoundary\\\"]\\nd:I[13944,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"MetadataBoundary\\\"]\\nf:I[40718,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\"],\\\"default\\\",1]\\n10:I[12054,[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\",\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2q_rxa5-03gh9.js\\\"],\\\"IndexPageInner\\\"]\\n:HL[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2knmvlzwzsv42.css\\\",\\\"style\\\"]\\n:HL[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F0tikn6gi9lee7.css\\\",\\\"style\\\"]\\n:HL[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fmedia\u002F797e433ab948586e.p.1v5bejj26fx9h.woff2\\\",\\\"font\\\",{\\\"crossOrigin\\\":\\\"\\\",\\\"type\\\":\\\"font\u002Fwoff2\\\"}]\\n:HL[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fmedia\u002FGeist_Variable-s.p.2v-cf9syxr5ni.woff2\\\",\\\"font\\\",{\\\"crossOrigin\\\":\\\"\\\",\\\"type\\\":\\\"font\u002Fwoff2\\\"}]\\n:HL[\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2vswdguc8nn24.css\\\",\\\"style\\\"]\\na:X\\n0:{\\\"P\\\":null,\\\"c\\\":[\\\"\\\",\\\"api\\\",\\\"auth\\\",\\\"get-session?slug=api%2Fauth%2Fget-session\\\"],\\\"q\\\":\\\"?slug=api%2Fauth%2Fget-session\\\",\\\"i\\\":false,\\\"f\\\":[[[\\\"\\\",{\\\"children\\\":[[\\\"slug\\\",\\\"api\u002Fauth\u002Fget-session\\\",\\\"oc\\\",[\\\"demo\\\"]],{\\\"children\\\":[\\\"__PAGE__?{\\\\\\\"slug\\\\\\\":\\\\\\\"api\u002Fauth\u002Fget-session\\\\\\\"}\\\",{},\\\"$undefined\\\",\\\"$undefined\\\",4096]},\\\"$undefined\\\",\\\"$undefined\\\",4096]},\\\"$undefined\\\",\\\"$undefined\\\",4112],[[\\\"$\\\",\\\"$1\\\",\\\"c\\\",{\\\"children\\\":[[[\\\"$\\\",\\\"link\\\",\\\"0\\\",{\\\"rel\\\":\\\"stylesheet\\\",\\\"href\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2knmvlzwzsv42.css\\\",\\\"precedence\\\":\\\"next\\\",\\\"crossOrigin\\\":\\\"$undefined\\\",\\\"nonce\\\":\\\"$undefined\\\"}],[\\\"$\\\",\\\"link\\\",\\\"1\\\",{\\\"rel\\\":\\\"stylesheet\\\",\\\"href\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F0tikn6gi9lee7.css\\\",\\\"precedence\\\":\\\"next\\\",\\\"crossOrigin\\\":\\\"$undefined\\\",\\\"nonce\\\":\\\"$undefined\\\"}],[\\\"$\\\",\\\"script\\\",\\\"script-0\\\",{\\\"src\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2z7kolpphc8-9.js\\\",\\\"async\\\":true,\\\"nonce\\\":\\\"$undefined\\\"}],[\\\"$\\\",\\\"script\\\",\\\"script-1\\\",{\\\"src\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2v08bphns3tr-.js\\\",\\\"async\\\":true,\\\"nonce\\\":\\\"$undefined\\\"}]],[\\\"$\\\",\\\"html\\\",null,{\\\"className\\\":\\\"geist_mono_fe992356-module__VEU71W__className geistsans_d5a4f12f-module__7Z0MQa__className geist_mono_fe992356-module__VEU71W__variable geistsans_d5a4f12f-module__7Z0MQa__variable overflow-hidden antialiased bg-background-100\\\",\\\"lang\\\":\\\"en\\\",\\\"suppressHydrationWarning\\\":true,\\\"children\\\":[\\\"$\\\",\\\"body\\\",null,{\\\"children\\\":[[\\\"$\\\",\\\"$L2\\\",null,{\\\"children\\\":[\\\"\"])\u003C\u002Fscript>\u003Cscript>self.__next_f.push([1,\"$\\\",\\\"$L3\\\",null,{\\\"parallelRouterKey\\\":\\\"children\\\",\\\"error\\\":\\\"$undefined\\\",\\\"errorStyles\\\":\\\"$undefined\\\",\\\"errorScripts\\\":\\\"$undefined\\\",\\\"template\\\":[\\\"$\\\",\\\"$L4\\\",null,{}],\\\"templateStyles\\\":\\\"$undefined\\\",\\\"templateScripts\\\":\\\"$undefined\\\",\\\"notFound\\\":[[[\\\"$\\\",\\\"title\\\",null,{\\\"children\\\":\\\"404: This page could not be found.\\\"}],[\\\"$\\\",\\\"div\\\",null,{\\\"style\\\":{\\\"fontFamily\\\":\\\"system-ui,\\\\\\\"Segoe UI\\\\\\\",Roboto,Helvetica,Arial,sans-serif,\\\\\\\"Apple Color Emoji\\\\\\\",\\\\\\\"Segoe UI Emoji\\\\\\\"\\\",\\\"height\\\":\\\"100vh\\\",\\\"textAlign\\\":\\\"center\\\",\\\"display\\\":\\\"flex\\\",\\\"flexDirection\\\":\\\"column\\\",\\\"alignItems\\\":\\\"center\\\",\\\"justifyContent\\\":\\\"center\\\"},\\\"children\\\":[\\\"$\\\",\\\"div\\\",null,{\\\"children\\\":[[\\\"$\\\",\\\"style\\\",null,{\\\"dangerouslySetInnerHTML\\\":{\\\"__html\\\":\\\"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}\\\"}}],[\\\"$\\\",\\\"h1\\\",null,{\\\"className\\\":\\\"next-error-h1\\\",\\\"style\\\":{\\\"display\\\":\\\"inline-block\\\",\\\"margin\\\":\\\"0 20px 0 0\\\",\\\"padding\\\":\\\"0 23px 0 0\\\",\\\"fontSize\\\":24,\\\"fontWeight\\\":500,\\\"verticalAlign\\\":\\\"top\\\",\\\"lineHeight\\\":\\\"49px\\\"},\\\"children\\\":404}],[\\\"$\\\",\\\"div\\\",null,{\\\"style\\\":{\\\"display\\\":\\\"inline-block\\\"},\\\"children\\\":[\\\"$\\\",\\\"h2\\\",null,{\\\"style\\\":{\\\"fontSize\\\":14,\\\"fontWeight\\\":400,\\\"lineHeight\\\":\\\"49px\\\",\\\"margin\\\":0},\\\"children\\\":\\\"This page could not be found.\\\"}]}]]}]}]],[]],\\\"forbidden\\\":\\\"$undefined\\\",\\\"unauthorized\\\":\\\"$undefined\\\"}]}],[\\\"$\\\",\\\"$L5\\\",null,{}]]}]}]]}],{\\\"children\\\":[[\\\"$\\\",\\\"$1\\\",\\\"c\\\",{\\\"children\\\":[null,[\\\"$\\\",\\\"$L3\\\",null,{\\\"parallelRouterKey\\\":\\\"children\\\",\\\"error\\\":\\\"$undefined\\\",\\\"errorStyles\\\":\\\"$undefined\\\",\\\"errorScripts\\\":\\\"$undefined\\\",\\\"template\\\":[\\\"$\\\",\\\"$L4\\\",null,{}],\\\"templateStyles\\\":\\\"$undefined\\\",\\\"templateScripts\\\":\\\"$undefined\\\",\\\"notFound\\\":\\\"$undefined\\\",\\\"forbidden\\\":\\\"$undefined\\\",\\\"unauthorized\\\":\\\"$undefined\\\"}]]}],{\\\"children\\\":[[\\\"$\\\",\\\"$1\\\",\\\"c\\\",{\\\"children\\\":[\\\"$L6\\\",[[\\\"$\\\",\\\"link\\\",\\\"0\\\",{\\\"rel\\\":\\\"stylesheet\\\",\\\"href\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2vswdguc8nn24.css\\\",\\\"precedence\\\":\\\"next\\\",\\\"crossOrigin\\\":\\\"$undefined\\\",\\\"nonce\\\":\\\"$undefined\\\"}],[\\\"$\\\",\\\"script\\\",\\\"script-0\\\",{\\\"src\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2q_rxa5-03gh9.js\\\",\\\"async\\\":true,\\\"nonce\\\":\\\"$undefined\\\"}]],[\\\"$\\\",\\\"$L7\\\",null,{\\\"children\\\":[\\\"$\\\",\\\"$8\\\",null,{\\\"name\\\":\\\"Next.MetadataOutlet\\\",\\\"children\\\":\\\"$@9\\\"}]}]]}],{},null,false,null]},null,false,\\\"$a\\\"]},null,false,null],[\\\"$\\\",\\\"$1\\\",\\\"h\\\",{\\\"children\\\":[null,[\\\"$\\\",\\\"$Lb\\\",null,{\\\"children\\\":\\\"$Lc\\\"}],[\\\"$\\\",\\\"div\\\",null,{\\\"hidden\\\":true,\\\"children\\\":[\\\"$\\\",\\\"$Ld\\\",null,{\\\"children\\\":[\\\"$\\\",\\\"$8\\\",null,{\\\"name\\\":\\\"Next.Metadata\\\",\\\"children\\\":\\\"$Le\\\"}]}]}],[\\\"$\\\",\\\"meta\\\",null,{\\\"name\\\":\\\"next-size-adjust\\\",\\\"content\\\":\\\"\\\"}]]}],false]],\\\"m\\\":\\\"$undefined\\\",\\\"G\\\":[\\\"$f\\\",[[\\\"$\\\",\\\"link\\\",\\\"0\\\",{\\\"rel\\\":\\\"stylesheet\\\",\\\"href\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F2knmvlzwzsv42.css\\\",\\\"precedence\\\":\\\"next\\\",\\\"crossOrigin\\\":\\\"$undefined\\\",\\\"nonce\\\":\\\"$undefined\\\"}],[\\\"$\\\",\\\"link\\\",\\\"1\\\",{\\\"rel\\\":\\\"stylesheet\\\",\\\"href\\\":\\\"https:\u002F\u002Finstant-preview-site.vercel.app\u002F_next\u002Fstatic\u002Fimmutable\u002Fchunks\u002F0tikn6gi9lee7.css\\\",\\\"precedence\\\":\\\"next\\\",\\\"crossOrigin\\\":\\\"$undefined\\\",\\\"nonce\\\":\\\"$undefined\\\"}]]],\\\"S\\\":false,\\\"h\\\":null,\\\"r\\\":\\\"$undefined\\\",\\\"s\\\":\\\"$undefined\\\",\\\"a\\\":\\\"$undefined\\\",\\\"l\\\":\\\"$undefined\\\",\\\"p\\\":\\\"$undefined\\\",\\\"d\\\":\\\"$undefined\\\",\\\"b\\\":\\\"0hu6vySRqpezEGfzpOCW-\\\"}\\na:C\\n6:[\\\"$\\\",\\\"$L10\\\",null,{\\\"deploymentId\\\":\\\"dpl_2rhjRkbTkrpUZwhe7LosxWBurGyk\\\",\\\"hostname\\\":\\\"vantolbennett-1xpfyfjx8-vantol-bennetts-projects-e2eca9a9.vercel.app\\\",\\\"initialReadyState\\\":\\\"BUILDING\\\"}]\\nc:[[\\\"$\\\",\\\"meta\\\",\\\"0\\\",{\\\"charSet\\\":\\\"utf-8\\\"}],[\\\"$\\\",\\\"meta\\\",\\\"1\\\",{\\\"name\\\":\\\"viewport\\\",\\\"content\\\":\\\"width=device-width, initial-scale=1\\\"}]]\\n9:null\\ne:[]\\n\"])\u003C\u002Fscript>\u003C\u002Fbody>\u003C\u002Fhtml>",[5,137,323,3795,4169,7086,8318,9886,10444,14595,14657,15735,17142,18144,20696,20899,20964,21015,21243,21936,22569,22890,23647,28600,29944,30041,30792,31017,32394,33205,34343,38023,38613],{"id":6,"title":7,"author":8,"body":12,"canonical":116,"categories":116,"date":117,"description":118,"excerpt":116,"extension":119,"img":120,"keywords":121,"language":128,"meta":129,"navigation":130,"ogImage":116,"path":131,"rating":132,"readTime":133,"seo":134,"stem":135,"tag":126,"__hash__":136},"blog\u002Fblog\u002Fadd-env-variables-runtime.md","Adding environment variables to the runtime in Nuxt3 [Beta]",{"name":9,"website":10,"img":11},"Vantol Bennett","www.teammps.com","https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1636919468\u002FDSC_0988_zsfhgy.jpg",{"type":13,"value":14,"toc":110},"minimark",[15,20,24,31,36,44,47,57,60,64,67,72,75,77,83,89,92,98,101,107],[16,17,19],"h1",{"id":18},"runtime-config-in-nuxt-3","Runtime Config in Nuxt 3",[21,22,23],"p",{},"Nuxt3 is still in beta and is not ready for production use (ish), but that hasn't stopped many developers to take advantage of the unique features, power and significant performance boost that Nuxt3 offers.",[25,26,28],"blog-alert",{"type":27},"warning",[21,29,30],{},"Nuxt 3 is now stable! The information below refers to early RC versions but the core concepts of Runtime Config remain valid.",[32,33,35],"h2",{"id":34},"exposing-environment-variables-to-the-runtime","Exposing Environment Variables to the Runtime",[21,37,38,39,43],{},"All the heavy lifting is done by Nuxt in the nuxt.config.js file. This file is where you can set up the environment variables that you want to expose to the rest of the application. This is done by adding the ",[40,41,42],"code",{},"privateRuntimeConfig"," or publicRuntimeConfig` property to the nuxt.config.js file. As the name suggests, both of these properties are used based on accessiblity on the client side or server side.",[21,45,46],{},"Example:",[48,49,54],"pre",{"className":50,"code":52,"language":53},[51],"language-text","\nexport default defineNuxtConfig({\n  ...\n  publicRuntimeConfig: {\n    \u002F\u002F Will be available on both server and client\n    apiUrl: 'https:\u002F\u002Fapi.example.com',\n  },\n  privateRuntimeConfig: {\n    apiUrl: process.env.API_URL,\n    apiKey: process.env.API_KEY,\n    ...\n  },\n  ...\n})\n","text",[40,55,52],{"__ignoreMap":56},"",[21,58,59],{},"The publicRuntimeConfig property will be added to Nuxt pages through the apiUrl allowing for universal access for apiUrl to both client and server.",[32,61,63],{"id":62},"environmental-variables-using-supabase","Environmental Variables - Using Supabase",[21,65,66],{},"Ok so, whether you are a new developer or a experienced one environment variables are common way to access configuration. We add a .env file to the root directory, Nuxt will added these variables to process.env and is accessible in nuxt.config.js. Let see what how to add Supabase url and key.",[21,68,69],{},[40,70,71],{},"npm install @supabase\u002Fsupabase-js",[73,74],"hr",{},[21,76,46],{},[48,78,81],{"className":79,"code":80,"language":53},[51],".env\n\nSUPABASE_URL=\u002F* URL KEY HERE*\u002F\nSUPABASE_ANON_KEY=\u002F* KEY ADDED HERE *\u002F\n\n",[40,82,80],{"__ignoreMap":56},[48,84,87],{"className":85,"code":86,"language":53},[51],"\nexport default defineNuxtConfig({\n  ...\n  publicRuntimeConfig: {\n    \u002F\u002F Will be available on both server and client\n    SUPABASE_URL: process.env.SUPABASE_URL,\n    SUPABASE_ANON_KEY: process.env.SUPABASE_ANON_KEY\n  },\n  ...\n})\n",[40,88,86],{"__ignoreMap":56},[21,90,91],{},"This is important part of this post, accessing the supabase variables in the publicRunTime config can only be done on the client side using the useRunTimeConfig in the setup or lifecycle hooks. This is a crucial step to remember!",[48,93,96],{"className":94,"code":95,"language":53},[51],"\u003Ctemplate>\n  \u003Cdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nconst config = useRuntimeConfig()\n\u003C\u002Fscript>\n",[40,97,95],{"__ignoreMap":56},[21,99,100],{},"useRuntimeConfig only works in setup or lifecycle hooks.... this was a pain for me as I could not discover the error because of this, and more importantly since env variables are accessed in the publicRuntimeConfig so only on the client side. Therefore in setting up we could connect supabase with our runtime.",[48,102,105],{"className":103,"code":104,"language":53},[51],"import { createClient } from '@supabase\u002Fsupabase-js'\n\n.....\n\n....\n\nconst config = useRuntimeConfig()\n\nconst supabase = createClient(config.SUPABASE_URL, config.SUPABASE_KEY)\n\n\u002F* Followed by the supabase implementation you would like to work on*\u002F\n",[40,106,104],{"__ignoreMap":56},[21,108,109],{},"And thats how you get access to your env variables. In my next post we will explore how to you use composables and plugins to achieve this whole setup but with cleaner out look.",{"title":56,"searchDepth":111,"depth":111,"links":112},3,[113,115],{"id":34,"depth":114,"text":35},2,{"id":62,"depth":114,"text":63},null,"2022-01-15","Exposing environment variables to the runtime in Nuxt3","md","https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1642350413\u002Fvantol\u002Fblack-text_emxagi.png",[122,123,124,125,126,127],"developer","jamaican","vue.js","supabase","Nuxt3","vite","Javascript - Vue.js",{},true,"\u002Fblog\u002Fadd-env-variables-runtime",0,10,{"title":7,"description":118},"blog\u002Fadd-env-variables-runtime","4SK4zZT99RtjhiV4e4jmQppT3cFMuHnutSwWRAwb8Lo",{"id":138,"title":139,"author":140,"body":141,"canonical":116,"categories":116,"date":306,"description":307,"excerpt":116,"extension":119,"img":120,"keywords":308,"language":315,"meta":316,"navigation":130,"ogImage":116,"path":317,"rating":132,"readTime":318,"seo":319,"stem":320,"tag":321,"__hash__":322},"blog\u002Fblog\u002Falways-protect-the-king.md","Always Protect the King - Notes from a Founder Who Stopped Searching",{"name":9,"website":10,"img":11},{"type":13,"value":142,"toc":299},[143,147,159,166,173,177,184,187,193,199,206,209,213,216,219,223,226,229,232,235,239,245,248,254,257,261,264,282,285,288,291,294],[16,144,146],{"id":145},"always-protect-the-king","Always Protect the King",[21,148,149,150,154,155],{},"I started coding around my early twenties. The first company I put a name to was ",[151,152,153],"strong",{},"Teckare Online"," - not a software shop, not really. We were hardware-and-software people. We'd go around to different schools and see if we could beef up their systems, so to speak. It was a good learning experience, and it taught me the first half of a mantra I still carry: ",[156,157,158],"em",{},"keep evolving, keep adapting to the situation.",[21,160,161,162,165],{},"Then came the web work - WordPress at first, then more. And in 2015, at twenty-nine, I started ",[151,163,164],{},"Mashed Potato Studios",".",[21,167,168,169,172],{},"If you'd asked me then what I was searching for, I'd have had a hundred answers and none of them right. That's the thing about starting young. You think the struggle is ",[156,170,171],{},"finding the thing",". It isn't. The struggle is everything that happens after you've already decided to build.",[32,174,176],{"id":175},"the-struggle-was-never-the-code","The struggle was never the code",[21,178,179,180,183],{},"At Mashed Potato Studios we have a rule: ",[156,181,182],{},"you must evolve with code."," That came straight out of Teckare. The technology moves, so you move. Easy to say about frameworks. Harder to say about people.",[21,185,186],{},"Because the real struggle of building a company young wasn't technical. It was two things.",[21,188,189,192],{},[151,190,191],{},"The first was being too ambitious."," Wanting more than the moment could hold. That's a kind of debt you pay for years.",[21,194,195,198],{},[151,196,197],{},"The second was betrayal."," Me and my partners - we've had people who should have been in our corner turn and stab us in the back. More than once. The friend cycle. People change over the course of years, and some of them changed right out of your life. That's part of business, I guess. You learn it the hard way, and then you keep walking.",[21,200,201,202,205],{},"There's a particular kind of tired that ",[156,203,204],{},"isn't"," exhaustion. It's the realization that sometimes it isn't what you know - it's who you know. The shadiness of business. The politics. The rooms you aren't in. That's essentially what betrayal is: someone choosing the room you aren't in over the one you built together.",[21,207,208],{},"I won't pretend it didn't sting. But here's what I'll tell you plainly: the never-quit attitude didn't hurt me. It's been in my family for years. It's been a driving factor for the Bennett name. My name is important. Family is important. The track stayed the same for me while the company changed, while friends changed, while business partners changed. I kept on going.",[32,210,212],{"id":211},"what-drives-me-now","What drives me now",[21,214,215],{},"What drives me most now is my daughter. I believe I should provide for her everything I didn't have, and more. That's the cleanest fuel there is - not the young-founder ego, not the chase, but the quiet certainty that something I build outlives me in her.",[21,217,218],{},"And that's the part people get wrong about \"finding your place as you grow older.\" They frame it like a crisis. Like at some age you're supposed to panic and wonder who you are. I don't. The drive didn't fade and it didn't turn on me. It just got aimed at the right thing.",[32,220,222],{"id":221},"i-stopped-searching","I stopped searching",[21,224,225],{},"Here's where I am at forty.",[21,227,228],{},"We're not searching anymore - me, the team, the company. Everything we have as a team, everything we have built, we're not out looking for a missing piece. We just want the opportunity to put forward what we have.",[21,230,231],{},"Everybody gets the same opportunity to put something forward. It's fair that way. But it requires a little luck, and a little persistence. I've got the persistence. The luck is the only thing left to meet us halfway.",[21,233,234],{},"So no - I'm not searching. I'm getting ready. There's a difference, and it took me most of my thirties to feel it.",[32,236,238],{"id":237},"always-protect-the-king-1","Always protect the King",[21,240,241,242],{},"If there's a single thing that makes any of this real, it's this: ",[151,243,244],{},"always protect the King.",[21,246,247],{},"For a long time I thought the King was me - my vision, my company, my name. That's the young-founder mistake. The King isn't you.",[21,249,250,253],{},[151,251,252],{},"The King is the person in front of you."," The person around you. The client, the teammate, the partner, the stranger in the room. You protect them. And if you do it right, they should see you - the person who is me - as a King too. Not above them. Beside them. Sovereign, and owed the same care you just gave.",[21,255,256],{},"That's the creed I try to lead with at all times. It goes past core values. It's how I show up. Because everything I do touches the three things that actually shaped me: the ambition that nearly sank me, the betrayal that taught me who was really in the corner, and the daughter who gave the whole engine a new reason to run. You protect the King, and all three of those fall into place behind it.",[32,258,260],{"id":259},"king-in-the-moment","King in the moment",[21,262,263],{},"The mantra evolved. It had to.",[21,265,266,269,270,273,274,277,278,281],{},[151,267,268],{},"King in the moment."," Different times, different points in your life - in that moment, you are the King. You are the person. You are ",[156,271,272],{},"him"," - the one who did what you did. Not the one who ",[156,275,276],{},"will"," act, not the one who ",[156,279,280],{},"should have."," The one who acted, right there, in the moment that already happened.",[21,283,284],{},"That's the difference between the twenty-nine-year-old who started Mashed Potato Studios and the forty-year-old writing this. The young one was protecting a King he thought was out there somewhere - a future, a unicorn, a version of himself he hadn't met. The older one knows the King was the person in front of him the whole time, and that in each moment he was already the King who had to show up.",[21,286,287],{},"I didn't find my place. My place found me, the way it finds anyone who refuses to leave the track. The code evolved. The partners came and went. The friends cycled. The betrayal came and the persistence outlasted it. And through all of it, the rule held:",[21,289,290],{},"Protect the person in front of you. Be the King in the moment.",[21,292,293],{},"That's the whole post. That's the whole company. That's the whole life, if you let it be.",[21,295,296],{},[156,297,298],{},"Now we just need the luck to put it forward. And I've got time.",{"title":56,"searchDepth":111,"depth":111,"links":300},[301,302,303,304,305],{"id":175,"depth":114,"text":176},{"id":211,"depth":114,"text":212},{"id":221,"depth":114,"text":222},{"id":237,"depth":114,"text":238},{"id":259,"depth":114,"text":260},"2026-07-20","On starting young, surviving betrayal, and the creed that carried me to 40 - protect the person in front of you, and be the King in the moment.",[309,123,310,311,312,313,314],"founder","startup","mashed potato studios","teckare","entrepreneurship","personal","English",{},"\u002Fblog\u002Falways-protect-the-king",12,{"title":139,"description":307},"blog\u002Falways-protect-the-king","Founder","d--A4RnurzX-X5WsHmbs0Ddq6JP9aIkwT_jn0d4lK_Y",{"id":324,"title":325,"author":326,"body":328,"canonical":116,"categories":116,"date":3757,"description":3758,"excerpt":3759,"extension":119,"img":3779,"keywords":3780,"language":3788,"meta":3789,"navigation":130,"ogImage":116,"path":3790,"rating":474,"readTime":526,"seo":3791,"stem":3792,"tag":3793,"__hash__":3794},"blog\u002Fblog\u002Fbuilding-null-agent.md","Building null-agent: A Multi-Provider Coding Assistant with 49 Built-in Tools",{"name":9,"img":11,"website":327},"https:\u002F\u002Fvantolbennett.com",{"type":13,"value":329,"toc":3731},[330,334,337,343,361,375,379,382,423,426,434,438,441,536,539,543,546,664,668,812,815,834,837,878,883,887,902,906,909,971,975,978,1082,1087,1129,1133,1136,1270,1274,1277,1281,1369,1373,1376,1529,1550,1554,1560,1703,1707,1710,1862,1866,1869,1992,1996,1999,2115,2119,2122,2206,2209,2213,2217,2220,2282,2291,2295,2298,2310,2314,2317,2341,2365,2369,2372,2396,2400,2403,2407,2410,2455,2459,2532,2548,2552,2574,2581,2585,2588,2602,2606,2804,2815,2819,2822,2826,2918,2921,2925,2928,3039,3060,3064,3071,3178,3182,3185,3283,3303,3307,3310,3539,3543,3546,3603,3621,3625,3628,3664,3667,3678,3682,3685,3688,3691,3694,3724,3727],[16,331,333],{"id":332},"introduction","Introduction",[21,335,336],{},"Most coding assistants lock you into a single provider, a single interface, and a black-box you can't extend. If you want OpenAI for one project, Anthropic for another, and a local model for sensitive work, you're juggling three different tools with three different CLIs.",[21,338,339,342],{},[151,340,341],{},"null-agent"," is an open-source TypeScript library that solves this by unifying multi-provider LLM support, a rich tool system, a terminal UI, an HTTP API, and even a developer accountability tracker — all behind one configurable interface.",[21,344,345,346,349,350,349,353,356,357,360],{},"It ships with ",[151,347,348],{},"49 built-in tools",", ",[151,351,352],{},"5 LLM providers",[151,354,355],{},"4 interfaces",", and a ",[151,358,359],{},"plugin system"," that lets you extend it without forking the codebase.",[25,362,364],{"type":363},"info",[21,365,366,367,370,371,374],{},"null-agent is built with Vite+ and ships as both a library (",[40,368,369],{},"npm install null-agent",") and a global CLI (",[40,372,373],{},"npm install -g null-agent",").",[16,376,378],{"id":377},"what-is-null-agent","What Is null-agent?",[21,380,381],{},"null-agent is an interactive coding assistant that works as:",[383,384,385,399,405,411,417],"ul",{},[386,387,388,391,392,349,395,398],"li",{},[151,389,390],{},"A library"," — import ",[40,393,394],{},"Agent",[40,396,397],{},"ToolRegistry",", and providers into your own tools",[386,400,401,404],{},[151,402,403],{},"A TUI"," — an Ink-based terminal interface with live activity tracking",[386,406,407,410],{},[151,408,409],{},"A REPL"," — a lightweight readline interface with zero extra dependencies",[386,412,413,416],{},[151,414,415],{},"An HTTP server"," — REST API with SSE streaming",[386,418,419,422],{},[151,420,421],{},"A one-shot CLI"," — send a single message and get a response",[21,424,425],{},"It auto-detects which LLM provider you have configured, manages API keys through your OS keychain, and persists conversations to disk so you can resume them later.",[427,428,431],"callout",{"icon":429,"title":430},"🚀","Why Another Agent Library?",[21,432,433],{},"Because most agent frameworks are either too opinionated (you can't swap providers) or too minimal (you write your own tool registry). null-agent sits in the middle: batteries included, but everything is overridable.",[16,435,437],{"id":436},"architecture-at-a-glance","Architecture at a Glance",[21,439,440],{},"The package is organized into focused modules:",[48,442,446],{"className":443,"code":444,"language":445,"meta":56,"style":56},"language-txt shiki shiki-themes github-light github-dark","src\u002F\n  providers\u002F      # LLM abstraction (OpenAI, Anthropic, Gemini, OpenRouter, Tavily)\n  tools\u002F          # 49 built-in tools across file, shell, git, workflow, review, testing, web\n  agent\u002F          # Core loop, orchestrator, personality, task extraction, suggestions\n  tui\u002F            # Ink terminal UI with status bar, chat panel, daily summary\n  cli\u002F            # Entry points for REPL, one-shot, and server modes\n  server\u002F         # HTTP API with SSE streaming\n  memory\u002F         # Conversation persistence and export\n  context\u002F        # Project scanning and context window management\n  awareness\u002F      # Git monitoring and file watching\n  accountability\u002F # Developer day tracker with goals and reports\n  bus\u002F            # Event bus for decoupled communication\n  config\u002F         # Unified config system\n  permission\u002F     # Mode-based permissions (auto \u002F confirm \u002F plan)\n  plugin\u002F         # Plugin architecture\n","txt",[40,447,448,456,461,466,472,478,484,490,496,502,507,513,518,524,530],{"__ignoreMap":56},[449,450,453],"span",{"class":451,"line":452},"line",1,[449,454,455],{},"src\u002F\n",[449,457,458],{"class":451,"line":114},[449,459,460],{},"  providers\u002F      # LLM abstraction (OpenAI, Anthropic, Gemini, OpenRouter, Tavily)\n",[449,462,463],{"class":451,"line":111},[449,464,465],{},"  tools\u002F          # 49 built-in tools across file, shell, git, workflow, review, testing, web\n",[449,467,469],{"class":451,"line":468},4,[449,470,471],{},"  agent\u002F          # Core loop, orchestrator, personality, task extraction, suggestions\n",[449,473,475],{"class":451,"line":474},5,[449,476,477],{},"  tui\u002F            # Ink terminal UI with status bar, chat panel, daily summary\n",[449,479,481],{"class":451,"line":480},6,[449,482,483],{},"  cli\u002F            # Entry points for REPL, one-shot, and server modes\n",[449,485,487],{"class":451,"line":486},7,[449,488,489],{},"  server\u002F         # HTTP API with SSE streaming\n",[449,491,493],{"class":451,"line":492},8,[449,494,495],{},"  memory\u002F         # Conversation persistence and export\n",[449,497,499],{"class":451,"line":498},9,[449,500,501],{},"  context\u002F        # Project scanning and context window management\n",[449,503,504],{"class":451,"line":133},[449,505,506],{},"  awareness\u002F      # Git monitoring and file watching\n",[449,508,510],{"class":451,"line":509},11,[449,511,512],{},"  accountability\u002F # Developer day tracker with goals and reports\n",[449,514,515],{"class":451,"line":318},[449,516,517],{},"  bus\u002F            # Event bus for decoupled communication\n",[449,519,521],{"class":451,"line":520},13,[449,522,523],{},"  config\u002F         # Unified config system\n",[449,525,527],{"class":451,"line":526},14,[449,528,529],{},"  permission\u002F     # Mode-based permissions (auto \u002F confirm \u002F plan)\n",[449,531,533],{"class":451,"line":532},15,[449,534,535],{},"  plugin\u002F         # Plugin architecture\n",[21,537,538],{},"Every module is independently importable. You can use the agent loop without the TUI, the tool registry without the agent, or the accountability tracker as a standalone utility.",[16,540,542],{"id":541},"multi-provider-llm-support","Multi-Provider LLM Support",[21,544,545],{},"null-agent doesn't pick sides. It supports five providers and auto-detects which one you have configured:",[547,548,549,568],"table",{},[550,551,552],"thead",{},[553,554,555,559,562,565],"tr",{},[556,557,558],"th",{},"Provider",[556,560,561],{},"Env Variable",[556,563,564],{},"Default Model",[556,566,567],{},"Free Tier",[569,570,571,590,607,626,649],"tbody",{},[553,572,573,577,582,587],{},[574,575,576],"td",{},"OpenAI",[574,578,579],{},[40,580,581],{},"OPENAI_API_KEY",[574,583,584],{},[40,585,586],{},"gpt-5.4",[574,588,589],{},"—",[553,591,592,595,600,605],{},[574,593,594],{},"Anthropic",[574,596,597],{},[40,598,599],{},"ANTHROPIC_API_KEY",[574,601,602],{},[40,603,604],{},"claude-sonnet-4-6",[574,606,589],{},[553,608,609,612,617,622],{},[574,610,611],{},"Google Gemini",[574,613,614],{},[40,615,616],{},"GEMINI_API_KEY",[574,618,619],{},[40,620,621],{},"gemini-3.1-flash",[574,623,624],{},[40,625,621],{},[553,627,628,631,636,641],{},[574,629,630],{},"OpenRouter",[574,632,633],{},[40,634,635],{},"OPENROUTER_API_KEY",[574,637,638],{},[40,639,640],{},"anthropic\u002Fclaude-sonnet-4-6",[574,642,643,349,646],{},[40,644,645],{},"google\u002Fgemini-3.1-flash",[40,647,648],{},"llama-3.1",[553,650,651,654,659,661],{},[574,652,653],{},"Tavily",[574,655,656],{},[40,657,658],{},"TAVILY_API_KEY",[574,660,589],{},[574,662,663],{},"1000 searches\u002Fmonth",[32,665,667],{"id":666},"using-providers-as-a-library","Using Providers as a Library",[48,669,674],{"className":670,"code":671,"filename":672,"language":673,"meta":56,"style":56},"language-ts shiki shiki-themes github-light github-dark","import { createProvider, detectProvider, getAvailableProviders } from \"null-agent\";\n\n\u002F\u002F Auto-detect from available keys\nconst provider = createProvider(detectProvider() ?? \"openai\");\n\n\u002F\u002F Explicit provider with custom model\nconst claude = createProvider(\"anthropic\", {\n  model: \"claude-opus-4-20250514\",\n});\n\n\u002F\u002F See what's available\nconst available = getAvailableProviders(); \u002F\u002F [\"openai\", \"gemini\"]\n","example.ts","ts",[40,675,676,696,701,707,741,745,750,769,780,785,789,794],{"__ignoreMap":56},[449,677,678,682,686,689,693],{"class":451,"line":452},[449,679,681],{"class":680},"szBVR","import",[449,683,685],{"class":684},"sVt8B"," { createProvider, detectProvider, getAvailableProviders } ",[449,687,688],{"class":680},"from",[449,690,692],{"class":691},"sZZnC"," \"null-agent\"",[449,694,695],{"class":684},";\n",[449,697,698],{"class":451,"line":114},[449,699,700],{"emptyLinePlaceholder":130},"\n",[449,702,703],{"class":451,"line":111},[449,704,706],{"class":705},"sJ8bj","\u002F\u002F Auto-detect from available keys\n",[449,708,709,712,716,719,723,726,729,732,735,738],{"class":451,"line":468},[449,710,711],{"class":680},"const",[449,713,715],{"class":714},"sj4cs"," provider",[449,717,718],{"class":680}," =",[449,720,722],{"class":721},"sScJk"," createProvider",[449,724,725],{"class":684},"(",[449,727,728],{"class":721},"detectProvider",[449,730,731],{"class":684},"() ",[449,733,734],{"class":680},"??",[449,736,737],{"class":691}," \"openai\"",[449,739,740],{"class":684},");\n",[449,742,743],{"class":451,"line":474},[449,744,700],{"emptyLinePlaceholder":130},[449,746,747],{"class":451,"line":480},[449,748,749],{"class":705},"\u002F\u002F Explicit provider with custom model\n",[449,751,752,754,757,759,761,763,766],{"class":451,"line":486},[449,753,711],{"class":680},[449,755,756],{"class":714}," claude",[449,758,718],{"class":680},[449,760,722],{"class":721},[449,762,725],{"class":684},[449,764,765],{"class":691},"\"anthropic\"",[449,767,768],{"class":684},", {\n",[449,770,771,774,777],{"class":451,"line":492},[449,772,773],{"class":684},"  model: ",[449,775,776],{"class":691},"\"claude-opus-4-20250514\"",[449,778,779],{"class":684},",\n",[449,781,782],{"class":451,"line":498},[449,783,784],{"class":684},"});\n",[449,786,787],{"class":451,"line":133},[449,788,700],{"emptyLinePlaceholder":130},[449,790,791],{"class":451,"line":509},[449,792,793],{"class":705},"\u002F\u002F See what's available\n",[449,795,796,798,801,803,806,809],{"class":451,"line":318},[449,797,711],{"class":680},[449,799,800],{"class":714}," available",[449,802,718],{"class":680},[449,804,805],{"class":721}," getAvailableProviders",[449,807,808],{"class":684},"(); ",[449,810,811],{"class":705},"\u002F\u002F [\"openai\", \"gemini\"]\n",[21,813,814],{},"Keys are resolved in order of priority:",[816,817,818,821,828],"ol",{},[386,819,820],{},"Environment variable",[386,822,823,824,827],{},"OS keychain (macOS Keychain \u002F Windows Credential Manager via ",[40,825,826],{},"keytar",")",[386,829,830,833],{},[40,831,832],{},"~\u002F.null-agent\u002Fcredentials.json"," file fallback",[21,835,836],{},"You can configure keys interactively:",[48,838,842],{"className":839,"code":840,"language":841,"meta":56,"style":56},"language-bash shiki shiki-themes github-light github-dark","null-agent auth              # Pick a provider, enter key interactively\nnull-agent auth openai       # Configure one provider directly\nnull-agent auth status       # Show which providers are configured\n","bash",[40,843,844,854,866],{"__ignoreMap":56},[449,845,846,848,851],{"class":451,"line":452},[449,847,341],{"class":721},[449,849,850],{"class":691}," auth",[449,852,853],{"class":705},"              # Pick a provider, enter key interactively\n",[449,855,856,858,860,863],{"class":451,"line":114},[449,857,341],{"class":721},[449,859,850],{"class":691},[449,861,862],{"class":691}," openai",[449,864,865],{"class":705},"       # Configure one provider directly\n",[449,867,868,870,872,875],{"class":451,"line":111},[449,869,341],{"class":721},[449,871,850],{"class":691},[449,873,874],{"class":691}," status",[449,876,877],{"class":705},"       # Show which providers are configured\n",[25,879,880],{"type":27},[21,881,882],{},"No API key? Gemini and OpenRouter both offer free tiers. You can start using null-agent without spending a dollar.",[16,884,886],{"id":885},"the-tool-system-49-built-in-tools","The Tool System: 49 Built-in Tools",[21,888,889,890,893,894,897,898,901],{},"Tools are the core of what makes null-agent useful. They're organized into three capability areas: ",[151,891,892],{},"Mind"," (reasoning), ",[151,895,896],{},"Hand"," (file manipulation), and ",[151,899,900],{},"Feet"," (task orchestration).",[32,903,905],{"id":904},"mind-core-operations","Mind — Core Operations",[21,907,908],{},"The foundational trio every agent needs:",[547,910,911,924],{},[550,912,913],{},[553,914,915,918,921],{},[556,916,917],{},"Tool",[556,919,920],{},"Name",[556,922,923],{},"Description",[569,925,926,941,956],{},[553,927,928,933,938],{},[574,929,930],{},[40,931,932],{},"fileReadTool",[574,934,935],{},[40,936,937],{},"file_read",[574,939,940],{},"Read file contents",[553,942,943,948,953],{},[574,944,945],{},[40,946,947],{},"fileWriteTool",[574,949,950],{},[40,951,952],{},"file_write",[574,954,955],{},"Write file contents (creates parent dirs)",[553,957,958,963,968],{},[574,959,960],{},[40,961,962],{},"shellTool",[574,964,965],{},[40,966,967],{},"shell",[574,969,970],{},"Run shell commands (30s timeout, 1MB buffer)",[32,972,974],{"id":973},"hand-file-manipulation","Hand — File Manipulation",[21,976,977],{},"Advanced file operations with safety features:",[547,979,980,990],{},[550,981,982],{},[553,983,984,986,988],{},[556,985,917],{},[556,987,920],{},[556,989,923],{},[569,991,992,1007,1022,1037,1052,1067],{},[553,993,994,999,1004],{},[574,995,996],{},[40,997,998],{},"fileMoveTool",[574,1000,1001],{},[40,1002,1003],{},"file_move",[574,1005,1006],{},"Move files with undo support",[553,1008,1009,1014,1019],{},[574,1010,1011],{},[40,1012,1013],{},"fileCopyTool",[574,1015,1016],{},[40,1017,1018],{},"file_copy",[574,1020,1021],{},"Copy files",[553,1023,1024,1029,1034],{},[574,1025,1026],{},[40,1027,1028],{},"fileDeleteTool",[574,1030,1031],{},[40,1032,1033],{},"file_delete",[574,1035,1036],{},"Delete files (moves to trash, supports restore)",[553,1038,1039,1044,1049],{},[574,1040,1041],{},[40,1042,1043],{},"fileGlobTool",[574,1045,1046],{},[40,1047,1048],{},"file_glob",[574,1050,1051],{},"Find files matching glob patterns",[553,1053,1054,1059,1064],{},[574,1055,1056],{},[40,1057,1058],{},"fileRestoreTool",[574,1060,1061],{},[40,1062,1063],{},"file_restore",[574,1065,1066],{},"List\u002Frestore files from trash",[553,1068,1069,1074,1079],{},[574,1070,1071],{},[40,1072,1073],{},"fileBulkTool",[574,1075,1076],{},[40,1077,1078],{},"file_bulk",[574,1080,1081],{},"Execute batch file operations",[21,1083,1084],{},[151,1085,1086],{},"Safety features built in:",[383,1088,1089,1095,1105],{},[386,1090,1091,1094],{},[151,1092,1093],{},"Root boundary"," — all operations validate paths against a configurable root (default: project root) to prevent traversal attacks",[386,1096,1097,1100,1101,1104],{},[151,1098,1099],{},"Trash-based delete"," — deleted files move to ",[40,1102,1103],{},"~\u002F.null-agent\u002Ftrash\u002F"," instead of permanent deletion",[386,1106,1107,1110,1111,1113,1114,349,1117,1120,1121,1124,1125,1128],{},[151,1108,1109],{},"Glob patterns"," — ",[40,1112,1048],{}," supports ",[40,1115,1116],{},"**\u002F*.ts",[40,1118,1119],{},"src\u002F**\u002F*.js",", etc. with automatic ",[40,1122,1123],{},"node_modules\u002F"," \u002F ",[40,1126,1127],{},".git\u002F"," exclusion",[32,1130,1132],{"id":1131},"feet-task-orchestration","Feet — Task Orchestration",[21,1134,1135],{},"Script execution, process management, and terminal sessions:",[547,1137,1138,1148],{},[550,1139,1140],{},[553,1141,1142,1144,1146],{},[556,1143,917],{},[556,1145,920],{},[556,1147,923],{},[569,1149,1150,1165,1180,1195,1210,1225,1240,1255],{},[553,1151,1152,1157,1162],{},[574,1153,1154],{},[40,1155,1156],{},"scriptDetectTool",[574,1158,1159],{},[40,1160,1161],{},"script_detect",[574,1163,1164],{},"Auto-detect scripts from package.json\u002FMakefile",[553,1166,1167,1172,1177],{},[574,1168,1169],{},[40,1170,1171],{},"scriptRunTool",[574,1173,1174],{},[40,1175,1176],{},"script_run",[574,1178,1179],{},"Run detected or custom scripts",[553,1181,1182,1187,1192],{},[574,1183,1184],{},[40,1185,1186],{},"processStartTool",[574,1188,1189],{},[40,1190,1191],{},"process_start",[574,1193,1194],{},"Start background processes",[553,1196,1197,1202,1207],{},[574,1198,1199],{},[40,1200,1201],{},"processStopTool",[574,1203,1204],{},[40,1205,1206],{},"process_stop",[574,1208,1209],{},"Stop background processes",[553,1211,1212,1217,1222],{},[574,1213,1214],{},[40,1215,1216],{},"processListTool",[574,1218,1219],{},[40,1220,1221],{},"process_list",[574,1223,1224],{},"List running processes",[553,1226,1227,1232,1237],{},[574,1228,1229],{},[40,1230,1231],{},"sessionCreateTool",[574,1233,1234],{},[40,1235,1236],{},"session_create",[574,1238,1239],{},"Create persistent terminal sessions",[553,1241,1242,1247,1252],{},[574,1243,1244],{},[40,1245,1246],{},"sessionAttachTool",[574,1248,1249],{},[40,1250,1251],{},"session_attach",[574,1253,1254],{},"Attach to existing sessions",[553,1256,1257,1262,1267],{},[574,1258,1259],{},[40,1260,1261],{},"taskSprintTool",[574,1263,1264],{},[40,1265,1266],{},"task_sprint",[574,1268,1269],{},"Run concurrent tasks with progress",[32,1271,1273],{"id":1272},"git-workflow-review-testing-and-web","Git, Workflow, Review, Testing, and Web",[21,1275,1276],{},"The package also includes 15 git tools, 8 dev workflow tools (PRs, issues, changelog, CI status), code review utilities, test generation\u002Frunners, and web search\u002Ffetch via Tavily.",[32,1278,1280],{"id":1279},"using-the-tool-registry","Using the Tool Registry",[48,1282,1284],{"className":670,"code":1283,"filename":672,"language":673,"meta":56,"style":56},"import { createDefaultRegistry, ToolRegistry, fileReadTool, shellTool } from \"null-agent\";\n\n\u002F\u002F All 49 tools\nconst registry = createDefaultRegistry();\n\n\u002F\u002F Or pick specific tools\nconst minimal = new ToolRegistry();\nminimal.register(fileReadTool);\nminimal.register(shellTool);\n",[40,1285,1286,1299,1303,1308,1323,1327,1332,1349,1360],{"__ignoreMap":56},[449,1287,1288,1290,1293,1295,1297],{"class":451,"line":452},[449,1289,681],{"class":680},[449,1291,1292],{"class":684}," { createDefaultRegistry, ToolRegistry, fileReadTool, shellTool } ",[449,1294,688],{"class":680},[449,1296,692],{"class":691},[449,1298,695],{"class":684},[449,1300,1301],{"class":451,"line":114},[449,1302,700],{"emptyLinePlaceholder":130},[449,1304,1305],{"class":451,"line":111},[449,1306,1307],{"class":705},"\u002F\u002F All 49 tools\n",[449,1309,1310,1312,1315,1317,1320],{"class":451,"line":468},[449,1311,711],{"class":680},[449,1313,1314],{"class":714}," registry",[449,1316,718],{"class":680},[449,1318,1319],{"class":721}," createDefaultRegistry",[449,1321,1322],{"class":684},"();\n",[449,1324,1325],{"class":451,"line":474},[449,1326,700],{"emptyLinePlaceholder":130},[449,1328,1329],{"class":451,"line":480},[449,1330,1331],{"class":705},"\u002F\u002F Or pick specific tools\n",[449,1333,1334,1336,1339,1341,1344,1347],{"class":451,"line":486},[449,1335,711],{"class":680},[449,1337,1338],{"class":714}," minimal",[449,1340,718],{"class":680},[449,1342,1343],{"class":680}," new",[449,1345,1346],{"class":721}," ToolRegistry",[449,1348,1322],{"class":684},[449,1350,1351,1354,1357],{"class":451,"line":492},[449,1352,1353],{"class":684},"minimal.",[449,1355,1356],{"class":721},"register",[449,1358,1359],{"class":684},"(fileReadTool);\n",[449,1361,1362,1364,1366],{"class":451,"line":498},[449,1363,1353],{"class":684},[449,1365,1356],{"class":721},[449,1367,1368],{"class":684},"(shellTool);\n",[32,1370,1372],{"id":1371},"custom-tools","Custom Tools",[21,1374,1375],{},"Adding your own tool is straightforward:",[48,1377,1380],{"className":670,"code":1378,"filename":1379,"language":673,"meta":56,"style":56},"registry.register({\n  name: \"deploy\",\n  description: \"Deploy the application\",\n  parameters: {\n    type: \"object\",\n    properties: {\n      environment: { type: \"string\", enum: [\"staging\", \"production\"] },\n    },\n    required: [\"environment\"],\n  },\n  execute: async ({ environment }) => {\n    return { content: `Deployed to ${environment}` };\n  },\n});\n","custom-tool.ts",[40,1381,1382,1392,1402,1412,1417,1427,1432,1454,1459,1470,1475,1502,1521,1525],{"__ignoreMap":56},[449,1383,1384,1387,1389],{"class":451,"line":452},[449,1385,1386],{"class":684},"registry.",[449,1388,1356],{"class":721},[449,1390,1391],{"class":684},"({\n",[449,1393,1394,1397,1400],{"class":451,"line":114},[449,1395,1396],{"class":684},"  name: ",[449,1398,1399],{"class":691},"\"deploy\"",[449,1401,779],{"class":684},[449,1403,1404,1407,1410],{"class":451,"line":111},[449,1405,1406],{"class":684},"  description: ",[449,1408,1409],{"class":691},"\"Deploy the application\"",[449,1411,779],{"class":684},[449,1413,1414],{"class":451,"line":468},[449,1415,1416],{"class":684},"  parameters: {\n",[449,1418,1419,1422,1425],{"class":451,"line":474},[449,1420,1421],{"class":684},"    type: ",[449,1423,1424],{"class":691},"\"object\"",[449,1426,779],{"class":684},[449,1428,1429],{"class":451,"line":480},[449,1430,1431],{"class":684},"    properties: {\n",[449,1433,1434,1437,1440,1443,1446,1448,1451],{"class":451,"line":486},[449,1435,1436],{"class":684},"      environment: { type: ",[449,1438,1439],{"class":691},"\"string\"",[449,1441,1442],{"class":684},", enum: [",[449,1444,1445],{"class":691},"\"staging\"",[449,1447,349],{"class":684},[449,1449,1450],{"class":691},"\"production\"",[449,1452,1453],{"class":684},"] },\n",[449,1455,1456],{"class":451,"line":492},[449,1457,1458],{"class":684},"    },\n",[449,1460,1461,1464,1467],{"class":451,"line":498},[449,1462,1463],{"class":684},"    required: [",[449,1465,1466],{"class":691},"\"environment\"",[449,1468,1469],{"class":684},"],\n",[449,1471,1472],{"class":451,"line":133},[449,1473,1474],{"class":684},"  },\n",[449,1476,1477,1480,1483,1486,1489,1493,1496,1499],{"class":451,"line":509},[449,1478,1479],{"class":721},"  execute",[449,1481,1482],{"class":684},": ",[449,1484,1485],{"class":680},"async",[449,1487,1488],{"class":684}," ({ ",[449,1490,1492],{"class":1491},"s4XuR","environment",[449,1494,1495],{"class":684}," }) ",[449,1497,1498],{"class":680},"=>",[449,1500,1501],{"class":684}," {\n",[449,1503,1504,1507,1510,1513,1515,1518],{"class":451,"line":318},[449,1505,1506],{"class":680},"    return",[449,1508,1509],{"class":684}," { content: ",[449,1511,1512],{"class":691},"`Deployed to ${",[449,1514,1492],{"class":684},[449,1516,1517],{"class":691},"}`",[449,1519,1520],{"class":684}," };\n",[449,1522,1523],{"class":451,"line":520},[449,1524,1474],{"class":684},[449,1526,1527],{"class":451,"line":526},[449,1528,784],{"class":684},[427,1530,1533],{"icon":1531,"title":1532},"🔧","TypeBox Schemas",[21,1534,1535,1536,349,1539,1542,1543,1546,1547,1549],{},"For type-safe parameter validation, null-agent supports TypeBox schemas alongside JSON Schema. Import ",[40,1537,1538],{},"toolParams",[40,1540,1541],{},"String",", and ",[40,1544,1545],{},"Object"," from ",[40,1548,341],{}," for fully typed tool definitions.",[16,1551,1553],{"id":1552},"the-agent-loop","The Agent Loop",[21,1555,1556,1557,1559],{},"The ",[40,1558,394],{}," class is the heart of null-agent. It handles the conversation loop, tool execution, streaming, events, and orchestration.",[48,1561,1564],{"className":670,"code":1562,"filename":1563,"language":673,"meta":56,"style":56},"import { Agent, createProvider, createDefaultRegistry } from \"null-agent\";\n\nconst agent = new Agent({\n  provider: createProvider(\"anthropic\"),\n  tools: createDefaultRegistry(),\n  systemPrompt: \"You are a helpful coding assistant.\",\n});\n\nconst result = await agent.chat(\"Explain this function\");\nconsole.log(result.content);\nconsole.log(`Completed in ${result.iterations} iterations`);\n","agent.ts",[40,1565,1566,1579,1583,1599,1614,1625,1635,1639,1643,1668,1679],{"__ignoreMap":56},[449,1567,1568,1570,1573,1575,1577],{"class":451,"line":452},[449,1569,681],{"class":680},[449,1571,1572],{"class":684}," { Agent, createProvider, createDefaultRegistry } ",[449,1574,688],{"class":680},[449,1576,692],{"class":691},[449,1578,695],{"class":684},[449,1580,1581],{"class":451,"line":114},[449,1582,700],{"emptyLinePlaceholder":130},[449,1584,1585,1587,1590,1592,1594,1597],{"class":451,"line":111},[449,1586,711],{"class":680},[449,1588,1589],{"class":714}," agent",[449,1591,718],{"class":680},[449,1593,1343],{"class":680},[449,1595,1596],{"class":721}," Agent",[449,1598,1391],{"class":684},[449,1600,1601,1604,1607,1609,1611],{"class":451,"line":468},[449,1602,1603],{"class":684},"  provider: ",[449,1605,1606],{"class":721},"createProvider",[449,1608,725],{"class":684},[449,1610,765],{"class":691},[449,1612,1613],{"class":684},"),\n",[449,1615,1616,1619,1622],{"class":451,"line":474},[449,1617,1618],{"class":684},"  tools: ",[449,1620,1621],{"class":721},"createDefaultRegistry",[449,1623,1624],{"class":684},"(),\n",[449,1626,1627,1630,1633],{"class":451,"line":480},[449,1628,1629],{"class":684},"  systemPrompt: ",[449,1631,1632],{"class":691},"\"You are a helpful coding assistant.\"",[449,1634,779],{"class":684},[449,1636,1637],{"class":451,"line":486},[449,1638,784],{"class":684},[449,1640,1641],{"class":451,"line":492},[449,1642,700],{"emptyLinePlaceholder":130},[449,1644,1645,1647,1650,1652,1655,1658,1661,1663,1666],{"class":451,"line":498},[449,1646,711],{"class":680},[449,1648,1649],{"class":714}," result",[449,1651,718],{"class":680},[449,1653,1654],{"class":680}," await",[449,1656,1657],{"class":684}," agent.",[449,1659,1660],{"class":721},"chat",[449,1662,725],{"class":684},[449,1664,1665],{"class":691},"\"Explain this function\"",[449,1667,740],{"class":684},[449,1669,1670,1673,1676],{"class":451,"line":133},[449,1671,1672],{"class":684},"console.",[449,1674,1675],{"class":721},"log",[449,1677,1678],{"class":684},"(result.content);\n",[449,1680,1681,1683,1685,1687,1690,1693,1695,1698,1701],{"class":451,"line":509},[449,1682,1672],{"class":684},[449,1684,1675],{"class":721},[449,1686,725],{"class":684},[449,1688,1689],{"class":691},"`Completed in ${",[449,1691,1692],{"class":684},"result",[449,1694,165],{"class":691},[449,1696,1697],{"class":684},"iterations",[449,1699,1700],{"class":691},"} iterations`",[449,1702,740],{"class":684},[32,1704,1706],{"id":1705},"streaming-events","Streaming Events",[21,1708,1709],{},"For building UIs or logging, the agent emits rich events:",[48,1711,1714],{"className":670,"code":1712,"filename":1713,"language":673,"meta":56,"style":56},"const agent = new Agent({\n  provider,\n  tools,\n  eventHandlers: {\n    onAgentStart: (data) => console.log(\"Started:\", data.message),\n    onToolExecutionStart: (data) => console.log(\"Running:\", data.name),\n    onToolExecutionEnd: (data) => console.log(\"Done:\", data.name, data.isError),\n    onAgentEnd: (data) => console.log(\"Complete in\", data.iterations, \"iterations\"),\n  },\n});\n","events.ts",[40,1715,1716,1730,1735,1740,1745,1774,1799,1824,1854,1858],{"__ignoreMap":56},[449,1717,1718,1720,1722,1724,1726,1728],{"class":451,"line":452},[449,1719,711],{"class":680},[449,1721,1589],{"class":714},[449,1723,718],{"class":680},[449,1725,1343],{"class":680},[449,1727,1596],{"class":721},[449,1729,1391],{"class":684},[449,1731,1732],{"class":451,"line":114},[449,1733,1734],{"class":684},"  provider,\n",[449,1736,1737],{"class":451,"line":111},[449,1738,1739],{"class":684},"  tools,\n",[449,1741,1742],{"class":451,"line":468},[449,1743,1744],{"class":684},"  eventHandlers: {\n",[449,1746,1747,1750,1753,1756,1759,1761,1764,1766,1768,1771],{"class":451,"line":474},[449,1748,1749],{"class":721},"    onAgentStart",[449,1751,1752],{"class":684},": (",[449,1754,1755],{"class":1491},"data",[449,1757,1758],{"class":684},") ",[449,1760,1498],{"class":680},[449,1762,1763],{"class":684}," console.",[449,1765,1675],{"class":721},[449,1767,725],{"class":684},[449,1769,1770],{"class":691},"\"Started:\"",[449,1772,1773],{"class":684},", data.message),\n",[449,1775,1776,1779,1781,1783,1785,1787,1789,1791,1793,1796],{"class":451,"line":480},[449,1777,1778],{"class":721},"    onToolExecutionStart",[449,1780,1752],{"class":684},[449,1782,1755],{"class":1491},[449,1784,1758],{"class":684},[449,1786,1498],{"class":680},[449,1788,1763],{"class":684},[449,1790,1675],{"class":721},[449,1792,725],{"class":684},[449,1794,1795],{"class":691},"\"Running:\"",[449,1797,1798],{"class":684},", data.name),\n",[449,1800,1801,1804,1806,1808,1810,1812,1814,1816,1818,1821],{"class":451,"line":486},[449,1802,1803],{"class":721},"    onToolExecutionEnd",[449,1805,1752],{"class":684},[449,1807,1755],{"class":1491},[449,1809,1758],{"class":684},[449,1811,1498],{"class":680},[449,1813,1763],{"class":684},[449,1815,1675],{"class":721},[449,1817,725],{"class":684},[449,1819,1820],{"class":691},"\"Done:\"",[449,1822,1823],{"class":684},", data.name, data.isError),\n",[449,1825,1826,1829,1831,1833,1835,1837,1839,1841,1843,1846,1849,1852],{"class":451,"line":492},[449,1827,1828],{"class":721},"    onAgentEnd",[449,1830,1752],{"class":684},[449,1832,1755],{"class":1491},[449,1834,1758],{"class":684},[449,1836,1498],{"class":680},[449,1838,1763],{"class":684},[449,1840,1675],{"class":721},[449,1842,725],{"class":684},[449,1844,1845],{"class":691},"\"Complete in\"",[449,1847,1848],{"class":684},", data.iterations, ",[449,1850,1851],{"class":691},"\"iterations\"",[449,1853,1613],{"class":684},[449,1855,1856],{"class":451,"line":498},[449,1857,1474],{"class":684},[449,1859,1860],{"class":451,"line":133},[449,1861,784],{"class":684},[32,1863,1865],{"id":1864},"tool-hooks","Tool Hooks",[21,1867,1868],{},"Intercept and control tool execution:",[48,1870,1873],{"className":670,"code":1871,"filename":1872,"language":673,"meta":56,"style":56},"const agent = new Agent({\n  provider,\n  tools,\n  toolHooks: {\n    beforeToolCall: async (context) => {\n      console.log(\"About to run:\", context.name);\n      \u002F\u002F Return false to block execution\n    },\n    afterToolCall: async (context) => {\n      console.log(\"Finished:\", context.name, context.result);\n    },\n  },\n});\n","hooks.ts",[40,1874,1875,1889,1893,1897,1902,1923,1938,1943,1947,1966,1980,1984,1988],{"__ignoreMap":56},[449,1876,1877,1879,1881,1883,1885,1887],{"class":451,"line":452},[449,1878,711],{"class":680},[449,1880,1589],{"class":714},[449,1882,718],{"class":680},[449,1884,1343],{"class":680},[449,1886,1596],{"class":721},[449,1888,1391],{"class":684},[449,1890,1891],{"class":451,"line":114},[449,1892,1734],{"class":684},[449,1894,1895],{"class":451,"line":111},[449,1896,1739],{"class":684},[449,1898,1899],{"class":451,"line":468},[449,1900,1901],{"class":684},"  toolHooks: {\n",[449,1903,1904,1907,1909,1911,1914,1917,1919,1921],{"class":451,"line":474},[449,1905,1906],{"class":721},"    beforeToolCall",[449,1908,1482],{"class":684},[449,1910,1485],{"class":680},[449,1912,1913],{"class":684}," (",[449,1915,1916],{"class":1491},"context",[449,1918,1758],{"class":684},[449,1920,1498],{"class":680},[449,1922,1501],{"class":684},[449,1924,1925,1928,1930,1932,1935],{"class":451,"line":480},[449,1926,1927],{"class":684},"      console.",[449,1929,1675],{"class":721},[449,1931,725],{"class":684},[449,1933,1934],{"class":691},"\"About to run:\"",[449,1936,1937],{"class":684},", context.name);\n",[449,1939,1940],{"class":451,"line":486},[449,1941,1942],{"class":705},"      \u002F\u002F Return false to block execution\n",[449,1944,1945],{"class":451,"line":492},[449,1946,1458],{"class":684},[449,1948,1949,1952,1954,1956,1958,1960,1962,1964],{"class":451,"line":498},[449,1950,1951],{"class":721},"    afterToolCall",[449,1953,1482],{"class":684},[449,1955,1485],{"class":680},[449,1957,1913],{"class":684},[449,1959,1916],{"class":1491},[449,1961,1758],{"class":684},[449,1963,1498],{"class":680},[449,1965,1501],{"class":684},[449,1967,1968,1970,1972,1974,1977],{"class":451,"line":133},[449,1969,1927],{"class":684},[449,1971,1675],{"class":721},[449,1973,725],{"class":684},[449,1975,1976],{"class":691},"\"Finished:\"",[449,1978,1979],{"class":684},", context.name, context.result);\n",[449,1981,1982],{"class":451,"line":509},[449,1983,1458],{"class":684},[449,1985,1986],{"class":451,"line":318},[449,1987,1474],{"class":684},[449,1989,1990],{"class":451,"line":520},[449,1991,784],{"class":684},[32,1993,1995],{"id":1994},"steering","Steering",[21,1997,1998],{},"Inject messages or queue follow-up work:",[48,2000,2003],{"className":670,"code":2001,"filename":2002,"language":673,"meta":56,"style":56},"const agent = new Agent({ provider, tools });\n\n\u002F\u002F Inject context before next chat\nagent.steer({ role: \"user\", content: \"Remember: use TypeScript\" });\nawait agent.chat(\"Hello\");\n\n\u002F\u002F Queue work after agent stops\nagent.followUp(async () => {\n  await agent.chat(\"Now summarize what we did\");\n});\n","steering.ts",[40,2004,2005,2020,2024,2029,2052,2068,2072,2077,2095,2111],{"__ignoreMap":56},[449,2006,2007,2009,2011,2013,2015,2017],{"class":451,"line":452},[449,2008,711],{"class":680},[449,2010,1589],{"class":714},[449,2012,718],{"class":680},[449,2014,1343],{"class":680},[449,2016,1596],{"class":721},[449,2018,2019],{"class":684},"({ provider, tools });\n",[449,2021,2022],{"class":451,"line":114},[449,2023,700],{"emptyLinePlaceholder":130},[449,2025,2026],{"class":451,"line":111},[449,2027,2028],{"class":705},"\u002F\u002F Inject context before next chat\n",[449,2030,2031,2034,2037,2040,2043,2046,2049],{"class":451,"line":468},[449,2032,2033],{"class":684},"agent.",[449,2035,2036],{"class":721},"steer",[449,2038,2039],{"class":684},"({ role: ",[449,2041,2042],{"class":691},"\"user\"",[449,2044,2045],{"class":684},", content: ",[449,2047,2048],{"class":691},"\"Remember: use TypeScript\"",[449,2050,2051],{"class":684}," });\n",[449,2053,2054,2057,2059,2061,2063,2066],{"class":451,"line":474},[449,2055,2056],{"class":680},"await",[449,2058,1657],{"class":684},[449,2060,1660],{"class":721},[449,2062,725],{"class":684},[449,2064,2065],{"class":691},"\"Hello\"",[449,2067,740],{"class":684},[449,2069,2070],{"class":451,"line":480},[449,2071,700],{"emptyLinePlaceholder":130},[449,2073,2074],{"class":451,"line":486},[449,2075,2076],{"class":705},"\u002F\u002F Queue work after agent stops\n",[449,2078,2079,2081,2084,2086,2088,2091,2093],{"class":451,"line":492},[449,2080,2033],{"class":684},[449,2082,2083],{"class":721},"followUp",[449,2085,725],{"class":684},[449,2087,1485],{"class":680},[449,2089,2090],{"class":684}," () ",[449,2092,1498],{"class":680},[449,2094,1501],{"class":684},[449,2096,2097,2100,2102,2104,2106,2109],{"class":451,"line":498},[449,2098,2099],{"class":680},"  await",[449,2101,1657],{"class":684},[449,2103,1660],{"class":721},[449,2105,725],{"class":684},[449,2107,2108],{"class":691},"\"Now summarize what we did\"",[449,2110,740],{"class":684},[449,2112,2113],{"class":451,"line":133},[449,2114,784],{"class":684},[32,2116,2118],{"id":2117},"multi-agent-orchestration","Multi-Agent Orchestration",[21,2120,2121],{},"For complex tasks, the agent can spawn parallel sub-agents:",[48,2123,2126],{"className":670,"code":2124,"filename":2125,"language":673,"meta":56,"style":56},"const agent = new Agent({\n  provider: createProvider(\"anthropic\"),\n  tools: createDefaultRegistry(),\n  enableOrchestrator: true,\n});\n\n\u002F\u002F The agent can use the \"spawn_task\" tool to delegate work\nconst result = await agent.chat(\"Investigate these 3 files in parallel\");\n","orchestrator.ts",[40,2127,2128,2142,2154,2162,2172,2176,2180,2185],{"__ignoreMap":56},[449,2129,2130,2132,2134,2136,2138,2140],{"class":451,"line":452},[449,2131,711],{"class":680},[449,2133,1589],{"class":714},[449,2135,718],{"class":680},[449,2137,1343],{"class":680},[449,2139,1596],{"class":721},[449,2141,1391],{"class":684},[449,2143,2144,2146,2148,2150,2152],{"class":451,"line":114},[449,2145,1603],{"class":684},[449,2147,1606],{"class":721},[449,2149,725],{"class":684},[449,2151,765],{"class":691},[449,2153,1613],{"class":684},[449,2155,2156,2158,2160],{"class":451,"line":111},[449,2157,1618],{"class":684},[449,2159,1621],{"class":721},[449,2161,1624],{"class":684},[449,2163,2164,2167,2170],{"class":451,"line":468},[449,2165,2166],{"class":684},"  enableOrchestrator: ",[449,2168,2169],{"class":714},"true",[449,2171,779],{"class":684},[449,2173,2174],{"class":451,"line":474},[449,2175,784],{"class":684},[449,2177,2178],{"class":451,"line":480},[449,2179,700],{"emptyLinePlaceholder":130},[449,2181,2182],{"class":451,"line":486},[449,2183,2184],{"class":705},"\u002F\u002F The agent can use the \"spawn_task\" tool to delegate work\n",[449,2186,2187,2189,2191,2193,2195,2197,2199,2201,2204],{"class":451,"line":492},[449,2188,711],{"class":680},[449,2190,1649],{"class":714},[449,2192,718],{"class":680},[449,2194,1654],{"class":680},[449,2196,1657],{"class":684},[449,2198,1660],{"class":721},[449,2200,725],{"class":684},[449,2202,2203],{"class":691},"\"Investigate these 3 files in parallel\"",[449,2205,740],{"class":684},[21,2207,2208],{},"Safety limits are enforced: 5 concurrent sub-agents, 3 spawns per turn, 30s timeout per sub-agent.",[16,2210,2212],{"id":2211},"four-interfaces","Four Interfaces",[32,2214,2216],{"id":2215},"terminal-ui-default","Terminal UI (Default)",[21,2218,2219],{},"The Ink-based TUI is the default experience. It features:",[383,2221,2222,2228,2234,2243,2263,2269],{},[386,2223,2224,2227],{},[151,2225,2226],{},"Status bar"," with live activity tracking and project context",[386,2229,2230,2233],{},[151,2231,2232],{},"Chat panel"," with message bubbles and formatted tool call display",[386,2235,2236,1913,2239,2242],{},[151,2237,2238],{},"Animated mascot",[40,2240,2241],{},"◉",") with 11 moods that change based on activity",[386,2244,2245,1913,2248,349,2251,349,2254,349,2257,349,2260,827],{},[151,2246,2247],{},"Slash commands",[40,2249,2250],{},"\u002Fhelp",[40,2252,2253],{},"\u002Fclear",[40,2255,2256],{},"\u002Fcontext",[40,2258,2259],{},"\u002Freport",[40,2261,2262],{},"\u002Fgoals",[386,2264,2265,2268],{},[151,2266,2267],{},"Daily summary panel"," with meetings, goals, and yesterday's breakdown",[386,2270,2271,1913,2274,2277,2278,2281],{},[151,2272,2273],{},"Keyboard shortcuts",[40,2275,2276],{},"Ctrl+H"," for help, ",[40,2279,2280],{},"Ctrl+S"," for summary, arrow keys for history)",[48,2283,2285],{"className":839,"code":2284,"language":841,"meta":56,"style":56},"null-agent\n",[40,2286,2287],{"__ignoreMap":56},[449,2288,2289],{"class":451,"line":452},[449,2290,2284],{"class":721},[32,2292,2294],{"id":2293},"plain-repl","Plain REPL",[21,2296,2297],{},"No extra dependencies. Just readline with colored output:",[48,2299,2301],{"className":839,"code":2300,"language":841,"meta":56,"style":56},"null-agent --plain\n",[40,2302,2303],{"__ignoreMap":56},[449,2304,2305,2307],{"class":451,"line":452},[449,2306,341],{"class":721},[449,2308,2309],{"class":714}," --plain\n",[32,2311,2313],{"id":2312},"http-api-server","HTTP API Server",[21,2315,2316],{},"REST API with SSE streaming. Default port 3737:",[48,2318,2320],{"className":839,"code":2319,"language":841,"meta":56,"style":56},"null-agent --server --port 3737 --host 0.0.0.0\n",[40,2321,2322],{"__ignoreMap":56},[449,2323,2324,2326,2329,2332,2335,2338],{"class":451,"line":452},[449,2325,341],{"class":721},[449,2327,2328],{"class":714}," --server",[449,2330,2331],{"class":714}," --port",[449,2333,2334],{"class":714}," 3737",[449,2336,2337],{"class":714}," --host",[449,2339,2340],{"class":714}," 0.0.0.0\n",[21,2342,2343,2344,349,2347,349,2350,349,2353,349,2356,349,2359,1542,2362,165],{},"Endpoints include ",[40,2345,2346],{},"\u002Fchat",[40,2348,2349],{},"\u002Fchat\u002Fstream",[40,2351,2352],{},"\u002Fhistory",[40,2354,2355],{},"\u002Fconversations",[40,2357,2358],{},"\u002Ftasks",[40,2360,2361],{},"\u002Fconfig",[40,2363,2364],{},"\u002Fhealth",[32,2366,2368],{"id":2367},"one-shot-cli","One-Shot CLI",[21,2370,2371],{},"Send a single message and print the response. Perfect for scripting:",[48,2373,2375],{"className":839,"code":2374,"language":841,"meta":56,"style":56},"null-agent \"what does this function do?\"\nnull-agent --provider openai \"summarize the changes\"\n",[40,2376,2377,2384],{"__ignoreMap":56},[449,2378,2379,2381],{"class":451,"line":452},[449,2380,341],{"class":721},[449,2382,2383],{"class":691}," \"what does this function do?\"\n",[449,2385,2386,2388,2391,2393],{"class":451,"line":114},[449,2387,341],{"class":721},[449,2389,2390],{"class":714}," --provider",[449,2392,862],{"class":691},[449,2394,2395],{"class":691}," \"summarize the changes\"\n",[16,2397,2399],{"id":2398},"accountability-developer-day-tracker","Accountability & Developer Day Tracker",[21,2401,2402],{},"This is the feature that surprised me most while building it. null-agent includes a self-aware developer day companion that tracks what you're working on, keeps you accountable to goals, and generates daily and weekly reports — all without leaving the terminal.",[32,2404,2406],{"id":2405},"how-tracking-works","How Tracking Works",[21,2408,2409],{},"Activity is tracked in two ways:",[383,2411,2412,2445],{},[386,2413,2414,2417,2418,2421,2422,2425,2426,2429,2430,2433,2434,2429,2437,349,2439,2429,2442,374],{},[151,2415,2416],{},"Inferred"," — every tool call is mapped to an activity type automatically. Reading files → ",[40,2419,2420],{},"coding",". Running tests → ",[40,2423,2424],{},"testing",". ",[40,2427,2428],{},"git diff"," → ",[40,2431,2432],{},"review",". Shell commands are classified by regex (",[40,2435,2436],{},"jest",[40,2438,2424],{},[40,2440,2441],{},"inspect",[40,2443,2444],{},"debugging",[386,2446,2447,2450,2451,2454],{},[151,2448,2449],{},"Explicit"," — use ",[40,2452,2453],{},"\u002Ftrack \u003Ctype>"," to manually declare what you're working on.",[32,2456,2458],{"id":2457},"goal-tracking","Goal Tracking",[48,2460,2462],{"className":839,"code":2461,"language":841,"meta":56,"style":56},"\u002Fgoals               # List today's goals with status icons\n\u002Fgoal add \u003Ctext>     # Create a new daily goal\n\u002Fgoal done \u003Cid>      # Mark a goal complete (triggers celebration)\n\u002Fgoal rm \u003Cid>        # Delete a goal\n",[40,2463,2464,2471,2494,2514],{"__ignoreMap":56},[449,2465,2466,2468],{"class":451,"line":452},[449,2467,2262],{"class":721},[449,2469,2470],{"class":705},"               # List today's goals with status icons\n",[449,2472,2473,2476,2479,2482,2485,2488,2491],{"class":451,"line":114},[449,2474,2475],{"class":721},"\u002Fgoal",[449,2477,2478],{"class":691}," add",[449,2480,2481],{"class":680}," \u003C",[449,2483,2484],{"class":691},"tex",[449,2486,2487],{"class":684},"t",[449,2489,2490],{"class":680},">",[449,2492,2493],{"class":705},"     # Create a new daily goal\n",[449,2495,2496,2498,2501,2503,2506,2509,2511],{"class":451,"line":111},[449,2497,2475],{"class":721},[449,2499,2500],{"class":691}," done",[449,2502,2481],{"class":680},[449,2504,2505],{"class":691},"i",[449,2507,2508],{"class":684},"d",[449,2510,2490],{"class":680},[449,2512,2513],{"class":705},"      # Mark a goal complete (triggers celebration)\n",[449,2515,2516,2518,2521,2523,2525,2527,2529],{"class":451,"line":468},[449,2517,2475],{"class":721},[449,2519,2520],{"class":691}," rm",[449,2522,2481],{"class":680},[449,2524,2505],{"class":691},[449,2526,2508],{"class":684},[449,2528,2490],{"class":680},[449,2530,2531],{"class":705},"        # Delete a goal\n",[21,2533,2534,2535,2538,2539,2538,2542,2538,2545],{},"Goal statuses: ",[40,2536,2537],{},"○ pending"," · ",[40,2540,2541],{},"⟳ in-progress",[40,2543,2544],{},"✓ completed",[40,2546,2547],{},"✗ missed",[32,2549,2551],{"id":2550},"reports","Reports",[48,2553,2555],{"className":839,"code":2554,"language":841,"meta":56,"style":56},"\u002Freport              # Generate today's report\n\u002Freport week         # Generate this week's report\n",[40,2556,2557,2564],{"__ignoreMap":56},[449,2558,2559,2561],{"class":451,"line":452},[449,2560,2259],{"class":721},[449,2562,2563],{"class":705},"              # Generate today's report\n",[449,2565,2566,2568,2571],{"class":451,"line":114},[449,2567,2259],{"class":721},[449,2569,2570],{"class":691}," week",[449,2572,2573],{"class":705},"         # Generate this week's report\n",[21,2575,2576,2577,2580],{},"Reports are saved to ",[40,2578,2579],{},"~\u002F.null-agent\u002Faccountability\u002Freports\u002F"," in both Markdown and JSON formats.",[32,2582,2584],{"id":2583},"proactive-reminders","Proactive Reminders",[21,2586,2587],{},"The accountability engine polls every minute and surfaces messages when:",[383,2589,2590,2593,2596,2599],{},[386,2591,2592],{},"A goal has been pending all day with no progress",[386,2594,2595],{},"You've been coding or debugging for 2+ hours (break reminder)",[386,2597,2598],{},"It's 9–10 AM and you haven't checked in",[386,2600,2601],{},"It's 5–6 PM and the day is ending",[32,2603,2605],{"id":2604},"using-accountability-as-a-library","Using Accountability as a Library",[48,2607,2610],{"className":670,"code":2608,"filename":2609,"language":673,"meta":56,"style":56},"import { ActivityTracker, Reporter, GoalTracker } from \"null-agent\";\n\nconst tracker = new ActivityTracker();\nawait tracker.init();\n\n\u002F\u002F Explicit tracking\nconst activityId = await tracker.startActivity(\"coding\", \"Auth module refactor\");\nawait tracker.endActivity(activityId);\n\n\u002F\u002F Inferred tracking — call this in your tool hook\nawait tracker.recordToolCall(\"file_write\", { path: \"src\u002Fauth.ts\" }, \"ok\");\n\n\u002F\u002F Daily report\nconst reporter = new Reporter(store, tracker);\nconst report = await reporter.generateDailyReport();\nconsole.log(reporter.formatDailyReport(report));\n","accountability.ts",[40,2611,2612,2625,2629,2645,2657,2661,2666,2694,2706,2710,2715,2743,2747,2752,2769,2788],{"__ignoreMap":56},[449,2613,2614,2616,2619,2621,2623],{"class":451,"line":452},[449,2615,681],{"class":680},[449,2617,2618],{"class":684}," { ActivityTracker, Reporter, GoalTracker } ",[449,2620,688],{"class":680},[449,2622,692],{"class":691},[449,2624,695],{"class":684},[449,2626,2627],{"class":451,"line":114},[449,2628,700],{"emptyLinePlaceholder":130},[449,2630,2631,2633,2636,2638,2640,2643],{"class":451,"line":111},[449,2632,711],{"class":680},[449,2634,2635],{"class":714}," tracker",[449,2637,718],{"class":680},[449,2639,1343],{"class":680},[449,2641,2642],{"class":721}," ActivityTracker",[449,2644,1322],{"class":684},[449,2646,2647,2649,2652,2655],{"class":451,"line":468},[449,2648,2056],{"class":680},[449,2650,2651],{"class":684}," tracker.",[449,2653,2654],{"class":721},"init",[449,2656,1322],{"class":684},[449,2658,2659],{"class":451,"line":474},[449,2660,700],{"emptyLinePlaceholder":130},[449,2662,2663],{"class":451,"line":480},[449,2664,2665],{"class":705},"\u002F\u002F Explicit tracking\n",[449,2667,2668,2670,2673,2675,2677,2679,2682,2684,2687,2689,2692],{"class":451,"line":486},[449,2669,711],{"class":680},[449,2671,2672],{"class":714}," activityId",[449,2674,718],{"class":680},[449,2676,1654],{"class":680},[449,2678,2651],{"class":684},[449,2680,2681],{"class":721},"startActivity",[449,2683,725],{"class":684},[449,2685,2686],{"class":691},"\"coding\"",[449,2688,349],{"class":684},[449,2690,2691],{"class":691},"\"Auth module refactor\"",[449,2693,740],{"class":684},[449,2695,2696,2698,2700,2703],{"class":451,"line":492},[449,2697,2056],{"class":680},[449,2699,2651],{"class":684},[449,2701,2702],{"class":721},"endActivity",[449,2704,2705],{"class":684},"(activityId);\n",[449,2707,2708],{"class":451,"line":498},[449,2709,700],{"emptyLinePlaceholder":130},[449,2711,2712],{"class":451,"line":133},[449,2713,2714],{"class":705},"\u002F\u002F Inferred tracking — call this in your tool hook\n",[449,2716,2717,2719,2721,2724,2726,2729,2732,2735,2738,2741],{"class":451,"line":509},[449,2718,2056],{"class":680},[449,2720,2651],{"class":684},[449,2722,2723],{"class":721},"recordToolCall",[449,2725,725],{"class":684},[449,2727,2728],{"class":691},"\"file_write\"",[449,2730,2731],{"class":684},", { path: ",[449,2733,2734],{"class":691},"\"src\u002Fauth.ts\"",[449,2736,2737],{"class":684}," }, ",[449,2739,2740],{"class":691},"\"ok\"",[449,2742,740],{"class":684},[449,2744,2745],{"class":451,"line":318},[449,2746,700],{"emptyLinePlaceholder":130},[449,2748,2749],{"class":451,"line":520},[449,2750,2751],{"class":705},"\u002F\u002F Daily report\n",[449,2753,2754,2756,2759,2761,2763,2766],{"class":451,"line":526},[449,2755,711],{"class":680},[449,2757,2758],{"class":714}," reporter",[449,2760,718],{"class":680},[449,2762,1343],{"class":680},[449,2764,2765],{"class":721}," Reporter",[449,2767,2768],{"class":684},"(store, tracker);\n",[449,2770,2771,2773,2776,2778,2780,2783,2786],{"class":451,"line":532},[449,2772,711],{"class":680},[449,2774,2775],{"class":714}," report",[449,2777,718],{"class":680},[449,2779,1654],{"class":680},[449,2781,2782],{"class":684}," reporter.",[449,2784,2785],{"class":721},"generateDailyReport",[449,2787,1322],{"class":684},[449,2789,2791,2793,2795,2798,2801],{"class":451,"line":2790},16,[449,2792,1672],{"class":684},[449,2794,1675],{"class":721},[449,2796,2797],{"class":684},"(reporter.",[449,2799,2800],{"class":721},"formatDailyReport",[449,2802,2803],{"class":684},"(report));\n",[2805,2806,2807,2812],"blog-card",{},[2808,2809,2811],"h3",{"id":2810},"why-accountability-in-a-coding-assistant","Why Accountability in a Coding Assistant?",[21,2813,2814],{},"Because the terminal is where developers spend most of their time. Putting time tracking, goal management, and daily reports there removes friction — no context switching to another app.",[16,2816,2818],{"id":2817},"project-awareness","Project Awareness",[21,2820,2821],{},"null-agent doesn't just chat — it understands your project.",[32,2823,2825],{"id":2824},"project-scanning","Project Scanning",[48,2827,2830],{"className":670,"code":2828,"filename":2829,"language":673,"meta":56,"style":56},"import { scanProject } from \"null-agent\";\n\nconst knowledge = await scanProject(\".\u002Fmy-project\");\nconsole.log(knowledge.language);      \u002F\u002F \"typescript\"\nconsole.log(knowledge.framework);     \u002F\u002F \"react\"\nconsole.log(knowledge.packageManager); \u002F\u002F \"pnpm\"\nconsole.log(knowledge.testCommand);   \u002F\u002F \"vitest\"\n","scan.ts",[40,2831,2832,2845,2849,2870,2882,2894,2906],{"__ignoreMap":56},[449,2833,2834,2836,2839,2841,2843],{"class":451,"line":452},[449,2835,681],{"class":680},[449,2837,2838],{"class":684}," { scanProject } ",[449,2840,688],{"class":680},[449,2842,692],{"class":691},[449,2844,695],{"class":684},[449,2846,2847],{"class":451,"line":114},[449,2848,700],{"emptyLinePlaceholder":130},[449,2850,2851,2853,2856,2858,2860,2863,2865,2868],{"class":451,"line":111},[449,2852,711],{"class":680},[449,2854,2855],{"class":714}," knowledge",[449,2857,718],{"class":680},[449,2859,1654],{"class":680},[449,2861,2862],{"class":721}," scanProject",[449,2864,725],{"class":684},[449,2866,2867],{"class":691},"\".\u002Fmy-project\"",[449,2869,740],{"class":684},[449,2871,2872,2874,2876,2879],{"class":451,"line":468},[449,2873,1672],{"class":684},[449,2875,1675],{"class":721},[449,2877,2878],{"class":684},"(knowledge.language);      ",[449,2880,2881],{"class":705},"\u002F\u002F \"typescript\"\n",[449,2883,2884,2886,2888,2891],{"class":451,"line":474},[449,2885,1672],{"class":684},[449,2887,1675],{"class":721},[449,2889,2890],{"class":684},"(knowledge.framework);     ",[449,2892,2893],{"class":705},"\u002F\u002F \"react\"\n",[449,2895,2896,2898,2900,2903],{"class":451,"line":480},[449,2897,1672],{"class":684},[449,2899,1675],{"class":721},[449,2901,2902],{"class":684},"(knowledge.packageManager); ",[449,2904,2905],{"class":705},"\u002F\u002F \"pnpm\"\n",[449,2907,2908,2910,2912,2915],{"class":451,"line":486},[449,2909,1672],{"class":684},[449,2911,1675],{"class":721},[449,2913,2914],{"class":684},"(knowledge.testCommand);   ",[449,2916,2917],{"class":705},"\u002F\u002F \"vitest\"\n",[21,2919,2920],{},"It detects Next.js, Nuxt, React, Vue, Express, Fastify, Hono, NestJS, and more.",[32,2922,2924],{"id":2923},"real-time-awareness","Real-Time Awareness",[21,2926,2927],{},"Git monitoring and file watching surface events as they happen:",[48,2929,2932],{"className":670,"code":2930,"filename":2931,"language":673,"meta":56,"style":56},"import { AwarenessManager } from \"null-agent\";\n\nconst awareness = new AwarenessManager({ projectDir: \".\u002Fmy-project\" });\nawareness.start({\n  onEvent: (event) => {\n    console.log(`${event.type}: ${event.message}`);\n  },\n});\n","awareness.ts",[40,2933,2934,2947,2951,2972,2982,2998,3031,3035],{"__ignoreMap":56},[449,2935,2936,2938,2941,2943,2945],{"class":451,"line":452},[449,2937,681],{"class":680},[449,2939,2940],{"class":684}," { AwarenessManager } ",[449,2942,688],{"class":680},[449,2944,692],{"class":691},[449,2946,695],{"class":684},[449,2948,2949],{"class":451,"line":114},[449,2950,700],{"emptyLinePlaceholder":130},[449,2952,2953,2955,2958,2960,2962,2965,2968,2970],{"class":451,"line":111},[449,2954,711],{"class":680},[449,2956,2957],{"class":714}," awareness",[449,2959,718],{"class":680},[449,2961,1343],{"class":680},[449,2963,2964],{"class":721}," AwarenessManager",[449,2966,2967],{"class":684},"({ projectDir: ",[449,2969,2867],{"class":691},[449,2971,2051],{"class":684},[449,2973,2974,2977,2980],{"class":451,"line":468},[449,2975,2976],{"class":684},"awareness.",[449,2978,2979],{"class":721},"start",[449,2981,1391],{"class":684},[449,2983,2984,2987,2989,2992,2994,2996],{"class":451,"line":474},[449,2985,2986],{"class":721},"  onEvent",[449,2988,1752],{"class":684},[449,2990,2991],{"class":1491},"event",[449,2993,1758],{"class":684},[449,2995,1498],{"class":680},[449,2997,1501],{"class":684},[449,2999,3000,3003,3005,3007,3010,3012,3014,3017,3020,3022,3024,3027,3029],{"class":451,"line":480},[449,3001,3002],{"class":684},"    console.",[449,3004,1675],{"class":721},[449,3006,725],{"class":684},[449,3008,3009],{"class":691},"`${",[449,3011,2991],{"class":684},[449,3013,165],{"class":691},[449,3015,3016],{"class":684},"type",[449,3018,3019],{"class":691},"}: ${",[449,3021,2991],{"class":684},[449,3023,165],{"class":691},[449,3025,3026],{"class":684},"message",[449,3028,1517],{"class":691},[449,3030,740],{"class":684},[449,3032,3033],{"class":451,"line":486},[449,3034,1474],{"class":684},[449,3036,3037],{"class":451,"line":492},[449,3038,784],{"class":684},[21,3040,3041,3042,349,3045,349,3048,349,3051,349,3054,1542,3057,165],{},"Events include ",[40,3043,3044],{},"git:change",[40,3046,3047],{},"git:branch",[40,3049,3050],{},"git:conflict",[40,3052,3053],{},"file:create",[40,3055,3056],{},"file:modify",[40,3058,3059],{},"file:delete",[16,3061,3063],{"id":3062},"memory-persistence","Memory & Persistence",[21,3065,3066,3067,3070],{},"Conversations are persisted to disk at ",[40,3068,3069],{},"~\u002F.null-agent\u002Fmemory\u002F",":",[48,3072,3075],{"className":670,"code":3073,"filename":3074,"language":673,"meta":56,"style":56},"import { Agent, MemoryStore } from \"null-agent\";\n\nconst memory = new MemoryStore();\nconst agent = new Agent({ provider, tools, memory });\n\n\u002F\u002F Resume a previous conversation\nawait agent.resumeConversation(\"conversation-id\");\n\n\u002F\u002F List saved conversations\nconst conversations = await memory.listConversations();\n","memory.ts",[40,3076,3077,3090,3094,3110,3125,3129,3134,3150,3154,3159],{"__ignoreMap":56},[449,3078,3079,3081,3084,3086,3088],{"class":451,"line":452},[449,3080,681],{"class":680},[449,3082,3083],{"class":684}," { Agent, MemoryStore } ",[449,3085,688],{"class":680},[449,3087,692],{"class":691},[449,3089,695],{"class":684},[449,3091,3092],{"class":451,"line":114},[449,3093,700],{"emptyLinePlaceholder":130},[449,3095,3096,3098,3101,3103,3105,3108],{"class":451,"line":111},[449,3097,711],{"class":680},[449,3099,3100],{"class":714}," memory",[449,3102,718],{"class":680},[449,3104,1343],{"class":680},[449,3106,3107],{"class":721}," MemoryStore",[449,3109,1322],{"class":684},[449,3111,3112,3114,3116,3118,3120,3122],{"class":451,"line":468},[449,3113,711],{"class":680},[449,3115,1589],{"class":714},[449,3117,718],{"class":680},[449,3119,1343],{"class":680},[449,3121,1596],{"class":721},[449,3123,3124],{"class":684},"({ provider, tools, memory });\n",[449,3126,3127],{"class":451,"line":474},[449,3128,700],{"emptyLinePlaceholder":130},[449,3130,3131],{"class":451,"line":480},[449,3132,3133],{"class":705},"\u002F\u002F Resume a previous conversation\n",[449,3135,3136,3138,3140,3143,3145,3148],{"class":451,"line":486},[449,3137,2056],{"class":680},[449,3139,1657],{"class":684},[449,3141,3142],{"class":721},"resumeConversation",[449,3144,725],{"class":684},[449,3146,3147],{"class":691},"\"conversation-id\"",[449,3149,740],{"class":684},[449,3151,3152],{"class":451,"line":492},[449,3153,700],{"emptyLinePlaceholder":130},[449,3155,3156],{"class":451,"line":498},[449,3157,3158],{"class":705},"\u002F\u002F List saved conversations\n",[449,3160,3161,3163,3166,3168,3170,3173,3176],{"class":451,"line":133},[449,3162,711],{"class":680},[449,3164,3165],{"class":714}," conversations",[449,3167,718],{"class":680},[449,3169,1654],{"class":680},[449,3171,3172],{"class":684}," memory.",[449,3174,3175],{"class":721},"listConversations",[449,3177,1322],{"class":684},[16,3179,3181],{"id":3180},"permission-system","Permission System",[21,3183,3184],{},"Control what the agent can do with three modes:",[48,3186,3189],{"className":670,"code":3187,"filename":3188,"language":673,"meta":56,"style":56},"import { PermissionManager } from \"null-agent\";\n\nconst permissions = new PermissionManager({\n  mode: \"confirm\", \u002F\u002F \"auto\" | \"confirm\" | \"plan\"\n  allowWrite: true,\n  allowShell: true,\n  allowGit: true,\n  denyPatterns: [\"rm -rf\", \"sudo\"],\n});\n","permissions.ts",[40,3190,3191,3204,3208,3224,3237,3246,3255,3264,3279],{"__ignoreMap":56},[449,3192,3193,3195,3198,3200,3202],{"class":451,"line":452},[449,3194,681],{"class":680},[449,3196,3197],{"class":684}," { PermissionManager } ",[449,3199,688],{"class":680},[449,3201,692],{"class":691},[449,3203,695],{"class":684},[449,3205,3206],{"class":451,"line":114},[449,3207,700],{"emptyLinePlaceholder":130},[449,3209,3210,3212,3215,3217,3219,3222],{"class":451,"line":111},[449,3211,711],{"class":680},[449,3213,3214],{"class":714}," permissions",[449,3216,718],{"class":680},[449,3218,1343],{"class":680},[449,3220,3221],{"class":721}," PermissionManager",[449,3223,1391],{"class":684},[449,3225,3226,3229,3232,3234],{"class":451,"line":468},[449,3227,3228],{"class":684},"  mode: ",[449,3230,3231],{"class":691},"\"confirm\"",[449,3233,349],{"class":684},[449,3235,3236],{"class":705},"\u002F\u002F \"auto\" | \"confirm\" | \"plan\"\n",[449,3238,3239,3242,3244],{"class":451,"line":474},[449,3240,3241],{"class":684},"  allowWrite: ",[449,3243,2169],{"class":714},[449,3245,779],{"class":684},[449,3247,3248,3251,3253],{"class":451,"line":480},[449,3249,3250],{"class":684},"  allowShell: ",[449,3252,2169],{"class":714},[449,3254,779],{"class":684},[449,3256,3257,3260,3262],{"class":451,"line":486},[449,3258,3259],{"class":684},"  allowGit: ",[449,3261,2169],{"class":714},[449,3263,779],{"class":684},[449,3265,3266,3269,3272,3274,3277],{"class":451,"line":492},[449,3267,3268],{"class":684},"  denyPatterns: [",[449,3270,3271],{"class":691},"\"rm -rf\"",[449,3273,349],{"class":684},[449,3275,3276],{"class":691},"\"sudo\"",[449,3278,1469],{"class":684},[449,3280,3281],{"class":451,"line":498},[449,3282,784],{"class":684},[383,3284,3285,3291,3297],{},[386,3286,3287,3290],{},[151,3288,3289],{},"auto"," — execute everything",[386,3292,3293,3296],{},[151,3294,3295],{},"confirm"," — ask before destructive operations",[386,3298,3299,3302],{},[151,3300,3301],{},"plan"," — read-only mode",[16,3304,3306],{"id":3305},"plugin-system","Plugin System",[21,3308,3309],{},"Extend null-agent without modifying core code:",[48,3311,3314],{"className":670,"code":3312,"filename":3313,"language":673,"meta":56,"style":56},"import { PluginManager, EventBus } from \"null-agent\";\n\nconst bus = new EventBus();\nconst manager = new PluginManager(bus, process.cwd());\n\nawait manager.register({\n  name: \"my-plugin\",\n  version: \"1.0.0\",\n  setup(context) {\n    context.registerTool({\n      name: \"my_tool\",\n      description: \"Does something useful\",\n      parameters: { type: \"object\", properties: {} },\n      execute: async () => ({ content: \"done\" }),\n    });\n    context.bus.on(\"agent:text\", (event) => {\n      console.log(\"Agent said:\", event.data);\n    });\n  },\n});\n","plugin.ts",[40,3315,3316,3329,3333,3349,3372,3376,3387,3396,3406,3418,3428,3438,3448,3458,3480,3485,3509,3524,3529,3534],{"__ignoreMap":56},[449,3317,3318,3320,3323,3325,3327],{"class":451,"line":452},[449,3319,681],{"class":680},[449,3321,3322],{"class":684}," { PluginManager, EventBus } ",[449,3324,688],{"class":680},[449,3326,692],{"class":691},[449,3328,695],{"class":684},[449,3330,3331],{"class":451,"line":114},[449,3332,700],{"emptyLinePlaceholder":130},[449,3334,3335,3337,3340,3342,3344,3347],{"class":451,"line":111},[449,3336,711],{"class":680},[449,3338,3339],{"class":714}," bus",[449,3341,718],{"class":680},[449,3343,1343],{"class":680},[449,3345,3346],{"class":721}," EventBus",[449,3348,1322],{"class":684},[449,3350,3351,3353,3356,3358,3360,3363,3366,3369],{"class":451,"line":468},[449,3352,711],{"class":680},[449,3354,3355],{"class":714}," manager",[449,3357,718],{"class":680},[449,3359,1343],{"class":680},[449,3361,3362],{"class":721}," PluginManager",[449,3364,3365],{"class":684},"(bus, process.",[449,3367,3368],{"class":721},"cwd",[449,3370,3371],{"class":684},"());\n",[449,3373,3374],{"class":451,"line":474},[449,3375,700],{"emptyLinePlaceholder":130},[449,3377,3378,3380,3383,3385],{"class":451,"line":480},[449,3379,2056],{"class":680},[449,3381,3382],{"class":684}," manager.",[449,3384,1356],{"class":721},[449,3386,1391],{"class":684},[449,3388,3389,3391,3394],{"class":451,"line":486},[449,3390,1396],{"class":684},[449,3392,3393],{"class":691},"\"my-plugin\"",[449,3395,779],{"class":684},[449,3397,3398,3401,3404],{"class":451,"line":492},[449,3399,3400],{"class":684},"  version: ",[449,3402,3403],{"class":691},"\"1.0.0\"",[449,3405,779],{"class":684},[449,3407,3408,3411,3413,3415],{"class":451,"line":498},[449,3409,3410],{"class":721},"  setup",[449,3412,725],{"class":684},[449,3414,1916],{"class":1491},[449,3416,3417],{"class":684},") {\n",[449,3419,3420,3423,3426],{"class":451,"line":133},[449,3421,3422],{"class":684},"    context.",[449,3424,3425],{"class":721},"registerTool",[449,3427,1391],{"class":684},[449,3429,3430,3433,3436],{"class":451,"line":509},[449,3431,3432],{"class":684},"      name: ",[449,3434,3435],{"class":691},"\"my_tool\"",[449,3437,779],{"class":684},[449,3439,3440,3443,3446],{"class":451,"line":318},[449,3441,3442],{"class":684},"      description: ",[449,3444,3445],{"class":691},"\"Does something useful\"",[449,3447,779],{"class":684},[449,3449,3450,3453,3455],{"class":451,"line":520},[449,3451,3452],{"class":684},"      parameters: { type: ",[449,3454,1424],{"class":691},[449,3456,3457],{"class":684},", properties: {} },\n",[449,3459,3460,3463,3465,3467,3469,3471,3474,3477],{"class":451,"line":526},[449,3461,3462],{"class":721},"      execute",[449,3464,1482],{"class":684},[449,3466,1485],{"class":680},[449,3468,2090],{"class":684},[449,3470,1498],{"class":680},[449,3472,3473],{"class":684}," ({ content: ",[449,3475,3476],{"class":691},"\"done\"",[449,3478,3479],{"class":684}," }),\n",[449,3481,3482],{"class":451,"line":532},[449,3483,3484],{"class":684},"    });\n",[449,3486,3487,3490,3493,3495,3498,3501,3503,3505,3507],{"class":451,"line":2790},[449,3488,3489],{"class":684},"    context.bus.",[449,3491,3492],{"class":721},"on",[449,3494,725],{"class":684},[449,3496,3497],{"class":691},"\"agent:text\"",[449,3499,3500],{"class":684},", (",[449,3502,2991],{"class":1491},[449,3504,1758],{"class":684},[449,3506,1498],{"class":680},[449,3508,1501],{"class":684},[449,3510,3512,3514,3516,3518,3521],{"class":451,"line":3511},17,[449,3513,1927],{"class":684},[449,3515,1675],{"class":721},[449,3517,725],{"class":684},[449,3519,3520],{"class":691},"\"Agent said:\"",[449,3522,3523],{"class":684},", event.data);\n",[449,3525,3527],{"class":451,"line":3526},18,[449,3528,3484],{"class":684},[449,3530,3532],{"class":451,"line":3531},19,[449,3533,1474],{"class":684},[449,3535,3537],{"class":451,"line":3536},20,[449,3538,784],{"class":684},[16,3540,3542],{"id":3541},"testing-code-review","Testing & Code Review",[21,3544,3545],{},"null-agent can generate tests, run them, analyze failures, and perform comprehensive code reviews:",[48,3547,3550],{"className":670,"code":3548,"filename":3549,"language":673,"meta":56,"style":56},"import { reviewCode, formatReviewReport } from \"null-agent\";\n\nconst result = await reviewCode({ diff: true });\nconsole.log(formatReviewReport(result));\n","review.ts",[40,3551,3552,3565,3569,3589],{"__ignoreMap":56},[449,3553,3554,3556,3559,3561,3563],{"class":451,"line":452},[449,3555,681],{"class":680},[449,3557,3558],{"class":684}," { reviewCode, formatReviewReport } ",[449,3560,688],{"class":680},[449,3562,692],{"class":691},[449,3564,695],{"class":684},[449,3566,3567],{"class":451,"line":114},[449,3568,700],{"emptyLinePlaceholder":130},[449,3570,3571,3573,3575,3577,3579,3582,3585,3587],{"class":451,"line":111},[449,3572,711],{"class":680},[449,3574,1649],{"class":714},[449,3576,718],{"class":680},[449,3578,1654],{"class":680},[449,3580,3581],{"class":721}," reviewCode",[449,3583,3584],{"class":684},"({ diff: ",[449,3586,2169],{"class":714},[449,3588,2051],{"class":684},[449,3590,3591,3593,3595,3597,3600],{"class":451,"line":468},[449,3592,1672],{"class":684},[449,3594,1675],{"class":721},[449,3596,725],{"class":684},[449,3598,3599],{"class":721},"formatReviewReport",[449,3601,3602],{"class":684},"(result));\n",[21,3604,3605,3606,3609,3610,3613,3614,3617,3618,3620],{},"Review categories include ",[151,3607,3608],{},"security"," (SQL injection, hardcoded secrets), ",[151,3611,3612],{},"performance"," (N+1 queries, sync ops), ",[151,3615,3616],{},"quality"," (long functions, deep nesting), and ",[151,3619,2424],{}," (missing test files).",[16,3622,3624],{"id":3623},"building-and-publishing","Building and Publishing",[21,3626,3627],{},"null-agent is built with Vite+:",[48,3629,3631],{"className":839,"code":3630,"language":841,"meta":56,"style":56},"vp check        # Format, lint, and type-check\nvp test         # Run all 255 tests\nvp pack         # Build for npm\n",[40,3632,3633,3644,3654],{"__ignoreMap":56},[449,3634,3635,3638,3641],{"class":451,"line":452},[449,3636,3637],{"class":721},"vp",[449,3639,3640],{"class":691}," check",[449,3642,3643],{"class":705},"        # Format, lint, and type-check\n",[449,3645,3646,3648,3651],{"class":451,"line":114},[449,3647,3637],{"class":721},[449,3649,3650],{"class":691}," test",[449,3652,3653],{"class":705},"         # Run all 255 tests\n",[449,3655,3656,3658,3661],{"class":451,"line":111},[449,3657,3637],{"class":721},[449,3659,3660],{"class":691}," pack",[449,3662,3663],{"class":705},"         # Build for npm\n",[21,3665,3666],{},"The package exports ESM with TypeScript declarations, and the CLI is available as both a library import and a global binary.",[427,3668,3671],{"icon":3669,"title":3670},"📦","Open Source",[21,3672,3673,3674,3677],{},"null-agent is MIT licensed and available on npm. The source lives in the ",[40,3675,3676],{},"packages\u002Fnull-agent"," directory of the monorepo.",[16,3679,3681],{"id":3680},"conclusion","Conclusion",[21,3683,3684],{},"null-agent started as a personal tool to avoid switching between different LLM interfaces. It grew into something larger: a unified platform for AI-assisted development that respects your existing workflow rather than replacing it.",[21,3686,3687],{},"Whether you need a library to embed in your own tools, a terminal UI for daily coding, an HTTP API for integrations, or just a reliable REPL that works with any provider — null-agent covers it.",[21,3689,3690],{},"The accountability tracker was an experiment that stuck. Having goals, time breakdowns, and daily reports inside the terminal removes the friction of context switching to yet another productivity app.",[21,3692,3693],{},"If you're building developer tools or just want a coding assistant you can actually customize, give it a try:",[48,3695,3697],{"className":839,"code":3696,"language":841,"meta":56,"style":56},"npm install -g null-agent\nnull-agent auth\nnull-agent\n",[40,3698,3699,3713,3720],{"__ignoreMap":56},[449,3700,3701,3704,3707,3710],{"class":451,"line":452},[449,3702,3703],{"class":721},"npm",[449,3705,3706],{"class":691}," install",[449,3708,3709],{"class":714}," -g",[449,3711,3712],{"class":691}," null-agent\n",[449,3714,3715,3717],{"class":451,"line":114},[449,3716,341],{"class":721},[449,3718,3719],{"class":691}," auth\n",[449,3721,3722],{"class":451,"line":111},[449,3723,2284],{"class":721},[21,3725,3726],{},"Ship code. Track time. Stay accountable.",[3728,3729,3730],"style",{},"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":3732},[3733,3734,3735,3736,3737,3738,3739,3740,3741,3742,3743,3744,3745,3746,3747,3748,3749,3750,3751,3752,3755,3756],{"id":666,"depth":114,"text":667},{"id":904,"depth":114,"text":905},{"id":973,"depth":114,"text":974},{"id":1131,"depth":114,"text":1132},{"id":1272,"depth":114,"text":1273},{"id":1279,"depth":114,"text":1280},{"id":1371,"depth":114,"text":1372},{"id":1705,"depth":114,"text":1706},{"id":1864,"depth":114,"text":1865},{"id":1994,"depth":114,"text":1995},{"id":2117,"depth":114,"text":2118},{"id":2215,"depth":114,"text":2216},{"id":2293,"depth":114,"text":2294},{"id":2312,"depth":114,"text":2313},{"id":2367,"depth":114,"text":2368},{"id":2405,"depth":114,"text":2406},{"id":2457,"depth":114,"text":2458},{"id":2550,"depth":114,"text":2551},{"id":2583,"depth":114,"text":2584},{"id":2604,"depth":114,"text":2605,"children":3753},[3754],{"id":2810,"depth":111,"text":2811},{"id":2824,"depth":114,"text":2825},{"id":2923,"depth":114,"text":2924},"2026-05-25","A deep dive into null-agent — an open-source TypeScript library that brings multi-provider LLM support, a rich terminal UI, developer accountability tracking, and 49 built-in tools to your coding workflow.",{"type":13,"value":3760},[3761,3763,3765,3769],[16,3762,333],{"id":332},[21,3764,336],{},[21,3766,3767,342],{},[151,3768,341],{},[21,3770,345,3771,349,3773,349,3775,356,3777,360],{},[151,3772,348],{},[151,3774,352],{},[151,3776,355],{},[151,3778,359],{},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555066931-4365d14bab8c",[341,3781,3782,3783,3784,3785,3786,3787],"llm","ai","coding-assistant","typescript","cli","terminal-ui","open-source","TypeScript",{},"\u002Fblog\u002Fbuilding-null-agent",{"title":325,"description":3758},"blog\u002Fbuilding-null-agent","Technology","YU7If0pMcOQ7vNZB_MH1fhRbCVPxmITOchCxWzcZyxc",{"id":3796,"title":3797,"author":3798,"body":3799,"canonical":116,"categories":116,"date":4149,"description":4150,"excerpt":4151,"extension":119,"img":4159,"keywords":4160,"language":4162,"meta":4163,"navigation":130,"ogImage":116,"path":4164,"rating":132,"readTime":4032,"seo":4165,"stem":4166,"tag":4167,"__hash__":4168},"blog\u002Fblog\u002Fbuilding-vue3-progressive-app.md","Building a Vue 3 Progressive Koffee Lovers App Part One",{"name":9,"website":10,"img":11},{"type":13,"value":3800,"toc":4141},[3801,3805,3809,3812,3815,3819,3826,3829,3835,3839,3842,3848,3851,3857,3866,3870,3873,3877,3883,3887,3890,3896,3899,4114,4117,4120,4126,4129,4135,4138],[16,3802,3804],{"id":3803},"what-are-we-making","What are We Making?",[32,3806,3808],{"id":3807},"koffee-lovers-vue3-pwa","Koffee Lovers Vue3 PWA",[21,3810,3811],{},"In this post, we will look at building a coffee web application with progressive web app (PWA).",[21,3813,3814],{},"Examining the workings of Vue3, creating components, using directives and use props to pass data to components.  We can also try and utilize mixins to reduce reptition of code by abstracting out similar features in these components.",[32,3816,3818],{"id":3817},"teacher-mode-what-is-a-pwa","Teacher Mode: What is a PWA?",[427,3820,3823],{"icon":3821,"title":3822},"🎓","Teacher Mode",[21,3824,3825],{},"“PWAs are special web apps that can be installed on the user's computer, and the browser manages these installed apps. They differ from regular web apps as they let us access some computer hardware natively. When we visit a PWA in our browsers, we can choose to install the PWA”",[21,3827,3828],{},"This is basically what a PWA is - an app that can work offline once install on the users device.",[3830,3831,3832],"tip",{},[21,3833,3834],{},"Always test your PWA in an Incognito window or after clearing site data to ensure the service worker is fetching fresh assets correctly during development.",[32,3836,3838],{"id":3837},"lets-code","Lets Code!",[21,3840,3841],{},"We will be using Vite for quick prototyping our app.",[48,3843,3846],{"className":3844,"code":3845,"language":53},[51],"npm init vite@latest\n",[40,3847,3845],{"__ignoreMap":56},[21,3849,3850],{},"Follow the prompt name your app whatever you like but we will call this vue3-koffee-lovers",[48,3852,3855],{"className":3853,"code":3854,"language":53},[51],"npm run dev\n",[40,3856,3854],{"__ignoreMap":56},[21,3858,3859,3860],{},"Your development server should be running on ",[3861,3862,3863],"a",{"href":3863,"rel":3864},"http:\u002F\u002Flocalhost:3000",[3865],"nofollow",[32,3867,3869],{"id":3868},"layout","Layout",[21,3871,3872],{},"Generally its important to get the layout of your web application up and running by creating all the folders necessary for you web application to work smoothly. One such folder is your Components Folder. Vue.js is a progressive framework for building user interfaces, through the use of components.",[32,3874,3876],{"id":3875},"setting-up-pwa","Setting up PWA",[48,3878,3881],{"className":3879,"code":3880,"language":53},[51],"npm install vite-plugin-pwa workbox-precaching -D\n",[40,3882,3880],{"__ignoreMap":56},[32,3884,3886],{"id":3885},"config","Config",[21,3888,3889],{},"Configuration can be done in vite.config.js file. Installation of the plugin would look something this:",[48,3891,3894],{"className":3892,"code":3893,"language":53},[51],"import { defineConfig } from \"vite\"\nimport vue from \"@vitejs\u002Fplugin-vue\"\nimport { VitePWA } from \"vite-plugin-pwa\"\n\nexport default defineConfig({\n     plugins: [ vue(), VitePWA()],\n})\n\n",[40,3895,3893],{"__ignoreMap":56},[21,3897,3898],{},"This basically enables the plugin however there are additional setup required in order to get PWA to work correctly on devices. Look at the following code:",[48,3900,3905],{"className":3901,"code":3902,"language":3903,"meta":3904,"style":56},"language-\u002F\u002F shiki shiki-themes github-light github-dark","export default defineConfig({\n  plugins: [\n    vue(),\n    VitePWA({\n      mode: \"development\",\n      base: \"\u002F\",\n      srcDir: \"src\",\n      filename: \"sw.ts\",\n      includeAssets: [\"\u002Ffavicon.png\"],\n      strategies: \"injectManifest\",\n      manifest: {\n        name: \"Test Project\",\n        short_name: \"Test\",\n        theme_color: \"#ffffff\",\n        start_url: \"\u002F\",\n        display: \"standalone\",\n        background_color: \"#ffffff\",\n        icons: [\n          {\n            src: \"icon-192.png\",\n            sizes: \"192x192\",\n            type: \"image\u002Fpng\",\n          },\n          {\n            src: \"\u002Ficon-512.png\",\n            sizes: \"512x512\",\n            type: \"image\u002Fpng\",\n          },\n          {\n            src: \"icon-512.png\",\n            sizes: \"512x512\",\n            type: \"image\u002Fpng\",\n            purpose: \"any maskable\",\n          },\n        ],\n      },\n    }),\n  ],\n})\n\n","\u002F\u002F","https:\u002F\u002Fvitejs.dev\u002Fconfig\u002F",[40,3906,3907,3912,3917,3922,3927,3932,3937,3942,3947,3952,3957,3962,3967,3972,3977,3982,3987,3992,3997,4002,4007,4013,4019,4025,4030,4036,4042,4047,4052,4057,4063,4068,4073,4079,4084,4090,4096,4102,4108],{"__ignoreMap":56},[449,3908,3909],{"class":451,"line":452},[449,3910,3911],{},"export default defineConfig({\n",[449,3913,3914],{"class":451,"line":114},[449,3915,3916],{},"  plugins: [\n",[449,3918,3919],{"class":451,"line":111},[449,3920,3921],{},"    vue(),\n",[449,3923,3924],{"class":451,"line":468},[449,3925,3926],{},"    VitePWA({\n",[449,3928,3929],{"class":451,"line":474},[449,3930,3931],{},"      mode: \"development\",\n",[449,3933,3934],{"class":451,"line":480},[449,3935,3936],{},"      base: \"\u002F\",\n",[449,3938,3939],{"class":451,"line":486},[449,3940,3941],{},"      srcDir: \"src\",\n",[449,3943,3944],{"class":451,"line":492},[449,3945,3946],{},"      filename: \"sw.ts\",\n",[449,3948,3949],{"class":451,"line":498},[449,3950,3951],{},"      includeAssets: [\"\u002Ffavicon.png\"],\n",[449,3953,3954],{"class":451,"line":133},[449,3955,3956],{},"      strategies: \"injectManifest\",\n",[449,3958,3959],{"class":451,"line":509},[449,3960,3961],{},"      manifest: {\n",[449,3963,3964],{"class":451,"line":318},[449,3965,3966],{},"        name: \"Test Project\",\n",[449,3968,3969],{"class":451,"line":520},[449,3970,3971],{},"        short_name: \"Test\",\n",[449,3973,3974],{"class":451,"line":526},[449,3975,3976],{},"        theme_color: \"#ffffff\",\n",[449,3978,3979],{"class":451,"line":532},[449,3980,3981],{},"        start_url: \"\u002F\",\n",[449,3983,3984],{"class":451,"line":2790},[449,3985,3986],{},"        display: \"standalone\",\n",[449,3988,3989],{"class":451,"line":3511},[449,3990,3991],{},"        background_color: \"#ffffff\",\n",[449,3993,3994],{"class":451,"line":3526},[449,3995,3996],{},"        icons: [\n",[449,3998,3999],{"class":451,"line":3531},[449,4000,4001],{},"          {\n",[449,4003,4004],{"class":451,"line":3536},[449,4005,4006],{},"            src: \"icon-192.png\",\n",[449,4008,4010],{"class":451,"line":4009},21,[449,4011,4012],{},"            sizes: \"192x192\",\n",[449,4014,4016],{"class":451,"line":4015},22,[449,4017,4018],{},"            type: \"image\u002Fpng\",\n",[449,4020,4022],{"class":451,"line":4021},23,[449,4023,4024],{},"          },\n",[449,4026,4028],{"class":451,"line":4027},24,[449,4029,4001],{},[449,4031,4033],{"class":451,"line":4032},25,[449,4034,4035],{},"            src: \"\u002Ficon-512.png\",\n",[449,4037,4039],{"class":451,"line":4038},26,[449,4040,4041],{},"            sizes: \"512x512\",\n",[449,4043,4045],{"class":451,"line":4044},27,[449,4046,4018],{},[449,4048,4050],{"class":451,"line":4049},28,[449,4051,4024],{},[449,4053,4055],{"class":451,"line":4054},29,[449,4056,4001],{},[449,4058,4060],{"class":451,"line":4059},30,[449,4061,4062],{},"            src: \"icon-512.png\",\n",[449,4064,4066],{"class":451,"line":4065},31,[449,4067,4041],{},[449,4069,4071],{"class":451,"line":4070},32,[449,4072,4018],{},[449,4074,4076],{"class":451,"line":4075},33,[449,4077,4078],{},"            purpose: \"any maskable\",\n",[449,4080,4082],{"class":451,"line":4081},34,[449,4083,4024],{},[449,4085,4087],{"class":451,"line":4086},35,[449,4088,4089],{},"        ],\n",[449,4091,4093],{"class":451,"line":4092},36,[449,4094,4095],{},"      },\n",[449,4097,4099],{"class":451,"line":4098},37,[449,4100,4101],{},"    }),\n",[449,4103,4105],{"class":451,"line":4104},38,[449,4106,4107],{},"  ],\n",[449,4109,4111],{"class":451,"line":4110},39,[449,4112,4113],{},"})\n",[21,4115,4116],{},"Every PWA must have a manifest. A Manifest is a file that defines (in JSON format) the name of the application, the background color that it will have on the device once installed, the location, formats of the icons used and the root url for PWA (web application can have different url for web and PWA). That is, the minimum requirements necessary for PWA.",[21,4118,4119],{},"Next is to config the Service Worker (this allows code to run in the background and secondary actions being able to work). Service Worker is located in src\u002Fswt.(js or ts)",[48,4121,4124],{"className":4122,"code":4123,"language":53},[51],"import { precacheAndRoute } from 'workbox-precaching'\ndeclare let self: ServiceWorkerGlobalScope\nself.addEventListener('message', (event) => {\n  if (event.data && event.data.type === 'SKIP_WAITING') self.skipWaiting()\n})\n\u002F\u002F self.__WB_MANIFEST is default injection point\nprecacheAndRoute(self.__WB_MANIFEST)\n",[40,4125,4123],{"__ignoreMap":56},[21,4127,4128],{},"Finally! its just to register the PWA using a component.",[48,4130,4133],{"className":4131,"code":4132,"language":53},[51],"\u003Cscript lang=\"ts\">\nimport { defineComponent } from \"vue\";\nimport { useRegisterSW } from \"virtual:pwa-register\u002Fvue\";\nconst { updateServiceWorker } = useRegisterSW();\nexport default defineComponent({\n  name: \"ReloadPWA\",\n  setup() {\n    const { offlineReady, needRefresh, updateServiceWorker } = useRegisterSW();\n    const close = async () => {\n      offlineReady.value = false;\n      needRefresh.value = false;\n    };\n    return { offlineReady, needRefresh, updateServiceWorker, close };\n  },\n  methods: {\n    async close() {\n      this.offlineReady.value = false;\n      this.needRefresh.value = false;\n    },\n    async updateServiceWorker() {\n      await updateServiceWorker();\n    },\n  },\n});\n\u003C\u002Fscript>\n",[40,4134,4132],{"__ignoreMap":56},[21,4136,4137],{},"Once connection is made in this component to App.vue, PWA will be fully setup",[3728,4139,4140],{},"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);}",{"title":56,"searchDepth":111,"depth":111,"links":4142},[4143,4144,4145,4146,4147,4148],{"id":3807,"depth":114,"text":3808},{"id":3817,"depth":114,"text":3818},{"id":3837,"depth":114,"text":3838},{"id":3868,"depth":114,"text":3869},{"id":3875,"depth":114,"text":3876},{"id":3885,"depth":114,"text":3886},"2022-01-20","Examine the inner workings of a Vue 3 app combining technologies like Supabase, Pinia to complete this code.",{"type":13,"value":4152},[4153,4155,4157],[16,4154,3804],{"id":3803},[32,4156,3808],{"id":3807},[21,4158,3811],{},"https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1642478176\u002Fvantol\u002FScreenshot_2021-07-29_at_11.07.21_PM_mepvjg.png",[122,123,124,125,4161,127],"pinia","Vue.js",{},"\u002Fblog\u002Fbuilding-vue3-progressive-app",{"title":3797,"description":4150},"blog\u002Fbuilding-vue3-progressive-app","Coffee App","nSXNk7VFe35Q4XtBMRQ7h9qffsD9OFtiLz6IpNEqhfs",{"id":4170,"title":4171,"author":4172,"body":4173,"canonical":116,"categories":116,"date":7042,"description":7043,"excerpt":7044,"extension":119,"img":7074,"keywords":7075,"language":3788,"meta":7081,"navigation":130,"ogImage":116,"path":7082,"rating":474,"readTime":133,"seo":7083,"stem":7084,"tag":3793,"__hash__":7085},"blog\u002Fblog\u002Fbun-nitro-stack.md","Bun + Nitro: The Fast Server Stack for 2026",{"name":9,"img":11,"website":327},{"type":13,"value":4174,"toc":7010},[4175,4177,4184,4191,4194,4211,4218,4222,4225,4229,4261,4266,4268,4272,4275,4358,4361,4368,4370,4374,4378,4381,4428,4431,4449,4451,4455,4458,4493,4498,4501,4513,4516,4605,4607,4611,4614,4620,4684,4687,4689,4693,4696,4702,4769,4775,4948,4951,5276,5278,5282,5285,5300,5307,5310,5329,5331,5335,5338,5343,5991,5994,6030,6032,6036,6040,6043,6204,6208,6211,6294,6298,6301,6488,6490,6494,6498,6605,6609,6633,6635,6639,6643,6648,6665,6669,6725,6729,6789,6793,6796,6820,6826,6828,6832,6835,6852,6856,6921,6923,6925,6931,6962,6965,6969,6986,6990,7001,7004,7007],[16,4176,333],{"id":332},[21,4178,4179,4180,4183],{},"The JavaScript ecosystem is evolving fast, and ",[151,4181,4182],{},"Bun"," is leading the charge. Since its 1.0 release, Bun has proven itself as a legitimate alternative to Node.js—offering significantly faster startup times, native TypeScript support, and an all-in-one toolkit that includes a package manager, bundler, and test runner.",[21,4185,4186,4187,4190],{},"When paired with ",[151,4188,4189],{},"Nitro",", you get a server stack that's both incredibly fast and deployment-agnostic.",[21,4192,4193],{},"In this post, we'll explore:",[383,4195,4196,4199,4202,4205,4208],{},[386,4197,4198],{},"Why Bun matters in 2026",[386,4200,4201],{},"Setting up Bun with Nitro",[386,4203,4204],{},"Performance benefits and benchmarks",[386,4206,4207],{},"Migration strategies from Node.js",[386,4209,4210],{},"Real-world deployment",[21,4212,4213,4214,4217],{},"Let's build the ",[151,4215,4216],{},"fastest server stack"," you've ever used.",[16,4219,4221],{"id":4220},"why-bun-in-2026","Why Bun in 2026?",[21,4223,4224],{},"Bun isn't just a faster Node.js—it's a reimagining of the JavaScript runtime. Here's what makes it special:",[32,4226,4228],{"id":4227},"key-features","Key Features",[383,4230,4231,4237,4243,4249,4255],{},[386,4232,4233,4236],{},[151,4234,4235],{},"Native TypeScript"," — No transpilation needed, runs .ts files directly",[386,4238,4239,4242],{},[151,4240,4241],{},"Zig-based runtime"," — Built with performance in mind from the ground up",[386,4244,4245,4248],{},[151,4246,4247],{},"All-in-one toolkit"," — Package manager, bundler, test runner included",[386,4250,4251,4254],{},[151,4252,4253],{},"Web API compatibility"," — fetch, WebSocket, Buffer built-in",[386,4256,4257,4260],{},[151,4258,4259],{},"Native Node.js compatibility"," — Most npm packages work out of the box",[25,4262,4263],{"type":363},[21,4264,4265],{},"Bun's startup time is 4-6x faster than Node.js, and it can handle significantly more requests per second in I\u002FO-heavy workloads.",[73,4267],{},[32,4269,4271],{"id":4270},"performance-benchmarks","Performance Benchmarks",[21,4273,4274],{},"Let's look at real-world performance comparisons:",[547,4276,4277,4292],{},[550,4278,4279],{},[553,4280,4281,4284,4287,4289],{},[556,4282,4283],{},"Metric",[556,4285,4286],{},"Node.js",[556,4288,4182],{},[556,4290,4291],{},"Improvement",[569,4293,4294,4310,4326,4342],{},[553,4295,4296,4299,4302,4305],{},[574,4297,4298],{},"Startup time",[574,4300,4301],{},"45ms",[574,4303,4304],{},"8ms",[574,4306,4307],{},[151,4308,4309],{},"5.6x faster",[553,4311,4312,4315,4318,4321],{},[574,4313,4314],{},"HTTP requests\u002Fsec",[574,4316,4317],{},"12,400",[574,4319,4320],{},"48,200",[574,4322,4323],{},[151,4324,4325],{},"3.9x faster",[553,4327,4328,4331,4334,4337],{},[574,4329,4330],{},"TypeScript execution",[574,4332,4333],{},"180ms",[574,4335,4336],{},"12ms",[574,4338,4339],{},[151,4340,4341],{},"15x faster",[553,4343,4344,4347,4350,4353],{},[574,4345,4346],{},"Memory usage",[574,4348,4349],{},"120MB",[574,4351,4352],{},"85MB",[574,4354,4355],{},[151,4356,4357],{},"29% less",[21,4359,4360],{},"These aren't synthetic benchmarks—they're representative of real API workloads.",[427,4362,4365],{"icon":4363,"title":4364},"📊","Real Application Performance",[21,4366,4367],{},"In our BrewStop API tests, switching from Node.js to Bun reduced cold start times from 2.1s to 340ms—an 85% improvement.",[73,4369],{},[16,4371,4373],{"id":4372},"main-content","Main Content",[32,4375,4377],{"id":4376},"step-1-install-bun","Step 1: Install Bun",[21,4379,4380],{},"If you haven't installed Bun yet, it's a single command:",[48,4382,4385],{"className":839,"code":4383,"filename":4384,"language":841,"meta":56,"style":56},"# macOS, Linux, and WSL\ncurl -fsSL https:\u002F\u002Fbun.sh\u002Finstall | bash\n\n# Windows (PowerShell)\nwinget install oven-sh.bun\n","terminal",[40,4386,4387,4392,4409,4413,4418],{"__ignoreMap":56},[449,4388,4389],{"class":451,"line":452},[449,4390,4391],{"class":705},"# macOS, Linux, and WSL\n",[449,4393,4394,4397,4400,4403,4406],{"class":451,"line":114},[449,4395,4396],{"class":721},"curl",[449,4398,4399],{"class":714}," -fsSL",[449,4401,4402],{"class":691}," https:\u002F\u002Fbun.sh\u002Finstall",[449,4404,4405],{"class":680}," |",[449,4407,4408],{"class":721}," bash\n",[449,4410,4411],{"class":451,"line":111},[449,4412,700],{"emptyLinePlaceholder":130},[449,4414,4415],{"class":451,"line":468},[449,4416,4417],{"class":705},"# Windows (PowerShell)\n",[449,4419,4420,4423,4425],{"class":451,"line":474},[449,4421,4422],{"class":721},"winget",[449,4424,3706],{"class":691},[449,4426,4427],{"class":691}," oven-sh.bun\n",[21,4429,4430],{},"Verify installation:",[48,4432,4434],{"className":839,"code":4433,"filename":4384,"language":841,"meta":56,"style":56},"bun --version\n# Should output: 1.2.x or higher\n",[40,4435,4436,4444],{"__ignoreMap":56},[449,4437,4438,4441],{"class":451,"line":452},[449,4439,4440],{"class":721},"bun",[449,4442,4443],{"class":714}," --version\n",[449,4445,4446],{"class":451,"line":114},[449,4447,4448],{"class":705},"# Should output: 1.2.x or higher\n",[73,4450],{},[32,4452,4454],{"id":4453},"step-2-create-a-bun-nitro-project","Step 2: Create a Bun + Nitro Project",[21,4456,4457],{},"The easiest way to start is with Nuxt, which has built-in Nitro support:",[48,4459,4461],{"className":839,"code":4460,"filename":4384,"language":841,"meta":56,"style":56},"# Create a new Nuxt project (uses Nitro internally)\nbunx nuxi@latest init brewstop-bun\n\ncd brewstop-bun\n",[40,4462,4463,4468,4482,4486],{"__ignoreMap":56},[449,4464,4465],{"class":451,"line":452},[449,4466,4467],{"class":705},"# Create a new Nuxt project (uses Nitro internally)\n",[449,4469,4470,4473,4476,4479],{"class":451,"line":114},[449,4471,4472],{"class":721},"bunx",[449,4474,4475],{"class":691}," nuxi@latest",[449,4477,4478],{"class":691}," init",[449,4480,4481],{"class":691}," brewstop-bun\n",[449,4483,4484],{"class":451,"line":111},[449,4485,700],{"emptyLinePlaceholder":130},[449,4487,4488,4491],{"class":451,"line":468},[449,4489,4490],{"class":714},"cd",[449,4492,4481],{"class":691},[25,4494,4495],{"type":27},[21,4496,4497],{},"Bun's package manager (bun install) is significantly faster than npm\u002Fyarn\u002Fpnpm. Use it for the best experience.",[21,4499,4500],{},"Install dependencies with Bun:",[48,4502,4504],{"className":839,"code":4503,"filename":4384,"language":841,"meta":56,"style":56},"bun install\n",[40,4505,4506],{"__ignoreMap":56},[449,4507,4508,4510],{"class":451,"line":452},[449,4509,4440],{"class":721},[449,4511,4512],{"class":691}," install\n",[21,4514,4515],{},"Update the package.json to use Bun as the package manager:",[48,4517,4522],{"className":4518,"code":4519,"filename":4520,"language":4521,"meta":56,"style":56},"language-json shiki shiki-themes github-light github-dark","{\n  \"packageManager\": \"bun@1.2.0\",\n  \"scripts\": {\n    \"dev\": \"bunx nuxi dev\",\n    \"build\": \"bunx nuxi build\",\n    \"preview\": \"bunx nuxi preview\",\n    \"start\": \"bunx nuxi start\"\n  }\n}\n","package.json","json",[40,4523,4524,4529,4541,4549,4561,4573,4585,4595,4600],{"__ignoreMap":56},[449,4525,4526],{"class":451,"line":452},[449,4527,4528],{"class":684},"{\n",[449,4530,4531,4534,4536,4539],{"class":451,"line":114},[449,4532,4533],{"class":714},"  \"packageManager\"",[449,4535,1482],{"class":684},[449,4537,4538],{"class":691},"\"bun@1.2.0\"",[449,4540,779],{"class":684},[449,4542,4543,4546],{"class":451,"line":111},[449,4544,4545],{"class":714},"  \"scripts\"",[449,4547,4548],{"class":684},": {\n",[449,4550,4551,4554,4556,4559],{"class":451,"line":468},[449,4552,4553],{"class":714},"    \"dev\"",[449,4555,1482],{"class":684},[449,4557,4558],{"class":691},"\"bunx nuxi dev\"",[449,4560,779],{"class":684},[449,4562,4563,4566,4568,4571],{"class":451,"line":474},[449,4564,4565],{"class":714},"    \"build\"",[449,4567,1482],{"class":684},[449,4569,4570],{"class":691},"\"bunx nuxi build\"",[449,4572,779],{"class":684},[449,4574,4575,4578,4580,4583],{"class":451,"line":480},[449,4576,4577],{"class":714},"    \"preview\"",[449,4579,1482],{"class":684},[449,4581,4582],{"class":691},"\"bunx nuxi preview\"",[449,4584,779],{"class":684},[449,4586,4587,4590,4592],{"class":451,"line":486},[449,4588,4589],{"class":714},"    \"start\"",[449,4591,1482],{"class":684},[449,4593,4594],{"class":691},"\"bunx nuxi start\"\n",[449,4596,4597],{"class":451,"line":492},[449,4598,4599],{"class":684},"  }\n",[449,4601,4602],{"class":451,"line":498},[449,4603,4604],{"class":684},"}\n",[73,4606],{},[32,4608,4610],{"id":4609},"step-3-configure-nitro-to-use-bun","Step 3: Configure Nitro to Use Bun",[21,4612,4613],{},"Nuxt automatically detects Bun when it's available. However, let's explicitly configure it for clarity.",[21,4615,4616,4617,3070],{},"Update ",[40,4618,4619],{},"nuxt.config.ts",[48,4621,4623],{"className":670,"code":4622,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  nitro: {\n    preset: \"bun\",\n  },\n  \n  devtools: {\n    enabled: true,\n  },\n});\n",[40,4624,4625,4638,4643,4653,4657,4662,4667,4676,4680],{"__ignoreMap":56},[449,4626,4627,4630,4633,4636],{"class":451,"line":452},[449,4628,4629],{"class":680},"export",[449,4631,4632],{"class":680}," default",[449,4634,4635],{"class":721}," defineNuxtConfig",[449,4637,1391],{"class":684},[449,4639,4640],{"class":451,"line":114},[449,4641,4642],{"class":684},"  nitro: {\n",[449,4644,4645,4648,4651],{"class":451,"line":111},[449,4646,4647],{"class":684},"    preset: ",[449,4649,4650],{"class":691},"\"bun\"",[449,4652,779],{"class":684},[449,4654,4655],{"class":451,"line":468},[449,4656,1474],{"class":684},[449,4658,4659],{"class":451,"line":474},[449,4660,4661],{"class":684},"  \n",[449,4663,4664],{"class":451,"line":480},[449,4665,4666],{"class":684},"  devtools: {\n",[449,4668,4669,4672,4674],{"class":451,"line":486},[449,4670,4671],{"class":684},"    enabled: ",[449,4673,2169],{"class":714},[449,4675,779],{"class":684},[449,4677,4678],{"class":451,"line":492},[449,4679,1474],{"class":684},[449,4681,4682],{"class":451,"line":498},[449,4683,784],{"class":684},[21,4685,4686],{},"This ensures Nitro uses Bun's optimized runtime for both development and production.",[73,4688],{},[32,4690,4692],{"id":4691},"step-4-create-api-endpoints","Step 4: Create API Endpoints",[21,4694,4695],{},"Let's build a simple API to test Bun's performance.",[21,4697,4698,4699,3070],{},"Create ",[40,4700,4701],{},"server\u002Fapi\u002Fping.get.ts",[48,4703,4705],{"className":670,"code":4704,"filename":4701,"language":673,"meta":56,"style":56},"export default defineEventHandler(() => {\n  return {\n    message: \"Pong from Bun!\",\n    runtime: \"Bun\",\n    timestamp: Date.now(),\n  };\n});\n",[40,4706,4707,4723,4730,4740,4750,4760,4765],{"__ignoreMap":56},[449,4708,4709,4711,4713,4716,4719,4721],{"class":451,"line":452},[449,4710,4629],{"class":680},[449,4712,4632],{"class":680},[449,4714,4715],{"class":721}," defineEventHandler",[449,4717,4718],{"class":684},"(() ",[449,4720,1498],{"class":680},[449,4722,1501],{"class":684},[449,4724,4725,4728],{"class":451,"line":114},[449,4726,4727],{"class":680},"  return",[449,4729,1501],{"class":684},[449,4731,4732,4735,4738],{"class":451,"line":111},[449,4733,4734],{"class":684},"    message: ",[449,4736,4737],{"class":691},"\"Pong from Bun!\"",[449,4739,779],{"class":684},[449,4741,4742,4745,4748],{"class":451,"line":468},[449,4743,4744],{"class":684},"    runtime: ",[449,4746,4747],{"class":691},"\"Bun\"",[449,4749,779],{"class":684},[449,4751,4752,4755,4758],{"class":451,"line":474},[449,4753,4754],{"class":684},"    timestamp: Date.",[449,4756,4757],{"class":721},"now",[449,4759,1624],{"class":684},[449,4761,4762],{"class":451,"line":480},[449,4763,4764],{"class":684},"  };\n",[449,4766,4767],{"class":451,"line":486},[449,4768,784],{"class":684},[21,4770,4698,4771,4774],{},[40,4772,4773],{},"server\u002Fapi\u002Fproducts.get.ts"," with some data:",[48,4776,4778],{"className":670,"code":4777,"filename":4773,"language":673,"meta":56,"style":56},"const products = [\n  { id: 1, name: \"Latte\", price: 4.5, category: \"coffee\" },\n  { id: 2, name: \"Espresso\", price: 3.0, category: \"coffee\" },\n  { id: 3, name: \"Croissant\", price: 3.5, category: \"food\" },\n  { id: 4, name: \"Chai Tea\", price: 4.0, category: \"tea\" },\n  { id: 5, name: \"Muffin\", price: 3.0, category: \"food\" },\n];\n\nexport default defineEventHandler(() => {\n  return { products };\n});\n",[40,4779,4780,4792,4821,4844,4868,4892,4914,4919,4923,4937,4944],{"__ignoreMap":56},[449,4781,4782,4784,4787,4789],{"class":451,"line":452},[449,4783,711],{"class":680},[449,4785,4786],{"class":714}," products",[449,4788,718],{"class":680},[449,4790,4791],{"class":684}," [\n",[449,4793,4794,4797,4800,4803,4806,4809,4812,4815,4818],{"class":451,"line":114},[449,4795,4796],{"class":684},"  { id: ",[449,4798,4799],{"class":714},"1",[449,4801,4802],{"class":684},", name: ",[449,4804,4805],{"class":691},"\"Latte\"",[449,4807,4808],{"class":684},", price: ",[449,4810,4811],{"class":714},"4.5",[449,4813,4814],{"class":684},", category: ",[449,4816,4817],{"class":691},"\"coffee\"",[449,4819,4820],{"class":684}," },\n",[449,4822,4823,4825,4828,4830,4833,4835,4838,4840,4842],{"class":451,"line":111},[449,4824,4796],{"class":684},[449,4826,4827],{"class":714},"2",[449,4829,4802],{"class":684},[449,4831,4832],{"class":691},"\"Espresso\"",[449,4834,4808],{"class":684},[449,4836,4837],{"class":714},"3.0",[449,4839,4814],{"class":684},[449,4841,4817],{"class":691},[449,4843,4820],{"class":684},[449,4845,4846,4848,4851,4853,4856,4858,4861,4863,4866],{"class":451,"line":468},[449,4847,4796],{"class":684},[449,4849,4850],{"class":714},"3",[449,4852,4802],{"class":684},[449,4854,4855],{"class":691},"\"Croissant\"",[449,4857,4808],{"class":684},[449,4859,4860],{"class":714},"3.5",[449,4862,4814],{"class":684},[449,4864,4865],{"class":691},"\"food\"",[449,4867,4820],{"class":684},[449,4869,4870,4872,4875,4877,4880,4882,4885,4887,4890],{"class":451,"line":474},[449,4871,4796],{"class":684},[449,4873,4874],{"class":714},"4",[449,4876,4802],{"class":684},[449,4878,4879],{"class":691},"\"Chai Tea\"",[449,4881,4808],{"class":684},[449,4883,4884],{"class":714},"4.0",[449,4886,4814],{"class":684},[449,4888,4889],{"class":691},"\"tea\"",[449,4891,4820],{"class":684},[449,4893,4894,4896,4899,4901,4904,4906,4908,4910,4912],{"class":451,"line":480},[449,4895,4796],{"class":684},[449,4897,4898],{"class":714},"5",[449,4900,4802],{"class":684},[449,4902,4903],{"class":691},"\"Muffin\"",[449,4905,4808],{"class":684},[449,4907,4837],{"class":714},[449,4909,4814],{"class":684},[449,4911,4865],{"class":691},[449,4913,4820],{"class":684},[449,4915,4916],{"class":451,"line":486},[449,4917,4918],{"class":684},"];\n",[449,4920,4921],{"class":451,"line":492},[449,4922,700],{"emptyLinePlaceholder":130},[449,4924,4925,4927,4929,4931,4933,4935],{"class":451,"line":498},[449,4926,4629],{"class":680},[449,4928,4632],{"class":680},[449,4930,4715],{"class":721},[449,4932,4718],{"class":684},[449,4934,1498],{"class":680},[449,4936,1501],{"class":684},[449,4938,4939,4941],{"class":451,"line":133},[449,4940,4727],{"class":680},[449,4942,4943],{"class":684}," { products };\n",[449,4945,4946],{"class":451,"line":509},[449,4947,784],{"class":684},[21,4949,4950],{},"Create a more complex endpoint with database simulation:",[48,4952,4955],{"className":670,"code":4953,"filename":4954,"language":673,"meta":56,"style":56},"interface OrderItem {\n  productId: number;\n  quantity: number;\n}\n\nconst orders: any[] = [];\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event) as OrderItem[];\n  \n  if (!body || body.length === 0) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Order must contain at least one item\",\n    });\n  }\n  \n  \u002F\u002F Simulate database delay\n  await new Promise((resolve) => setTimeout(resolve, 100));\n  \n  const order = {\n    id: crypto.randomUUID(),\n    items: body,\n    status: \"pending\",\n    createdAt: new Date().toISOString(),\n  };\n  \n  orders.push(order);\n  \n  return order;\n});\n","server\u002Fapi\u002Forders.post.ts",[40,4956,4957,4967,4979,4990,4994,4998,5019,5023,5045,5071,5075,5105,5115,5125,5135,5139,5143,5147,5152,5183,5187,5198,5208,5213,5223,5242,5246,5250,5261,5265,5272],{"__ignoreMap":56},[449,4958,4959,4962,4965],{"class":451,"line":452},[449,4960,4961],{"class":680},"interface",[449,4963,4964],{"class":721}," OrderItem",[449,4966,1501],{"class":684},[449,4968,4969,4972,4974,4977],{"class":451,"line":114},[449,4970,4971],{"class":1491},"  productId",[449,4973,3070],{"class":680},[449,4975,4976],{"class":714}," number",[449,4978,695],{"class":684},[449,4980,4981,4984,4986,4988],{"class":451,"line":111},[449,4982,4983],{"class":1491},"  quantity",[449,4985,3070],{"class":680},[449,4987,4976],{"class":714},[449,4989,695],{"class":684},[449,4991,4992],{"class":451,"line":468},[449,4993,4604],{"class":684},[449,4995,4996],{"class":451,"line":474},[449,4997,700],{"emptyLinePlaceholder":130},[449,4999,5000,5002,5005,5007,5010,5013,5016],{"class":451,"line":480},[449,5001,711],{"class":680},[449,5003,5004],{"class":714}," orders",[449,5006,3070],{"class":680},[449,5008,5009],{"class":714}," any",[449,5011,5012],{"class":684},"[] ",[449,5014,5015],{"class":680},"=",[449,5017,5018],{"class":684}," [];\n",[449,5020,5021],{"class":451,"line":486},[449,5022,700],{"emptyLinePlaceholder":130},[449,5024,5025,5027,5029,5031,5033,5035,5037,5039,5041,5043],{"class":451,"line":492},[449,5026,4629],{"class":680},[449,5028,4632],{"class":680},[449,5030,4715],{"class":721},[449,5032,725],{"class":684},[449,5034,1485],{"class":680},[449,5036,1913],{"class":684},[449,5038,2991],{"class":1491},[449,5040,1758],{"class":684},[449,5042,1498],{"class":680},[449,5044,1501],{"class":684},[449,5046,5047,5050,5053,5055,5057,5060,5063,5066,5068],{"class":451,"line":498},[449,5048,5049],{"class":680},"  const",[449,5051,5052],{"class":714}," body",[449,5054,718],{"class":680},[449,5056,1654],{"class":680},[449,5058,5059],{"class":721}," readBody",[449,5061,5062],{"class":684},"(event) ",[449,5064,5065],{"class":680},"as",[449,5067,4964],{"class":721},[449,5069,5070],{"class":684},"[];\n",[449,5072,5073],{"class":451,"line":133},[449,5074,4661],{"class":684},[449,5076,5077,5080,5082,5085,5088,5091,5094,5097,5100,5103],{"class":451,"line":509},[449,5078,5079],{"class":680},"  if",[449,5081,1913],{"class":684},[449,5083,5084],{"class":680},"!",[449,5086,5087],{"class":684},"body ",[449,5089,5090],{"class":680},"||",[449,5092,5093],{"class":684}," body.",[449,5095,5096],{"class":714},"length",[449,5098,5099],{"class":680}," ===",[449,5101,5102],{"class":714}," 0",[449,5104,3417],{"class":684},[449,5106,5107,5110,5113],{"class":451,"line":318},[449,5108,5109],{"class":680},"    throw",[449,5111,5112],{"class":721}," createError",[449,5114,1391],{"class":684},[449,5116,5117,5120,5123],{"class":451,"line":520},[449,5118,5119],{"class":684},"      statusCode: ",[449,5121,5122],{"class":714},"400",[449,5124,779],{"class":684},[449,5126,5127,5130,5133],{"class":451,"line":526},[449,5128,5129],{"class":684},"      statusMessage: ",[449,5131,5132],{"class":691},"\"Order must contain at least one item\"",[449,5134,779],{"class":684},[449,5136,5137],{"class":451,"line":532},[449,5138,3484],{"class":684},[449,5140,5141],{"class":451,"line":2790},[449,5142,4599],{"class":684},[449,5144,5145],{"class":451,"line":3511},[449,5146,4661],{"class":684},[449,5148,5149],{"class":451,"line":3526},[449,5150,5151],{"class":705},"  \u002F\u002F Simulate database delay\n",[449,5153,5154,5156,5158,5161,5164,5167,5169,5171,5174,5177,5180],{"class":451,"line":3531},[449,5155,2099],{"class":680},[449,5157,1343],{"class":680},[449,5159,5160],{"class":714}," Promise",[449,5162,5163],{"class":684},"((",[449,5165,5166],{"class":1491},"resolve",[449,5168,1758],{"class":684},[449,5170,1498],{"class":680},[449,5172,5173],{"class":721}," setTimeout",[449,5175,5176],{"class":684},"(resolve, ",[449,5178,5179],{"class":714},"100",[449,5181,5182],{"class":684},"));\n",[449,5184,5185],{"class":451,"line":3536},[449,5186,4661],{"class":684},[449,5188,5189,5191,5194,5196],{"class":451,"line":4009},[449,5190,5049],{"class":680},[449,5192,5193],{"class":714}," order",[449,5195,718],{"class":680},[449,5197,1501],{"class":684},[449,5199,5200,5203,5206],{"class":451,"line":4015},[449,5201,5202],{"class":684},"    id: crypto.",[449,5204,5205],{"class":721},"randomUUID",[449,5207,1624],{"class":684},[449,5209,5210],{"class":451,"line":4021},[449,5211,5212],{"class":684},"    items: body,\n",[449,5214,5215,5218,5221],{"class":451,"line":4027},[449,5216,5217],{"class":684},"    status: ",[449,5219,5220],{"class":691},"\"pending\"",[449,5222,779],{"class":684},[449,5224,5225,5228,5231,5234,5237,5240],{"class":451,"line":4032},[449,5226,5227],{"class":684},"    createdAt: ",[449,5229,5230],{"class":680},"new",[449,5232,5233],{"class":721}," Date",[449,5235,5236],{"class":684},"().",[449,5238,5239],{"class":721},"toISOString",[449,5241,1624],{"class":684},[449,5243,5244],{"class":451,"line":4038},[449,5245,4764],{"class":684},[449,5247,5248],{"class":451,"line":4044},[449,5249,4661],{"class":684},[449,5251,5252,5255,5258],{"class":451,"line":4049},[449,5253,5254],{"class":684},"  orders.",[449,5256,5257],{"class":721},"push",[449,5259,5260],{"class":684},"(order);\n",[449,5262,5263],{"class":451,"line":4054},[449,5264,4661],{"class":684},[449,5266,5267,5269],{"class":451,"line":4059},[449,5268,4727],{"class":680},[449,5270,5271],{"class":684}," order;\n",[449,5273,5274],{"class":451,"line":4065},[449,5275,784],{"class":684},[73,5277],{},[32,5279,5281],{"id":5280},"step-5-run-the-server","Step 5: Run the Server",[21,5283,5284],{},"Start the development server:",[48,5286,5288],{"className":839,"code":5287,"filename":4384,"language":841,"meta":56,"style":56},"bun run dev\n",[40,5289,5290],{"__ignoreMap":56},[449,5291,5292,5294,5297],{"class":451,"line":452},[449,5293,4440],{"class":721},[449,5295,5296],{"class":691}," run",[449,5298,5299],{"class":691}," dev\n",[427,5301,5304],{"icon":5302,"title":5303},"⚡","Notice the Speed",[21,5305,5306],{},"Bun's dev server starts in milliseconds—often before you can release the keys after typing the command.",[21,5308,5309],{},"Test the endpoints:",[48,5311,5313],{"className":839,"code":5312,"filename":4384,"language":841,"meta":56,"style":56},"curl http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fping\ncurl http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fproducts\n",[40,5314,5315,5322],{"__ignoreMap":56},[449,5316,5317,5319],{"class":451,"line":452},[449,5318,4396],{"class":721},[449,5320,5321],{"class":691}," http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fping\n",[449,5323,5324,5326],{"class":451,"line":114},[449,5325,4396],{"class":721},[449,5327,5328],{"class":691}," http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fproducts\n",[73,5330],{},[32,5332,5334],{"id":5333},"step-6-benchmarking","Step 6: Benchmarking",[21,5336,5337],{},"Let's create a simple benchmark script to compare Bun vs Node.js performance.",[21,5339,4698,5340,3070],{},[40,5341,5342],{},"benchmark.mjs",[48,5344,5346],{"className":670,"code":5345,"filename":5342,"language":673,"meta":56,"style":56},"import http from \"http\";\n\nconst HOST = \"localhost\";\nconst PORT = 3000;\nconst REQUESTS = 10000;\nconst CONCURRENCY = 100;\n\nasync function benchmark() {\n  console.log(`Running benchmark: ${REQUESTS} requests with ${CONCURRENCY} concurrency\\n`);\n  \n  const start = Date.now();\n  let completed = 0;\n  \n  const promises = [];\n  \n  for (let i = 0; i \u003C CONCURRENCY; i++) {\n    promises.push(\n      new Promise((resolve) => {\n        const makeRequest = async () => {\n          if (completed >= REQUESTS) {\n            resolve(true);\n            return;\n          }\n          \n          try {\n            await fetch(`http:\u002F\u002F${HOST}:${PORT}\u002Fapi\u002Fproducts`);\n            completed++;\n            \n            if (completed \u003C REQUESTS) {\n              makeRequest();\n            } else {\n              resolve(true);\n            }\n          } catch (e) {\n            resolve(true);\n          }\n        };\n        \n        makeRequest();\n      })\n    );\n  }\n  \n  await Promise.all(promises);\n  \n  const duration = Date.now() - start;\n  const rps = Math.round(REQUESTS \u002F (duration \u002F 1000));\n  \n  console.log(`Results:`);\n  console.log(`  Total time: ${duration}ms`);\n  console.log(`  Requests\u002Fsec: ${rps}`);\n  console.log(`  Avg latency: ${(duration \u002F REQUESTS).toFixed(2)}ms`);\n}\n\nbenchmark();\n",[40,5347,5348,5362,5366,5380,5394,5408,5422,5426,5439,5471,5475,5491,5505,5509,5520,5524,5557,5567,5584,5603,5618,5629,5636,5641,5646,5653,5680,5689,5694,5707,5714,5724,5735,5740,5751,5761,5765,5770,5775,5782,5788,5794,5799,5804,5819,5824,5846,5880,5885,5899,5919,5938,5973,5978,5983],{"__ignoreMap":56},[449,5349,5350,5352,5355,5357,5360],{"class":451,"line":452},[449,5351,681],{"class":680},[449,5353,5354],{"class":684}," http ",[449,5356,688],{"class":680},[449,5358,5359],{"class":691}," \"http\"",[449,5361,695],{"class":684},[449,5363,5364],{"class":451,"line":114},[449,5365,700],{"emptyLinePlaceholder":130},[449,5367,5368,5370,5373,5375,5378],{"class":451,"line":111},[449,5369,711],{"class":680},[449,5371,5372],{"class":714}," HOST",[449,5374,718],{"class":680},[449,5376,5377],{"class":691}," \"localhost\"",[449,5379,695],{"class":684},[449,5381,5382,5384,5387,5389,5392],{"class":451,"line":468},[449,5383,711],{"class":680},[449,5385,5386],{"class":714}," PORT",[449,5388,718],{"class":680},[449,5390,5391],{"class":714}," 3000",[449,5393,695],{"class":684},[449,5395,5396,5398,5401,5403,5406],{"class":451,"line":474},[449,5397,711],{"class":680},[449,5399,5400],{"class":714}," REQUESTS",[449,5402,718],{"class":680},[449,5404,5405],{"class":714}," 10000",[449,5407,695],{"class":684},[449,5409,5410,5412,5415,5417,5420],{"class":451,"line":480},[449,5411,711],{"class":680},[449,5413,5414],{"class":714}," CONCURRENCY",[449,5416,718],{"class":680},[449,5418,5419],{"class":714}," 100",[449,5421,695],{"class":684},[449,5423,5424],{"class":451,"line":486},[449,5425,700],{"emptyLinePlaceholder":130},[449,5427,5428,5430,5433,5436],{"class":451,"line":492},[449,5429,1485],{"class":680},[449,5431,5432],{"class":680}," function",[449,5434,5435],{"class":721}," benchmark",[449,5437,5438],{"class":684},"() {\n",[449,5440,5441,5444,5446,5448,5451,5454,5457,5460,5463,5466,5469],{"class":451,"line":498},[449,5442,5443],{"class":684},"  console.",[449,5445,1675],{"class":721},[449,5447,725],{"class":684},[449,5449,5450],{"class":691},"`Running benchmark: ${",[449,5452,5453],{"class":714},"REQUESTS",[449,5455,5456],{"class":691},"} requests with ${",[449,5458,5459],{"class":714},"CONCURRENCY",[449,5461,5462],{"class":691},"} concurrency",[449,5464,5465],{"class":714},"\\n",[449,5467,5468],{"class":691},"`",[449,5470,740],{"class":684},[449,5472,5473],{"class":451,"line":133},[449,5474,4661],{"class":684},[449,5476,5477,5479,5482,5484,5487,5489],{"class":451,"line":509},[449,5478,5049],{"class":680},[449,5480,5481],{"class":714}," start",[449,5483,718],{"class":680},[449,5485,5486],{"class":684}," Date.",[449,5488,4757],{"class":721},[449,5490,1322],{"class":684},[449,5492,5493,5496,5499,5501,5503],{"class":451,"line":318},[449,5494,5495],{"class":680},"  let",[449,5497,5498],{"class":684}," completed ",[449,5500,5015],{"class":680},[449,5502,5102],{"class":714},[449,5504,695],{"class":684},[449,5506,5507],{"class":451,"line":520},[449,5508,4661],{"class":684},[449,5510,5511,5513,5516,5518],{"class":451,"line":526},[449,5512,5049],{"class":680},[449,5514,5515],{"class":714}," promises",[449,5517,718],{"class":680},[449,5519,5018],{"class":684},[449,5521,5522],{"class":451,"line":532},[449,5523,4661],{"class":684},[449,5525,5526,5529,5531,5534,5537,5539,5541,5544,5547,5549,5552,5555],{"class":451,"line":2790},[449,5527,5528],{"class":680},"  for",[449,5530,1913],{"class":684},[449,5532,5533],{"class":680},"let",[449,5535,5536],{"class":684}," i ",[449,5538,5015],{"class":680},[449,5540,5102],{"class":714},[449,5542,5543],{"class":684},"; i ",[449,5545,5546],{"class":680},"\u003C",[449,5548,5414],{"class":714},[449,5550,5551],{"class":684},"; i",[449,5553,5554],{"class":680},"++",[449,5556,3417],{"class":684},[449,5558,5559,5562,5564],{"class":451,"line":3511},[449,5560,5561],{"class":684},"    promises.",[449,5563,5257],{"class":721},[449,5565,5566],{"class":684},"(\n",[449,5568,5569,5572,5574,5576,5578,5580,5582],{"class":451,"line":3526},[449,5570,5571],{"class":680},"      new",[449,5573,5160],{"class":714},[449,5575,5163],{"class":684},[449,5577,5166],{"class":1491},[449,5579,1758],{"class":684},[449,5581,1498],{"class":680},[449,5583,1501],{"class":684},[449,5585,5586,5589,5592,5594,5597,5599,5601],{"class":451,"line":3531},[449,5587,5588],{"class":680},"        const",[449,5590,5591],{"class":721}," makeRequest",[449,5593,718],{"class":680},[449,5595,5596],{"class":680}," async",[449,5598,2090],{"class":684},[449,5600,1498],{"class":680},[449,5602,1501],{"class":684},[449,5604,5605,5608,5611,5614,5616],{"class":451,"line":3536},[449,5606,5607],{"class":680},"          if",[449,5609,5610],{"class":684}," (completed ",[449,5612,5613],{"class":680},">=",[449,5615,5400],{"class":714},[449,5617,3417],{"class":684},[449,5619,5620,5623,5625,5627],{"class":451,"line":4009},[449,5621,5622],{"class":721},"            resolve",[449,5624,725],{"class":684},[449,5626,2169],{"class":714},[449,5628,740],{"class":684},[449,5630,5631,5634],{"class":451,"line":4015},[449,5632,5633],{"class":680},"            return",[449,5635,695],{"class":684},[449,5637,5638],{"class":451,"line":4021},[449,5639,5640],{"class":684},"          }\n",[449,5642,5643],{"class":451,"line":4027},[449,5644,5645],{"class":684},"          \n",[449,5647,5648,5651],{"class":451,"line":4032},[449,5649,5650],{"class":680},"          try",[449,5652,1501],{"class":684},[449,5654,5655,5658,5661,5663,5666,5669,5672,5675,5678],{"class":451,"line":4038},[449,5656,5657],{"class":680},"            await",[449,5659,5660],{"class":721}," fetch",[449,5662,725],{"class":684},[449,5664,5665],{"class":691},"`http:\u002F\u002F${",[449,5667,5668],{"class":714},"HOST",[449,5670,5671],{"class":691},"}:${",[449,5673,5674],{"class":714},"PORT",[449,5676,5677],{"class":691},"}\u002Fapi\u002Fproducts`",[449,5679,740],{"class":684},[449,5681,5682,5685,5687],{"class":451,"line":4044},[449,5683,5684],{"class":684},"            completed",[449,5686,5554],{"class":680},[449,5688,695],{"class":684},[449,5690,5691],{"class":451,"line":4049},[449,5692,5693],{"class":684},"            \n",[449,5695,5696,5699,5701,5703,5705],{"class":451,"line":4054},[449,5697,5698],{"class":680},"            if",[449,5700,5610],{"class":684},[449,5702,5546],{"class":680},[449,5704,5400],{"class":714},[449,5706,3417],{"class":684},[449,5708,5709,5712],{"class":451,"line":4059},[449,5710,5711],{"class":721},"              makeRequest",[449,5713,1322],{"class":684},[449,5715,5716,5719,5722],{"class":451,"line":4065},[449,5717,5718],{"class":684},"            } ",[449,5720,5721],{"class":680},"else",[449,5723,1501],{"class":684},[449,5725,5726,5729,5731,5733],{"class":451,"line":4070},[449,5727,5728],{"class":721},"              resolve",[449,5730,725],{"class":684},[449,5732,2169],{"class":714},[449,5734,740],{"class":684},[449,5736,5737],{"class":451,"line":4075},[449,5738,5739],{"class":684},"            }\n",[449,5741,5742,5745,5748],{"class":451,"line":4081},[449,5743,5744],{"class":684},"          } ",[449,5746,5747],{"class":680},"catch",[449,5749,5750],{"class":684}," (e) {\n",[449,5752,5753,5755,5757,5759],{"class":451,"line":4086},[449,5754,5622],{"class":721},[449,5756,725],{"class":684},[449,5758,2169],{"class":714},[449,5760,740],{"class":684},[449,5762,5763],{"class":451,"line":4092},[449,5764,5640],{"class":684},[449,5766,5767],{"class":451,"line":4098},[449,5768,5769],{"class":684},"        };\n",[449,5771,5772],{"class":451,"line":4104},[449,5773,5774],{"class":684},"        \n",[449,5776,5777,5780],{"class":451,"line":4110},[449,5778,5779],{"class":721},"        makeRequest",[449,5781,1322],{"class":684},[449,5783,5785],{"class":451,"line":5784},40,[449,5786,5787],{"class":684},"      })\n",[449,5789,5791],{"class":451,"line":5790},41,[449,5792,5793],{"class":684},"    );\n",[449,5795,5797],{"class":451,"line":5796},42,[449,5798,4599],{"class":684},[449,5800,5802],{"class":451,"line":5801},43,[449,5803,4661],{"class":684},[449,5805,5807,5809,5811,5813,5816],{"class":451,"line":5806},44,[449,5808,2099],{"class":680},[449,5810,5160],{"class":714},[449,5812,165],{"class":684},[449,5814,5815],{"class":721},"all",[449,5817,5818],{"class":684},"(promises);\n",[449,5820,5822],{"class":451,"line":5821},45,[449,5823,4661],{"class":684},[449,5825,5827,5829,5832,5834,5836,5838,5840,5843],{"class":451,"line":5826},46,[449,5828,5049],{"class":680},[449,5830,5831],{"class":714}," duration",[449,5833,718],{"class":680},[449,5835,5486],{"class":684},[449,5837,4757],{"class":721},[449,5839,731],{"class":684},[449,5841,5842],{"class":680},"-",[449,5844,5845],{"class":684}," start;\n",[449,5847,5849,5851,5854,5856,5859,5862,5864,5866,5869,5872,5875,5878],{"class":451,"line":5848},47,[449,5850,5049],{"class":680},[449,5852,5853],{"class":714}," rps",[449,5855,718],{"class":680},[449,5857,5858],{"class":684}," Math.",[449,5860,5861],{"class":721},"round",[449,5863,725],{"class":684},[449,5865,5453],{"class":714},[449,5867,5868],{"class":680}," \u002F",[449,5870,5871],{"class":684}," (duration ",[449,5873,5874],{"class":680},"\u002F",[449,5876,5877],{"class":714}," 1000",[449,5879,5182],{"class":684},[449,5881,5883],{"class":451,"line":5882},48,[449,5884,4661],{"class":684},[449,5886,5888,5890,5892,5894,5897],{"class":451,"line":5887},49,[449,5889,5443],{"class":684},[449,5891,1675],{"class":721},[449,5893,725],{"class":684},[449,5895,5896],{"class":691},"`Results:`",[449,5898,740],{"class":684},[449,5900,5902,5904,5906,5908,5911,5914,5917],{"class":451,"line":5901},50,[449,5903,5443],{"class":684},[449,5905,1675],{"class":721},[449,5907,725],{"class":684},[449,5909,5910],{"class":691},"`  Total time: ${",[449,5912,5913],{"class":684},"duration",[449,5915,5916],{"class":691},"}ms`",[449,5918,740],{"class":684},[449,5920,5922,5924,5926,5928,5931,5934,5936],{"class":451,"line":5921},51,[449,5923,5443],{"class":684},[449,5925,1675],{"class":721},[449,5927,725],{"class":684},[449,5929,5930],{"class":691},"`  Requests\u002Fsec: ${",[449,5932,5933],{"class":684},"rps",[449,5935,1517],{"class":691},[449,5937,740],{"class":684},[449,5939,5941,5943,5945,5947,5950,5952,5954,5956,5958,5960,5963,5965,5967,5969,5971],{"class":451,"line":5940},52,[449,5942,5443],{"class":684},[449,5944,1675],{"class":721},[449,5946,725],{"class":684},[449,5948,5949],{"class":691},"`  Avg latency: ${",[449,5951,725],{"class":691},[449,5953,5913],{"class":684},[449,5955,5868],{"class":680},[449,5957,5400],{"class":714},[449,5959,374],{"class":691},[449,5961,5962],{"class":721},"toFixed",[449,5964,725],{"class":691},[449,5966,4827],{"class":714},[449,5968,827],{"class":691},[449,5970,5916],{"class":691},[449,5972,740],{"class":684},[449,5974,5976],{"class":451,"line":5975},53,[449,5977,4604],{"class":684},[449,5979,5981],{"class":451,"line":5980},54,[449,5982,700],{"emptyLinePlaceholder":130},[449,5984,5986,5989],{"class":451,"line":5985},55,[449,5987,5988],{"class":721},"benchmark",[449,5990,1322],{"class":684},[21,5992,5993],{},"Run the benchmark:",[48,5995,5997],{"className":839,"code":5996,"filename":4384,"language":841,"meta":56,"style":56},"# First, start the server in one terminal\nbun run dev\n\n# Then run the benchmark in another\nbun run benchmark.mjs\n",[40,5998,5999,6004,6012,6016,6021],{"__ignoreMap":56},[449,6000,6001],{"class":451,"line":452},[449,6002,6003],{"class":705},"# First, start the server in one terminal\n",[449,6005,6006,6008,6010],{"class":451,"line":114},[449,6007,4440],{"class":721},[449,6009,5296],{"class":691},[449,6011,5299],{"class":691},[449,6013,6014],{"class":451,"line":111},[449,6015,700],{"emptyLinePlaceholder":130},[449,6017,6018],{"class":451,"line":468},[449,6019,6020],{"class":705},"# Then run the benchmark in another\n",[449,6022,6023,6025,6027],{"class":451,"line":474},[449,6024,4440],{"class":721},[449,6026,5296],{"class":691},[449,6028,6029],{"class":691}," benchmark.mjs\n",[73,6031],{},[32,6033,6035],{"id":6034},"step-7-using-buns-native-features","Step 7: Using Bun's Native Features",[2808,6037,6039],{"id":6038},"typescript-direct-execution","TypeScript Direct Execution",[21,6041,6042],{},"Bun runs TypeScript natively—no ts-node, no compilation step needed:",[48,6044,6047],{"className":670,"code":6045,"filename":6046,"language":673,"meta":56,"style":56},"\u002F\u002F This runs directly in Bun—no transpilation!\ninterface Product {\n  id: number;\n  name: string;\n  price: number;\n}\n\nconst products: Product[] = [\n  { id: 1, name: \"Latte\", price: 4.5 },\n  { id: 2, name: \"Espresso\", price: 3.0 },\n];\n\nexport default defineEventHandler(() => {\n  return {\n    products,\n    runtime: Bun.version,\n    platform: Bun.environment.platform,\n  };\n});\n","server\u002Fapi\u002Fbun-features.get.ts",[40,6048,6049,6054,6063,6074,6086,6097,6101,6105,6121,6137,6153,6157,6161,6175,6181,6186,6191,6196,6200],{"__ignoreMap":56},[449,6050,6051],{"class":451,"line":452},[449,6052,6053],{"class":705},"\u002F\u002F This runs directly in Bun—no transpilation!\n",[449,6055,6056,6058,6061],{"class":451,"line":114},[449,6057,4961],{"class":680},[449,6059,6060],{"class":721}," Product",[449,6062,1501],{"class":684},[449,6064,6065,6068,6070,6072],{"class":451,"line":111},[449,6066,6067],{"class":1491},"  id",[449,6069,3070],{"class":680},[449,6071,4976],{"class":714},[449,6073,695],{"class":684},[449,6075,6076,6079,6081,6084],{"class":451,"line":468},[449,6077,6078],{"class":1491},"  name",[449,6080,3070],{"class":680},[449,6082,6083],{"class":714}," string",[449,6085,695],{"class":684},[449,6087,6088,6091,6093,6095],{"class":451,"line":474},[449,6089,6090],{"class":1491},"  price",[449,6092,3070],{"class":680},[449,6094,4976],{"class":714},[449,6096,695],{"class":684},[449,6098,6099],{"class":451,"line":480},[449,6100,4604],{"class":684},[449,6102,6103],{"class":451,"line":486},[449,6104,700],{"emptyLinePlaceholder":130},[449,6106,6107,6109,6111,6113,6115,6117,6119],{"class":451,"line":492},[449,6108,711],{"class":680},[449,6110,4786],{"class":714},[449,6112,3070],{"class":680},[449,6114,6060],{"class":721},[449,6116,5012],{"class":684},[449,6118,5015],{"class":680},[449,6120,4791],{"class":684},[449,6122,6123,6125,6127,6129,6131,6133,6135],{"class":451,"line":498},[449,6124,4796],{"class":684},[449,6126,4799],{"class":714},[449,6128,4802],{"class":684},[449,6130,4805],{"class":691},[449,6132,4808],{"class":684},[449,6134,4811],{"class":714},[449,6136,4820],{"class":684},[449,6138,6139,6141,6143,6145,6147,6149,6151],{"class":451,"line":133},[449,6140,4796],{"class":684},[449,6142,4827],{"class":714},[449,6144,4802],{"class":684},[449,6146,4832],{"class":691},[449,6148,4808],{"class":684},[449,6150,4837],{"class":714},[449,6152,4820],{"class":684},[449,6154,6155],{"class":451,"line":509},[449,6156,4918],{"class":684},[449,6158,6159],{"class":451,"line":318},[449,6160,700],{"emptyLinePlaceholder":130},[449,6162,6163,6165,6167,6169,6171,6173],{"class":451,"line":520},[449,6164,4629],{"class":680},[449,6166,4632],{"class":680},[449,6168,4715],{"class":721},[449,6170,4718],{"class":684},[449,6172,1498],{"class":680},[449,6174,1501],{"class":684},[449,6176,6177,6179],{"class":451,"line":526},[449,6178,4727],{"class":680},[449,6180,1501],{"class":684},[449,6182,6183],{"class":451,"line":532},[449,6184,6185],{"class":684},"    products,\n",[449,6187,6188],{"class":451,"line":2790},[449,6189,6190],{"class":684},"    runtime: Bun.version,\n",[449,6192,6193],{"class":451,"line":3511},[449,6194,6195],{"class":684},"    platform: Bun.environment.platform,\n",[449,6197,6198],{"class":451,"line":3526},[449,6199,4764],{"class":684},[449,6201,6202],{"class":451,"line":3531},[449,6203,784],{"class":684},[2808,6205,6207],{"id":6206},"using-bunfile-for-static-files","Using Bun.file for Static Files",[21,6209,6210],{},"Bun has built-in support for file operations:",[48,6212,6215],{"className":670,"code":6213,"filename":6214,"language":673,"meta":56,"style":56},"export default defineEventHandler(async (event) => {\n  const file = Bun.file(\".\u002Fserver\u002Fdata\u002Fproducts.json\");\n  const contents = await file.json();\n  \n  return contents;\n});\n","server\u002Fapi\u002Fread-file.get.ts",[40,6216,6217,6239,6261,6279,6283,6290],{"__ignoreMap":56},[449,6218,6219,6221,6223,6225,6227,6229,6231,6233,6235,6237],{"class":451,"line":452},[449,6220,4629],{"class":680},[449,6222,4632],{"class":680},[449,6224,4715],{"class":721},[449,6226,725],{"class":684},[449,6228,1485],{"class":680},[449,6230,1913],{"class":684},[449,6232,2991],{"class":1491},[449,6234,1758],{"class":684},[449,6236,1498],{"class":680},[449,6238,1501],{"class":684},[449,6240,6241,6243,6246,6248,6251,6254,6256,6259],{"class":451,"line":114},[449,6242,5049],{"class":680},[449,6244,6245],{"class":714}," file",[449,6247,718],{"class":680},[449,6249,6250],{"class":684}," Bun.",[449,6252,6253],{"class":721},"file",[449,6255,725],{"class":684},[449,6257,6258],{"class":691},"\".\u002Fserver\u002Fdata\u002Fproducts.json\"",[449,6260,740],{"class":684},[449,6262,6263,6265,6268,6270,6272,6275,6277],{"class":451,"line":111},[449,6264,5049],{"class":680},[449,6266,6267],{"class":714}," contents",[449,6269,718],{"class":680},[449,6271,1654],{"class":680},[449,6273,6274],{"class":684}," file.",[449,6276,4521],{"class":721},[449,6278,1322],{"class":684},[449,6280,6281],{"class":451,"line":468},[449,6282,4661],{"class":684},[449,6284,6285,6287],{"class":451,"line":474},[449,6286,4727],{"class":680},[449,6288,6289],{"class":684}," contents;\n",[449,6291,6292],{"class":451,"line":480},[449,6293,784],{"class":684},[2808,6295,6297],{"id":6296},"parallel-data-fetching","Parallel Data Fetching",[21,6299,6300],{},"Bun's concurrent fetching is blazingly fast:",[48,6302,6305],{"className":670,"code":6303,"filename":6304,"language":673,"meta":56,"style":56},"export default defineEventHandler(async () => {\n  const urls = [\n    \"https:\u002F\u002Fapi.github.com\u002Fusers\u002Fokinea\",\n    \"https:\u002F\u002Fapi.github.com\u002Fusers\u002Ffacebook\",\n    \"https:\u002F\u002Fapi.github.com\u002Fusers\u002Fnuxt\",\n  ];\n  \n  \u002F\u002F Fetch all URLs in parallel - Bun optimizes this natively\n  const responses = await Promise.all(\n    urls.map((url) => fetch(url).then((r) => r.json()))\n  );\n  \n  return {\n    users: responses.map((r) => r.login),\n    fetchedAt: new Date().toISOString(),\n  };\n});\n","server\u002Fapi\u002Fexternal.get.ts",[40,6306,6307,6325,6336,6343,6350,6357,6362,6366,6371,6390,6432,6437,6441,6447,6465,6480,6484],{"__ignoreMap":56},[449,6308,6309,6311,6313,6315,6317,6319,6321,6323],{"class":451,"line":452},[449,6310,4629],{"class":680},[449,6312,4632],{"class":680},[449,6314,4715],{"class":721},[449,6316,725],{"class":684},[449,6318,1485],{"class":680},[449,6320,2090],{"class":684},[449,6322,1498],{"class":680},[449,6324,1501],{"class":684},[449,6326,6327,6329,6332,6334],{"class":451,"line":114},[449,6328,5049],{"class":680},[449,6330,6331],{"class":714}," urls",[449,6333,718],{"class":680},[449,6335,4791],{"class":684},[449,6337,6338,6341],{"class":451,"line":111},[449,6339,6340],{"class":691},"    \"https:\u002F\u002Fapi.github.com\u002Fusers\u002Fokinea\"",[449,6342,779],{"class":684},[449,6344,6345,6348],{"class":451,"line":468},[449,6346,6347],{"class":691},"    \"https:\u002F\u002Fapi.github.com\u002Fusers\u002Ffacebook\"",[449,6349,779],{"class":684},[449,6351,6352,6355],{"class":451,"line":474},[449,6353,6354],{"class":691},"    \"https:\u002F\u002Fapi.github.com\u002Fusers\u002Fnuxt\"",[449,6356,779],{"class":684},[449,6358,6359],{"class":451,"line":480},[449,6360,6361],{"class":684},"  ];\n",[449,6363,6364],{"class":451,"line":486},[449,6365,4661],{"class":684},[449,6367,6368],{"class":451,"line":492},[449,6369,6370],{"class":705},"  \u002F\u002F Fetch all URLs in parallel - Bun optimizes this natively\n",[449,6372,6373,6375,6378,6380,6382,6384,6386,6388],{"class":451,"line":498},[449,6374,5049],{"class":680},[449,6376,6377],{"class":714}," responses",[449,6379,718],{"class":680},[449,6381,1654],{"class":680},[449,6383,5160],{"class":714},[449,6385,165],{"class":684},[449,6387,5815],{"class":721},[449,6389,5566],{"class":684},[449,6391,6392,6395,6398,6400,6403,6405,6407,6409,6412,6415,6417,6420,6422,6424,6427,6429],{"class":451,"line":133},[449,6393,6394],{"class":684},"    urls.",[449,6396,6397],{"class":721},"map",[449,6399,5163],{"class":684},[449,6401,6402],{"class":1491},"url",[449,6404,1758],{"class":684},[449,6406,1498],{"class":680},[449,6408,5660],{"class":721},[449,6410,6411],{"class":684},"(url).",[449,6413,6414],{"class":721},"then",[449,6416,5163],{"class":684},[449,6418,6419],{"class":1491},"r",[449,6421,1758],{"class":684},[449,6423,1498],{"class":680},[449,6425,6426],{"class":684}," r.",[449,6428,4521],{"class":721},[449,6430,6431],{"class":684},"()))\n",[449,6433,6434],{"class":451,"line":509},[449,6435,6436],{"class":684},"  );\n",[449,6438,6439],{"class":451,"line":318},[449,6440,4661],{"class":684},[449,6442,6443,6445],{"class":451,"line":520},[449,6444,4727],{"class":680},[449,6446,1501],{"class":684},[449,6448,6449,6452,6454,6456,6458,6460,6462],{"class":451,"line":526},[449,6450,6451],{"class":684},"    users: responses.",[449,6453,6397],{"class":721},[449,6455,5163],{"class":684},[449,6457,6419],{"class":1491},[449,6459,1758],{"class":684},[449,6461,1498],{"class":680},[449,6463,6464],{"class":684}," r.login),\n",[449,6466,6467,6470,6472,6474,6476,6478],{"class":451,"line":532},[449,6468,6469],{"class":684},"    fetchedAt: ",[449,6471,5230],{"class":680},[449,6473,5233],{"class":721},[449,6475,5236],{"class":684},[449,6477,5239],{"class":721},[449,6479,1624],{"class":684},[449,6481,6482],{"class":451,"line":2790},[449,6483,4764],{"class":684},[449,6485,6486],{"class":451,"line":3511},[449,6487,784],{"class":684},[73,6489],{},[32,6491,6493],{"id":6492},"step-8-migration-guide","Step 8: Migration Guide",[2808,6495,6497],{"id":6496},"from-nodejs-to-bun","From Node.js to Bun",[816,6499,6500,6524,6580],{},[386,6501,6502,6505,6520,6523],{},[151,6503,6504],{},"Test your dependencies",[48,6506,6508],{"className":839,"code":6507,"language":841,"meta":56,"style":56},"bun pm trust\n",[40,6509,6510],{"__ignoreMap":56},[449,6511,6512,6514,6517],{"class":451,"line":452},[449,6513,4440],{"class":721},[449,6515,6516],{"class":691}," pm",[449,6518,6519],{"class":691}," trust\n",[6521,6522],"br",{},"This ensures all packages work with Bun's module resolution.",[386,6525,6526,6529],{},[151,6527,6528],{},"Update scripts",[48,6530,6532],{"className":4518,"code":6531,"language":4521,"meta":56,"style":56},"{\n  \"scripts\": {\n    \"dev\": \"bunx nuxi dev\",\n    \"build\": \"bunx nuxi build\",\n    \"start\": \"bunx nuxi start\"\n  }\n}\n",[40,6533,6534,6538,6544,6554,6564,6572,6576],{"__ignoreMap":56},[449,6535,6536],{"class":451,"line":452},[449,6537,4528],{"class":684},[449,6539,6540,6542],{"class":451,"line":114},[449,6541,4545],{"class":714},[449,6543,4548],{"class":684},[449,6545,6546,6548,6550,6552],{"class":451,"line":111},[449,6547,4553],{"class":714},[449,6549,1482],{"class":684},[449,6551,4558],{"class":691},[449,6553,779],{"class":684},[449,6555,6556,6558,6560,6562],{"class":451,"line":468},[449,6557,4565],{"class":714},[449,6559,1482],{"class":684},[449,6561,4570],{"class":691},[449,6563,779],{"class":684},[449,6565,6566,6568,6570],{"class":451,"line":474},[449,6567,4589],{"class":714},[449,6569,1482],{"class":684},[449,6571,4594],{"class":691},[449,6573,6574],{"class":451,"line":480},[449,6575,4599],{"class":684},[449,6577,6578],{"class":451,"line":486},[449,6579,4604],{"class":684},[386,6581,6582,6585],{},[151,6583,6584],{},"Check for Node.js-specific code",[48,6586,6588],{"className":839,"code":6587,"language":841,"meta":56,"style":56},"# Bun supports most Node.js APIs, but verify:\nbun --compat node_modules\n",[40,6589,6590,6595],{"__ignoreMap":56},[449,6591,6592],{"class":451,"line":452},[449,6593,6594],{"class":705},"# Bun supports most Node.js APIs, but verify:\n",[449,6596,6597,6599,6602],{"class":451,"line":114},[449,6598,4440],{"class":721},[449,6600,6601],{"class":714}," --compat",[449,6603,6604],{"class":691}," node_modules\n",[2808,6606,6608],{"id":6607},"known-compatibility-issues","Known Compatibility Issues",[25,6610,6611,6625],{"type":27},[383,6612,6613,6619,6622],{},[386,6614,6615,6616],{},"Some native modules may require rebuilding: ",[40,6617,6618],{},"bun pm rebuild",[386,6620,6621],{},"Very old packages with C++ addons might not work",[386,6623,6624],{},"Edge cases in crypto API differ slightly",[21,6626,6627,6628,6632],{},"Most modern packages work perfectly—check ",[3861,6629,6630],{"href":6630,"rel":6631},"https:\u002F\u002Fbun.sh\u002Fcompatibility",[3865]," for updates.",[73,6634],{},[32,6636,6638],{"id":6637},"step-9-deployment","Step 9: Deployment",[2808,6640,6642],{"id":6641},"deploy-to-various-platforms","Deploy to Various Platforms",[6644,6645,6647],"h4",{"id":6646},"vercel","Vercel",[48,6649,6651],{"className":839,"code":6650,"filename":4384,"language":841,"meta":56,"style":56},"# Vercel automatically detects Bun\nvercel deploy\n",[40,6652,6653,6658],{"__ignoreMap":56},[449,6654,6655],{"class":451,"line":452},[449,6656,6657],{"class":705},"# Vercel automatically detects Bun\n",[449,6659,6660,6662],{"class":451,"line":114},[449,6661,6646],{"class":721},[449,6663,6664],{"class":691}," deploy\n",[6644,6666,6668],{"id":6667},"railway","Railway",[48,6670,6673],{"className":4518,"code":6671,"filename":6672,"language":4521,"meta":56,"style":56},"{\n  \"build\": {\n    \"builder\": \"NIXPACKS_BUN\"\n  },\n  \"run\": {\n    \"command\": \"bun run start\"\n  }\n}\n","railway.json",[40,6674,6675,6679,6686,6696,6700,6707,6717,6721],{"__ignoreMap":56},[449,6676,6677],{"class":451,"line":452},[449,6678,4528],{"class":684},[449,6680,6681,6684],{"class":451,"line":114},[449,6682,6683],{"class":714},"  \"build\"",[449,6685,4548],{"class":684},[449,6687,6688,6691,6693],{"class":451,"line":111},[449,6689,6690],{"class":714},"    \"builder\"",[449,6692,1482],{"class":684},[449,6694,6695],{"class":691},"\"NIXPACKS_BUN\"\n",[449,6697,6698],{"class":451,"line":468},[449,6699,1474],{"class":684},[449,6701,6702,6705],{"class":451,"line":474},[449,6703,6704],{"class":714},"  \"run\"",[449,6706,4548],{"class":684},[449,6708,6709,6712,6714],{"class":451,"line":480},[449,6710,6711],{"class":714},"    \"command\"",[449,6713,1482],{"class":684},[449,6715,6716],{"class":691},"\"bun run start\"\n",[449,6718,6719],{"class":451,"line":486},[449,6720,4599],{"class":684},[449,6722,6723],{"class":451,"line":492},[449,6724,4604],{"class":684},[6644,6726,6728],{"id":6727},"docker","Docker",[48,6730,6735],{"className":6731,"code":6732,"filename":6733,"language":6734,"meta":56,"style":56},"language-dockerfile shiki shiki-themes github-light github-dark","FROM oven\u002Fbun:1.2 AS base\nWORKDIR \u002Fapp\n\nCOPY package.json bun.lockb* .\u002F\nRUN bun install --frozen-lockfile\n\nCOPY . .\nRUN bun run build\n\nEXPOSE 3000\nCMD [\"bun\", \"run\", \"start\"]\n","Dockerfile","dockerfile",[40,6736,6737,6742,6747,6751,6756,6761,6765,6770,6775,6779,6784],{"__ignoreMap":56},[449,6738,6739],{"class":451,"line":452},[449,6740,6741],{},"FROM oven\u002Fbun:1.2 AS base\n",[449,6743,6744],{"class":451,"line":114},[449,6745,6746],{},"WORKDIR \u002Fapp\n",[449,6748,6749],{"class":451,"line":111},[449,6750,700],{"emptyLinePlaceholder":130},[449,6752,6753],{"class":451,"line":468},[449,6754,6755],{},"COPY package.json bun.lockb* .\u002F\n",[449,6757,6758],{"class":451,"line":474},[449,6759,6760],{},"RUN bun install --frozen-lockfile\n",[449,6762,6763],{"class":451,"line":480},[449,6764,700],{"emptyLinePlaceholder":130},[449,6766,6767],{"class":451,"line":486},[449,6768,6769],{},"COPY . .\n",[449,6771,6772],{"class":451,"line":492},[449,6773,6774],{},"RUN bun run build\n",[449,6776,6777],{"class":451,"line":498},[449,6778,700],{"emptyLinePlaceholder":130},[449,6780,6781],{"class":451,"line":133},[449,6782,6783],{},"EXPOSE 3000\n",[449,6785,6786],{"class":451,"line":509},[449,6787,6788],{},"CMD [\"bun\", \"run\", \"start\"]\n",[6644,6790,6792],{"id":6791},"buns-native-server","Bun's Native Server",[21,6794,6795],{},"Bun can serve directly:",[48,6797,6799],{"className":839,"code":6798,"filename":4384,"language":841,"meta":56,"style":56},"bunx nuxi build\nbun run .output\u002Fserver\u002Findex.mjs\n",[40,6800,6801,6811],{"__ignoreMap":56},[449,6802,6803,6805,6808],{"class":451,"line":452},[449,6804,4472],{"class":721},[449,6806,6807],{"class":691}," nuxi",[449,6809,6810],{"class":691}," build\n",[449,6812,6813,6815,6817],{"class":451,"line":114},[449,6814,4440],{"class":721},[449,6816,5296],{"class":691},[449,6818,6819],{"class":691}," .output\u002Fserver\u002Findex.mjs\n",[427,6821,6823],{"icon":429,"title":6822},"Production Ready",[21,6824,6825],{},"Bun is production-ready as of v1.2. Major companies are using it in production today. However, always test your specific dependency stack before deploying.",[73,6827],{},[32,6829,6831],{"id":6830},"what-weve-built","What We've Built",[21,6833,6834],{},"By the end of this guide, you have:",[383,6836,6837,6840,6843,6846,6849],{},[386,6838,6839],{},"A fully configured Bun + Nitro development environment",[386,6841,6842],{},"Fast API endpoints running on Bun",[386,6844,6845],{},"Performance benchmarks comparing to Node.js",[386,6847,6848],{},"Migration guidance from Node.js",[386,6850,6851],{},"Deployment configurations for multiple platforms",[2808,6853,6855],{"id":6854},"api-endpoints-summary","API Endpoints Summary",[547,6857,6858,6867],{},[550,6859,6860],{},[553,6861,6862,6865],{},[556,6863,6864],{},"Endpoint",[556,6866,923],{},[569,6868,6869,6880,6890,6900,6910],{},[553,6870,6871,6877],{},[574,6872,6873,6874],{},"GET ",[40,6875,6876],{},"\u002Fapi\u002Fping",[574,6878,6879],{},"Health check with Bun info",[553,6881,6882,6887],{},[574,6883,6873,6884],{},[40,6885,6886],{},"\u002Fapi\u002Fproducts",[574,6888,6889],{},"Product list",[553,6891,6892,6897],{},[574,6893,6873,6894],{},[40,6895,6896],{},"\u002Fapi\u002Fbun-features",[574,6898,6899],{},"Bun-specific features demo",[553,6901,6902,6907],{},[574,6903,6873,6904],{},[40,6905,6906],{},"\u002Fapi\u002Fexternal",[574,6908,6909],{},"Parallel external API calls",[553,6911,6912,6918],{},[574,6913,6914,6915],{},"POST ",[40,6916,6917],{},"\u002Fapi\u002Forders",[574,6919,6920],{},"Order creation",[73,6922],{},[16,6924,3681],{"id":3680},[21,6926,6927,6930],{},[151,6928,6929],{},"Bun + Nitro"," represents the future of JavaScript server development. The combination of Bun's incredible performance and Nitro's deployment flexibility creates a stack that's hard to beat:",[383,6932,6933,6939,6944,6950,6956],{},[386,6934,6935,6938],{},[151,6936,6937],{},"Blazing fast startup"," — 5-6x faster than Node.js",[386,6940,6941,6943],{},[151,6942,4235],{}," — No compilation step",[386,6945,6946,6949],{},[151,6947,6948],{},"Native Bun APIs"," — file, fetch, WebSocket built-in",[386,6951,6952,6955],{},[151,6953,6954],{},"Universal deployment"," — Node, serverless, edge",[386,6957,6958,6961],{},[151,6959,6960],{},"Same developer experience"," — Nuxt just works",[21,6963,6964],{},"The learning curve is minimal—if you know Nuxt and Node.js, you're already 95% of the way there. The remaining 5% is enjoying the speed.",[2808,6966,6968],{"id":6967},"when-to-use-bun","When to Use Bun",[383,6970,6971,6974,6977,6980,6983],{},[386,6972,6973],{},"✅ New projects where performance matters",[386,6975,6976],{},"✅ Serverless functions (cold start is critical)",[386,6978,6979],{},"✅ Rapid prototyping",[386,6981,6982],{},"✅ TypeScript-first projects",[386,6984,6985],{},"⚠️ Projects with many native C++ modules (test first)",[2808,6987,6989],{"id":6988},"when-to-stick-with-nodejs","When to Stick with Node.js",[383,6991,6992,6995,6998],{},[386,6993,6994],{},"✅ Enterprise projects with strict stability requirements",[386,6996,6997],{},"✅ Heavy reliance on legacy packages",[386,6999,7000],{},"✅ Teams less familiar with Bun's nuances",[21,7002,7003],{},"Start small—try Bun on your next side project or a single microservice. Once you experience the speed, you'll never want to go back. ☕⚡",[21,7005,7006],{},"Ready to build faster? Install Bun and try it today!",[3728,7008,7009],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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 .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":7011},[7012,7013,7014,7015,7016,7017,7018,7019,7020,7025,7029,7037],{"id":4227,"depth":114,"text":4228},{"id":4270,"depth":114,"text":4271},{"id":4376,"depth":114,"text":4377},{"id":4453,"depth":114,"text":4454},{"id":4609,"depth":114,"text":4610},{"id":4691,"depth":114,"text":4692},{"id":5280,"depth":114,"text":5281},{"id":5333,"depth":114,"text":5334},{"id":6034,"depth":114,"text":6035,"children":7021},[7022,7023,7024],{"id":6038,"depth":111,"text":6039},{"id":6206,"depth":111,"text":6207},{"id":6296,"depth":111,"text":6297},{"id":6492,"depth":114,"text":6493,"children":7026},[7027,7028],{"id":6496,"depth":111,"text":6497},{"id":6607,"depth":111,"text":6608},{"id":6637,"depth":114,"text":6638,"children":7030},[7031],{"id":6641,"depth":111,"text":6642,"children":7032},[7033,7034,7035,7036],{"id":6646,"depth":468,"text":6647},{"id":6667,"depth":468,"text":6668},{"id":6727,"depth":468,"text":6728},{"id":6791,"depth":468,"text":6792},{"id":6830,"depth":114,"text":6831,"children":7038},[7039,7040,7041],{"id":6854,"depth":111,"text":6855},{"id":6967,"depth":111,"text":6968},{"id":6988,"depth":111,"text":6989},"2026-03-03","Discover why Bun is the runtime to watch in 2026 and learn how to build blazing-fast APIs with Bun and Nitro. From setup to deployment, this guide covers everything.",{"type":13,"value":7045},[7046,7048,7052,7056,7058,7070],[16,7047,333],{"id":332},[21,7049,4179,7050,4183],{},[151,7051,4182],{},[21,7053,4186,7054,4190],{},[151,7055,4189],{},[21,7057,4193],{},[383,7059,7060,7062,7064,7066,7068],{},[386,7061,4198],{},[386,7063,4201],{},[386,7065,4204],{},[386,7067,4207],{},[386,7069,4210],{},[21,7071,4213,7072,4217],{},[151,7073,4216],{},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555949963-aa79dcee981c",[4440,7076,7077,3612,7078,7079,7080],"nitro","runtime","server","api","javascript",{},"\u002Fblog\u002Fbun-nitro-stack",{"title":4171,"description":7043},"blog\u002Fbun-nitro-stack","EJ5JBl9uLItAKJXjm_rHnmH3iTTvv3nqUyB2xgmJeh0",{"id":7087,"title":7088,"author":7089,"body":7090,"canonical":116,"categories":116,"date":8300,"description":8301,"excerpt":116,"extension":119,"img":8302,"keywords":8303,"language":3788,"meta":8312,"navigation":130,"ogImage":116,"path":8313,"rating":116,"readTime":486,"seo":8314,"stem":8315,"tag":8316,"__hash__":8317},"blog\u002Fblog\u002Fdeploying-react-native-expo-to-app-store.md","Deploying React Native\u002FExpo Apps to the Apple App Store",{"name":9,"img":11,"website":327},{"type":13,"value":7091,"toc":8262},[7092,7095,7097,7100,7103,7107,7156,7159,7162,7176,7179,7183,7186,7218,7222,7245,7248,7251,7255,7258,7298,7301,7331,7335,7339,7342,7359,7363,7369,7564,7567,7570,7574,7602,7606,7613,7630,7634,7639,7688,7691,7694,7698,7701,7757,7761,7764,7827,7831,7834,7875,7878,7881,7885,7976,7980,8011,8014,8017,8021,8065,8069,8072,8113,8116,8120,8173,8177,8180,8212,8214,8217,8220,8223,8249,8252,8254,8259],[16,7093,7088],{"id":7094},"deploying-react-nativeexpo-apps-to-the-apple-app-store",[32,7096,333],{"id":332},[21,7098,7099],{},"Deploying a React Native or Expo app to the Apple App Store can feel like a daunting task, especially when you're coming from a web development background. The process involves understanding Apple's ecosystem, setting up development accounts, configuring build configurations, and navigating the App Store Review process.",[21,7101,7102],{},"In this comprehensive guide, I'll walk you through the entire deployment journey, from setting up your development environment to submitting your app for review. Whether you're a seasoned developer or just starting with React Native\u002FExpo, you'll learn everything you need to know to get your app on the App Store.",[32,7104,7106],{"id":7105},"table-of-contents","Table of Contents",[816,7108,7109,7115,7121,7127,7133,7139,7145,7151],{},[386,7110,7111],{},[3861,7112,7114],{"href":7113},"#setting-up-your-apple-developer-account","Setting Up Your Apple Developer Account",[386,7116,7117],{},[3861,7118,7120],{"href":7119},"#configuring-your-project-for-ios","Configuring Your Project for iOS",[386,7122,7123],{},[3861,7124,7126],{"href":7125},"#setting-up-code-signing","Setting Up Code Signing",[386,7128,7129],{},[3861,7130,7132],{"href":7131},"#preparing-for-app-store-submission","Preparing for App Store Submission",[386,7134,7135],{},[3861,7136,7138],{"href":7137},"#submitting-to-the-app-store","Submitting to the App Store",[386,7140,7141],{},[3861,7142,7144],{"href":7143},"#post-submission-and-maintenance","Post-Submission and Maintenance",[386,7146,7147],{},[3861,7148,7150],{"href":7149},"#troubleshooting-common-issues","Troubleshooting Common Issues",[386,7152,7153],{},[3861,7154,3681],{"href":7155},"#conclusion",[32,7157,7114],{"id":7158},"setting-up-your-apple-developer-account",[21,7160,7161],{},"Before you can deploy to the App Store, you need an Apple Developer account. There are two types:",[383,7163,7164,7170],{},[386,7165,7166,7169],{},[151,7167,7168],{},"Free Account"," (limited to testing on up to 100 devices)",[386,7171,7172,7175],{},[151,7173,7174],{},"Paid Account"," ($99\u002Fyear, full App Store access)",[21,7177,7178],{},"For production deployment, you'll need the paid account.",[2808,7180,7182],{"id":7181},"required-assets","Required Assets",[21,7184,7185],{},"When you purchase your Apple Developer account, you'll need to set up:",[816,7187,7188,7194,7200,7206,7212],{},[386,7189,7190,7193],{},[151,7191,7192],{},"Apple ID"," (your primary account)",[386,7195,7196,7199],{},[151,7197,7198],{},"Team ID"," (automatically generated)",[386,7201,7202,7205],{},[151,7203,7204],{},"App IDs"," (for your specific applications)",[386,7207,7208,7211],{},[151,7209,7210],{},"Certificates"," (for code signing)",[386,7213,7214,7217],{},[151,7215,7216],{},"Devices"," (for testing)",[2808,7219,7221],{"id":7220},"getting-started-with-the-developer-portal","Getting Started with the Developer Portal",[816,7223,7224,7232,7235,7242],{},[386,7225,7226,7227],{},"Visit ",[3861,7228,7231],{"href":7229,"rel":7230},"https:\u002F\u002Fdeveloper.apple.com",[3865],"developer.apple.com",[386,7233,7234],{},"Sign in with your Apple ID",[386,7236,7237,7238,7241],{},"Navigate to the ",[151,7239,7240],{},"Certificates, Identifiers & Profiles"," section",[386,7243,7244],{},"Create your team if you haven't already",[32,7246,7120],{"id":7247},"configuring-your-project-for-ios",[21,7249,7250],{},"Once you have your Apple Developer account set up, you need to configure your React Native or Expo project for iOS deployment.",[2808,7252,7254],{"id":7253},"project-structure","Project Structure",[21,7256,7257],{},"For React Native projects using the default configuration:",[48,7259,7261],{"className":839,"code":7260,"language":841,"meta":56,"style":56},"my-app\u002F\n├── android\u002F\n├── ios\u002F\n├── package.json\n└── App.js\n",[40,7262,7263,7268,7276,7283,7290],{"__ignoreMap":56},[449,7264,7265],{"class":451,"line":452},[449,7266,7267],{"class":721},"my-app\u002F\n",[449,7269,7270,7273],{"class":451,"line":114},[449,7271,7272],{"class":721},"├──",[449,7274,7275],{"class":691}," android\u002F\n",[449,7277,7278,7280],{"class":451,"line":111},[449,7279,7272],{"class":721},[449,7281,7282],{"class":691}," ios\u002F\n",[449,7284,7285,7287],{"class":451,"line":468},[449,7286,7272],{"class":721},[449,7288,7289],{"class":691}," package.json\n",[449,7291,7292,7295],{"class":451,"line":474},[449,7293,7294],{"class":721},"└──",[449,7296,7297],{"class":691}," App.js\n",[21,7299,7300],{},"For Expo projects using managed workflow:",[48,7302,7304],{"className":839,"code":7303,"language":841,"meta":56,"style":56},"my-expo-app\u002F\n├── app.json\n├── package.json\n└── .gitignore\n",[40,7305,7306,7311,7318,7324],{"__ignoreMap":56},[449,7307,7308],{"class":451,"line":452},[449,7309,7310],{"class":721},"my-expo-app\u002F\n",[449,7312,7313,7315],{"class":451,"line":114},[449,7314,7272],{"class":721},[449,7316,7317],{"class":691}," app.json\n",[449,7319,7320,7322],{"class":451,"line":111},[449,7321,7272],{"class":721},[449,7323,7289],{"class":691},[449,7325,7326,7328],{"class":451,"line":468},[449,7327,7294],{"class":721},[449,7329,7330],{"class":691}," .gitignore\n",[2808,7332,7334],{"id":7333},"ios-specific-configuration","iOS-Specific Configuration",[6644,7336,7338],{"id":7337},"react-native-bare-workflow","React Native (Bare Workflow)",[21,7340,7341],{},"If you're using React Native's bare workflow, you'll need to manually configure the iOS project:",[816,7343,7344,7349,7354],{},[386,7345,7346],{},[151,7347,7348],{},"Update Info.plist",[386,7350,7351],{},[151,7352,7353],{},"Configure entitlements",[386,7355,7356],{},[151,7357,7358],{},"Set up capabilities",[6644,7360,7362],{"id":7361},"expo-managed-workflow","Expo (Managed Workflow)",[21,7364,7365,7366,3070],{},"Expo makes iOS configuration much simpler. Most settings are handled through ",[40,7367,7368],{},"app.json",[48,7370,7372],{"className":4518,"code":7371,"language":4521,"meta":56,"style":56},"{\n  \"expo\": {\n    \"name\": \"My App\",\n    \"slug\": \"my-app\",\n    \"version\": \"1.0.0\",\n    \"orientation\": \"portrait\",\n    \"icon\": \".\u002Fassets\u002Ficon.png\",\n    \"userInterfaceStyle\": \"light\",\n    \"splash\": {\n      \"image\": \".\u002Fassets\u002Fsplash.png\",\n      \"resizeMode\": \"contain\",\n      \"backgroundColor\": \"#ffffff\"\n    },\n    \"ios\": {\n      \"bundleIdentifier\": \"com.yourcompany.yourapp\",\n      \"buildNumber\": \"1\"\n    },\n    \"android\": {\n      \"package\": \"com.yourcompany.yourapp\"\n    }\n  }\n}\n",[40,7373,7374,7378,7385,7397,7409,7420,7432,7444,7456,7463,7475,7487,7497,7501,7508,7520,7530,7534,7541,7551,7556,7560],{"__ignoreMap":56},[449,7375,7376],{"class":451,"line":452},[449,7377,4528],{"class":684},[449,7379,7380,7383],{"class":451,"line":114},[449,7381,7382],{"class":714},"  \"expo\"",[449,7384,4548],{"class":684},[449,7386,7387,7390,7392,7395],{"class":451,"line":111},[449,7388,7389],{"class":714},"    \"name\"",[449,7391,1482],{"class":684},[449,7393,7394],{"class":691},"\"My App\"",[449,7396,779],{"class":684},[449,7398,7399,7402,7404,7407],{"class":451,"line":468},[449,7400,7401],{"class":714},"    \"slug\"",[449,7403,1482],{"class":684},[449,7405,7406],{"class":691},"\"my-app\"",[449,7408,779],{"class":684},[449,7410,7411,7414,7416,7418],{"class":451,"line":474},[449,7412,7413],{"class":714},"    \"version\"",[449,7415,1482],{"class":684},[449,7417,3403],{"class":691},[449,7419,779],{"class":684},[449,7421,7422,7425,7427,7430],{"class":451,"line":480},[449,7423,7424],{"class":714},"    \"orientation\"",[449,7426,1482],{"class":684},[449,7428,7429],{"class":691},"\"portrait\"",[449,7431,779],{"class":684},[449,7433,7434,7437,7439,7442],{"class":451,"line":486},[449,7435,7436],{"class":714},"    \"icon\"",[449,7438,1482],{"class":684},[449,7440,7441],{"class":691},"\".\u002Fassets\u002Ficon.png\"",[449,7443,779],{"class":684},[449,7445,7446,7449,7451,7454],{"class":451,"line":492},[449,7447,7448],{"class":714},"    \"userInterfaceStyle\"",[449,7450,1482],{"class":684},[449,7452,7453],{"class":691},"\"light\"",[449,7455,779],{"class":684},[449,7457,7458,7461],{"class":451,"line":498},[449,7459,7460],{"class":714},"    \"splash\"",[449,7462,4548],{"class":684},[449,7464,7465,7468,7470,7473],{"class":451,"line":133},[449,7466,7467],{"class":714},"      \"image\"",[449,7469,1482],{"class":684},[449,7471,7472],{"class":691},"\".\u002Fassets\u002Fsplash.png\"",[449,7474,779],{"class":684},[449,7476,7477,7480,7482,7485],{"class":451,"line":509},[449,7478,7479],{"class":714},"      \"resizeMode\"",[449,7481,1482],{"class":684},[449,7483,7484],{"class":691},"\"contain\"",[449,7486,779],{"class":684},[449,7488,7489,7492,7494],{"class":451,"line":318},[449,7490,7491],{"class":714},"      \"backgroundColor\"",[449,7493,1482],{"class":684},[449,7495,7496],{"class":691},"\"#ffffff\"\n",[449,7498,7499],{"class":451,"line":520},[449,7500,1458],{"class":684},[449,7502,7503,7506],{"class":451,"line":526},[449,7504,7505],{"class":714},"    \"ios\"",[449,7507,4548],{"class":684},[449,7509,7510,7513,7515,7518],{"class":451,"line":532},[449,7511,7512],{"class":714},"      \"bundleIdentifier\"",[449,7514,1482],{"class":684},[449,7516,7517],{"class":691},"\"com.yourcompany.yourapp\"",[449,7519,779],{"class":684},[449,7521,7522,7525,7527],{"class":451,"line":2790},[449,7523,7524],{"class":714},"      \"buildNumber\"",[449,7526,1482],{"class":684},[449,7528,7529],{"class":691},"\"1\"\n",[449,7531,7532],{"class":451,"line":3511},[449,7533,1458],{"class":684},[449,7535,7536,7539],{"class":451,"line":3526},[449,7537,7538],{"class":714},"    \"android\"",[449,7540,4548],{"class":684},[449,7542,7543,7546,7548],{"class":451,"line":3531},[449,7544,7545],{"class":714},"      \"package\"",[449,7547,1482],{"class":684},[449,7549,7550],{"class":691},"\"com.yourcompany.yourapp\"\n",[449,7552,7553],{"class":451,"line":3536},[449,7554,7555],{"class":684},"    }\n",[449,7557,7558],{"class":451,"line":4009},[449,7559,4599],{"class":684},[449,7561,7562],{"class":451,"line":4015},[449,7563,4604],{"class":684},[32,7565,7126],{"id":7566},"setting-up-code-signing",[21,7568,7569],{},"Code signing is Apple's way of ensuring your app hasn't been tampered with. This is a critical step in the App Store submission process.",[2808,7571,7573],{"id":7572},"generating-certificates","Generating Certificates",[816,7575,7576,7589],{},[386,7577,7578,7581],{},[151,7579,7580],{},"App Signing Certificate",[383,7582,7583,7586],{},[386,7584,7585],{},"Generate a certificate in the Developer Portal",[386,7587,7588],{},"Download and install it in your Keychain",[386,7590,7591,7594],{},[151,7592,7593],{},"Provisioning Profiles",[383,7595,7596,7599],{},[386,7597,7598],{},"Create app-specific provisioning profiles for development and distribution",[386,7600,7601],{},"These profiles associate your certificates with your app and devices",[2808,7603,7605],{"id":7604},"code-signing-in-react-native","Code Signing in React Native",[21,7607,7608,7609,7612],{},"For React Native projects, you'll need to configure code signing in the ",[40,7610,7611],{},"ios\u002F"," directory:",[816,7614,7615,7620,7625],{},[386,7616,7617],{},[151,7618,7619],{},"Locate your project.pbxproj file",[386,7621,7622],{},[151,7623,7624],{},"Update the development team",[386,7626,7627],{},[151,7628,7629],{},"Configure code signing identity",[2808,7631,7633],{"id":7632},"code-signing-in-expo","Code Signing in Expo",[21,7635,7636,7637,3070],{},"Expo handles code signing automatically for you. The main configuration is in ",[40,7638,7368],{},[48,7640,7642],{"className":4518,"code":7641,"language":4521,"meta":56,"style":56},"\"ios\": {\n  \"codeSigningCertificate\": \"YourCertificate.pem\",\n  \"codeSigningMetadata\": {\n    \"publicKeyHash\": \"your-public-key-hash\"\n  }\n}\n",[40,7643,7644,7651,7663,7670,7680,7684],{"__ignoreMap":56},[449,7645,7646,7649],{"class":451,"line":452},[449,7647,7648],{"class":691},"\"ios\"",[449,7650,4548],{"class":684},[449,7652,7653,7656,7658,7661],{"class":451,"line":114},[449,7654,7655],{"class":714},"  \"codeSigningCertificate\"",[449,7657,1482],{"class":684},[449,7659,7660],{"class":691},"\"YourCertificate.pem\"",[449,7662,779],{"class":684},[449,7664,7665,7668],{"class":451,"line":111},[449,7666,7667],{"class":714},"  \"codeSigningMetadata\"",[449,7669,4548],{"class":684},[449,7671,7672,7675,7677],{"class":451,"line":468},[449,7673,7674],{"class":714},"    \"publicKeyHash\"",[449,7676,1482],{"class":684},[449,7678,7679],{"class":691},"\"your-public-key-hash\"\n",[449,7681,7682],{"class":451,"line":474},[449,7683,4599],{"class":684},[449,7685,7686],{"class":451,"line":480},[449,7687,4604],{"class":684},[32,7689,7132],{"id":7690},"preparing-for-app-store-submission",[21,7692,7693],{},"Before you can submit your app to the App Store, you need to ensure it's ready for review.",[2808,7695,7697],{"id":7696},"app-store-connect-requirements","App Store Connect Requirements",[21,7699,7700],{},"Your app must meet several requirements:",[816,7702,7703,7719,7732,7744],{},[386,7704,7705,7708],{},[151,7706,7707],{},"Bundle Identifier",[383,7709,7710,7713],{},[386,7711,7712],{},"Must match the one registered in the Developer Portal",[386,7714,7715,7716],{},"Format: ",[40,7717,7718],{},"com.companyname.appname",[386,7720,7721,7724],{},[151,7722,7723],{},"Version Number",[383,7725,7726,7729],{},[386,7727,7728],{},"Must be a positive integer",[386,7730,7731],{},"Increment with each update",[386,7733,7734,7737],{},[151,7735,7736],{},"Build Number",[383,7738,7739,7741],{},[386,7740,7728],{},[386,7742,7743],{},"Should increment with each build",[386,7745,7746,7749],{},[151,7747,7748],{},"App Name",[383,7750,7751,7754],{},[386,7752,7753],{},"Must be between 1 and 50 characters",[386,7755,7756],{},"Cannot contain prohibited words",[2808,7758,7760],{"id":7759},"app-metadata","App Metadata",[21,7762,7763],{},"Prepare the following metadata for your app:",[816,7765,7766,7782,7798,7814],{},[386,7767,7768,7771],{},[151,7769,7770],{},"App Icon",[383,7772,7773,7776,7779],{},[386,7774,7775],{},"1024×1024 pixels (PNG)",[386,7777,7778],{},"Must be square",[386,7780,7781],{},"Should be clear and readable",[386,7783,7784,7787],{},[151,7785,7786],{},"App Screenshots",[383,7788,7789,7792,7795],{},[386,7790,7791],{},"iPhone: 2732×2048 pixels (5.5-inch display)",[386,7793,7794],{},"iPad: 2732×2048 pixels (12.9-inch display)",[386,7796,7797],{},"Include at least 2 screenshots",[386,7799,7800,7803],{},[151,7801,7802],{},"App Description",[383,7804,7805,7808,7811],{},[386,7806,7807],{},"Must be between 30 and 255 characters",[386,7809,7810],{},"Include relevant keywords",[386,7812,7813],{},"Highlight key features",[386,7815,7816,7819],{},[151,7817,7818],{},"Privacy Policy",[383,7820,7821,7824],{},[386,7822,7823],{},"Required for all apps",[386,7825,7826],{},"Must be accessible from the App Store listing",[2808,7828,7830],{"id":7829},"testing-your-app","Testing Your App",[21,7832,7833],{},"Before submitting, thoroughly test your app:",[816,7835,7836,7849,7862],{},[386,7837,7838,7841],{},[151,7839,7840],{},"Unit Tests",[383,7842,7843,7846],{},[386,7844,7845],{},"Ensure core functionality works",[386,7847,7848],{},"Test edge cases",[386,7850,7851,7854],{},[151,7852,7853],{},"Integration Tests",[383,7855,7856,7859],{},[386,7857,7858],{},"Test with real devices",[386,7860,7861],{},"Test with different iOS versions",[386,7863,7864,7867],{},[151,7865,7866],{},"App Store Connect TestFlight",[383,7868,7869,7872],{},[386,7870,7871],{},"Use TestFlight for beta testing",[386,7873,7874],{},"Collect feedback from testers",[32,7876,7138],{"id":7877},"submitting-to-the-app-store",[21,7879,7880],{},"Once your app is ready, you can submit it to the App Store.",[2808,7882,7884],{"id":7883},"step-by-step-submission-process","Step-by-Step Submission Process",[816,7886,7887,7908,7921,7937,7950,7963],{},[386,7888,7889,7892],{},[151,7890,7891],{},"Create App in App Store Connect",[383,7893,7894,7902,7905],{},[386,7895,7896,7897],{},"Log in to ",[3861,7898,7901],{"href":7899,"rel":7900},"https:\u002F\u002Fappstoreconnect.apple.com",[3865],"App Store Connect",[386,7903,7904],{},"Click \"Add New App\"",[386,7906,7907],{},"Enter your app's bundle identifier",[386,7909,7910,7913],{},[151,7911,7912],{},"Upload App Binary",[383,7914,7915,7918],{},[386,7916,7917],{},"Build your app for distribution",[386,7919,7920],{},"Upload the .ipa file to App Store Connect",[386,7922,7923,7926],{},[151,7924,7925],{},"Fill Out App Metadata",[383,7927,7928,7931,7934],{},[386,7929,7930],{},"App name, description, keywords",[386,7932,7933],{},"Category and sub-category",[386,7935,7936],{},"Privacy policy URL",[386,7938,7939,7942],{},[151,7940,7941],{},"Set Pricing and Availability",[383,7943,7944,7947],{},[386,7945,7946],{},"Choose your pricing model",[386,7948,7949],{},"Select countries where you want to distribute",[386,7951,7952,7955],{},[151,7953,7954],{},"Submit for Review",[383,7956,7957,7960],{},[386,7958,7959],{},"Review all requirements",[386,7961,7962],{},"Submit for App Store Review",[386,7964,7965,7968],{},[151,7966,7967],{},"Wait for Review",[383,7969,7970,7973],{},[386,7971,7972],{},"Apple typically reviews apps within 24-72 hours",[386,7974,7975],{},"You can track the review status in App Store Connect",[2808,7977,7979],{"id":7978},"common-submission-issues","Common Submission Issues",[383,7981,7982,7998],{},[386,7983,7984,7987],{},[151,7985,7986],{},"App Store Rejection Reasons",[383,7988,7989,7992,7995],{},[386,7990,7991],{},"Read the rejection email carefully",[386,7993,7994],{},"Address all issues before resubmitting",[386,7996,7997],{},"Common reasons include crashes, privacy policy issues, and missing metadata",[386,7999,8000,8003],{},[151,8001,8002],{},"Review Delays",[383,8004,8005,8008],{},[386,8006,8007],{},"Be patient during the review process",[386,8009,8010],{},"Contact Apple Support if there are delays",[32,8012,7144],{"id":8013},"post-submission-and-maintenance",[21,8015,8016],{},"After your app is approved, you need to manage its presence on the App Store.",[2808,8018,8020],{"id":8019},"publishing-your-app","Publishing Your App",[816,8022,8023,8036,8049],{},[386,8024,8025,8028],{},[151,8026,8027],{},"Release to Production",[383,8029,8030,8033],{},[386,8031,8032],{},"Set your app to \"Ready for Sale\"",[386,8034,8035],{},"Choose your release date",[386,8037,8038,8041],{},[151,8039,8040],{},"Monitor Performance",[383,8042,8043,8046],{},[386,8044,8045],{},"Track downloads and ratings",[386,8047,8048],{},"Monitor user feedback",[386,8050,8051,8054],{},[151,8052,8053],{},"Update Regularly",[383,8055,8056,8059,8062],{},[386,8057,8058],{},"Fix bugs promptly",[386,8060,8061],{},"Add requested features",[386,8063,8064],{},"Update app metadata",[2808,8066,8068],{"id":8067},"app-store-optimization-aso","App Store Optimization (ASO)",[21,8070,8071],{},"Optimize your app listing for better visibility:",[816,8073,8074,8087,8100],{},[386,8075,8076,8079],{},[151,8077,8078],{},"Keywords",[383,8080,8081,8084],{},[386,8082,8083],{},"Research relevant keywords",[386,8085,8086],{},"Include them in your app description",[386,8088,8089,8092],{},[151,8090,8091],{},"Metadata",[383,8093,8094,8097],{},[386,8095,8096],{},"Use compelling app name",[386,8098,8099],{},"Write engaging description",[386,8101,8102,8105],{},[151,8103,8104],{},"Screenshots",[383,8106,8107,8110],{},[386,8108,8109],{},"Update screenshots regularly",[386,8111,8112],{},"Show new features",[32,8114,7150],{"id":8115},"troubleshooting-common-issues",[2808,8117,8119],{"id":8118},"common-problems-and-solutions","Common Problems and Solutions",[816,8121,8122,8135,8148,8160],{},[386,8123,8124,8127],{},[151,8125,8126],{},"Code Signing Errors",[383,8128,8129,8132],{},[386,8130,8131],{},"Regenerate certificates if needed",[386,8133,8134],{},"Update provisioning profiles",[386,8136,8137,8140],{},[151,8138,8139],{},"Build Failures",[383,8141,8142,8145],{},[386,8143,8144],{},"Check for missing dependencies",[386,8146,8147],{},"Update to the latest React Native\u002FExpo version",[386,8149,8150,8153],{},[151,8151,8152],{},"App Store Rejection",[383,8154,8155,8157],{},[386,8156,7991],{},[386,8158,8159],{},"Fix all issues before resubmitting",[386,8161,8162,8165],{},[151,8163,8164],{},"Performance Issues",[383,8166,8167,8170],{},[386,8168,8169],{},"Optimize app startup time",[386,8171,8172],{},"Reduce memory usage",[2808,8174,8176],{"id":8175},"getting-help","Getting Help",[21,8178,8179],{},"If you encounter issues:",[816,8181,8182,8192,8202],{},[386,8183,8184,8187],{},[151,8185,8186],{},"Apple Developer Forums",[383,8188,8189],{},[386,8190,8191],{},"Ask questions and get help from other developers",[386,8193,8194,8197],{},[151,8195,8196],{},"Stack Overflow",[383,8198,8199],{},[386,8200,8201],{},"Search for existing solutions",[386,8203,8204,8207],{},[151,8205,8206],{},"React Native\u002FExpo Community",[383,8208,8209],{},[386,8210,8211],{},"Join Discord or other communities",[32,8213,3681],{"id":3680},[21,8215,8216],{},"Deploying a React Native or Expo app to the Apple App Store is a multi-step process that requires careful planning and execution. By following the steps outlined in this guide, you can successfully navigate the entire deployment journey.",[21,8218,8219],{},"Remember that the App Store submission process is rigorous, but with proper preparation and attention to detail, you can get your app approved and available to users worldwide.",[21,8221,8222],{},"The key to success is:",[816,8224,8225,8231,8237,8243],{},[386,8226,8227,8230],{},[151,8228,8229],{},"Proper Planning"," - Understand all requirements before starting",[386,8232,8233,8236],{},[151,8234,8235],{},"Thorough Testing"," - Test your app thoroughly before submission",[386,8238,8239,8242],{},[151,8240,8241],{},"Attention to Detail"," - Pay attention to metadata and requirements",[386,8244,8245,8248],{},[151,8246,8247],{},"Patience"," - The review process takes time",[21,8250,8251],{},"Good luck with your App Store deployment!",[73,8253],{},[21,8255,8256],{},[156,8257,8258],{},"This guide covers the deployment process for both React Native (bare workflow) and Expo (managed workflow). For specific issues with your particular setup, consult the official documentation for your framework.",[3728,8260,8261],{},"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 .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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":56,"searchDepth":111,"depth":111,"links":8263},[8264,8265,8266,8270,8277,8282,8287,8291,8295,8299],{"id":332,"depth":114,"text":333},{"id":7105,"depth":114,"text":7106},{"id":7158,"depth":114,"text":7114,"children":8267},[8268,8269],{"id":7181,"depth":111,"text":7182},{"id":7220,"depth":111,"text":7221},{"id":7247,"depth":114,"text":7120,"children":8271},[8272,8273],{"id":7253,"depth":111,"text":7254},{"id":7333,"depth":111,"text":7334,"children":8274},[8275,8276],{"id":7337,"depth":468,"text":7338},{"id":7361,"depth":468,"text":7362},{"id":7566,"depth":114,"text":7126,"children":8278},[8279,8280,8281],{"id":7572,"depth":111,"text":7573},{"id":7604,"depth":111,"text":7605},{"id":7632,"depth":111,"text":7633},{"id":7690,"depth":114,"text":7132,"children":8283},[8284,8285,8286],{"id":7696,"depth":111,"text":7697},{"id":7759,"depth":111,"text":7760},{"id":7829,"depth":111,"text":7830},{"id":7877,"depth":114,"text":7138,"children":8288},[8289,8290],{"id":7883,"depth":111,"text":7884},{"id":7978,"depth":111,"text":7979},{"id":8013,"depth":114,"text":7144,"children":8292},[8293,8294],{"id":8019,"depth":111,"text":8020},{"id":8067,"depth":111,"text":8068},{"id":8115,"depth":114,"text":7150,"children":8296},[8297,8298],{"id":8118,"depth":111,"text":8119},{"id":8175,"depth":111,"text":8176},{"id":3680,"depth":114,"text":3681},"2026-06-17","A comprehensive walkthrough of shipping a React Native or Expo app to Apple's App Store — from developer account setup and code signing, through App Store Connect metadata, submission, and post-launch maintenance.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1512941937669-90a1b58e7e9c",[8304,8305,8306,8307,8308,8309,8310,8311],"react-native","expo","ios","app-store","deployment","mobile","code-signing","testflight",{},"\u002Fblog\u002Fdeploying-react-native-expo-to-app-store",{"title":7088,"description":8301},"blog\u002Fdeploying-react-native-expo-to-app-store","Application Development","j1T8LuYI10BRCiCZyWC9gSNJfdrk7z3kbOOwHcrrcvc",{"id":8319,"title":8320,"author":8321,"body":8322,"canonical":116,"categories":116,"date":8300,"description":9875,"excerpt":116,"extension":119,"img":9876,"keywords":9877,"language":3788,"meta":9881,"navigation":130,"ogImage":116,"path":9882,"rating":116,"readTime":498,"seo":9883,"stem":9884,"tag":8316,"__hash__":9885},"blog\u002Fblog\u002Fdeploying-react-native-expo-to-play-store.md","Deploying React Native\u002FExpo Apps to the Google Play Store",{"name":9,"img":11,"website":327},{"type":13,"value":8323,"toc":9828},[8324,8327,8329,8332,8335,8337,8387,8390,8393,8407,8410,8412,8415,8439,8443,8463,8466,8469,8471,8474,8506,8509,8543,8547,8549,8555,8578,8580,8585,8810,8813,8824,8828,8831,8851,8855,8858,8877,8881,8884,8981,8984,9055,9058,9061,9065,9084,9088,9107,9109,9115,9254,9256,9262,9342,9345,9348,9352,9354,9401,9403,9405,9462,9464,9466,9501,9504,9507,9509,9597,9599,9625,9627,9630,9632,9667,9671,9673,9706,9708,9710,9756,9758,9760,9788,9790,9793,9796,9798,9816,9819,9821,9825],[16,8325,8320],{"id":8326},"deploying-react-nativeexpo-apps-to-the-google-play-store",[32,8328,333],{"id":332},[21,8330,8331],{},"Deploying a React Native or Expo app to the Google Play Store opens up your application to billions of Android users worldwide. While the Android ecosystem is generally more permissive than Apple's, the Play Store submission process still requires careful preparation and adherence to Google's policies.",[21,8333,8334],{},"In this comprehensive guide, I'll walk you through the entire Android deployment journey, from setting up your Google account to publishing your app on the Play Store. Whether you're a seasoned developer or just starting with React Native\u002FExpo, you'll learn everything you need to know to get your app on the Play Store.",[32,8336,7106],{"id":7105},[816,8338,8339,8345,8351,8357,8363,8369,8375,8379,8383],{},[386,8340,8341],{},[3861,8342,8344],{"href":8343},"#setting-up-your-google-play-console-account","Setting Up Your Google Play Console Account",[386,8346,8347],{},[3861,8348,8350],{"href":8349},"#configuring-your-project-for-android","Configuring Your Project for Android",[386,8352,8353],{},[3861,8354,8356],{"href":8355},"#preparing-app-bundles-and-apks","Preparing App Bundles and APKs",[386,8358,8359],{},[3861,8360,8362],{"href":8361},"#setting-up-google-play-app-signing","Setting Up Google Play App Signing",[386,8364,8365],{},[3861,8366,8368],{"href":8367},"#preparing-for-play-store-submission","Preparing for Play Store Submission",[386,8370,8371],{},[3861,8372,8374],{"href":8373},"#submitting-to-the-play-store","Submitting to the Play Store",[386,8376,8377],{},[3861,8378,7144],{"href":7143},[386,8380,8381],{},[3861,8382,7150],{"href":7149},[386,8384,8385],{},[3861,8386,3681],{"href":7155},[32,8388,8344],{"id":8389},"setting-up-your-google-play-console-account",[21,8391,8392],{},"Before you can deploy to the Play Store, you need a Google Play Console account. There are two types:",[383,8394,8395,8401],{},[386,8396,8397,8400],{},[151,8398,8399],{},"Free Developer Console"," ($25 one-time registration fee)",[386,8402,8403,8406],{},[151,8404,8405],{},"Google Play for Teams"," (additional features for organizations)",[21,8408,8409],{},"For most individual developers, the free developer console is sufficient.",[2808,8411,7182],{"id":7181},[21,8413,8414],{},"When you register, you'll need to set up:",[816,8416,8417,8422,8428,8434],{},[386,8418,8419,7193],{},[151,8420,8421],{},"Google Account",[386,8423,8424,8427],{},[151,8425,8426],{},"Developer Account"," (with payment information)",[386,8429,8430,8433],{},[151,8431,8432],{},"Project Name"," (your company's\u002Fproject name)",[386,8435,8436,8438],{},[151,8437,7818],{}," (required for all apps)",[2808,8440,8442],{"id":8441},"getting-started-with-the-play-console","Getting Started with the Play Console",[816,8444,8445,8452,8455,8458],{},[386,8446,7226,8447],{},[3861,8448,8451],{"href":8449,"rel":8450},"https:\u002F\u002Fplay.google.com\u002Fconsole",[3865],"play.google.com\u002Fconsole",[386,8453,8454],{},"Sign in with your Google Account",[386,8456,8457],{},"Complete the developer registration process",[386,8459,7237,8460,7241],{},[151,8461,8462],{},"My apps",[32,8464,8350],{"id":8465},"configuring-your-project-for-android",[21,8467,8468],{},"Once you have your Google Play Console account set up, you need to configure your React Native or Expo project for Android deployment.",[2808,8470,7254],{"id":7253},[21,8472,8473],{},"For React Native projects:",[48,8475,8476],{"className":839,"code":7260,"language":841,"meta":56,"style":56},[40,8477,8478,8482,8488,8494,8500],{"__ignoreMap":56},[449,8479,8480],{"class":451,"line":452},[449,8481,7267],{"class":721},[449,8483,8484,8486],{"class":451,"line":114},[449,8485,7272],{"class":721},[449,8487,7275],{"class":691},[449,8489,8490,8492],{"class":451,"line":111},[449,8491,7272],{"class":721},[449,8493,7282],{"class":691},[449,8495,8496,8498],{"class":451,"line":468},[449,8497,7272],{"class":721},[449,8499,7289],{"class":691},[449,8501,8502,8504],{"class":451,"line":474},[449,8503,7294],{"class":721},[449,8505,7297],{"class":691},[21,8507,8508],{},"For Expo projects:",[48,8510,8512],{"className":839,"code":8511,"language":841,"meta":56,"style":56},"my-expo-app\u002F\n├── app.json\n├── package.json\n├── .gitignore\n└── expo-modules.config.json\n",[40,8513,8514,8518,8524,8530,8536],{"__ignoreMap":56},[449,8515,8516],{"class":451,"line":452},[449,8517,7310],{"class":721},[449,8519,8520,8522],{"class":451,"line":114},[449,8521,7272],{"class":721},[449,8523,7317],{"class":691},[449,8525,8526,8528],{"class":451,"line":111},[449,8527,7272],{"class":721},[449,8529,7289],{"class":691},[449,8531,8532,8534],{"class":451,"line":468},[449,8533,7272],{"class":721},[449,8535,7330],{"class":691},[449,8537,8538,8540],{"class":451,"line":474},[449,8539,7294],{"class":721},[449,8541,8542],{"class":691}," expo-modules.config.json\n",[2808,8544,8546],{"id":8545},"android-specific-configuration","Android-Specific Configuration",[6644,8548,7338],{"id":7337},[21,8550,8551,8552,7612],{},"For React Native projects, you'll need to configure the Android project in the ",[40,8553,8554],{},"android\u002F",[816,8556,8557,8564,8573],{},[386,8558,8559],{},[151,8560,4616,8561],{},[40,8562,8563],{},"AndroidManifest.xml",[386,8565,8566],{},[151,8567,8568,8569,8572],{},"Configure ",[40,8570,8571],{},"build.gradle"," files",[386,8574,8575],{},[151,8576,8577],{},"Set up app signing",[6644,8579,7362],{"id":7361},[21,8581,8582,8583,3070],{},"Expo makes Android configuration much simpler. Most settings are handled through ",[40,8584,7368],{},[48,8586,8588],{"className":4518,"code":8587,"language":4521,"meta":56,"style":56},"{\n  \"expo\": {\n    \"name\": \"My App\",\n    \"slug\": \"my-app\",\n    \"version\": \"1.0.0\",\n    \"orientation\": \"portrait\",\n    \"icon\": \".\u002Fassets\u002Ficon.png\",\n    \"userInterfaceStyle\": \"light\",\n    \"splash\": {\n      \"image\": \".\u002Fassets\u002Fsplash.png\",\n      \"resizeMode\": \"contain\",\n      \"backgroundColor\": \"#ffffff\"\n    },\n    \"ios\": {\n      \"bundleIdentifier\": \"com.yourcompany.yourapp\",\n      \"buildNumber\": \"1\"\n    },\n    \"android\": {\n      \"package\": \"com.yourcompany.yourapp\",\n      \"versionCode\": 1,\n      \"googleServicesFile\": \".\u002Fgoogle-services.json\",\n      \"adaptiveIcon\": {\n        \"foregroundImage\": \".\u002Fassets\u002Fadaptive-icon.png\",\n        \"backgroundColor\": \"#ffffff\"\n      }\n    }\n  }\n}\n",[40,8589,8590,8594,8600,8610,8620,8630,8640,8650,8660,8666,8676,8686,8694,8698,8704,8714,8722,8726,8732,8742,8753,8765,8772,8784,8793,8798,8802,8806],{"__ignoreMap":56},[449,8591,8592],{"class":451,"line":452},[449,8593,4528],{"class":684},[449,8595,8596,8598],{"class":451,"line":114},[449,8597,7382],{"class":714},[449,8599,4548],{"class":684},[449,8601,8602,8604,8606,8608],{"class":451,"line":111},[449,8603,7389],{"class":714},[449,8605,1482],{"class":684},[449,8607,7394],{"class":691},[449,8609,779],{"class":684},[449,8611,8612,8614,8616,8618],{"class":451,"line":468},[449,8613,7401],{"class":714},[449,8615,1482],{"class":684},[449,8617,7406],{"class":691},[449,8619,779],{"class":684},[449,8621,8622,8624,8626,8628],{"class":451,"line":474},[449,8623,7413],{"class":714},[449,8625,1482],{"class":684},[449,8627,3403],{"class":691},[449,8629,779],{"class":684},[449,8631,8632,8634,8636,8638],{"class":451,"line":480},[449,8633,7424],{"class":714},[449,8635,1482],{"class":684},[449,8637,7429],{"class":691},[449,8639,779],{"class":684},[449,8641,8642,8644,8646,8648],{"class":451,"line":486},[449,8643,7436],{"class":714},[449,8645,1482],{"class":684},[449,8647,7441],{"class":691},[449,8649,779],{"class":684},[449,8651,8652,8654,8656,8658],{"class":451,"line":492},[449,8653,7448],{"class":714},[449,8655,1482],{"class":684},[449,8657,7453],{"class":691},[449,8659,779],{"class":684},[449,8661,8662,8664],{"class":451,"line":498},[449,8663,7460],{"class":714},[449,8665,4548],{"class":684},[449,8667,8668,8670,8672,8674],{"class":451,"line":133},[449,8669,7467],{"class":714},[449,8671,1482],{"class":684},[449,8673,7472],{"class":691},[449,8675,779],{"class":684},[449,8677,8678,8680,8682,8684],{"class":451,"line":509},[449,8679,7479],{"class":714},[449,8681,1482],{"class":684},[449,8683,7484],{"class":691},[449,8685,779],{"class":684},[449,8687,8688,8690,8692],{"class":451,"line":318},[449,8689,7491],{"class":714},[449,8691,1482],{"class":684},[449,8693,7496],{"class":691},[449,8695,8696],{"class":451,"line":520},[449,8697,1458],{"class":684},[449,8699,8700,8702],{"class":451,"line":526},[449,8701,7505],{"class":714},[449,8703,4548],{"class":684},[449,8705,8706,8708,8710,8712],{"class":451,"line":532},[449,8707,7512],{"class":714},[449,8709,1482],{"class":684},[449,8711,7517],{"class":691},[449,8713,779],{"class":684},[449,8715,8716,8718,8720],{"class":451,"line":2790},[449,8717,7524],{"class":714},[449,8719,1482],{"class":684},[449,8721,7529],{"class":691},[449,8723,8724],{"class":451,"line":3511},[449,8725,1458],{"class":684},[449,8727,8728,8730],{"class":451,"line":3526},[449,8729,7538],{"class":714},[449,8731,4548],{"class":684},[449,8733,8734,8736,8738,8740],{"class":451,"line":3531},[449,8735,7545],{"class":714},[449,8737,1482],{"class":684},[449,8739,7517],{"class":691},[449,8741,779],{"class":684},[449,8743,8744,8747,8749,8751],{"class":451,"line":3536},[449,8745,8746],{"class":714},"      \"versionCode\"",[449,8748,1482],{"class":684},[449,8750,4799],{"class":714},[449,8752,779],{"class":684},[449,8754,8755,8758,8760,8763],{"class":451,"line":4009},[449,8756,8757],{"class":714},"      \"googleServicesFile\"",[449,8759,1482],{"class":684},[449,8761,8762],{"class":691},"\".\u002Fgoogle-services.json\"",[449,8764,779],{"class":684},[449,8766,8767,8770],{"class":451,"line":4015},[449,8768,8769],{"class":714},"      \"adaptiveIcon\"",[449,8771,4548],{"class":684},[449,8773,8774,8777,8779,8782],{"class":451,"line":4021},[449,8775,8776],{"class":714},"        \"foregroundImage\"",[449,8778,1482],{"class":684},[449,8780,8781],{"class":691},"\".\u002Fassets\u002Fadaptive-icon.png\"",[449,8783,779],{"class":684},[449,8785,8786,8789,8791],{"class":451,"line":4027},[449,8787,8788],{"class":714},"        \"backgroundColor\"",[449,8790,1482],{"class":684},[449,8792,7496],{"class":691},[449,8794,8795],{"class":451,"line":4032},[449,8796,8797],{"class":684},"      }\n",[449,8799,8800],{"class":451,"line":4038},[449,8801,7555],{"class":684},[449,8803,8804],{"class":451,"line":4044},[449,8805,4599],{"class":684},[449,8807,8808],{"class":451,"line":4049},[449,8809,4604],{"class":684},[32,8811,8356],{"id":8812},"preparing-app-bundles-and-apks",[21,8814,8815,8816,8819,8820,8823],{},"Google Play requires you to upload app bundles (",[40,8817,8818],{},".aab",") for internal testing and production, while APKs (",[40,8821,8822],{},".apk",") are still needed for some older devices and testing.",[2808,8825,8827],{"id":8826},"app-bundles-aab","App Bundles (.aab)",[21,8829,8830],{},"App bundles are the preferred format for Play Store distribution because:",[816,8832,8833,8839,8845],{},[386,8834,8835,8838],{},[151,8836,8837],{},"Smaller download sizes"," - Google strips out only the resources your device needs",[386,8840,8841,8844],{},[151,8842,8843],{},"Faster installs"," - Optimized for different device configurations",[386,8846,8847,8850],{},[151,8848,8849],{},"Better user experience"," - Reduced app size and faster updates",[2808,8852,8854],{"id":8853},"apks-apk","APKs (.apk)",[21,8856,8857],{},"APKs are still needed for:",[816,8859,8860,8866,8872],{},[386,8861,8862,8865],{},[151,8863,8864],{},"Older devices"," that don't support app bundles",[386,8867,8868,8871],{},[151,8869,8870],{},"Enterprise distribution"," in some cases",[386,8873,8874],{},[151,8875,8876],{},"Sideload testing",[2808,8878,8880],{"id":8879},"building-your-app","Building Your App",[6644,8882,8883],{"id":8304},"React Native",[48,8885,8887],{"className":839,"code":8886,"language":841,"meta":56,"style":56},"# Build app bundle for production\nnpx react-native bundle --platform android --dev false --entry-file index.js --bundle-output android\u002Fapp\u002Fsrc\u002Fmain\u002Fassets\u002Findex.android.bundle --sourcemap-output android\u002Fapp\u002Fsrc\u002Fmain\u002Fassets\u002Findex.android.bundle.map\n\n# Build APK\ncd android && .\u002Fgradlew assembleRelease\n\n# Build App Bundle\ncd android && .\u002Fgradlew bundleRelease\n",[40,8888,8889,8894,8935,8939,8944,8959,8963,8968],{"__ignoreMap":56},[449,8890,8891],{"class":451,"line":452},[449,8892,8893],{"class":705},"# Build app bundle for production\n",[449,8895,8896,8899,8902,8905,8908,8911,8914,8917,8920,8923,8926,8929,8932],{"class":451,"line":114},[449,8897,8898],{"class":721},"npx",[449,8900,8901],{"class":691}," react-native",[449,8903,8904],{"class":691}," bundle",[449,8906,8907],{"class":714}," --platform",[449,8909,8910],{"class":691}," android",[449,8912,8913],{"class":714}," --dev",[449,8915,8916],{"class":714}," false",[449,8918,8919],{"class":714}," --entry-file",[449,8921,8922],{"class":691}," index.js",[449,8924,8925],{"class":714}," --bundle-output",[449,8927,8928],{"class":691}," android\u002Fapp\u002Fsrc\u002Fmain\u002Fassets\u002Findex.android.bundle",[449,8930,8931],{"class":714}," --sourcemap-output",[449,8933,8934],{"class":691}," android\u002Fapp\u002Fsrc\u002Fmain\u002Fassets\u002Findex.android.bundle.map\n",[449,8936,8937],{"class":451,"line":111},[449,8938,700],{"emptyLinePlaceholder":130},[449,8940,8941],{"class":451,"line":468},[449,8942,8943],{"class":705},"# Build APK\n",[449,8945,8946,8948,8950,8953,8956],{"class":451,"line":474},[449,8947,4490],{"class":714},[449,8949,8910],{"class":691},[449,8951,8952],{"class":684}," && ",[449,8954,8955],{"class":721},".\u002Fgradlew",[449,8957,8958],{"class":691}," assembleRelease\n",[449,8960,8961],{"class":451,"line":480},[449,8962,700],{"emptyLinePlaceholder":130},[449,8964,8965],{"class":451,"line":486},[449,8966,8967],{"class":705},"# Build App Bundle\n",[449,8969,8970,8972,8974,8976,8978],{"class":451,"line":492},[449,8971,4490],{"class":714},[449,8973,8910],{"class":691},[449,8975,8952],{"class":684},[449,8977,8955],{"class":721},[449,8979,8980],{"class":691}," bundleRelease\n",[6644,8982,8983],{"id":8305},"Expo",[48,8985,8987],{"className":839,"code":8986,"language":841,"meta":56,"style":56},"# Build for production\nnpx expo export --platform android --dev false\n\n# Build APK\nnpx expo build:android --release\n\n# Build App Bundle\nnpx expo build:android --type app-bundle\n",[40,8988,8989,8994,9013,9017,9021,9033,9037,9041],{"__ignoreMap":56},[449,8990,8991],{"class":451,"line":452},[449,8992,8993],{"class":705},"# Build for production\n",[449,8995,8996,8998,9001,9004,9006,9008,9010],{"class":451,"line":114},[449,8997,8898],{"class":721},[449,8999,9000],{"class":691}," expo",[449,9002,9003],{"class":691}," export",[449,9005,8907],{"class":714},[449,9007,8910],{"class":691},[449,9009,8913],{"class":714},[449,9011,9012],{"class":714}," false\n",[449,9014,9015],{"class":451,"line":111},[449,9016,700],{"emptyLinePlaceholder":130},[449,9018,9019],{"class":451,"line":468},[449,9020,8943],{"class":705},[449,9022,9023,9025,9027,9030],{"class":451,"line":474},[449,9024,8898],{"class":721},[449,9026,9000],{"class":691},[449,9028,9029],{"class":691}," build:android",[449,9031,9032],{"class":714}," --release\n",[449,9034,9035],{"class":451,"line":480},[449,9036,700],{"emptyLinePlaceholder":130},[449,9038,9039],{"class":451,"line":486},[449,9040,8967],{"class":705},[449,9042,9043,9045,9047,9049,9052],{"class":451,"line":492},[449,9044,8898],{"class":721},[449,9046,9000],{"class":691},[449,9048,9029],{"class":691},[449,9050,9051],{"class":714}," --type",[449,9053,9054],{"class":691}," app-bundle\n",[32,9056,8362],{"id":9057},"setting-up-google-play-app-signing",[21,9059,9060],{},"Google Play App Signing automatically re-signs your app with Google's certificate when you upload to the Play Store, ensuring your app remains secure and up-to-date.",[2808,9062,9064],{"id":9063},"benefits","Benefits",[816,9066,9067,9073,9079],{},[386,9068,9069,9072],{},[151,9070,9071],{},"Automatic security updates"," - Google handles certificate rotation",[386,9074,9075,9078],{},[151,9076,9077],{},"Reduced maintenance"," - No need to manage your own signing keys",[386,9080,9081,9083],{},[151,9082,8849],{}," - Seamless updates without reinstallation",[2808,9085,9087],{"id":9086},"setup-process","Setup Process",[816,9089,9090,9096,9102],{},[386,9091,9092,9095],{},[151,9093,9094],{},"Enable Google Play App Signing"," in the Play Console",[386,9097,9098,9101],{},[151,9099,9100],{},"Upload your app's signing certificate"," (or let Google generate one)",[386,9103,9104],{},[151,9105,9106],{},"Configure your build to use Google's signing",[2808,9108,7605],{"id":7604},[21,9110,9111,9112,3070],{},"For React Native projects, update your ",[40,9113,9114],{},"android\u002Fapp\u002Fbuild.gradle",[48,9116,9120],{"className":9117,"code":9118,"language":9119,"meta":56,"style":56},"language-gradle shiki shiki-themes github-light github-dark","android {\n    defaultConfig {\n        applicationId \"com.yourcompany.yourapp\"\n        versionCode 1\n        versionName \"1.0.0\"\n    }\n    \n    signingConfigs {\n        release {\n            if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {\n                storeFile file(MYAPP_RELEASE_STORE_FILE)\n                storePassword MYAPP_RELEASE_STORE_PASSWORD\n                keyAlias MYAPP_RELEASE_KEY_ALIAS\n                keyPassword MYAPP_RELEASE_KEY_PASSWORD\n            } else {\n                \u002F\u002F Use Google's signing configuration\n                v1Enabled true\n                v2Enabled true\n            }\n        }\n    }\n    \n    buildTypes {\n        release {\n            signingConfig release.signingConfigs.debug\n        }\n    }\n}\n","gradle",[40,9121,9122,9127,9132,9137,9142,9147,9151,9156,9161,9166,9171,9176,9181,9186,9191,9196,9201,9206,9211,9215,9220,9224,9228,9233,9237,9242,9246,9250],{"__ignoreMap":56},[449,9123,9124],{"class":451,"line":452},[449,9125,9126],{},"android {\n",[449,9128,9129],{"class":451,"line":114},[449,9130,9131],{},"    defaultConfig {\n",[449,9133,9134],{"class":451,"line":111},[449,9135,9136],{},"        applicationId \"com.yourcompany.yourapp\"\n",[449,9138,9139],{"class":451,"line":468},[449,9140,9141],{},"        versionCode 1\n",[449,9143,9144],{"class":451,"line":474},[449,9145,9146],{},"        versionName \"1.0.0\"\n",[449,9148,9149],{"class":451,"line":480},[449,9150,7555],{},[449,9152,9153],{"class":451,"line":486},[449,9154,9155],{},"    \n",[449,9157,9158],{"class":451,"line":492},[449,9159,9160],{},"    signingConfigs {\n",[449,9162,9163],{"class":451,"line":498},[449,9164,9165],{},"        release {\n",[449,9167,9168],{"class":451,"line":133},[449,9169,9170],{},"            if (project.hasProperty('MYAPP_RELEASE_STORE_FILE')) {\n",[449,9172,9173],{"class":451,"line":509},[449,9174,9175],{},"                storeFile file(MYAPP_RELEASE_STORE_FILE)\n",[449,9177,9178],{"class":451,"line":318},[449,9179,9180],{},"                storePassword MYAPP_RELEASE_STORE_PASSWORD\n",[449,9182,9183],{"class":451,"line":520},[449,9184,9185],{},"                keyAlias MYAPP_RELEASE_KEY_ALIAS\n",[449,9187,9188],{"class":451,"line":526},[449,9189,9190],{},"                keyPassword MYAPP_RELEASE_KEY_PASSWORD\n",[449,9192,9193],{"class":451,"line":532},[449,9194,9195],{},"            } else {\n",[449,9197,9198],{"class":451,"line":2790},[449,9199,9200],{},"                \u002F\u002F Use Google's signing configuration\n",[449,9202,9203],{"class":451,"line":3511},[449,9204,9205],{},"                v1Enabled true\n",[449,9207,9208],{"class":451,"line":3526},[449,9209,9210],{},"                v2Enabled true\n",[449,9212,9213],{"class":451,"line":3531},[449,9214,5739],{},[449,9216,9217],{"class":451,"line":3536},[449,9218,9219],{},"        }\n",[449,9221,9222],{"class":451,"line":4009},[449,9223,7555],{},[449,9225,9226],{"class":451,"line":4015},[449,9227,9155],{},[449,9229,9230],{"class":451,"line":4021},[449,9231,9232],{},"    buildTypes {\n",[449,9234,9235],{"class":451,"line":4027},[449,9236,9165],{},[449,9238,9239],{"class":451,"line":4032},[449,9240,9241],{},"            signingConfig release.signingConfigs.debug\n",[449,9243,9244],{"class":451,"line":4038},[449,9245,9219],{},[449,9247,9248],{"class":451,"line":4044},[449,9249,7555],{},[449,9251,9252],{"class":451,"line":4049},[449,9253,4604],{},[2808,9255,7633],{"id":7632},[21,9257,9258,9259,9261],{},"Expo handles Google Play App Signing automatically. Just ensure your ",[40,9260,7368],{}," includes the necessary configuration:",[48,9263,9265],{"className":4518,"code":9264,"language":4521,"meta":56,"style":56},"\"android\": {\n  \"package\": \"com.yourcompany.yourapp\",\n  \"versionCode\": 1,\n  \"googleServicesFile\": \".\u002Fgoogle-services.json\",\n  \"adaptiveIcon\": {\n    \"foregroundImage\": \".\u002Fassets\u002Fadaptive-icon.png\",\n    \"backgroundColor\": \"#ffffff\"\n  }\n}\n",[40,9266,9267,9274,9285,9296,9307,9314,9325,9334,9338],{"__ignoreMap":56},[449,9268,9269,9272],{"class":451,"line":452},[449,9270,9271],{"class":691},"\"android\"",[449,9273,4548],{"class":684},[449,9275,9276,9279,9281,9283],{"class":451,"line":114},[449,9277,9278],{"class":714},"  \"package\"",[449,9280,1482],{"class":684},[449,9282,7517],{"class":691},[449,9284,779],{"class":684},[449,9286,9287,9290,9292,9294],{"class":451,"line":111},[449,9288,9289],{"class":714},"  \"versionCode\"",[449,9291,1482],{"class":684},[449,9293,4799],{"class":714},[449,9295,779],{"class":684},[449,9297,9298,9301,9303,9305],{"class":451,"line":468},[449,9299,9300],{"class":714},"  \"googleServicesFile\"",[449,9302,1482],{"class":684},[449,9304,8762],{"class":691},[449,9306,779],{"class":684},[449,9308,9309,9312],{"class":451,"line":474},[449,9310,9311],{"class":714},"  \"adaptiveIcon\"",[449,9313,4548],{"class":684},[449,9315,9316,9319,9321,9323],{"class":451,"line":480},[449,9317,9318],{"class":714},"    \"foregroundImage\"",[449,9320,1482],{"class":684},[449,9322,8781],{"class":691},[449,9324,779],{"class":684},[449,9326,9327,9330,9332],{"class":451,"line":486},[449,9328,9329],{"class":714},"    \"backgroundColor\"",[449,9331,1482],{"class":684},[449,9333,7496],{"class":691},[449,9335,9336],{"class":451,"line":492},[449,9337,4599],{"class":684},[449,9339,9340],{"class":451,"line":498},[449,9341,4604],{"class":684},[32,9343,8368],{"id":9344},"preparing-for-play-store-submission",[21,9346,9347],{},"Before you can submit your app to the Play Store, you need to ensure it's ready for review.",[2808,9349,9351],{"id":9350},"play-store-connect-requirements","Play Store Connect Requirements",[21,9353,7700],{},[816,9355,9356,9370,9379,9391],{},[386,9357,9358,9361],{},[151,9359,9360],{},"Package Name",[383,9362,9363,9366],{},[386,9364,9365],{},"Must match the one registered in the Play Console",[386,9367,7715,9368],{},[40,9369,7718],{},[386,9371,9372,9374],{},[151,9373,7723],{},[383,9375,9376],{},[386,9377,9378],{},"Must follow semantic versioning (major.minor.patch)",[386,9380,9381,9384],{},[151,9382,9383],{},"Version Code",[383,9385,9386,9388],{},[386,9387,7728],{},[386,9389,9390],{},"Should increment with each update",[386,9392,9393,9395],{},[151,9394,7748],{},[383,9396,9397,9399],{},[386,9398,7753],{},[386,9400,7756],{},[2808,9402,7760],{"id":7759},[21,9404,7763],{},[816,9406,9407,9420,9435,9451],{},[386,9408,9409,9411],{},[151,9410,7770],{},[383,9412,9413,9416,9418],{},[386,9414,9415],{},"512×512 pixels (PNG)",[386,9417,7778],{},[386,9419,7781],{},[386,9421,9422,9424],{},[151,9423,7786],{},[383,9425,9426,9429,9432],{},[386,9427,9428],{},"Various screen sizes and orientations",[386,9430,9431],{},"Include at least 2-3 screenshots",[386,9433,9434],{},"Show key features and user interface",[386,9436,9437,9439],{},[151,9438,7802],{},[383,9440,9441,9444,9447,9449],{},[386,9442,9443],{},"Brief description: 20-30 characters",[386,9445,9446],{},"Full description: 100-500 characters",[386,9448,7810],{},[386,9450,7813],{},[386,9452,9453,9455],{},[151,9454,7818],{},[383,9456,9457,9459],{},[386,9458,7823],{},[386,9460,9461],{},"Must be accessible from the Play Store listing",[2808,9463,7830],{"id":7829},[21,9465,7833],{},[816,9467,9468,9478,9489],{},[386,9469,9470,9472],{},[151,9471,7840],{},[383,9473,9474,9476],{},[386,9475,7845],{},[386,9477,7848],{},[386,9479,9480,9482],{},[151,9481,7853],{},[383,9483,9484,9486],{},[386,9485,7858],{},[386,9487,9488],{},"Test with different Android versions",[386,9490,9491,9494],{},[151,9492,9493],{},"Google Play App Testing",[383,9495,9496,9499],{},[386,9497,9498],{},"Use Google Play's internal testing groups",[386,9500,7874],{},[32,9502,8374],{"id":9503},"submitting-to-the-play-store",[21,9505,9506],{},"Once your app is ready, you can submit it to the Play Store.",[2808,9508,7884],{"id":7883},[816,9510,9511,9530,9541,9558,9573,9585],{},[386,9512,9513,9516],{},[151,9514,9515],{},"Create App in Play Console",[383,9517,9518,9524,9527],{},[386,9519,7896,9520],{},[3861,9521,9523],{"href":8449,"rel":9522},[3865],"Google Play Console",[386,9525,9526],{},"Click \"Add new app\"",[386,9528,9529],{},"Enter your app's package name",[386,9531,9532,9534],{},[151,9533,7912],{},[383,9535,9536,9538],{},[386,9537,7917],{},[386,9539,9540],{},"Upload the .aab file (preferred) or .apk",[386,9542,9543,9545],{},[151,9544,7925],{},[383,9546,9547,9550,9553,9555],{},[386,9548,9549],{},"App title, short description, full description",[386,9551,9552],{},"Category and tags",[386,9554,7936],{},[386,9556,9557],{},"App icon and screenshots",[386,9559,9560,9563],{},[151,9561,9562],{},"Set Pricing and Distribution",[383,9564,9565,9568,9570],{},[386,9566,9567],{},"Choose your pricing model (free or paid)",[386,9569,7949],{},[386,9571,9572],{},"Configure device compatibility",[386,9574,9575,9578],{},[151,9576,9577],{},"Review and Submit",[383,9579,9580,9582],{},[386,9581,7959],{},[386,9583,9584],{},"Submit for review",[386,9586,9587,9589],{},[151,9588,7967],{},[383,9590,9591,9594],{},[386,9592,9593],{},"Google typically reviews apps within 24-72 hours",[386,9595,9596],{},"You can track the review status in Play Console",[2808,9598,7979],{"id":7978},[383,9600,9601,9614],{},[386,9602,9603,9606],{},[151,9604,9605],{},"Play Store Rejection Reasons",[383,9607,9608,9610,9612],{},[386,9609,7991],{},[386,9611,7994],{},[386,9613,7997],{},[386,9615,9616,9618],{},[151,9617,8002],{},[383,9619,9620,9622],{},[386,9621,8007],{},[386,9623,9624],{},"Contact Google support if there are delays",[32,9626,7144],{"id":8013},[21,9628,9629],{},"After your app is approved, you need to manage its presence on the Play Store.",[2808,9631,8020],{"id":8019},[816,9633,9634,9645,9655],{},[386,9635,9636,9638],{},[151,9637,8027],{},[383,9639,9640,9643],{},[386,9641,9642],{},"Set your app to \"Published\"",[386,9644,8035],{},[386,9646,9647,9649],{},[151,9648,8040],{},[383,9650,9651,9653],{},[386,9652,8045],{},[386,9654,8048],{},[386,9656,9657,9659],{},[151,9658,8053],{},[383,9660,9661,9663,9665],{},[386,9662,8058],{},[386,9664,8061],{},[386,9666,8064],{},[2808,9668,9670],{"id":9669},"play-store-optimization-aso","Play Store Optimization (ASO)",[21,9672,8071],{},[816,9674,9675,9685,9696],{},[386,9676,9677,9679],{},[151,9678,8078],{},[383,9680,9681,9683],{},[386,9682,8083],{},[386,9684,8086],{},[386,9686,9687,9689],{},[151,9688,8091],{},[383,9690,9691,9694],{},[386,9692,9693],{},"Use compelling app title",[386,9695,8099],{},[386,9697,9698,9700],{},[151,9699,8104],{},[383,9701,9702,9704],{},[386,9703,8109],{},[386,9705,8112],{},[32,9707,7150],{"id":8115},[2808,9709,8119],{"id":8118},[816,9711,9712,9725,9735,9746],{},[386,9713,9714,9717],{},[151,9715,9716],{},"App Signing Issues",[383,9718,9719,9722],{},[386,9720,9721],{},"Use Google Play App Signing",[386,9723,9724],{},"Ensure proper certificate management",[386,9726,9727,9729],{},[151,9728,8139],{},[383,9730,9731,9733],{},[386,9732,8144],{},[386,9734,8147],{},[386,9736,9737,9740],{},[151,9738,9739],{},"Play Store Rejection",[383,9741,9742,9744],{},[386,9743,7991],{},[386,9745,8159],{},[386,9747,9748,9750],{},[151,9749,8164],{},[383,9751,9752,9754],{},[386,9753,8169],{},[386,9755,8172],{},[2808,9757,8176],{"id":8175},[21,9759,8179],{},[816,9761,9762,9772,9780],{},[386,9763,9764,9767],{},[151,9765,9766],{},"Google Play Developer Support",[383,9768,9769],{},[386,9770,9771],{},"Submit a support request through Play Console",[386,9773,9774,9776],{},[151,9775,8196],{},[383,9777,9778],{},[386,9779,8201],{},[386,9781,9782,9784],{},[151,9783,8206],{},[383,9785,9786],{},[386,9787,8211],{},[32,9789,3681],{"id":3680},[21,9791,9792],{},"Deploying a React Native or Expo app to the Google Play Store opens up your application to billions of Android users. While the process has some differences from iOS deployment, it follows a similar pattern of preparation, testing, and submission.",[21,9794,9795],{},"Remember that the Play Store submission process is thorough, but with proper preparation and attention to detail, you can get your app approved and available to users worldwide.",[21,9797,8222],{},[816,9799,9800,9804,9808,9812],{},[386,9801,9802,8230],{},[151,9803,8229],{},[386,9805,9806,8236],{},[151,9807,8235],{},[386,9809,9810,8242],{},[151,9811,8241],{},[386,9813,9814,8248],{},[151,9815,8247],{},[21,9817,9818],{},"Good luck with your Play Store deployment!",[73,9820],{},[21,9822,9823],{},[156,9824,8258],{},[3728,9826,9827],{},"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 .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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":56,"searchDepth":111,"depth":111,"links":9829},[9830,9831,9832,9836,9843,9851,9857,9862,9866,9870,9874],{"id":332,"depth":114,"text":333},{"id":7105,"depth":114,"text":7106},{"id":8389,"depth":114,"text":8344,"children":9833},[9834,9835],{"id":7181,"depth":111,"text":7182},{"id":8441,"depth":111,"text":8442},{"id":8465,"depth":114,"text":8350,"children":9837},[9838,9839],{"id":7253,"depth":111,"text":7254},{"id":8545,"depth":111,"text":8546,"children":9840},[9841,9842],{"id":7337,"depth":468,"text":7338},{"id":7361,"depth":468,"text":7362},{"id":8812,"depth":114,"text":8356,"children":9844},[9845,9846,9847],{"id":8826,"depth":111,"text":8827},{"id":8853,"depth":111,"text":8854},{"id":8879,"depth":111,"text":8880,"children":9848},[9849,9850],{"id":8304,"depth":468,"text":8883},{"id":8305,"depth":468,"text":8983},{"id":9057,"depth":114,"text":8362,"children":9852},[9853,9854,9855,9856],{"id":9063,"depth":111,"text":9064},{"id":9086,"depth":111,"text":9087},{"id":7604,"depth":111,"text":7605},{"id":7632,"depth":111,"text":7633},{"id":9344,"depth":114,"text":8368,"children":9858},[9859,9860,9861],{"id":9350,"depth":111,"text":9351},{"id":7759,"depth":111,"text":7760},{"id":7829,"depth":111,"text":7830},{"id":9503,"depth":114,"text":8374,"children":9863},[9864,9865],{"id":7883,"depth":111,"text":7884},{"id":7978,"depth":111,"text":7979},{"id":8013,"depth":114,"text":7144,"children":9867},[9868,9869],{"id":8019,"depth":111,"text":8020},{"id":9669,"depth":111,"text":9670},{"id":8115,"depth":114,"text":7150,"children":9871},[9872,9873],{"id":8118,"depth":111,"text":8119},{"id":8175,"depth":111,"text":8176},{"id":3680,"depth":114,"text":3681},"A step-by-step guide to publishing a React Native or Expo app on the Google Play Store — Play Console setup, AAB vs APK, Google Play App Signing, store listing metadata, and ongoing release management.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1607252650355-f7fd0460ccdb",[8304,8305,9878,9879,8308,8309,9880,8310],"android","play-store","app-bundle",{},"\u002Fblog\u002Fdeploying-react-native-expo-to-play-store",{"title":8320,"description":9875},"blog\u002Fdeploying-react-native-expo-to-play-store","uGXVqWvQBKoV3g9YIZ2k4NGT4XkhzdGlgYwG9Wbkafc",{"id":9887,"title":9888,"author":9889,"body":9890,"canonical":116,"categories":116,"date":10415,"description":10416,"excerpt":10417,"extension":119,"img":10433,"keywords":10434,"language":3788,"meta":10439,"navigation":130,"ogImage":116,"path":10440,"rating":474,"readTime":133,"seo":10441,"stem":10442,"tag":3793,"__hash__":10443},"blog\u002Fblog\u002Fdioxus.md","Getting Started with Dioxus 0.7: A Cross-Platform Rust UI Framework",{"name":9,"img":11,"website":327},{"type":13,"value":9891,"toc":10394},[9892,9894,9913,9916,9918,9922,9939,9951,9958,9960,9964,9967,9998,10006,10008,10012,10021,10025,10055,10065,10071,10073,10077,10088,10097,10100,10102,10106,10113,10117,10143,10146,10148,10152,10167,10171,10249,10259,10261,10265,10276,10280,10309,10315,10317,10321,10332,10336,10347,10354,10356,10360,10371,10378,10380,10382,10385,10392],[16,9893,333],{"id":332},[21,9895,9896,9899,9900,9903,9904,9907,9908],{},[151,9897,9898],{},"Dioxus 0.7"," is the latest stable version of a powerful cross-platform UI framework built in ",[151,9901,9902],{},"Rust",". With Dioxus, developers can use a single Rust codebase to build applications that run on the ",[151,9905,9906],{},"web, desktop, and mobile platforms"," — all while leveraging Rust’s performance, strong type safety, and modern tooling. ",[9909,9910,9912],"content-reference",{"index":9911},"0","oaicite:0",[21,9914,9915],{},"In this post, we’ll explore what Dioxus is, how it works, and how you can start building your first app using its declarative UI syntax and cross-platform capabilities.",[16,9917,4373],{"id":4372},[32,9919,9921],{"id":9920},"what-is-dioxus","What Is Dioxus?",[21,9923,9924,9925,9928,9929,9932,9933,9935,9936],{},"At its core, ",[151,9926,9927],{},"Dioxus"," is a developer-focused UI framework designed to simplify building interactive applications while offering the performance and safety benefits of Rust. It’s similar in spirit to frameworks like ",[151,9930,9931],{},"Flutter"," or ",[151,9934,8883],{},", but deeply rooted in the Rust ecosystem. ",[9909,9937,9938],{"index":4799},"oaicite:1",[25,9940,9941],{"type":363},[21,9942,9943,9944,9947,9948],{},"Dioxus lets you build UI with familiar ",[151,9945,9946],{},"HTML and CSS"," semantics using Rust, and compile to web (WASM), desktop, and mobile targets from a single codebase. ",[9909,9949,9950],{"index":4827},"oaicite:2",[21,9952,9953,9954,9957],{},"The framework’s syntax is inspired by modern declarative UI paradigms, meaning you describe ",[156,9955,9956],{},"what your UI should look like"," instead of how to construct elements imperatively.",[73,9959],{},[32,9961,9963],{"id":9962},"why-use-dioxus","Why Use Dioxus?",[21,9965,9966],{},"Developers gravitate toward Dioxus because it offers:",[383,9968,9969,9975,9980,9986,9992],{},[386,9970,9971,9974],{},[151,9972,9973],{},"Cross-platform support"," (web, desktop, mobile)",[386,9976,9977],{},[151,9978,9979],{},"Rust’s performance and safety guarantees",[386,9981,9982,9985],{},[151,9983,9984],{},"RSX",", a macro-based declarative UI syntax",[386,9987,9988,9991],{},[151,9989,9990],{},"Hot-reloading"," for faster developer feedback",[386,9993,9994,9995],{},"Integrated tools for building, bundling, and deploying apps ",[9909,9996,9997],{"index":4850},"oaicite:3",[25,9999,10000],{"type":27},[21,10001,10002,10003],{},"Learning Rust’s syntax and its borrow checker can have a steeper learning curve if you’re coming from JavaScript or TypeScript — but the payoff is safer, faster applications. ",[9909,10004,10005],{"index":4874},"oaicite:4",[73,10007],{},[32,10009,10011],{"id":10010},"core-concepts-rsx-and-components","Core Concepts: RSX and Components",[21,10013,10014,10015,10017,10018],{},"Dioxus uses ",[151,10016,9984],{}," — a Rust macro that resembles JSX (from React) — to describe UI in a declarative way. Instead of manually creating and updating DOM elements, you declare what your components should render. ",[9909,10019,10020],{"index":4898},"oaicite:5",[2808,10022,10024],{"id":10023},"rsx-example","RSX Example",[48,10026,10030],{"className":10027,"code":10028,"language":10029,"meta":56,"style":56},"language-rust shiki shiki-themes github-light github-dark","fn App() -> Element {\n    rsx! {\n        div { \"Hello, world!\" }\n    }\n}\n","rust",[40,10031,10032,10037,10042,10047,10051],{"__ignoreMap":56},[449,10033,10034],{"class":451,"line":452},[449,10035,10036],{},"fn App() -> Element {\n",[449,10038,10039],{"class":451,"line":114},[449,10040,10041],{},"    rsx! {\n",[449,10043,10044],{"class":451,"line":111},[449,10045,10046],{},"        div { \"Hello, world!\" }\n",[449,10048,10049],{"class":451,"line":468},[449,10050,7555],{},[449,10052,10053],{"class":451,"line":474},[449,10054,4604],{},[21,10056,10057,10058,10064],{},"RSX syntax feels familiar to developers experienced with JSX or other declarative UI languages, but with the robustness of Rust’s compiler and type system powering it. (",[3861,10059,10063],{"href":10060,"rel":10061,"title":10062},"https:\u002F\u002Fdioxuslabs.com\u002Flearn\u002F0.7\u002Ftutorial\u002Frsx\u002F?utm_source=chatgpt.com",[3865],"Dioxus | Fullstack crossplatform app framework for Rust","Dioxus Labs",")\n::",[21,10066,10067,10068,827],{},"Hot-reloading is built in — when you save changes, your app updates instantly without a full rebuild. (",[3861,10069,10063],{"href":10060,"rel":10070,"title":10062},[3865],[73,10072],{},[32,10074,10076],{"id":10075},"state-management","State Management",[21,10078,10079,10080,10083,10084,827],{},"Dioxus has a built-in reactivity model using ",[151,10081,10082],{},"signals",", allowing your UI to update when underlying data changes. Signals wrap ordinary Rust values and trigger component re-renders whenever they’re updated. (",[3861,10085,10063],{"href":10086,"rel":10087,"title":10062},"https:\u002F\u002Fdioxuslabs.com\u002Flearn\u002F0.7\u002Ftutorial\u002Fstate\u002F?utm_source=chatgpt.com",[3865],[48,10089,10091],{"className":10027,"code":10090,"language":10029,"meta":56,"style":56},"let mut count = use_signal(|| 0);\n",[40,10092,10093],{"__ignoreMap":56},[449,10094,10095],{"class":451,"line":452},[449,10096,10090],{},[21,10098,10099],{},"This simple reactive primitive brings interactivity to your applications with minimal boilerplate.",[73,10101],{},[32,10103,10105],{"id":10104},"building-a-complete-app-the-hotdog-tutorial","Building a Complete App: The HotDog Tutorial",[21,10107,10108,10109,827],{},"To help developers get started, Dioxus provides a comprehensive tutorial where you’ll build a small cross-platform app called “HotDog” — a playful dog photo app that lets users swipe and save images. (",[3861,10110,10063],{"href":10111,"rel":10112,"title":10062},"https:\u002F\u002Fdioxuslabs.com\u002Flearn\u002F0.7\u002Ftutorial\u002F?utm_source=chatgpt.com",[3865],[2808,10114,10116],{"id":10115},"what-youll-learn","What You’ll Learn",[383,10118,10119,10122,10125,10128,10131,10134,10137],{},[386,10120,10121],{},"Tooling setup",[386,10123,10124],{},"Creating your first app project",[386,10126,10127],{},"Working with components",[386,10129,10130],{},"Adding state and interactivity",[386,10132,10133],{},"Fetching remote data",[386,10135,10136],{},"Routing and multi-page UI",[386,10138,10139,10140,827],{},"Bundling and deployment (",[3861,10141,10063],{"href":10111,"rel":10142,"title":10062},[3865],[21,10144,10145],{},"This guided experience walks you from zero to a full app that runs across platforms, showing how Dioxus handles fullstack needs efficiently.",[73,10147],{},[32,10149,10151],{"id":10150},"fetching-data-and-resources","Fetching Data and Resources",[21,10153,10154,10155,10158,10159,10162,10163,827],{},"Dioxus doesn’t have built-in data fetching utilities, so you’ll often leverage existing Rust crates like ",[40,10156,10157],{},"reqwest"," for HTTP client functionality and ",[40,10160,10161],{},"serde"," for JSON deserialization. (",[3861,10164,10063],{"href":10165,"rel":10166,"title":10062},"https:\u002F\u002Fdioxuslabs.com\u002Flearn\u002F0.7\u002Ftutorial\u002Fdata_fetching?utm_source=chatgpt.com",[3865],[2808,10168,10170],{"id":10169},"example-fetching-and-displaying-a-random-dog-image","Example: Fetching and Displaying a Random Dog Image",[48,10172,10174],{"className":10027,"code":10173,"language":10029,"meta":56,"style":56},"#[component]\nfn DogView() -> Element {\n    let mut img_src = use_resource(|| async move {\n        reqwest::get(\"https:\u002F\u002Fdog.ceo\u002Fapi\u002Fbreeds\u002Fimage\u002Frandom\")\n            .await.unwrap()\n            .json::\u003CDogApi>()\n            .await.unwrap()\n            .message\n    });\n\n    rsx! {\n        div {\n            img { src: img_src.cloned().unwrap_or_default() }\n        }\n    }\n}\n",[40,10175,10176,10181,10186,10191,10196,10201,10206,10210,10215,10219,10223,10227,10232,10237,10241,10245],{"__ignoreMap":56},[449,10177,10178],{"class":451,"line":452},[449,10179,10180],{},"#[component]\n",[449,10182,10183],{"class":451,"line":114},[449,10184,10185],{},"fn DogView() -> Element {\n",[449,10187,10188],{"class":451,"line":111},[449,10189,10190],{},"    let mut img_src = use_resource(|| async move {\n",[449,10192,10193],{"class":451,"line":468},[449,10194,10195],{},"        reqwest::get(\"https:\u002F\u002Fdog.ceo\u002Fapi\u002Fbreeds\u002Fimage\u002Frandom\")\n",[449,10197,10198],{"class":451,"line":474},[449,10199,10200],{},"            .await.unwrap()\n",[449,10202,10203],{"class":451,"line":480},[449,10204,10205],{},"            .json::\u003CDogApi>()\n",[449,10207,10208],{"class":451,"line":486},[449,10209,10200],{},[449,10211,10212],{"class":451,"line":492},[449,10213,10214],{},"            .message\n",[449,10216,10217],{"class":451,"line":498},[449,10218,3484],{},[449,10220,10221],{"class":451,"line":133},[449,10222,700],{"emptyLinePlaceholder":130},[449,10224,10225],{"class":451,"line":509},[449,10226,10041],{},[449,10228,10229],{"class":451,"line":318},[449,10230,10231],{},"        div {\n",[449,10233,10234],{"class":451,"line":520},[449,10235,10236],{},"            img { src: img_src.cloned().unwrap_or_default() }\n",[449,10238,10239],{"class":451,"line":526},[449,10240,9219],{},[449,10242,10243],{"class":451,"line":532},[449,10244,7555],{},[449,10246,10247],{"class":451,"line":2790},[449,10248,4604],{},[21,10250,10251,10252,10255,10256,827],{},"This pattern shows how Dioxus integrates asynchronous logic with its reactive system via ",[40,10253,10254],{},"use_resource",". (",[3861,10257,10063],{"href":10165,"rel":10258,"title":10062},[3865],[73,10260],{},[32,10262,10264],{"id":10263},"routing-and-navigation","Routing and Navigation",[21,10266,10267,10268,10271,10272,827],{},"Most real applications need multiple screens. Dioxus includes a ",[151,10269,10270],{},"type-safe router"," that maps enum variants to paths. (",[3861,10273,10063],{"href":10274,"rel":10275,"title":10062},"https:\u002F\u002Fdioxuslabs.com\u002Flearn\u002F0.7\u002Ftutorial\u002Frouting\u002F?utm_source=chatgpt.com",[3865],[2808,10277,10279],{"id":10278},"routing-example","Routing Example",[48,10281,10283],{"className":10027,"code":10282,"language":10029,"meta":56,"style":56},"#[derive(Routable, Clone, PartialEq)]\nenum Route {\n    #[route(\"\u002F\")]\n    DogView,\n}\n",[40,10284,10285,10290,10295,10300,10305],{"__ignoreMap":56},[449,10286,10287],{"class":451,"line":452},[449,10288,10289],{},"#[derive(Routable, Clone, PartialEq)]\n",[449,10291,10292],{"class":451,"line":114},[449,10293,10294],{},"enum Route {\n",[449,10296,10297],{"class":451,"line":111},[449,10298,10299],{},"    #[route(\"\u002F\")]\n",[449,10301,10302],{"class":451,"line":468},[449,10303,10304],{},"    DogView,\n",[449,10306,10307],{"class":451,"line":474},[449,10308,4604],{},[21,10310,10311,10312,827],{},"Once configured, navigating between pages is seamless and safe at compile time, reducing runtime bugs. (",[3861,10313,10063],{"href":10274,"rel":10314,"title":10062},[3865],[73,10316],{},[32,10318,10320],{"id":10319},"fullstack-capabilities","Fullstack Capabilities",[21,10322,10323,10324,10327,10328,827],{},"Dioxus doesn’t just handle the frontend—",[151,10325,10326],{},"it also integrates backend logic directly in Rust",". You can combine frontend UI code with backend server functions, state management, and SSR capabilities via the fullstack features. (",[3861,10329,10063],{"href":10330,"rel":10331,"title":10062},"https:\u002F\u002Fdioxuslabs.com\u002Flearn\u002F0.7\u002Fessentials\u002Ffullstack\u002F?utm_source=chatgpt.com",[3865],[2808,10333,10335],{"id":10334},"fullstack-features","Fullstack Features",[383,10337,10338,10341,10344],{},[386,10339,10340],{},"Server functions callable from the UI",[386,10342,10343],{},"Integration with backend frameworks",[386,10345,10346],{},"Static and dynamic content rendering\n::",[21,10348,10349,10350,10353],{},"This makes Dioxus ideal for ",[151,10351,10352],{},"end-to-end Rust applications"," where both client and server share a language and ecosystem.",[73,10355],{},[32,10357,10359],{"id":10358},"why-dioxus-matters","Why Dioxus Matters",[21,10361,10362,10363,10366,10367,827],{},"With Dioxus 0.7, Rust developers finally have a ",[151,10364,10365],{},"mature, ergonomic framework"," for building cross-platform UI applications that don’t compromise performance or safety. The combination of Rust’s compile-time guarantees, declarative UI, and comprehensive tooling makes it a compelling choice for modern application development. (",[3861,10368,10063],{"href":10369,"rel":10370,"title":10062},"https:\u002F\u002Fdioxuslabs.com\u002Flearn\u002F0.7\u002F?utm_source=chatgpt.com",[3865],[21,10372,10373,10374,10377],{},"Whether you’re building a ",[151,10375,10376],{},"mobile app, desktop client, or WASM-based web UI",", Dioxus helps you maintain a single codebase across all platforms without sacrificing developer productivity.",[73,10379],{},[16,10381,3681],{"id":3680},[21,10383,10384],{},"Dioxus 0.7 is more than just a UI framework — it’s a unified platform for building fullstack, cross-platform applications entirely in Rust. With intuitive UI syntax, powerful state management, routing, backend integration, and solid tooling, it’s positioned as a strong contender for modern app development in Rust.",[21,10386,10387,10388,10391],{},"If you’re ready to build ",[151,10389,10390],{},"fast, safe, and beautiful applications",", Dioxus is a framework worth exploring. 🚀",[3728,10393,4140],{},{"title":56,"searchDepth":111,"depth":111,"links":10395},[10396,10397,10398,10401,10402,10405,10408,10411,10414],{"id":9920,"depth":114,"text":9921},{"id":9962,"depth":114,"text":9963},{"id":10010,"depth":114,"text":10011,"children":10399},[10400],{"id":10023,"depth":111,"text":10024},{"id":10075,"depth":114,"text":10076},{"id":10104,"depth":114,"text":10105,"children":10403},[10404],{"id":10115,"depth":111,"text":10116},{"id":10150,"depth":114,"text":10151,"children":10406},[10407],{"id":10169,"depth":111,"text":10170},{"id":10263,"depth":114,"text":10264,"children":10409},[10410],{"id":10278,"depth":111,"text":10279},{"id":10319,"depth":114,"text":10320,"children":10412},[10413],{"id":10334,"depth":111,"text":10335},{"id":10358,"depth":114,"text":10359},"2026-01-10","Explore Dioxus 0.7 — what it is, how it works across platforms using Rust, and why it’s an exciting choice for building modern UI applications.",{"type":13,"value":10418},[10419,10421,10431],[16,10420,333],{"id":332},[21,10422,10423,9899,10425,9903,10427,9907,10429],{},[151,10424,9898],{},[151,10426,9902],{},[151,10428,9906],{},[9909,10430,9912],{"index":9911},[21,10432,9915],{},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1590608897129-79d5d1839d6b",[10435,10029,10436,10437,10438],"dioxus","cross-platform","ui","fullstack",{},"\u002Fblog\u002Fdioxus",{"title":9888,"description":10416},"blog\u002Fdioxus","ik7qwhV3ti2_3_TQgouu70cD-IzsGInsTWSbYMdqmWU",{"id":10445,"title":10446,"author":10447,"body":10448,"canonical":116,"categories":116,"date":7042,"description":14565,"excerpt":14566,"extension":119,"img":7074,"keywords":14584,"language":3788,"meta":14590,"navigation":130,"ogImage":116,"path":14591,"rating":474,"readTime":318,"seo":14592,"stem":14593,"tag":3793,"__hash__":14594},"blog\u002Fblog\u002Fdrizzle-nitro-api.md","Building a Production API with Drizzle ORM + Nitro: A Complete Guide",{"name":9,"img":11,"website":327},{"type":13,"value":10449,"toc":14540},[10450,10452,10459,10466,10469,10476,10480,10483,10515,10520,10522,10524,10528,10531,10542,10544,10548,10551,10613,10618,10620,10624,10627,10632,10719,10721,10725,10728,10748,10753,11393,11400,11402,11406,11409,11414,11703,11705,11709,11712,11717,12147,12149,12153,12157,12162,12252,12256,12261,12487,12491,12494,12498,13414,13418,13423,13923,13927,13932,14154,14156,14160,14163,14200,14215,14217,14221,14224,14236,14240,14258,14262,14274,14278,14345,14349,14383,14385,14387,14389,14406,14408,14496,14498,14500,14507,14510,14530,14537],[16,10451,333],{"id":332},[21,10453,10454,10455,10458],{},"In our ",[151,10456,10457],{},"BrewStop food truck series",", we used Unstorage for data persistence—an excellent choice for prototypes and simple applications. But when you're building for production, you need something more robust: a proper database with type-safe queries, migrations, and scalable architecture.",[21,10460,10461,10462,10465],{},"That's where ",[151,10463,10464],{},"Drizzle ORM"," comes in.",[21,10467,10468],{},"Drizzle is a lightweight, TypeScript-first ORM that gives you the control of raw SQL with the ergonomics of an ORM. It pairs beautifully with Nitro, offering a production-ready data layer that won't slow you down.",[21,10470,10471,10472,10475],{},"In this post, we'll build a complete database-backed API using ",[151,10473,10474],{},"Drizzle ORM + Nitro","—covering setup, schema design, queries, and deployment.",[16,10477,10479],{"id":10478},"why-drizzle-orm","Why Drizzle ORM?",[21,10481,10482],{},"Before we dive in, let's talk about why Drizzle is worth your attention:",[383,10484,10485,10491,10497,10503,10509],{},[386,10486,10487,10490],{},[151,10488,10489],{},"Type-safe queries"," — TypeScript understands your schema end-to-end",[386,10492,10493,10496],{},[151,10494,10495],{},"Lightweight"," — No heavy runtime, compiles to efficient SQL",[386,10498,10499,10502],{},[151,10500,10501],{},"Portable"," — Works with PostgreSQL, MySQL, SQLite, and more",[386,10504,10505,10508],{},[151,10506,10507],{},"Migration support"," — First-class migration tooling",[386,10510,10511,10514],{},[151,10512,10513],{},"Zero dependencies"," — Doesn't pull in unnecessary bloat",[25,10516,10517],{"type":363},[21,10518,10519],{},"Drizzle is designed to feel like you're writing SQL—but with full type safety and IDE autocomplete.",[73,10521],{},[16,10523,4373],{"id":4372},[32,10525,10527],{"id":10526},"prerequisites","Prerequisites",[21,10529,10530],{},"This guide assumes you have:",[383,10532,10533,10536,10539],{},[386,10534,10535],{},"Node.js 18+ installed",[386,10537,10538],{},"Basic knowledge of TypeScript",[386,10540,10541],{},"A PostgreSQL or SQLite database (we'll use SQLite for simplicity, but the code is mostly identical)",[73,10543],{},[32,10545,10547],{"id":10546},"step-1-project-setup","Step 1: Project Setup",[21,10549,10550],{},"Let's start by creating a new Nitro project and installing Drizzle.",[48,10552,10554],{"className":839,"code":10553,"filename":4384,"language":841,"meta":56,"style":56},"# Create a new Nitro project\nnpx create-nitro-app brewstop-api-v2\ncd brewstop-api-v2\n\n# Install Drizzle ORM and SQLite driver\nnpm install drizzle-orm better-sqlite3\nnpm install -D drizzle-kit @types\u002Fbetter-sqlite3\n",[40,10555,10556,10561,10571,10577,10581,10586,10598],{"__ignoreMap":56},[449,10557,10558],{"class":451,"line":452},[449,10559,10560],{"class":705},"# Create a new Nitro project\n",[449,10562,10563,10565,10568],{"class":451,"line":114},[449,10564,8898],{"class":721},[449,10566,10567],{"class":691}," create-nitro-app",[449,10569,10570],{"class":691}," brewstop-api-v2\n",[449,10572,10573,10575],{"class":451,"line":111},[449,10574,4490],{"class":714},[449,10576,10570],{"class":691},[449,10578,10579],{"class":451,"line":468},[449,10580,700],{"emptyLinePlaceholder":130},[449,10582,10583],{"class":451,"line":474},[449,10584,10585],{"class":705},"# Install Drizzle ORM and SQLite driver\n",[449,10587,10588,10590,10592,10595],{"class":451,"line":480},[449,10589,3703],{"class":721},[449,10591,3706],{"class":691},[449,10593,10594],{"class":691}," drizzle-orm",[449,10596,10597],{"class":691}," better-sqlite3\n",[449,10599,10600,10602,10604,10607,10610],{"class":451,"line":486},[449,10601,3703],{"class":721},[449,10603,3706],{"class":691},[449,10605,10606],{"class":714}," -D",[449,10608,10609],{"class":691}," drizzle-kit",[449,10611,10612],{"class":691}," @types\u002Fbetter-sqlite3\n",[25,10614,10615],{"type":27},[21,10616,10617],{},"Make sure to use the appropriate driver for your database (pg for PostgreSQL, mysql2 for MySQL, etc.). We'll use better-sqlite3 for this example.",[73,10619],{},[32,10621,10623],{"id":10622},"step-2-configure-drizzle","Step 2: Configure Drizzle",[21,10625,10626],{},"Create a configuration file for Drizzle. This tells Drizzle where your database is and how to handle migrations.",[21,10628,4698,10629,3070],{},[40,10630,10631],{},"drizzle.config.ts",[48,10633,10635],{"className":670,"code":10634,"filename":10631,"language":673,"meta":56,"style":56},"import { defineConfig } from \"drizzle-kit\";\n\nexport default defineConfig({\n  schema: \".\u002Fserver\u002Fdb\u002Fschema.ts\",\n  out: \".\u002Fdrizzle\",\n  dialect: \"sqlite\",\n  dbCredentials: {\n    url: \".\u002Fbrewstop.db\",\n  },\n});\n",[40,10636,10637,10651,10655,10666,10676,10686,10696,10701,10711,10715],{"__ignoreMap":56},[449,10638,10639,10641,10644,10646,10649],{"class":451,"line":452},[449,10640,681],{"class":680},[449,10642,10643],{"class":684}," { defineConfig } ",[449,10645,688],{"class":680},[449,10647,10648],{"class":691}," \"drizzle-kit\"",[449,10650,695],{"class":684},[449,10652,10653],{"class":451,"line":114},[449,10654,700],{"emptyLinePlaceholder":130},[449,10656,10657,10659,10661,10664],{"class":451,"line":111},[449,10658,4629],{"class":680},[449,10660,4632],{"class":680},[449,10662,10663],{"class":721}," defineConfig",[449,10665,1391],{"class":684},[449,10667,10668,10671,10674],{"class":451,"line":468},[449,10669,10670],{"class":684},"  schema: ",[449,10672,10673],{"class":691},"\".\u002Fserver\u002Fdb\u002Fschema.ts\"",[449,10675,779],{"class":684},[449,10677,10678,10681,10684],{"class":451,"line":474},[449,10679,10680],{"class":684},"  out: ",[449,10682,10683],{"class":691},"\".\u002Fdrizzle\"",[449,10685,779],{"class":684},[449,10687,10688,10691,10694],{"class":451,"line":480},[449,10689,10690],{"class":684},"  dialect: ",[449,10692,10693],{"class":691},"\"sqlite\"",[449,10695,779],{"class":684},[449,10697,10698],{"class":451,"line":486},[449,10699,10700],{"class":684},"  dbCredentials: {\n",[449,10702,10703,10706,10709],{"class":451,"line":492},[449,10704,10705],{"class":684},"    url: ",[449,10707,10708],{"class":691},"\".\u002Fbrewstop.db\"",[449,10710,779],{"class":684},[449,10712,10713],{"class":451,"line":498},[449,10714,1474],{"class":684},[449,10716,10717],{"class":451,"line":133},[449,10718,784],{"class":684},[73,10720],{},[32,10722,10724],{"id":10723},"step-3-define-your-schema","Step 3: Define Your Schema",[21,10726,10727],{},"Now let's design our database schema. For BrewStop, we need:",[383,10729,10730,10736,10742],{},[386,10731,10732,10735],{},[151,10733,10734],{},"Menu items"," — products the food truck sells",[386,10737,10738,10741],{},[151,10739,10740],{},"Orders"," — customer orders",[386,10743,10744,10747],{},[151,10745,10746],{},"Order items"," — individual items within an order",[21,10749,4698,10750,3070],{},[40,10751,10752],{},"server\u002Fdb\u002Fschema.ts",[48,10754,10756],{"className":670,"code":10755,"filename":10752,"language":673,"meta":56,"style":56},"import { sqliteTable, text, integer, real } from \"drizzle-orm\u002Fsqlite-core\";\nimport { sql } from \"drizzle-orm\";\n\n\u002F\u002F Menu Items Table\nexport const menuItems = sqliteTable(\"menu_items\", {\n  id: text(\"id\").primaryKey(),\n  name: text(\"name\").notNull(),\n  description: text(\"description\"),\n  price: real(\"price\").notNull(),\n  category: text(\"category\").notNull(), \u002F\u002F coffee, tea, food\n  available: integer(\"available\", { mode: \"boolean\" }).default(true),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" })\n    .default(sql`CURRENT_TIMESTAMP`),\n});\n\n\u002F\u002F Orders Table\nexport const orders = sqliteTable(\"orders\", {\n  id: text(\"id\").primaryKey(),\n  status: text(\"status\").notNull().default(\"pending\"), \u002F\u002F pending, preparing, ready, completed\n  customerName: text(\"customer_name\"),\n  total: real(\"total\").notNull(),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" })\n    .default(sql`CURRENT_TIMESTAMP`),\n  updatedAt: integer(\"updated_at\", { mode: \"timestamp\" })\n    .default(sql`CURRENT_TIMESTAMP`),\n});\n\n\u002F\u002F Order Items Table (junction table)\nexport const orderItems = sqliteTable(\"order_items\", {\n  id: text(\"id\").primaryKey(),\n  orderId: text(\"order_id\")\n    .notNull()\n    .references(() => orders.id),\n  menuItemId: text(\"menu_item_id\")\n    .notNull()\n    .references(() => menuItems.id),\n  quantity: integer(\"quantity\").notNull().default(1),\n  unitPrice: real(\"unit_price\").notNull(),\n});\n\n\u002F\u002F Type exports for TypeScript\nexport type MenuItem = typeof menuItems.$inferSelect;\nexport type NewMenuItem = typeof menuItems.$inferInsert;\nexport type Order = typeof orders.$inferSelect;\nexport type NewOrder = typeof orders.$inferInsert;\n",[40,10757,10758,10772,10786,10790,10795,10817,10836,10854,10867,10886,10908,10939,10959,10976,10980,10984,10989,11008,11024,11054,11068,11086,11102,11116,11134,11148,11152,11156,11161,11181,11197,11212,11221,11235,11249,11257,11270,11296,11314,11318,11322,11327,11345,11361,11377],{"__ignoreMap":56},[449,10759,10760,10762,10765,10767,10770],{"class":451,"line":452},[449,10761,681],{"class":680},[449,10763,10764],{"class":684}," { sqliteTable, text, integer, real } ",[449,10766,688],{"class":680},[449,10768,10769],{"class":691}," \"drizzle-orm\u002Fsqlite-core\"",[449,10771,695],{"class":684},[449,10773,10774,10776,10779,10781,10784],{"class":451,"line":114},[449,10775,681],{"class":680},[449,10777,10778],{"class":684}," { sql } ",[449,10780,688],{"class":680},[449,10782,10783],{"class":691}," \"drizzle-orm\"",[449,10785,695],{"class":684},[449,10787,10788],{"class":451,"line":111},[449,10789,700],{"emptyLinePlaceholder":130},[449,10791,10792],{"class":451,"line":468},[449,10793,10794],{"class":705},"\u002F\u002F Menu Items Table\n",[449,10796,10797,10799,10802,10805,10807,10810,10812,10815],{"class":451,"line":474},[449,10798,4629],{"class":680},[449,10800,10801],{"class":680}," const",[449,10803,10804],{"class":714}," menuItems",[449,10806,718],{"class":680},[449,10808,10809],{"class":721}," sqliteTable",[449,10811,725],{"class":684},[449,10813,10814],{"class":691},"\"menu_items\"",[449,10816,768],{"class":684},[449,10818,10819,10822,10824,10826,10829,10831,10834],{"class":451,"line":480},[449,10820,10821],{"class":684},"  id: ",[449,10823,53],{"class":721},[449,10825,725],{"class":684},[449,10827,10828],{"class":691},"\"id\"",[449,10830,374],{"class":684},[449,10832,10833],{"class":721},"primaryKey",[449,10835,1624],{"class":684},[449,10837,10838,10840,10842,10844,10847,10849,10852],{"class":451,"line":486},[449,10839,1396],{"class":684},[449,10841,53],{"class":721},[449,10843,725],{"class":684},[449,10845,10846],{"class":691},"\"name\"",[449,10848,374],{"class":684},[449,10850,10851],{"class":721},"notNull",[449,10853,1624],{"class":684},[449,10855,10856,10858,10860,10862,10865],{"class":451,"line":492},[449,10857,1406],{"class":684},[449,10859,53],{"class":721},[449,10861,725],{"class":684},[449,10863,10864],{"class":691},"\"description\"",[449,10866,1613],{"class":684},[449,10868,10869,10872,10875,10877,10880,10882,10884],{"class":451,"line":498},[449,10870,10871],{"class":684},"  price: ",[449,10873,10874],{"class":721},"real",[449,10876,725],{"class":684},[449,10878,10879],{"class":691},"\"price\"",[449,10881,374],{"class":684},[449,10883,10851],{"class":721},[449,10885,1624],{"class":684},[449,10887,10888,10891,10893,10895,10898,10900,10902,10905],{"class":451,"line":133},[449,10889,10890],{"class":684},"  category: ",[449,10892,53],{"class":721},[449,10894,725],{"class":684},[449,10896,10897],{"class":691},"\"category\"",[449,10899,374],{"class":684},[449,10901,10851],{"class":721},[449,10903,10904],{"class":684},"(), ",[449,10906,10907],{"class":705},"\u002F\u002F coffee, tea, food\n",[449,10909,10910,10913,10916,10918,10921,10924,10927,10930,10933,10935,10937],{"class":451,"line":509},[449,10911,10912],{"class":684},"  available: ",[449,10914,10915],{"class":721},"integer",[449,10917,725],{"class":684},[449,10919,10920],{"class":691},"\"available\"",[449,10922,10923],{"class":684},", { mode: ",[449,10925,10926],{"class":691},"\"boolean\"",[449,10928,10929],{"class":684}," }).",[449,10931,10932],{"class":721},"default",[449,10934,725],{"class":684},[449,10936,2169],{"class":714},[449,10938,1613],{"class":684},[449,10940,10941,10944,10946,10948,10951,10953,10956],{"class":451,"line":318},[449,10942,10943],{"class":684},"  createdAt: ",[449,10945,10915],{"class":721},[449,10947,725],{"class":684},[449,10949,10950],{"class":691},"\"created_at\"",[449,10952,10923],{"class":684},[449,10954,10955],{"class":691},"\"timestamp\"",[449,10957,10958],{"class":684}," })\n",[449,10960,10961,10964,10966,10968,10971,10974],{"class":451,"line":520},[449,10962,10963],{"class":684},"    .",[449,10965,10932],{"class":721},[449,10967,725],{"class":684},[449,10969,10970],{"class":721},"sql",[449,10972,10973],{"class":691},"`CURRENT_TIMESTAMP`",[449,10975,1613],{"class":684},[449,10977,10978],{"class":451,"line":526},[449,10979,784],{"class":684},[449,10981,10982],{"class":451,"line":532},[449,10983,700],{"emptyLinePlaceholder":130},[449,10985,10986],{"class":451,"line":2790},[449,10987,10988],{"class":705},"\u002F\u002F Orders Table\n",[449,10990,10991,10993,10995,10997,10999,11001,11003,11006],{"class":451,"line":3511},[449,10992,4629],{"class":680},[449,10994,10801],{"class":680},[449,10996,5004],{"class":714},[449,10998,718],{"class":680},[449,11000,10809],{"class":721},[449,11002,725],{"class":684},[449,11004,11005],{"class":691},"\"orders\"",[449,11007,768],{"class":684},[449,11009,11010,11012,11014,11016,11018,11020,11022],{"class":451,"line":3526},[449,11011,10821],{"class":684},[449,11013,53],{"class":721},[449,11015,725],{"class":684},[449,11017,10828],{"class":691},[449,11019,374],{"class":684},[449,11021,10833],{"class":721},[449,11023,1624],{"class":684},[449,11025,11026,11029,11031,11033,11036,11038,11040,11042,11044,11046,11048,11051],{"class":451,"line":3531},[449,11027,11028],{"class":684},"  status: ",[449,11030,53],{"class":721},[449,11032,725],{"class":684},[449,11034,11035],{"class":691},"\"status\"",[449,11037,374],{"class":684},[449,11039,10851],{"class":721},[449,11041,5236],{"class":684},[449,11043,10932],{"class":721},[449,11045,725],{"class":684},[449,11047,5220],{"class":691},[449,11049,11050],{"class":684},"), ",[449,11052,11053],{"class":705},"\u002F\u002F pending, preparing, ready, completed\n",[449,11055,11056,11059,11061,11063,11066],{"class":451,"line":3536},[449,11057,11058],{"class":684},"  customerName: ",[449,11060,53],{"class":721},[449,11062,725],{"class":684},[449,11064,11065],{"class":691},"\"customer_name\"",[449,11067,1613],{"class":684},[449,11069,11070,11073,11075,11077,11080,11082,11084],{"class":451,"line":4009},[449,11071,11072],{"class":684},"  total: ",[449,11074,10874],{"class":721},[449,11076,725],{"class":684},[449,11078,11079],{"class":691},"\"total\"",[449,11081,374],{"class":684},[449,11083,10851],{"class":721},[449,11085,1624],{"class":684},[449,11087,11088,11090,11092,11094,11096,11098,11100],{"class":451,"line":4015},[449,11089,10943],{"class":684},[449,11091,10915],{"class":721},[449,11093,725],{"class":684},[449,11095,10950],{"class":691},[449,11097,10923],{"class":684},[449,11099,10955],{"class":691},[449,11101,10958],{"class":684},[449,11103,11104,11106,11108,11110,11112,11114],{"class":451,"line":4021},[449,11105,10963],{"class":684},[449,11107,10932],{"class":721},[449,11109,725],{"class":684},[449,11111,10970],{"class":721},[449,11113,10973],{"class":691},[449,11115,1613],{"class":684},[449,11117,11118,11121,11123,11125,11128,11130,11132],{"class":451,"line":4027},[449,11119,11120],{"class":684},"  updatedAt: ",[449,11122,10915],{"class":721},[449,11124,725],{"class":684},[449,11126,11127],{"class":691},"\"updated_at\"",[449,11129,10923],{"class":684},[449,11131,10955],{"class":691},[449,11133,10958],{"class":684},[449,11135,11136,11138,11140,11142,11144,11146],{"class":451,"line":4032},[449,11137,10963],{"class":684},[449,11139,10932],{"class":721},[449,11141,725],{"class":684},[449,11143,10970],{"class":721},[449,11145,10973],{"class":691},[449,11147,1613],{"class":684},[449,11149,11150],{"class":451,"line":4038},[449,11151,784],{"class":684},[449,11153,11154],{"class":451,"line":4044},[449,11155,700],{"emptyLinePlaceholder":130},[449,11157,11158],{"class":451,"line":4049},[449,11159,11160],{"class":705},"\u002F\u002F Order Items Table (junction table)\n",[449,11162,11163,11165,11167,11170,11172,11174,11176,11179],{"class":451,"line":4054},[449,11164,4629],{"class":680},[449,11166,10801],{"class":680},[449,11168,11169],{"class":714}," orderItems",[449,11171,718],{"class":680},[449,11173,10809],{"class":721},[449,11175,725],{"class":684},[449,11177,11178],{"class":691},"\"order_items\"",[449,11180,768],{"class":684},[449,11182,11183,11185,11187,11189,11191,11193,11195],{"class":451,"line":4059},[449,11184,10821],{"class":684},[449,11186,53],{"class":721},[449,11188,725],{"class":684},[449,11190,10828],{"class":691},[449,11192,374],{"class":684},[449,11194,10833],{"class":721},[449,11196,1624],{"class":684},[449,11198,11199,11202,11204,11206,11209],{"class":451,"line":4065},[449,11200,11201],{"class":684},"  orderId: ",[449,11203,53],{"class":721},[449,11205,725],{"class":684},[449,11207,11208],{"class":691},"\"order_id\"",[449,11210,11211],{"class":684},")\n",[449,11213,11214,11216,11218],{"class":451,"line":4070},[449,11215,10963],{"class":684},[449,11217,10851],{"class":721},[449,11219,11220],{"class":684},"()\n",[449,11222,11223,11225,11228,11230,11232],{"class":451,"line":4075},[449,11224,10963],{"class":684},[449,11226,11227],{"class":721},"references",[449,11229,4718],{"class":684},[449,11231,1498],{"class":680},[449,11233,11234],{"class":684}," orders.id),\n",[449,11236,11237,11240,11242,11244,11247],{"class":451,"line":4081},[449,11238,11239],{"class":684},"  menuItemId: ",[449,11241,53],{"class":721},[449,11243,725],{"class":684},[449,11245,11246],{"class":691},"\"menu_item_id\"",[449,11248,11211],{"class":684},[449,11250,11251,11253,11255],{"class":451,"line":4086},[449,11252,10963],{"class":684},[449,11254,10851],{"class":721},[449,11256,11220],{"class":684},[449,11258,11259,11261,11263,11265,11267],{"class":451,"line":4092},[449,11260,10963],{"class":684},[449,11262,11227],{"class":721},[449,11264,4718],{"class":684},[449,11266,1498],{"class":680},[449,11268,11269],{"class":684}," menuItems.id),\n",[449,11271,11272,11275,11277,11279,11282,11284,11286,11288,11290,11292,11294],{"class":451,"line":4098},[449,11273,11274],{"class":684},"  quantity: ",[449,11276,10915],{"class":721},[449,11278,725],{"class":684},[449,11280,11281],{"class":691},"\"quantity\"",[449,11283,374],{"class":684},[449,11285,10851],{"class":721},[449,11287,5236],{"class":684},[449,11289,10932],{"class":721},[449,11291,725],{"class":684},[449,11293,4799],{"class":714},[449,11295,1613],{"class":684},[449,11297,11298,11301,11303,11305,11308,11310,11312],{"class":451,"line":4104},[449,11299,11300],{"class":684},"  unitPrice: ",[449,11302,10874],{"class":721},[449,11304,725],{"class":684},[449,11306,11307],{"class":691},"\"unit_price\"",[449,11309,374],{"class":684},[449,11311,10851],{"class":721},[449,11313,1624],{"class":684},[449,11315,11316],{"class":451,"line":4110},[449,11317,784],{"class":684},[449,11319,11320],{"class":451,"line":5784},[449,11321,700],{"emptyLinePlaceholder":130},[449,11323,11324],{"class":451,"line":5790},[449,11325,11326],{"class":705},"\u002F\u002F Type exports for TypeScript\n",[449,11328,11329,11331,11334,11337,11339,11342],{"class":451,"line":5796},[449,11330,4629],{"class":680},[449,11332,11333],{"class":680}," type",[449,11335,11336],{"class":721}," MenuItem",[449,11338,718],{"class":680},[449,11340,11341],{"class":680}," typeof",[449,11343,11344],{"class":684}," menuItems.$inferSelect;\n",[449,11346,11347,11349,11351,11354,11356,11358],{"class":451,"line":5801},[449,11348,4629],{"class":680},[449,11350,11333],{"class":680},[449,11352,11353],{"class":721}," NewMenuItem",[449,11355,718],{"class":680},[449,11357,11341],{"class":680},[449,11359,11360],{"class":684}," menuItems.$inferInsert;\n",[449,11362,11363,11365,11367,11370,11372,11374],{"class":451,"line":5806},[449,11364,4629],{"class":680},[449,11366,11333],{"class":680},[449,11368,11369],{"class":721}," Order",[449,11371,718],{"class":680},[449,11373,11341],{"class":680},[449,11375,11376],{"class":684}," orders.$inferSelect;\n",[449,11378,11379,11381,11383,11386,11388,11390],{"class":451,"line":5821},[449,11380,4629],{"class":680},[449,11382,11333],{"class":680},[449,11384,11385],{"class":721}," NewOrder",[449,11387,718],{"class":680},[449,11389,11341],{"class":680},[449,11391,11392],{"class":684}," orders.$inferInsert;\n",[427,11394,11397],{"icon":11395,"title":11396},"🔑","Schema Best Practices",[21,11398,11399],{},"Always define your schema in TypeScript. Drizzle's type inference means your queries will be fully typed—no more guessing what fields exist on your models.",[73,11401],{},[32,11403,11405],{"id":11404},"step-4-initialize-the-database-connection","Step 4: Initialize the Database Connection",[21,11407,11408],{},"Create a database connection utility that Nitro can use.",[21,11410,4698,11411,3070],{},[40,11412,11413],{},"server\u002Fdb\u002Findex.ts",[48,11415,11417],{"className":670,"code":11416,"filename":11413,"language":673,"meta":56,"style":56},"import Database from \"better-sqlite3\";\nimport { drizzle } from \"drizzle-orm\u002Fbetter-sqlite3\";\nimport * as schema from \".\u002Fschema\";\n\nconst sqlite = new Database(\"brewstop.db\");\nexport const db = drizzle(sqlite, { schema });\n\n\u002F\u002F Initialize tables on startup\nexport function initializeDatabase() {\n  sqlite.exec(`\n    CREATE TABLE IF NOT EXISTS menu_items (\n      id TEXT PRIMARY KEY,\n      name TEXT NOT NULL,\n      description TEXT,\n      price REAL NOT NULL,\n      category TEXT NOT NULL,\n      available INTEGER DEFAULT 1,\n      created_at INTEGER DEFAULT (strftime('%s', 'now'))\n    );\n\n    CREATE TABLE IF NOT EXISTS orders (\n      id TEXT PRIMARY KEY,\n      status TEXT NOT NULL DEFAULT 'pending',\n      customer_name TEXT,\n      total REAL NOT NULL,\n      created_at INTEGER DEFAULT (strftime('%s', 'now')),\n      updated_at INTEGER DEFAULT (strftime('%s', 'now'))\n    );\n\n    CREATE TABLE IF NOT EXISTS order_items (\n      id TEXT PRIMARY KEY,\n      order_id TEXT NOT NULL,\n      menu_item_id TEXT NOT NULL,\n      quantity INTEGER DEFAULT 1,\n      unit_price REAL NOT NULL,\n      FOREIGN KEY (order_id) REFERENCES orders(id),\n      FOREIGN KEY (menu_item_id) REFERENCES menu_items(id)\n    );\n  `);\n  \n  console.log(\"✅ Database initialized\");\n}\n",[40,11418,11419,11433,11447,11467,11471,11492,11509,11513,11518,11529,11542,11547,11552,11557,11562,11567,11572,11577,11582,11586,11590,11595,11599,11604,11609,11614,11619,11624,11628,11632,11637,11641,11646,11651,11656,11661,11666,11671,11675,11682,11686,11699],{"__ignoreMap":56},[449,11420,11421,11423,11426,11428,11431],{"class":451,"line":452},[449,11422,681],{"class":680},[449,11424,11425],{"class":684}," Database ",[449,11427,688],{"class":680},[449,11429,11430],{"class":691}," \"better-sqlite3\"",[449,11432,695],{"class":684},[449,11434,11435,11437,11440,11442,11445],{"class":451,"line":114},[449,11436,681],{"class":680},[449,11438,11439],{"class":684}," { drizzle } ",[449,11441,688],{"class":680},[449,11443,11444],{"class":691}," \"drizzle-orm\u002Fbetter-sqlite3\"",[449,11446,695],{"class":684},[449,11448,11449,11451,11454,11457,11460,11462,11465],{"class":451,"line":111},[449,11450,681],{"class":680},[449,11452,11453],{"class":714}," *",[449,11455,11456],{"class":680}," as",[449,11458,11459],{"class":684}," schema ",[449,11461,688],{"class":680},[449,11463,11464],{"class":691}," \".\u002Fschema\"",[449,11466,695],{"class":684},[449,11468,11469],{"class":451,"line":468},[449,11470,700],{"emptyLinePlaceholder":130},[449,11472,11473,11475,11478,11480,11482,11485,11487,11490],{"class":451,"line":474},[449,11474,711],{"class":680},[449,11476,11477],{"class":714}," sqlite",[449,11479,718],{"class":680},[449,11481,1343],{"class":680},[449,11483,11484],{"class":721}," Database",[449,11486,725],{"class":684},[449,11488,11489],{"class":691},"\"brewstop.db\"",[449,11491,740],{"class":684},[449,11493,11494,11496,11498,11501,11503,11506],{"class":451,"line":480},[449,11495,4629],{"class":680},[449,11497,10801],{"class":680},[449,11499,11500],{"class":714}," db",[449,11502,718],{"class":680},[449,11504,11505],{"class":721}," drizzle",[449,11507,11508],{"class":684},"(sqlite, { schema });\n",[449,11510,11511],{"class":451,"line":486},[449,11512,700],{"emptyLinePlaceholder":130},[449,11514,11515],{"class":451,"line":492},[449,11516,11517],{"class":705},"\u002F\u002F Initialize tables on startup\n",[449,11519,11520,11522,11524,11527],{"class":451,"line":498},[449,11521,4629],{"class":680},[449,11523,5432],{"class":680},[449,11525,11526],{"class":721}," initializeDatabase",[449,11528,5438],{"class":684},[449,11530,11531,11534,11537,11539],{"class":451,"line":133},[449,11532,11533],{"class":684},"  sqlite.",[449,11535,11536],{"class":721},"exec",[449,11538,725],{"class":684},[449,11540,11541],{"class":691},"`\n",[449,11543,11544],{"class":451,"line":509},[449,11545,11546],{"class":691},"    CREATE TABLE IF NOT EXISTS menu_items (\n",[449,11548,11549],{"class":451,"line":318},[449,11550,11551],{"class":691},"      id TEXT PRIMARY KEY,\n",[449,11553,11554],{"class":451,"line":520},[449,11555,11556],{"class":691},"      name TEXT NOT NULL,\n",[449,11558,11559],{"class":451,"line":526},[449,11560,11561],{"class":691},"      description TEXT,\n",[449,11563,11564],{"class":451,"line":532},[449,11565,11566],{"class":691},"      price REAL NOT NULL,\n",[449,11568,11569],{"class":451,"line":2790},[449,11570,11571],{"class":691},"      category TEXT NOT NULL,\n",[449,11573,11574],{"class":451,"line":3511},[449,11575,11576],{"class":691},"      available INTEGER DEFAULT 1,\n",[449,11578,11579],{"class":451,"line":3526},[449,11580,11581],{"class":691},"      created_at INTEGER DEFAULT (strftime('%s', 'now'))\n",[449,11583,11584],{"class":451,"line":3531},[449,11585,5793],{"class":691},[449,11587,11588],{"class":451,"line":3536},[449,11589,700],{"emptyLinePlaceholder":130},[449,11591,11592],{"class":451,"line":4009},[449,11593,11594],{"class":691},"    CREATE TABLE IF NOT EXISTS orders (\n",[449,11596,11597],{"class":451,"line":4015},[449,11598,11551],{"class":691},[449,11600,11601],{"class":451,"line":4021},[449,11602,11603],{"class":691},"      status TEXT NOT NULL DEFAULT 'pending',\n",[449,11605,11606],{"class":451,"line":4027},[449,11607,11608],{"class":691},"      customer_name TEXT,\n",[449,11610,11611],{"class":451,"line":4032},[449,11612,11613],{"class":691},"      total REAL NOT NULL,\n",[449,11615,11616],{"class":451,"line":4038},[449,11617,11618],{"class":691},"      created_at INTEGER DEFAULT (strftime('%s', 'now')),\n",[449,11620,11621],{"class":451,"line":4044},[449,11622,11623],{"class":691},"      updated_at INTEGER DEFAULT (strftime('%s', 'now'))\n",[449,11625,11626],{"class":451,"line":4049},[449,11627,5793],{"class":691},[449,11629,11630],{"class":451,"line":4054},[449,11631,700],{"emptyLinePlaceholder":130},[449,11633,11634],{"class":451,"line":4059},[449,11635,11636],{"class":691},"    CREATE TABLE IF NOT EXISTS order_items (\n",[449,11638,11639],{"class":451,"line":4065},[449,11640,11551],{"class":691},[449,11642,11643],{"class":451,"line":4070},[449,11644,11645],{"class":691},"      order_id TEXT NOT NULL,\n",[449,11647,11648],{"class":451,"line":4075},[449,11649,11650],{"class":691},"      menu_item_id TEXT NOT NULL,\n",[449,11652,11653],{"class":451,"line":4081},[449,11654,11655],{"class":691},"      quantity INTEGER DEFAULT 1,\n",[449,11657,11658],{"class":451,"line":4086},[449,11659,11660],{"class":691},"      unit_price REAL NOT NULL,\n",[449,11662,11663],{"class":451,"line":4092},[449,11664,11665],{"class":691},"      FOREIGN KEY (order_id) REFERENCES orders(id),\n",[449,11667,11668],{"class":451,"line":4098},[449,11669,11670],{"class":691},"      FOREIGN KEY (menu_item_id) REFERENCES menu_items(id)\n",[449,11672,11673],{"class":451,"line":4104},[449,11674,5793],{"class":691},[449,11676,11677,11680],{"class":451,"line":4110},[449,11678,11679],{"class":691},"  `",[449,11681,740],{"class":684},[449,11683,11684],{"class":451,"line":5784},[449,11685,4661],{"class":684},[449,11687,11688,11690,11692,11694,11697],{"class":451,"line":5790},[449,11689,5443],{"class":684},[449,11691,1675],{"class":721},[449,11693,725],{"class":684},[449,11695,11696],{"class":691},"\"✅ Database initialized\"",[449,11698,740],{"class":684},[449,11700,11701],{"class":451,"line":5796},[449,11702,4604],{"class":684},[73,11704],{},[32,11706,11708],{"id":11707},"step-5-seed-some-data","Step 5: Seed Some Data",[21,11710,11711],{},"Let's add an endpoint to populate our menu with sample data.",[21,11713,4698,11714,3070],{},[40,11715,11716],{},"server\u002Fapi\u002Fseed.post.ts",[48,11718,11720],{"className":670,"code":11719,"filename":11716,"language":673,"meta":56,"style":56},"import { db } from \"..\u002Fdb\";\nimport { menuItems } from \"..\u002Fdb\u002Fschema\";\n\nconst defaultMenuItems = [\n  { id: \"latte\", name: \"Latte\", description: \"Smooth espresso with steamed milk\", price: 4.5, category: \"coffee\" },\n  { id: \"espresso\", name: \"Espresso\", description: \"Rich, bold espresso shot\", price: 3.0, category: \"coffee\" },\n  { id: \"americano\", name: \"Americano\", description: \"Espresso with hot water\", price: 3.5, category: \"coffee\" },\n  { id: \"cappuccino\", name: \"Cappuccino\", description: \"Espresso with equal parts milk foam\", price: 4.5, category: \"coffee\" },\n  { id: \"chai\", name: \"Chai Tea\", description: \"Spiced black tea with milk\", price: 4.0, category: \"tea\" },\n  { id: \"green-tea\", name: \"Green Tea\", description: \"Organic Japanese green tea\", price: 3.5, category: \"tea\" },\n  { id: \"croissant\", name: \"Croissant\", description: \"Buttery French pastry\", price: 3.5, category: \"food\" },\n  { id: \"muffin\", name: \"Blueberry Muffin\", description: \"Freshly baked muffin\", price: 3.0, category: \"food\" },\n  { id: \"bagel\", name: \"Bagel with Cream Cheese\", description: \"Toasted bagel with cream cheese\", price: 4.0, category: \"food\" },\n];\n\nexport default defineEventHandler(async () => {\n  \u002F\u002F Clear existing menu items\n  await db.delete(menuItems).run();\n  \n  \u002F\u002F Insert default items\n  for (const item of defaultMenuItems) {\n    await db.insert(menuItems).values({\n      ...item,\n      available: true,\n    });\n  }\n  \n  return { message: \"Menu seeded successfully\", count: defaultMenuItems.length };\n});\n",[40,11721,11722,11736,11750,11754,11765,11792,11818,11845,11872,11898,11925,11951,11978,12005,12009,12013,12031,12036,12054,12058,12063,12080,12097,12105,12114,12118,12122,12126,12143],{"__ignoreMap":56},[449,11723,11724,11726,11729,11731,11734],{"class":451,"line":452},[449,11725,681],{"class":680},[449,11727,11728],{"class":684}," { db } ",[449,11730,688],{"class":680},[449,11732,11733],{"class":691}," \"..\u002Fdb\"",[449,11735,695],{"class":684},[449,11737,11738,11740,11743,11745,11748],{"class":451,"line":114},[449,11739,681],{"class":680},[449,11741,11742],{"class":684}," { menuItems } ",[449,11744,688],{"class":680},[449,11746,11747],{"class":691}," \"..\u002Fdb\u002Fschema\"",[449,11749,695],{"class":684},[449,11751,11752],{"class":451,"line":111},[449,11753,700],{"emptyLinePlaceholder":130},[449,11755,11756,11758,11761,11763],{"class":451,"line":468},[449,11757,711],{"class":680},[449,11759,11760],{"class":714}," defaultMenuItems",[449,11762,718],{"class":680},[449,11764,4791],{"class":684},[449,11766,11767,11769,11772,11774,11776,11779,11782,11784,11786,11788,11790],{"class":451,"line":474},[449,11768,4796],{"class":684},[449,11770,11771],{"class":691},"\"latte\"",[449,11773,4802],{"class":684},[449,11775,4805],{"class":691},[449,11777,11778],{"class":684},", description: ",[449,11780,11781],{"class":691},"\"Smooth espresso with steamed milk\"",[449,11783,4808],{"class":684},[449,11785,4811],{"class":714},[449,11787,4814],{"class":684},[449,11789,4817],{"class":691},[449,11791,4820],{"class":684},[449,11793,11794,11796,11799,11801,11803,11805,11808,11810,11812,11814,11816],{"class":451,"line":480},[449,11795,4796],{"class":684},[449,11797,11798],{"class":691},"\"espresso\"",[449,11800,4802],{"class":684},[449,11802,4832],{"class":691},[449,11804,11778],{"class":684},[449,11806,11807],{"class":691},"\"Rich, bold espresso shot\"",[449,11809,4808],{"class":684},[449,11811,4837],{"class":714},[449,11813,4814],{"class":684},[449,11815,4817],{"class":691},[449,11817,4820],{"class":684},[449,11819,11820,11822,11825,11827,11830,11832,11835,11837,11839,11841,11843],{"class":451,"line":486},[449,11821,4796],{"class":684},[449,11823,11824],{"class":691},"\"americano\"",[449,11826,4802],{"class":684},[449,11828,11829],{"class":691},"\"Americano\"",[449,11831,11778],{"class":684},[449,11833,11834],{"class":691},"\"Espresso with hot water\"",[449,11836,4808],{"class":684},[449,11838,4860],{"class":714},[449,11840,4814],{"class":684},[449,11842,4817],{"class":691},[449,11844,4820],{"class":684},[449,11846,11847,11849,11852,11854,11857,11859,11862,11864,11866,11868,11870],{"class":451,"line":492},[449,11848,4796],{"class":684},[449,11850,11851],{"class":691},"\"cappuccino\"",[449,11853,4802],{"class":684},[449,11855,11856],{"class":691},"\"Cappuccino\"",[449,11858,11778],{"class":684},[449,11860,11861],{"class":691},"\"Espresso with equal parts milk foam\"",[449,11863,4808],{"class":684},[449,11865,4811],{"class":714},[449,11867,4814],{"class":684},[449,11869,4817],{"class":691},[449,11871,4820],{"class":684},[449,11873,11874,11876,11879,11881,11883,11885,11888,11890,11892,11894,11896],{"class":451,"line":498},[449,11875,4796],{"class":684},[449,11877,11878],{"class":691},"\"chai\"",[449,11880,4802],{"class":684},[449,11882,4879],{"class":691},[449,11884,11778],{"class":684},[449,11886,11887],{"class":691},"\"Spiced black tea with milk\"",[449,11889,4808],{"class":684},[449,11891,4884],{"class":714},[449,11893,4814],{"class":684},[449,11895,4889],{"class":691},[449,11897,4820],{"class":684},[449,11899,11900,11902,11905,11907,11910,11912,11915,11917,11919,11921,11923],{"class":451,"line":133},[449,11901,4796],{"class":684},[449,11903,11904],{"class":691},"\"green-tea\"",[449,11906,4802],{"class":684},[449,11908,11909],{"class":691},"\"Green Tea\"",[449,11911,11778],{"class":684},[449,11913,11914],{"class":691},"\"Organic Japanese green tea\"",[449,11916,4808],{"class":684},[449,11918,4860],{"class":714},[449,11920,4814],{"class":684},[449,11922,4889],{"class":691},[449,11924,4820],{"class":684},[449,11926,11927,11929,11932,11934,11936,11938,11941,11943,11945,11947,11949],{"class":451,"line":509},[449,11928,4796],{"class":684},[449,11930,11931],{"class":691},"\"croissant\"",[449,11933,4802],{"class":684},[449,11935,4855],{"class":691},[449,11937,11778],{"class":684},[449,11939,11940],{"class":691},"\"Buttery French pastry\"",[449,11942,4808],{"class":684},[449,11944,4860],{"class":714},[449,11946,4814],{"class":684},[449,11948,4865],{"class":691},[449,11950,4820],{"class":684},[449,11952,11953,11955,11958,11960,11963,11965,11968,11970,11972,11974,11976],{"class":451,"line":318},[449,11954,4796],{"class":684},[449,11956,11957],{"class":691},"\"muffin\"",[449,11959,4802],{"class":684},[449,11961,11962],{"class":691},"\"Blueberry Muffin\"",[449,11964,11778],{"class":684},[449,11966,11967],{"class":691},"\"Freshly baked muffin\"",[449,11969,4808],{"class":684},[449,11971,4837],{"class":714},[449,11973,4814],{"class":684},[449,11975,4865],{"class":691},[449,11977,4820],{"class":684},[449,11979,11980,11982,11985,11987,11990,11992,11995,11997,11999,12001,12003],{"class":451,"line":520},[449,11981,4796],{"class":684},[449,11983,11984],{"class":691},"\"bagel\"",[449,11986,4802],{"class":684},[449,11988,11989],{"class":691},"\"Bagel with Cream Cheese\"",[449,11991,11778],{"class":684},[449,11993,11994],{"class":691},"\"Toasted bagel with cream cheese\"",[449,11996,4808],{"class":684},[449,11998,4884],{"class":714},[449,12000,4814],{"class":684},[449,12002,4865],{"class":691},[449,12004,4820],{"class":684},[449,12006,12007],{"class":451,"line":526},[449,12008,4918],{"class":684},[449,12010,12011],{"class":451,"line":532},[449,12012,700],{"emptyLinePlaceholder":130},[449,12014,12015,12017,12019,12021,12023,12025,12027,12029],{"class":451,"line":2790},[449,12016,4629],{"class":680},[449,12018,4632],{"class":680},[449,12020,4715],{"class":721},[449,12022,725],{"class":684},[449,12024,1485],{"class":680},[449,12026,2090],{"class":684},[449,12028,1498],{"class":680},[449,12030,1501],{"class":684},[449,12032,12033],{"class":451,"line":3511},[449,12034,12035],{"class":705},"  \u002F\u002F Clear existing menu items\n",[449,12037,12038,12040,12043,12046,12049,12052],{"class":451,"line":3526},[449,12039,2099],{"class":680},[449,12041,12042],{"class":684}," db.",[449,12044,12045],{"class":721},"delete",[449,12047,12048],{"class":684},"(menuItems).",[449,12050,12051],{"class":721},"run",[449,12053,1322],{"class":684},[449,12055,12056],{"class":451,"line":3531},[449,12057,4661],{"class":684},[449,12059,12060],{"class":451,"line":3536},[449,12061,12062],{"class":705},"  \u002F\u002F Insert default items\n",[449,12064,12065,12067,12069,12071,12074,12077],{"class":451,"line":4009},[449,12066,5528],{"class":680},[449,12068,1913],{"class":684},[449,12070,711],{"class":680},[449,12072,12073],{"class":714}," item",[449,12075,12076],{"class":680}," of",[449,12078,12079],{"class":684}," defaultMenuItems) {\n",[449,12081,12082,12085,12087,12090,12092,12095],{"class":451,"line":4015},[449,12083,12084],{"class":680},"    await",[449,12086,12042],{"class":684},[449,12088,12089],{"class":721},"insert",[449,12091,12048],{"class":684},[449,12093,12094],{"class":721},"values",[449,12096,1391],{"class":684},[449,12098,12099,12102],{"class":451,"line":4021},[449,12100,12101],{"class":680},"      ...",[449,12103,12104],{"class":684},"item,\n",[449,12106,12107,12110,12112],{"class":451,"line":4027},[449,12108,12109],{"class":684},"      available: ",[449,12111,2169],{"class":714},[449,12113,779],{"class":684},[449,12115,12116],{"class":451,"line":4032},[449,12117,3484],{"class":684},[449,12119,12120],{"class":451,"line":4038},[449,12121,4599],{"class":684},[449,12123,12124],{"class":451,"line":4044},[449,12125,4661],{"class":684},[449,12127,12128,12130,12133,12136,12139,12141],{"class":451,"line":4049},[449,12129,4727],{"class":680},[449,12131,12132],{"class":684}," { message: ",[449,12134,12135],{"class":691},"\"Menu seeded successfully\"",[449,12137,12138],{"class":684},", count: defaultMenuItems.",[449,12140,5096],{"class":714},[449,12142,1520],{"class":684},[449,12144,12145],{"class":451,"line":4054},[449,12146,784],{"class":684},[73,12148],{},[32,12150,12152],{"id":12151},"step-6-create-api-endpoints","Step 6: Create API Endpoints",[2808,12154,12156],{"id":12155},"get-menu","Get Menu",[21,12158,4698,12159,3070],{},[40,12160,12161],{},"server\u002Fapi\u002Fmenu.get.ts",[48,12163,12165],{"className":670,"code":12164,"filename":12161,"language":673,"meta":56,"style":56},"import { db } from \"..\u002Fdb\";\nimport { menuItems } from \"..\u002Fdb\u002Fschema\";\n\nexport default defineEventHandler(async () => {\n  const items = await db.select().from(menuItems).where(menuItems.available);\n  return { items };\n});\n",[40,12166,12167,12179,12191,12195,12213,12241,12248],{"__ignoreMap":56},[449,12168,12169,12171,12173,12175,12177],{"class":451,"line":452},[449,12170,681],{"class":680},[449,12172,11728],{"class":684},[449,12174,688],{"class":680},[449,12176,11733],{"class":691},[449,12178,695],{"class":684},[449,12180,12181,12183,12185,12187,12189],{"class":451,"line":114},[449,12182,681],{"class":680},[449,12184,11742],{"class":684},[449,12186,688],{"class":680},[449,12188,11747],{"class":691},[449,12190,695],{"class":684},[449,12192,12193],{"class":451,"line":111},[449,12194,700],{"emptyLinePlaceholder":130},[449,12196,12197,12199,12201,12203,12205,12207,12209,12211],{"class":451,"line":468},[449,12198,4629],{"class":680},[449,12200,4632],{"class":680},[449,12202,4715],{"class":721},[449,12204,725],{"class":684},[449,12206,1485],{"class":680},[449,12208,2090],{"class":684},[449,12210,1498],{"class":680},[449,12212,1501],{"class":684},[449,12214,12215,12217,12220,12222,12224,12226,12229,12231,12233,12235,12238],{"class":451,"line":474},[449,12216,5049],{"class":680},[449,12218,12219],{"class":714}," items",[449,12221,718],{"class":680},[449,12223,1654],{"class":680},[449,12225,12042],{"class":684},[449,12227,12228],{"class":721},"select",[449,12230,5236],{"class":684},[449,12232,688],{"class":721},[449,12234,12048],{"class":684},[449,12236,12237],{"class":721},"where",[449,12239,12240],{"class":684},"(menuItems.available);\n",[449,12242,12243,12245],{"class":451,"line":480},[449,12244,4727],{"class":680},[449,12246,12247],{"class":684}," { items };\n",[449,12249,12250],{"class":451,"line":486},[449,12251,784],{"class":684},[2808,12253,12255],{"id":12254},"get-single-menu-item","Get Single Menu Item",[21,12257,4698,12258,3070],{},[40,12259,12260],{},"server\u002Fapi\u002Fmenu\u002F[id].get.ts",[48,12262,12264],{"className":670,"code":12263,"filename":12260,"language":673,"meta":56,"style":56},"import { db } from \"..\u002Fdb\";\nimport { menuItems } from \"..\u002Fdb\u002Fschema\";\n\nexport default defineEventHandler(async (event) => {\n  const id = getRouterParam(event, \"id\");\n  \n  if (!id) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Menu item ID is required\",\n    });\n  }\n  \n  const item = await db.select().from(menuItems).where(menuItems.id.equals(id)).get();\n  \n  if (!item) {\n    throw createError({\n      statusCode: 404,\n      statusMessage: \"Menu item not found\",\n    });\n  }\n  \n  return item;\n});\n",[40,12265,12266,12278,12290,12294,12316,12335,12339,12350,12358,12366,12375,12379,12383,12387,12423,12427,12438,12446,12455,12464,12468,12472,12476,12483],{"__ignoreMap":56},[449,12267,12268,12270,12272,12274,12276],{"class":451,"line":452},[449,12269,681],{"class":680},[449,12271,11728],{"class":684},[449,12273,688],{"class":680},[449,12275,11733],{"class":691},[449,12277,695],{"class":684},[449,12279,12280,12282,12284,12286,12288],{"class":451,"line":114},[449,12281,681],{"class":680},[449,12283,11742],{"class":684},[449,12285,688],{"class":680},[449,12287,11747],{"class":691},[449,12289,695],{"class":684},[449,12291,12292],{"class":451,"line":111},[449,12293,700],{"emptyLinePlaceholder":130},[449,12295,12296,12298,12300,12302,12304,12306,12308,12310,12312,12314],{"class":451,"line":468},[449,12297,4629],{"class":680},[449,12299,4632],{"class":680},[449,12301,4715],{"class":721},[449,12303,725],{"class":684},[449,12305,1485],{"class":680},[449,12307,1913],{"class":684},[449,12309,2991],{"class":1491},[449,12311,1758],{"class":684},[449,12313,1498],{"class":680},[449,12315,1501],{"class":684},[449,12317,12318,12320,12323,12325,12328,12331,12333],{"class":451,"line":474},[449,12319,5049],{"class":680},[449,12321,12322],{"class":714}," id",[449,12324,718],{"class":680},[449,12326,12327],{"class":721}," getRouterParam",[449,12329,12330],{"class":684},"(event, ",[449,12332,10828],{"class":691},[449,12334,740],{"class":684},[449,12336,12337],{"class":451,"line":480},[449,12338,4661],{"class":684},[449,12340,12341,12343,12345,12347],{"class":451,"line":486},[449,12342,5079],{"class":680},[449,12344,1913],{"class":684},[449,12346,5084],{"class":680},[449,12348,12349],{"class":684},"id) {\n",[449,12351,12352,12354,12356],{"class":451,"line":492},[449,12353,5109],{"class":680},[449,12355,5112],{"class":721},[449,12357,1391],{"class":684},[449,12359,12360,12362,12364],{"class":451,"line":498},[449,12361,5119],{"class":684},[449,12363,5122],{"class":714},[449,12365,779],{"class":684},[449,12367,12368,12370,12373],{"class":451,"line":133},[449,12369,5129],{"class":684},[449,12371,12372],{"class":691},"\"Menu item ID is required\"",[449,12374,779],{"class":684},[449,12376,12377],{"class":451,"line":509},[449,12378,3484],{"class":684},[449,12380,12381],{"class":451,"line":318},[449,12382,4599],{"class":684},[449,12384,12385],{"class":451,"line":520},[449,12386,4661],{"class":684},[449,12388,12389,12391,12393,12395,12397,12399,12401,12403,12405,12407,12409,12412,12415,12418,12421],{"class":451,"line":526},[449,12390,5049],{"class":680},[449,12392,12073],{"class":714},[449,12394,718],{"class":680},[449,12396,1654],{"class":680},[449,12398,12042],{"class":684},[449,12400,12228],{"class":721},[449,12402,5236],{"class":684},[449,12404,688],{"class":721},[449,12406,12048],{"class":684},[449,12408,12237],{"class":721},[449,12410,12411],{"class":684},"(menuItems.id.",[449,12413,12414],{"class":721},"equals",[449,12416,12417],{"class":684},"(id)).",[449,12419,12420],{"class":721},"get",[449,12422,1322],{"class":684},[449,12424,12425],{"class":451,"line":532},[449,12426,4661],{"class":684},[449,12428,12429,12431,12433,12435],{"class":451,"line":2790},[449,12430,5079],{"class":680},[449,12432,1913],{"class":684},[449,12434,5084],{"class":680},[449,12436,12437],{"class":684},"item) {\n",[449,12439,12440,12442,12444],{"class":451,"line":3511},[449,12441,5109],{"class":680},[449,12443,5112],{"class":721},[449,12445,1391],{"class":684},[449,12447,12448,12450,12453],{"class":451,"line":3526},[449,12449,5119],{"class":684},[449,12451,12452],{"class":714},"404",[449,12454,779],{"class":684},[449,12456,12457,12459,12462],{"class":451,"line":3531},[449,12458,5129],{"class":684},[449,12460,12461],{"class":691},"\"Menu item not found\"",[449,12463,779],{"class":684},[449,12465,12466],{"class":451,"line":3536},[449,12467,3484],{"class":684},[449,12469,12470],{"class":451,"line":4009},[449,12471,4599],{"class":684},[449,12473,12474],{"class":451,"line":4015},[449,12475,4661],{"class":684},[449,12477,12478,12480],{"class":451,"line":4021},[449,12479,4727],{"class":680},[449,12481,12482],{"class":684}," item;\n",[449,12484,12485],{"class":451,"line":4027},[449,12486,784],{"class":684},[2808,12488,12490],{"id":12489},"create-order","Create Order",[21,12492,12493],{},"This is where Drizzle really shines—let's create a proper order with related items.",[21,12495,4698,12496,3070],{},[40,12497,4954],{},[48,12499,12501],{"className":670,"code":12500,"filename":4954,"language":673,"meta":56,"style":56},"import { db } from \"..\u002Fdb\";\nimport { orders, orderItems, menuItems } from \"..\u002Fdb\u002Fschema\";\nimport { eq } from \"drizzle-orm\";\n\ninterface OrderItemInput {\n  menuItemId: string;\n  quantity: number;\n}\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event);\n  \n  \u002F\u002F Validate input\n  if (!body.items || !Array.isArray(body.items) || body.items.length === 0) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Order must contain at least one item\",\n    });\n  }\n  \n  \u002F\u002F Calculate total and fetch menu item prices\n  let total = 0;\n  const orderItemInputs: OrderItemInput[] = [];\n  \n  for (const item of body.items) {\n    const menuItem = await db\n      .select()\n      .from(menuItems)\n      .where(eq(menuItems.id, item.menuItemId))\n      .get();\n    \n    if (!menuItem) {\n      throw createError({\n        statusCode: 400,\n        statusMessage: `Menu item ${item.menuItemId} not found`,\n      });\n    }\n    \n    if (!menuItem.available) {\n      throw createError({\n        statusCode: 400,\n        statusMessage: `Menu item ${menuItem.name} is not available`,\n      });\n    }\n    \n    total += menuItem.price * item.quantity;\n    orderItemInputs.push({\n      menuItemId: item.menuItemId,\n      quantity: item.quantity,\n    });\n  }\n  \n  \u002F\u002F Create the order\n  const orderId = crypto.randomUUID();\n  const now = Date.now();\n  \n  await db.insert(orders).values({\n    id: orderId,\n    customerName: body.customerName || \"Guest\",\n    status: \"pending\",\n    total,\n    createdAt: new Date(now),\n    updatedAt: new Date(now),\n  });\n  \n  \u002F\u002F Create order items\n  for (const input of orderItemInputs) {\n    const menuItem = await db\n      .select()\n      .from(menuItems)\n      .where(eq(menuItems.id, input.menuItemId))\n      .get();\n    \n    await db.insert(orderItems).values({\n      id: crypto.randomUUID(),\n      orderId,\n      menuItemId: input.menuItemId,\n      quantity: input.quantity,\n      unitPrice: menuItem!.price,\n    });\n  }\n  \n  \u002F\u002F Fetch the complete order\n  const createdOrder = await db\n    .select()\n    .from(orders)\n    .where(eq(orders.id, orderId))\n    .get();\n  \n  const items = await db\n    .select()\n    .from(orderItems)\n    .where(eq(orderItems.orderId, orderId));\n  \n  return {\n    ...createdOrder,\n    items,\n  };\n});\n",[40,12502,12503,12515,12528,12541,12545,12554,12565,12575,12579,12583,12605,12620,12624,12629,12667,12675,12683,12691,12695,12699,12703,12708,12721,12738,12742,12757,12772,12781,12790,12804,12812,12816,12828,12837,12846,12867,12872,12876,12880,12891,12899,12907,12926,12930,12934,12938,12955,12964,12969,12974,12978,12982,12986,12991,13007,13022,13027,13043,13049,13062,13071,13077,13089,13101,13107,13112,13118,13135,13148,13157,13166,13180,13189,13194,13210,13220,13226,13232,13238,13249,13254,13259,13264,13270,13284,13293,13303,13317,13326,13331,13344,13353,13363,13377,13382,13389,13398,13404,13409],{"__ignoreMap":56},[449,12504,12505,12507,12509,12511,12513],{"class":451,"line":452},[449,12506,681],{"class":680},[449,12508,11728],{"class":684},[449,12510,688],{"class":680},[449,12512,11733],{"class":691},[449,12514,695],{"class":684},[449,12516,12517,12519,12522,12524,12526],{"class":451,"line":114},[449,12518,681],{"class":680},[449,12520,12521],{"class":684}," { orders, orderItems, menuItems } ",[449,12523,688],{"class":680},[449,12525,11747],{"class":691},[449,12527,695],{"class":684},[449,12529,12530,12532,12535,12537,12539],{"class":451,"line":111},[449,12531,681],{"class":680},[449,12533,12534],{"class":684}," { eq } ",[449,12536,688],{"class":680},[449,12538,10783],{"class":691},[449,12540,695],{"class":684},[449,12542,12543],{"class":451,"line":468},[449,12544,700],{"emptyLinePlaceholder":130},[449,12546,12547,12549,12552],{"class":451,"line":474},[449,12548,4961],{"class":680},[449,12550,12551],{"class":721}," OrderItemInput",[449,12553,1501],{"class":684},[449,12555,12556,12559,12561,12563],{"class":451,"line":480},[449,12557,12558],{"class":1491},"  menuItemId",[449,12560,3070],{"class":680},[449,12562,6083],{"class":714},[449,12564,695],{"class":684},[449,12566,12567,12569,12571,12573],{"class":451,"line":486},[449,12568,4983],{"class":1491},[449,12570,3070],{"class":680},[449,12572,4976],{"class":714},[449,12574,695],{"class":684},[449,12576,12577],{"class":451,"line":492},[449,12578,4604],{"class":684},[449,12580,12581],{"class":451,"line":498},[449,12582,700],{"emptyLinePlaceholder":130},[449,12584,12585,12587,12589,12591,12593,12595,12597,12599,12601,12603],{"class":451,"line":133},[449,12586,4629],{"class":680},[449,12588,4632],{"class":680},[449,12590,4715],{"class":721},[449,12592,725],{"class":684},[449,12594,1485],{"class":680},[449,12596,1913],{"class":684},[449,12598,2991],{"class":1491},[449,12600,1758],{"class":684},[449,12602,1498],{"class":680},[449,12604,1501],{"class":684},[449,12606,12607,12609,12611,12613,12615,12617],{"class":451,"line":509},[449,12608,5049],{"class":680},[449,12610,5052],{"class":714},[449,12612,718],{"class":680},[449,12614,1654],{"class":680},[449,12616,5059],{"class":721},[449,12618,12619],{"class":684},"(event);\n",[449,12621,12622],{"class":451,"line":318},[449,12623,4661],{"class":684},[449,12625,12626],{"class":451,"line":520},[449,12627,12628],{"class":705},"  \u002F\u002F Validate input\n",[449,12630,12631,12633,12635,12637,12640,12642,12645,12648,12651,12654,12656,12659,12661,12663,12665],{"class":451,"line":526},[449,12632,5079],{"class":680},[449,12634,1913],{"class":684},[449,12636,5084],{"class":680},[449,12638,12639],{"class":684},"body.items ",[449,12641,5090],{"class":680},[449,12643,12644],{"class":680}," !",[449,12646,12647],{"class":684},"Array.",[449,12649,12650],{"class":721},"isArray",[449,12652,12653],{"class":684},"(body.items) ",[449,12655,5090],{"class":680},[449,12657,12658],{"class":684}," body.items.",[449,12660,5096],{"class":714},[449,12662,5099],{"class":680},[449,12664,5102],{"class":714},[449,12666,3417],{"class":684},[449,12668,12669,12671,12673],{"class":451,"line":532},[449,12670,5109],{"class":680},[449,12672,5112],{"class":721},[449,12674,1391],{"class":684},[449,12676,12677,12679,12681],{"class":451,"line":2790},[449,12678,5119],{"class":684},[449,12680,5122],{"class":714},[449,12682,779],{"class":684},[449,12684,12685,12687,12689],{"class":451,"line":3511},[449,12686,5129],{"class":684},[449,12688,5132],{"class":691},[449,12690,779],{"class":684},[449,12692,12693],{"class":451,"line":3526},[449,12694,3484],{"class":684},[449,12696,12697],{"class":451,"line":3531},[449,12698,4599],{"class":684},[449,12700,12701],{"class":451,"line":3536},[449,12702,4661],{"class":684},[449,12704,12705],{"class":451,"line":4009},[449,12706,12707],{"class":705},"  \u002F\u002F Calculate total and fetch menu item prices\n",[449,12709,12710,12712,12715,12717,12719],{"class":451,"line":4015},[449,12711,5495],{"class":680},[449,12713,12714],{"class":684}," total ",[449,12716,5015],{"class":680},[449,12718,5102],{"class":714},[449,12720,695],{"class":684},[449,12722,12723,12725,12728,12730,12732,12734,12736],{"class":451,"line":4021},[449,12724,5049],{"class":680},[449,12726,12727],{"class":714}," orderItemInputs",[449,12729,3070],{"class":680},[449,12731,12551],{"class":721},[449,12733,5012],{"class":684},[449,12735,5015],{"class":680},[449,12737,5018],{"class":684},[449,12739,12740],{"class":451,"line":4027},[449,12741,4661],{"class":684},[449,12743,12744,12746,12748,12750,12752,12754],{"class":451,"line":4032},[449,12745,5528],{"class":680},[449,12747,1913],{"class":684},[449,12749,711],{"class":680},[449,12751,12073],{"class":714},[449,12753,12076],{"class":680},[449,12755,12756],{"class":684}," body.items) {\n",[449,12758,12759,12762,12765,12767,12769],{"class":451,"line":4038},[449,12760,12761],{"class":680},"    const",[449,12763,12764],{"class":714}," menuItem",[449,12766,718],{"class":680},[449,12768,1654],{"class":680},[449,12770,12771],{"class":684}," db\n",[449,12773,12774,12777,12779],{"class":451,"line":4044},[449,12775,12776],{"class":684},"      .",[449,12778,12228],{"class":721},[449,12780,11220],{"class":684},[449,12782,12783,12785,12787],{"class":451,"line":4049},[449,12784,12776],{"class":684},[449,12786,688],{"class":721},[449,12788,12789],{"class":684},"(menuItems)\n",[449,12791,12792,12794,12796,12798,12801],{"class":451,"line":4054},[449,12793,12776],{"class":684},[449,12795,12237],{"class":721},[449,12797,725],{"class":684},[449,12799,12800],{"class":721},"eq",[449,12802,12803],{"class":684},"(menuItems.id, item.menuItemId))\n",[449,12805,12806,12808,12810],{"class":451,"line":4059},[449,12807,12776],{"class":684},[449,12809,12420],{"class":721},[449,12811,1322],{"class":684},[449,12813,12814],{"class":451,"line":4065},[449,12815,9155],{"class":684},[449,12817,12818,12821,12823,12825],{"class":451,"line":4070},[449,12819,12820],{"class":680},"    if",[449,12822,1913],{"class":684},[449,12824,5084],{"class":680},[449,12826,12827],{"class":684},"menuItem) {\n",[449,12829,12830,12833,12835],{"class":451,"line":4075},[449,12831,12832],{"class":680},"      throw",[449,12834,5112],{"class":721},[449,12836,1391],{"class":684},[449,12838,12839,12842,12844],{"class":451,"line":4081},[449,12840,12841],{"class":684},"        statusCode: ",[449,12843,5122],{"class":714},[449,12845,779],{"class":684},[449,12847,12848,12851,12854,12857,12859,12862,12865],{"class":451,"line":4086},[449,12849,12850],{"class":684},"        statusMessage: ",[449,12852,12853],{"class":691},"`Menu item ${",[449,12855,12856],{"class":684},"item",[449,12858,165],{"class":691},[449,12860,12861],{"class":684},"menuItemId",[449,12863,12864],{"class":691},"} not found`",[449,12866,779],{"class":684},[449,12868,12869],{"class":451,"line":4092},[449,12870,12871],{"class":684},"      });\n",[449,12873,12874],{"class":451,"line":4098},[449,12875,7555],{"class":684},[449,12877,12878],{"class":451,"line":4104},[449,12879,9155],{"class":684},[449,12881,12882,12884,12886,12888],{"class":451,"line":4110},[449,12883,12820],{"class":680},[449,12885,1913],{"class":684},[449,12887,5084],{"class":680},[449,12889,12890],{"class":684},"menuItem.available) {\n",[449,12892,12893,12895,12897],{"class":451,"line":5784},[449,12894,12832],{"class":680},[449,12896,5112],{"class":721},[449,12898,1391],{"class":684},[449,12900,12901,12903,12905],{"class":451,"line":5790},[449,12902,12841],{"class":684},[449,12904,5122],{"class":714},[449,12906,779],{"class":684},[449,12908,12909,12911,12913,12916,12918,12921,12924],{"class":451,"line":5796},[449,12910,12850],{"class":684},[449,12912,12853],{"class":691},[449,12914,12915],{"class":684},"menuItem",[449,12917,165],{"class":691},[449,12919,12920],{"class":684},"name",[449,12922,12923],{"class":691},"} is not available`",[449,12925,779],{"class":684},[449,12927,12928],{"class":451,"line":5801},[449,12929,12871],{"class":684},[449,12931,12932],{"class":451,"line":5806},[449,12933,7555],{"class":684},[449,12935,12936],{"class":451,"line":5821},[449,12937,9155],{"class":684},[449,12939,12940,12943,12946,12949,12952],{"class":451,"line":5826},[449,12941,12942],{"class":684},"    total ",[449,12944,12945],{"class":680},"+=",[449,12947,12948],{"class":684}," menuItem.price ",[449,12950,12951],{"class":680},"*",[449,12953,12954],{"class":684}," item.quantity;\n",[449,12956,12957,12960,12962],{"class":451,"line":5848},[449,12958,12959],{"class":684},"    orderItemInputs.",[449,12961,5257],{"class":721},[449,12963,1391],{"class":684},[449,12965,12966],{"class":451,"line":5882},[449,12967,12968],{"class":684},"      menuItemId: item.menuItemId,\n",[449,12970,12971],{"class":451,"line":5887},[449,12972,12973],{"class":684},"      quantity: item.quantity,\n",[449,12975,12976],{"class":451,"line":5901},[449,12977,3484],{"class":684},[449,12979,12980],{"class":451,"line":5921},[449,12981,4599],{"class":684},[449,12983,12984],{"class":451,"line":5940},[449,12985,4661],{"class":684},[449,12987,12988],{"class":451,"line":5975},[449,12989,12990],{"class":705},"  \u002F\u002F Create the order\n",[449,12992,12993,12995,12998,13000,13003,13005],{"class":451,"line":5980},[449,12994,5049],{"class":680},[449,12996,12997],{"class":714}," orderId",[449,12999,718],{"class":680},[449,13001,13002],{"class":684}," crypto.",[449,13004,5205],{"class":721},[449,13006,1322],{"class":684},[449,13008,13009,13011,13014,13016,13018,13020],{"class":451,"line":5985},[449,13010,5049],{"class":680},[449,13012,13013],{"class":714}," now",[449,13015,718],{"class":680},[449,13017,5486],{"class":684},[449,13019,4757],{"class":721},[449,13021,1322],{"class":684},[449,13023,13025],{"class":451,"line":13024},56,[449,13026,4661],{"class":684},[449,13028,13030,13032,13034,13036,13039,13041],{"class":451,"line":13029},57,[449,13031,2099],{"class":680},[449,13033,12042],{"class":684},[449,13035,12089],{"class":721},[449,13037,13038],{"class":684},"(orders).",[449,13040,12094],{"class":721},[449,13042,1391],{"class":684},[449,13044,13046],{"class":451,"line":13045},58,[449,13047,13048],{"class":684},"    id: orderId,\n",[449,13050,13052,13055,13057,13060],{"class":451,"line":13051},59,[449,13053,13054],{"class":684},"    customerName: body.customerName ",[449,13056,5090],{"class":680},[449,13058,13059],{"class":691}," \"Guest\"",[449,13061,779],{"class":684},[449,13063,13065,13067,13069],{"class":451,"line":13064},60,[449,13066,5217],{"class":684},[449,13068,5220],{"class":691},[449,13070,779],{"class":684},[449,13072,13074],{"class":451,"line":13073},61,[449,13075,13076],{"class":684},"    total,\n",[449,13078,13080,13082,13084,13086],{"class":451,"line":13079},62,[449,13081,5227],{"class":684},[449,13083,5230],{"class":680},[449,13085,5233],{"class":721},[449,13087,13088],{"class":684},"(now),\n",[449,13090,13092,13095,13097,13099],{"class":451,"line":13091},63,[449,13093,13094],{"class":684},"    updatedAt: ",[449,13096,5230],{"class":680},[449,13098,5233],{"class":721},[449,13100,13088],{"class":684},[449,13102,13104],{"class":451,"line":13103},64,[449,13105,13106],{"class":684},"  });\n",[449,13108,13110],{"class":451,"line":13109},65,[449,13111,4661],{"class":684},[449,13113,13115],{"class":451,"line":13114},66,[449,13116,13117],{"class":705},"  \u002F\u002F Create order items\n",[449,13119,13121,13123,13125,13127,13130,13132],{"class":451,"line":13120},67,[449,13122,5528],{"class":680},[449,13124,1913],{"class":684},[449,13126,711],{"class":680},[449,13128,13129],{"class":714}," input",[449,13131,12076],{"class":680},[449,13133,13134],{"class":684}," orderItemInputs) {\n",[449,13136,13138,13140,13142,13144,13146],{"class":451,"line":13137},68,[449,13139,12761],{"class":680},[449,13141,12764],{"class":714},[449,13143,718],{"class":680},[449,13145,1654],{"class":680},[449,13147,12771],{"class":684},[449,13149,13151,13153,13155],{"class":451,"line":13150},69,[449,13152,12776],{"class":684},[449,13154,12228],{"class":721},[449,13156,11220],{"class":684},[449,13158,13160,13162,13164],{"class":451,"line":13159},70,[449,13161,12776],{"class":684},[449,13163,688],{"class":721},[449,13165,12789],{"class":684},[449,13167,13169,13171,13173,13175,13177],{"class":451,"line":13168},71,[449,13170,12776],{"class":684},[449,13172,12237],{"class":721},[449,13174,725],{"class":684},[449,13176,12800],{"class":721},[449,13178,13179],{"class":684},"(menuItems.id, input.menuItemId))\n",[449,13181,13183,13185,13187],{"class":451,"line":13182},72,[449,13184,12776],{"class":684},[449,13186,12420],{"class":721},[449,13188,1322],{"class":684},[449,13190,13192],{"class":451,"line":13191},73,[449,13193,9155],{"class":684},[449,13195,13197,13199,13201,13203,13206,13208],{"class":451,"line":13196},74,[449,13198,12084],{"class":680},[449,13200,12042],{"class":684},[449,13202,12089],{"class":721},[449,13204,13205],{"class":684},"(orderItems).",[449,13207,12094],{"class":721},[449,13209,1391],{"class":684},[449,13211,13213,13216,13218],{"class":451,"line":13212},75,[449,13214,13215],{"class":684},"      id: crypto.",[449,13217,5205],{"class":721},[449,13219,1624],{"class":684},[449,13221,13223],{"class":451,"line":13222},76,[449,13224,13225],{"class":684},"      orderId,\n",[449,13227,13229],{"class":451,"line":13228},77,[449,13230,13231],{"class":684},"      menuItemId: input.menuItemId,\n",[449,13233,13235],{"class":451,"line":13234},78,[449,13236,13237],{"class":684},"      quantity: input.quantity,\n",[449,13239,13241,13244,13246],{"class":451,"line":13240},79,[449,13242,13243],{"class":684},"      unitPrice: menuItem",[449,13245,5084],{"class":680},[449,13247,13248],{"class":684},".price,\n",[449,13250,13252],{"class":451,"line":13251},80,[449,13253,3484],{"class":684},[449,13255,13257],{"class":451,"line":13256},81,[449,13258,4599],{"class":684},[449,13260,13262],{"class":451,"line":13261},82,[449,13263,4661],{"class":684},[449,13265,13267],{"class":451,"line":13266},83,[449,13268,13269],{"class":705},"  \u002F\u002F Fetch the complete order\n",[449,13271,13273,13275,13278,13280,13282],{"class":451,"line":13272},84,[449,13274,5049],{"class":680},[449,13276,13277],{"class":714}," createdOrder",[449,13279,718],{"class":680},[449,13281,1654],{"class":680},[449,13283,12771],{"class":684},[449,13285,13287,13289,13291],{"class":451,"line":13286},85,[449,13288,10963],{"class":684},[449,13290,12228],{"class":721},[449,13292,11220],{"class":684},[449,13294,13296,13298,13300],{"class":451,"line":13295},86,[449,13297,10963],{"class":684},[449,13299,688],{"class":721},[449,13301,13302],{"class":684},"(orders)\n",[449,13304,13306,13308,13310,13312,13314],{"class":451,"line":13305},87,[449,13307,10963],{"class":684},[449,13309,12237],{"class":721},[449,13311,725],{"class":684},[449,13313,12800],{"class":721},[449,13315,13316],{"class":684},"(orders.id, orderId))\n",[449,13318,13320,13322,13324],{"class":451,"line":13319},88,[449,13321,10963],{"class":684},[449,13323,12420],{"class":721},[449,13325,1322],{"class":684},[449,13327,13329],{"class":451,"line":13328},89,[449,13330,4661],{"class":684},[449,13332,13334,13336,13338,13340,13342],{"class":451,"line":13333},90,[449,13335,5049],{"class":680},[449,13337,12219],{"class":714},[449,13339,718],{"class":680},[449,13341,1654],{"class":680},[449,13343,12771],{"class":684},[449,13345,13347,13349,13351],{"class":451,"line":13346},91,[449,13348,10963],{"class":684},[449,13350,12228],{"class":721},[449,13352,11220],{"class":684},[449,13354,13356,13358,13360],{"class":451,"line":13355},92,[449,13357,10963],{"class":684},[449,13359,688],{"class":721},[449,13361,13362],{"class":684},"(orderItems)\n",[449,13364,13366,13368,13370,13372,13374],{"class":451,"line":13365},93,[449,13367,10963],{"class":684},[449,13369,12237],{"class":721},[449,13371,725],{"class":684},[449,13373,12800],{"class":721},[449,13375,13376],{"class":684},"(orderItems.orderId, orderId));\n",[449,13378,13380],{"class":451,"line":13379},94,[449,13381,4661],{"class":684},[449,13383,13385,13387],{"class":451,"line":13384},95,[449,13386,4727],{"class":680},[449,13388,1501],{"class":684},[449,13390,13392,13395],{"class":451,"line":13391},96,[449,13393,13394],{"class":680},"    ...",[449,13396,13397],{"class":684},"createdOrder,\n",[449,13399,13401],{"class":451,"line":13400},97,[449,13402,13403],{"class":684},"    items,\n",[449,13405,13407],{"class":451,"line":13406},98,[449,13408,4764],{"class":684},[449,13410,13412],{"class":451,"line":13411},99,[449,13413,784],{"class":684},[2808,13415,13417],{"id":13416},"update-order-status","Update Order Status",[21,13419,4698,13420,3070],{},[40,13421,13422],{},"server\u002Fapi\u002Forders\u002F[id].patch.ts",[48,13424,13426],{"className":670,"code":13425,"filename":13422,"language":673,"meta":56,"style":56},"import { db } from \"..\u002Fdb\";\nimport { orders } from \"..\u002Fdb\u002Fschema\";\nimport { eq } from \"drizzle-orm\";\n\nconst validStatuses = [\"pending\", \"preparing\", \"ready\", \"completed\", \"cancelled\"];\n\nexport default defineEventHandler(async (event) => {\n  const id = getRouterParam(event, \"id\");\n  const body = await readBody(event);\n  \n  if (!id) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Order ID is required\",\n    });\n  }\n  \n  if (!body.status || !validStatuses.includes(body.status)) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: `Invalid status. Must be one of: ${validStatuses.join(\", \")}`,\n    });\n  }\n  \n  const existingOrder = await db\n    .select()\n    .from(orders)\n    .where(eq(orders.id, id))\n    .get();\n  \n  if (!existingOrder) {\n    throw createError({\n      statusCode: 404,\n      statusMessage: \"Order not found\",\n    });\n  }\n  \n  \u002F\u002F Update the order\n  await db\n    .update(orders)\n    .set({\n      status: body.status,\n      updatedAt: new Date(),\n    })\n    .where(eq(orders.id, id));\n  \n  const updatedOrder = await db\n    .select()\n    .from(orders)\n    .where(eq(orders.id, id))\n    .get();\n  \n  return updatedOrder;\n});\n",[40,13427,13428,13440,13453,13465,13469,13505,13509,13531,13547,13561,13565,13575,13583,13591,13600,13604,13608,13612,13636,13644,13652,13678,13682,13686,13690,13703,13711,13719,13732,13740,13744,13755,13763,13771,13780,13784,13788,13792,13797,13803,13812,13821,13826,13837,13842,13855,13859,13872,13880,13888,13900,13908,13912,13919],{"__ignoreMap":56},[449,13429,13430,13432,13434,13436,13438],{"class":451,"line":452},[449,13431,681],{"class":680},[449,13433,11728],{"class":684},[449,13435,688],{"class":680},[449,13437,11733],{"class":691},[449,13439,695],{"class":684},[449,13441,13442,13444,13447,13449,13451],{"class":451,"line":114},[449,13443,681],{"class":680},[449,13445,13446],{"class":684}," { orders } ",[449,13448,688],{"class":680},[449,13450,11747],{"class":691},[449,13452,695],{"class":684},[449,13454,13455,13457,13459,13461,13463],{"class":451,"line":111},[449,13456,681],{"class":680},[449,13458,12534],{"class":684},[449,13460,688],{"class":680},[449,13462,10783],{"class":691},[449,13464,695],{"class":684},[449,13466,13467],{"class":451,"line":468},[449,13468,700],{"emptyLinePlaceholder":130},[449,13470,13471,13473,13476,13478,13481,13483,13485,13488,13490,13493,13495,13498,13500,13503],{"class":451,"line":474},[449,13472,711],{"class":680},[449,13474,13475],{"class":714}," validStatuses",[449,13477,718],{"class":680},[449,13479,13480],{"class":684}," [",[449,13482,5220],{"class":691},[449,13484,349],{"class":684},[449,13486,13487],{"class":691},"\"preparing\"",[449,13489,349],{"class":684},[449,13491,13492],{"class":691},"\"ready\"",[449,13494,349],{"class":684},[449,13496,13497],{"class":691},"\"completed\"",[449,13499,349],{"class":684},[449,13501,13502],{"class":691},"\"cancelled\"",[449,13504,4918],{"class":684},[449,13506,13507],{"class":451,"line":480},[449,13508,700],{"emptyLinePlaceholder":130},[449,13510,13511,13513,13515,13517,13519,13521,13523,13525,13527,13529],{"class":451,"line":486},[449,13512,4629],{"class":680},[449,13514,4632],{"class":680},[449,13516,4715],{"class":721},[449,13518,725],{"class":684},[449,13520,1485],{"class":680},[449,13522,1913],{"class":684},[449,13524,2991],{"class":1491},[449,13526,1758],{"class":684},[449,13528,1498],{"class":680},[449,13530,1501],{"class":684},[449,13532,13533,13535,13537,13539,13541,13543,13545],{"class":451,"line":492},[449,13534,5049],{"class":680},[449,13536,12322],{"class":714},[449,13538,718],{"class":680},[449,13540,12327],{"class":721},[449,13542,12330],{"class":684},[449,13544,10828],{"class":691},[449,13546,740],{"class":684},[449,13548,13549,13551,13553,13555,13557,13559],{"class":451,"line":498},[449,13550,5049],{"class":680},[449,13552,5052],{"class":714},[449,13554,718],{"class":680},[449,13556,1654],{"class":680},[449,13558,5059],{"class":721},[449,13560,12619],{"class":684},[449,13562,13563],{"class":451,"line":133},[449,13564,4661],{"class":684},[449,13566,13567,13569,13571,13573],{"class":451,"line":509},[449,13568,5079],{"class":680},[449,13570,1913],{"class":684},[449,13572,5084],{"class":680},[449,13574,12349],{"class":684},[449,13576,13577,13579,13581],{"class":451,"line":318},[449,13578,5109],{"class":680},[449,13580,5112],{"class":721},[449,13582,1391],{"class":684},[449,13584,13585,13587,13589],{"class":451,"line":520},[449,13586,5119],{"class":684},[449,13588,5122],{"class":714},[449,13590,779],{"class":684},[449,13592,13593,13595,13598],{"class":451,"line":526},[449,13594,5129],{"class":684},[449,13596,13597],{"class":691},"\"Order ID is required\"",[449,13599,779],{"class":684},[449,13601,13602],{"class":451,"line":532},[449,13603,3484],{"class":684},[449,13605,13606],{"class":451,"line":2790},[449,13607,4599],{"class":684},[449,13609,13610],{"class":451,"line":3511},[449,13611,4661],{"class":684},[449,13613,13614,13616,13618,13620,13623,13625,13627,13630,13633],{"class":451,"line":3526},[449,13615,5079],{"class":680},[449,13617,1913],{"class":684},[449,13619,5084],{"class":680},[449,13621,13622],{"class":684},"body.status ",[449,13624,5090],{"class":680},[449,13626,12644],{"class":680},[449,13628,13629],{"class":684},"validStatuses.",[449,13631,13632],{"class":721},"includes",[449,13634,13635],{"class":684},"(body.status)) {\n",[449,13637,13638,13640,13642],{"class":451,"line":3531},[449,13639,5109],{"class":680},[449,13641,5112],{"class":721},[449,13643,1391],{"class":684},[449,13645,13646,13648,13650],{"class":451,"line":3536},[449,13647,5119],{"class":684},[449,13649,5122],{"class":714},[449,13651,779],{"class":684},[449,13653,13654,13656,13659,13662,13664,13667,13669,13672,13674,13676],{"class":451,"line":4009},[449,13655,5129],{"class":684},[449,13657,13658],{"class":691},"`Invalid status. Must be one of: ${",[449,13660,13661],{"class":684},"validStatuses",[449,13663,165],{"class":691},[449,13665,13666],{"class":721},"join",[449,13668,725],{"class":691},[449,13670,13671],{"class":691},"\", \"",[449,13673,827],{"class":691},[449,13675,1517],{"class":691},[449,13677,779],{"class":684},[449,13679,13680],{"class":451,"line":4015},[449,13681,3484],{"class":684},[449,13683,13684],{"class":451,"line":4021},[449,13685,4599],{"class":684},[449,13687,13688],{"class":451,"line":4027},[449,13689,4661],{"class":684},[449,13691,13692,13694,13697,13699,13701],{"class":451,"line":4032},[449,13693,5049],{"class":680},[449,13695,13696],{"class":714}," existingOrder",[449,13698,718],{"class":680},[449,13700,1654],{"class":680},[449,13702,12771],{"class":684},[449,13704,13705,13707,13709],{"class":451,"line":4038},[449,13706,10963],{"class":684},[449,13708,12228],{"class":721},[449,13710,11220],{"class":684},[449,13712,13713,13715,13717],{"class":451,"line":4044},[449,13714,10963],{"class":684},[449,13716,688],{"class":721},[449,13718,13302],{"class":684},[449,13720,13721,13723,13725,13727,13729],{"class":451,"line":4049},[449,13722,10963],{"class":684},[449,13724,12237],{"class":721},[449,13726,725],{"class":684},[449,13728,12800],{"class":721},[449,13730,13731],{"class":684},"(orders.id, id))\n",[449,13733,13734,13736,13738],{"class":451,"line":4054},[449,13735,10963],{"class":684},[449,13737,12420],{"class":721},[449,13739,1322],{"class":684},[449,13741,13742],{"class":451,"line":4059},[449,13743,4661],{"class":684},[449,13745,13746,13748,13750,13752],{"class":451,"line":4065},[449,13747,5079],{"class":680},[449,13749,1913],{"class":684},[449,13751,5084],{"class":680},[449,13753,13754],{"class":684},"existingOrder) {\n",[449,13756,13757,13759,13761],{"class":451,"line":4070},[449,13758,5109],{"class":680},[449,13760,5112],{"class":721},[449,13762,1391],{"class":684},[449,13764,13765,13767,13769],{"class":451,"line":4075},[449,13766,5119],{"class":684},[449,13768,12452],{"class":714},[449,13770,779],{"class":684},[449,13772,13773,13775,13778],{"class":451,"line":4081},[449,13774,5129],{"class":684},[449,13776,13777],{"class":691},"\"Order not found\"",[449,13779,779],{"class":684},[449,13781,13782],{"class":451,"line":4086},[449,13783,3484],{"class":684},[449,13785,13786],{"class":451,"line":4092},[449,13787,4599],{"class":684},[449,13789,13790],{"class":451,"line":4098},[449,13791,4661],{"class":684},[449,13793,13794],{"class":451,"line":4104},[449,13795,13796],{"class":705},"  \u002F\u002F Update the order\n",[449,13798,13799,13801],{"class":451,"line":4110},[449,13800,2099],{"class":680},[449,13802,12771],{"class":684},[449,13804,13805,13807,13810],{"class":451,"line":5784},[449,13806,10963],{"class":684},[449,13808,13809],{"class":721},"update",[449,13811,13302],{"class":684},[449,13813,13814,13816,13819],{"class":451,"line":5790},[449,13815,10963],{"class":684},[449,13817,13818],{"class":721},"set",[449,13820,1391],{"class":684},[449,13822,13823],{"class":451,"line":5796},[449,13824,13825],{"class":684},"      status: body.status,\n",[449,13827,13828,13831,13833,13835],{"class":451,"line":5801},[449,13829,13830],{"class":684},"      updatedAt: ",[449,13832,5230],{"class":680},[449,13834,5233],{"class":721},[449,13836,1624],{"class":684},[449,13838,13839],{"class":451,"line":5806},[449,13840,13841],{"class":684},"    })\n",[449,13843,13844,13846,13848,13850,13852],{"class":451,"line":5821},[449,13845,10963],{"class":684},[449,13847,12237],{"class":721},[449,13849,725],{"class":684},[449,13851,12800],{"class":721},[449,13853,13854],{"class":684},"(orders.id, id));\n",[449,13856,13857],{"class":451,"line":5826},[449,13858,4661],{"class":684},[449,13860,13861,13863,13866,13868,13870],{"class":451,"line":5848},[449,13862,5049],{"class":680},[449,13864,13865],{"class":714}," updatedOrder",[449,13867,718],{"class":680},[449,13869,1654],{"class":680},[449,13871,12771],{"class":684},[449,13873,13874,13876,13878],{"class":451,"line":5882},[449,13875,10963],{"class":684},[449,13877,12228],{"class":721},[449,13879,11220],{"class":684},[449,13881,13882,13884,13886],{"class":451,"line":5887},[449,13883,10963],{"class":684},[449,13885,688],{"class":721},[449,13887,13302],{"class":684},[449,13889,13890,13892,13894,13896,13898],{"class":451,"line":5901},[449,13891,10963],{"class":684},[449,13893,12237],{"class":721},[449,13895,725],{"class":684},[449,13897,12800],{"class":721},[449,13899,13731],{"class":684},[449,13901,13902,13904,13906],{"class":451,"line":5921},[449,13903,10963],{"class":684},[449,13905,12420],{"class":721},[449,13907,1322],{"class":684},[449,13909,13910],{"class":451,"line":5940},[449,13911,4661],{"class":684},[449,13913,13914,13916],{"class":451,"line":5975},[449,13915,4727],{"class":680},[449,13917,13918],{"class":684}," updatedOrder;\n",[449,13920,13921],{"class":451,"line":5980},[449,13922,784],{"class":684},[2808,13924,13926],{"id":13925},"get-all-orders","Get All Orders",[21,13928,4698,13929,3070],{},[40,13930,13931],{},"server\u002Fapi\u002Forders.get.ts",[48,13933,13935],{"className":670,"code":13934,"filename":13931,"language":673,"meta":56,"style":56},"import { db } from \"..\u002Fdb\";\nimport { orders, orderItems } from \"..\u002Fdb\u002Fschema\";\n\nexport default defineEventHandler(async () => {\n  const allOrders = await db\n    .select()\n    .from(orders)\n    .orderBy(orders.createdAt);\n  \n  \u002F\u002F Fetch items for each order\n  const ordersWithItems = await Promise.all(\n    allOrders.map(async (order) => {\n      const items = await db\n        .select()\n        .from(orderItems)\n        .where(orderItems.orderId.equals(order.id));\n      return { ...order, items };\n    })\n  );\n  \n  return { orders: ordersWithItems };\n});\n",[40,13936,13937,13949,13962,13966,13984,13997,14005,14013,14023,14027,14032,14051,14073,14086,14095,14103,14117,14131,14135,14139,14143,14150],{"__ignoreMap":56},[449,13938,13939,13941,13943,13945,13947],{"class":451,"line":452},[449,13940,681],{"class":680},[449,13942,11728],{"class":684},[449,13944,688],{"class":680},[449,13946,11733],{"class":691},[449,13948,695],{"class":684},[449,13950,13951,13953,13956,13958,13960],{"class":451,"line":114},[449,13952,681],{"class":680},[449,13954,13955],{"class":684}," { orders, orderItems } ",[449,13957,688],{"class":680},[449,13959,11747],{"class":691},[449,13961,695],{"class":684},[449,13963,13964],{"class":451,"line":111},[449,13965,700],{"emptyLinePlaceholder":130},[449,13967,13968,13970,13972,13974,13976,13978,13980,13982],{"class":451,"line":468},[449,13969,4629],{"class":680},[449,13971,4632],{"class":680},[449,13973,4715],{"class":721},[449,13975,725],{"class":684},[449,13977,1485],{"class":680},[449,13979,2090],{"class":684},[449,13981,1498],{"class":680},[449,13983,1501],{"class":684},[449,13985,13986,13988,13991,13993,13995],{"class":451,"line":474},[449,13987,5049],{"class":680},[449,13989,13990],{"class":714}," allOrders",[449,13992,718],{"class":680},[449,13994,1654],{"class":680},[449,13996,12771],{"class":684},[449,13998,13999,14001,14003],{"class":451,"line":480},[449,14000,10963],{"class":684},[449,14002,12228],{"class":721},[449,14004,11220],{"class":684},[449,14006,14007,14009,14011],{"class":451,"line":486},[449,14008,10963],{"class":684},[449,14010,688],{"class":721},[449,14012,13302],{"class":684},[449,14014,14015,14017,14020],{"class":451,"line":492},[449,14016,10963],{"class":684},[449,14018,14019],{"class":721},"orderBy",[449,14021,14022],{"class":684},"(orders.createdAt);\n",[449,14024,14025],{"class":451,"line":498},[449,14026,4661],{"class":684},[449,14028,14029],{"class":451,"line":133},[449,14030,14031],{"class":705},"  \u002F\u002F Fetch items for each order\n",[449,14033,14034,14036,14039,14041,14043,14045,14047,14049],{"class":451,"line":509},[449,14035,5049],{"class":680},[449,14037,14038],{"class":714}," ordersWithItems",[449,14040,718],{"class":680},[449,14042,1654],{"class":680},[449,14044,5160],{"class":714},[449,14046,165],{"class":684},[449,14048,5815],{"class":721},[449,14050,5566],{"class":684},[449,14052,14053,14056,14058,14060,14062,14064,14067,14069,14071],{"class":451,"line":318},[449,14054,14055],{"class":684},"    allOrders.",[449,14057,6397],{"class":721},[449,14059,725],{"class":684},[449,14061,1485],{"class":680},[449,14063,1913],{"class":684},[449,14065,14066],{"class":1491},"order",[449,14068,1758],{"class":684},[449,14070,1498],{"class":680},[449,14072,1501],{"class":684},[449,14074,14075,14078,14080,14082,14084],{"class":451,"line":520},[449,14076,14077],{"class":680},"      const",[449,14079,12219],{"class":714},[449,14081,718],{"class":680},[449,14083,1654],{"class":680},[449,14085,12771],{"class":684},[449,14087,14088,14091,14093],{"class":451,"line":526},[449,14089,14090],{"class":684},"        .",[449,14092,12228],{"class":721},[449,14094,11220],{"class":684},[449,14096,14097,14099,14101],{"class":451,"line":532},[449,14098,14090],{"class":684},[449,14100,688],{"class":721},[449,14102,13362],{"class":684},[449,14104,14105,14107,14109,14112,14114],{"class":451,"line":2790},[449,14106,14090],{"class":684},[449,14108,12237],{"class":721},[449,14110,14111],{"class":684},"(orderItems.orderId.",[449,14113,12414],{"class":721},[449,14115,14116],{"class":684},"(order.id));\n",[449,14118,14119,14122,14125,14128],{"class":451,"line":3511},[449,14120,14121],{"class":680},"      return",[449,14123,14124],{"class":684}," { ",[449,14126,14127],{"class":680},"...",[449,14129,14130],{"class":684},"order, items };\n",[449,14132,14133],{"class":451,"line":3526},[449,14134,13841],{"class":684},[449,14136,14137],{"class":451,"line":3531},[449,14138,6436],{"class":684},[449,14140,14141],{"class":451,"line":3536},[449,14142,4661],{"class":684},[449,14144,14145,14147],{"class":451,"line":4009},[449,14146,4727],{"class":680},[449,14148,14149],{"class":684}," { orders: ordersWithItems };\n",[449,14151,14152],{"class":451,"line":4015},[449,14153,784],{"class":684},[73,14155],{},[32,14157,14159],{"id":14158},"step-7-run-migrations","Step 7: Run Migrations",[21,14161,14162],{},"Now that our schema is defined, let's generate and push the migrations.",[48,14164,14166],{"className":839,"code":14165,"filename":4384,"language":841,"meta":56,"style":56},"# Generate migration files\nnpx drizzle-kit generate:sqlite\n\n# Push schema to database\nnpx drizzle-kit push:sqlite\n",[40,14167,14168,14173,14182,14186,14191],{"__ignoreMap":56},[449,14169,14170],{"class":451,"line":452},[449,14171,14172],{"class":705},"# Generate migration files\n",[449,14174,14175,14177,14179],{"class":451,"line":114},[449,14176,8898],{"class":721},[449,14178,10609],{"class":691},[449,14180,14181],{"class":691}," generate:sqlite\n",[449,14183,14184],{"class":451,"line":111},[449,14185,700],{"emptyLinePlaceholder":130},[449,14187,14188],{"class":451,"line":468},[449,14189,14190],{"class":705},"# Push schema to database\n",[449,14192,14193,14195,14197],{"class":451,"line":474},[449,14194,8898],{"class":721},[449,14196,10609],{"class":691},[449,14198,14199],{"class":691}," push:sqlite\n",[427,14201,14204],{"icon":14202,"title":14203},"🛠️","Migration Workflow",[21,14205,14206,14207,14210,14211,14214],{},"In development, you can use ",[40,14208,14209],{},"push:sqlite"," to sync changes quickly. For production, use ",[40,14212,14213],{},"generate"," to create migration files and run them separately.",[73,14216],{},[32,14218,14220],{"id":14219},"step-8-test-the-api","Step 8: Test the API",[21,14222,14223],{},"Start your server and test the endpoints:",[48,14225,14226],{"className":839,"code":3854,"filename":4384,"language":841,"meta":56,"style":56},[40,14227,14228],{"__ignoreMap":56},[449,14229,14230,14232,14234],{"class":451,"line":452},[449,14231,3703],{"class":721},[449,14233,5296],{"class":691},[449,14235,5299],{"class":691},[2808,14237,14239],{"id":14238},"seed-the-menu","Seed the menu:",[48,14241,14243],{"className":839,"code":14242,"language":841,"meta":56,"style":56},"curl -X POST http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fseed\n",[40,14244,14245],{"__ignoreMap":56},[449,14246,14247,14249,14252,14255],{"class":451,"line":452},[449,14248,4396],{"class":721},[449,14250,14251],{"class":714}," -X",[449,14253,14254],{"class":691}," POST",[449,14256,14257],{"class":691}," http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fseed\n",[2808,14259,14261],{"id":14260},"get-the-menu","Get the menu:",[48,14263,14265],{"className":839,"code":14264,"language":841,"meta":56,"style":56},"curl http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fmenu\n",[40,14266,14267],{"__ignoreMap":56},[449,14268,14269,14271],{"class":451,"line":452},[449,14270,4396],{"class":721},[449,14272,14273],{"class":691}," http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fmenu\n",[2808,14275,14277],{"id":14276},"create-an-order","Create an order:",[48,14279,14281],{"className":839,"code":14280,"language":841,"meta":56,"style":56},"curl -X POST http:\u002F\u002Flocalhost:3000\u002Fapi\u002Forders \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\n    \"customerName\": \"John\",\n    \"items\": [\n      { \"menuItemId\": \"latte\", \"quantity\": 2 },\n      { \"menuItemId\": \"croissant\", \"quantity\": 1 }\n    ]\n  }'\n",[40,14282,14283,14297,14307,14315,14320,14325,14330,14335,14340],{"__ignoreMap":56},[449,14284,14285,14287,14289,14291,14294],{"class":451,"line":452},[449,14286,4396],{"class":721},[449,14288,14251],{"class":714},[449,14290,14254],{"class":691},[449,14292,14293],{"class":691}," http:\u002F\u002Flocalhost:3000\u002Fapi\u002Forders",[449,14295,14296],{"class":714}," \\\n",[449,14298,14299,14302,14305],{"class":451,"line":114},[449,14300,14301],{"class":714},"  -H",[449,14303,14304],{"class":691}," \"Content-Type: application\u002Fjson\"",[449,14306,14296],{"class":714},[449,14308,14309,14312],{"class":451,"line":111},[449,14310,14311],{"class":714},"  -d",[449,14313,14314],{"class":691}," '{\n",[449,14316,14317],{"class":451,"line":468},[449,14318,14319],{"class":691},"    \"customerName\": \"John\",\n",[449,14321,14322],{"class":451,"line":474},[449,14323,14324],{"class":691},"    \"items\": [\n",[449,14326,14327],{"class":451,"line":480},[449,14328,14329],{"class":691},"      { \"menuItemId\": \"latte\", \"quantity\": 2 },\n",[449,14331,14332],{"class":451,"line":486},[449,14333,14334],{"class":691},"      { \"menuItemId\": \"croissant\", \"quantity\": 1 }\n",[449,14336,14337],{"class":451,"line":492},[449,14338,14339],{"class":691},"    ]\n",[449,14341,14342],{"class":451,"line":498},[449,14343,14344],{"class":691},"  }'\n",[2808,14346,14348],{"id":14347},"update-order-status-1","Update order status:",[48,14350,14352],{"className":839,"code":14351,"language":841,"meta":56,"style":56},"curl -X PATCH http:\u002F\u002Flocalhost:3000\u002Fapi\u002Forders\u002FORDER_ID \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"status\": \"preparing\"}'\n",[40,14353,14354,14368,14376],{"__ignoreMap":56},[449,14355,14356,14358,14360,14363,14366],{"class":451,"line":452},[449,14357,4396],{"class":721},[449,14359,14251],{"class":714},[449,14361,14362],{"class":691}," PATCH",[449,14364,14365],{"class":691}," http:\u002F\u002Flocalhost:3000\u002Fapi\u002Forders\u002FORDER_ID",[449,14367,14296],{"class":714},[449,14369,14370,14372,14374],{"class":451,"line":114},[449,14371,14301],{"class":714},[449,14373,14304],{"class":691},[449,14375,14296],{"class":714},[449,14377,14378,14380],{"class":451,"line":111},[449,14379,14311],{"class":714},[449,14381,14382],{"class":691}," '{\"status\": \"preparing\"}'\n",[73,14384],{},[32,14386,6831],{"id":6830},[21,14388,6834],{},[383,14390,14391,14394,14397,14400,14403],{},[386,14392,14393],{},"A fully typed database schema using Drizzle ORM",[386,14395,14396],{},"Type-safe queries throughout your API",[386,14398,14399],{},"Proper data validation and error handling",[386,14401,14402],{},"Order creation with transaction-like behavior",[386,14404,14405],{},"Status update workflow",[2808,14407,6855],{"id":6854},[547,14409,14410,14421],{},[550,14411,14412],{},[553,14413,14414,14417,14419],{},[556,14415,14416],{},"Method",[556,14418,6864],{},[556,14420,923],{},[569,14422,14423,14436,14448,14460,14471,14484],{},[553,14424,14425,14428,14433],{},[574,14426,14427],{},"GET",[574,14429,14430],{},[40,14431,14432],{},"\u002Fapi\u002Fmenu",[574,14434,14435],{},"Get available menu items",[553,14437,14438,14440,14445],{},[574,14439,14427],{},[574,14441,14442],{},[40,14443,14444],{},"\u002Fapi\u002Fmenu\u002F:id",[574,14446,14447],{},"Get single menu item",[553,14449,14450,14453,14457],{},[574,14451,14452],{},"POST",[574,14454,14455],{},[40,14456,6917],{},[574,14458,14459],{},"Create new order",[553,14461,14462,14464,14468],{},[574,14463,14427],{},[574,14465,14466],{},[40,14467,6917],{},[574,14469,14470],{},"Get all orders",[553,14472,14473,14476,14481],{},[574,14474,14475],{},"PATCH",[574,14477,14478],{},[40,14479,14480],{},"\u002Fapi\u002Forders\u002F:id",[574,14482,14483],{},"Update order status",[553,14485,14486,14488,14493],{},[574,14487,14452],{},[574,14489,14490],{},[40,14491,14492],{},"\u002Fapi\u002Fseed",[574,14494,14495],{},"Seed menu data",[73,14497],{},[16,14499,3681],{"id":3680},[21,14501,14502,14503,14506],{},"Drizzle ORM transforms your database layer from an afterthought into a ",[151,14504,14505],{},"first-class citizen"," of your application. The type safety alone justifies the switch—no more runtime errors from typos in column names, no more guessing what a query returns.",[21,14508,14509],{},"Combined with Nitro's filesystem routing and deployment flexibility, you have a production-ready stack that scales:",[383,14511,14512,14518,14524],{},[386,14513,14514,14517],{},[151,14515,14516],{},"Start with SQLite"," for development and small projects",[386,14519,14520,14523],{},[151,14521,14522],{},"Switch to PostgreSQL"," for production without changing your code",[386,14525,14526,14529],{},[151,14527,14528],{},"Deploy anywhere"," — Node.js, serverless, edge",[21,14531,14532,14533,14536],{},"In the next post, we'll add ",[151,14534,14535],{},"authentication"," to protect our order management endpoints. Until then, enjoy your type-safe API! ☕🚀",[3728,14538,14539],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":14541},[14542,14543,14544,14545,14546,14547,14548,14555,14556,14562],{"id":10526,"depth":114,"text":10527},{"id":10546,"depth":114,"text":10547},{"id":10622,"depth":114,"text":10623},{"id":10723,"depth":114,"text":10724},{"id":11404,"depth":114,"text":11405},{"id":11707,"depth":114,"text":11708},{"id":12151,"depth":114,"text":12152,"children":14549},[14550,14551,14552,14553,14554],{"id":12155,"depth":111,"text":12156},{"id":12254,"depth":111,"text":12255},{"id":12489,"depth":111,"text":12490},{"id":13416,"depth":111,"text":13417},{"id":13925,"depth":111,"text":13926},{"id":14158,"depth":114,"text":14159},{"id":14219,"depth":114,"text":14220,"children":14557},[14558,14559,14560,14561],{"id":14238,"depth":111,"text":14239},{"id":14260,"depth":111,"text":14261},{"id":14276,"depth":111,"text":14277},{"id":14347,"depth":111,"text":14348},{"id":6830,"depth":114,"text":6831,"children":14563},[14564],{"id":6854,"depth":111,"text":6855},"Learn how to build a production-ready API using Drizzle ORM with Nitro. From setup to deployment, this guide covers everything you need for type-safe database operations.",{"type":13,"value":14567},[14568,14570,14574,14578,14580],[16,14569,333],{"id":332},[21,14571,10454,14572,10458],{},[151,14573,10457],{},[21,14575,10461,14576,10465],{},[151,14577,10464],{},[21,14579,10468],{},[21,14581,10471,14582,10475],{},[151,14583,10474],{},[14585,14586,7076,14587,14588,14589,3784],"drizzle","orm","database","postgres","sqlite",{},"\u002Fblog\u002Fdrizzle-nitro-api",{"title":10446,"description":14565},"blog\u002Fdrizzle-nitro-api","hs3hXuZ30XGpX2W4zEGOKNf1WS5VkLpwh9An2SIz5jY",{"id":14596,"title":14597,"author":14598,"body":14599,"canonical":116,"categories":116,"date":14640,"description":14641,"excerpt":14642,"extension":119,"img":14648,"keywords":14649,"language":14650,"meta":14651,"navigation":130,"ogImage":116,"path":14652,"rating":132,"readTime":474,"seo":14653,"stem":14654,"tag":14655,"__hash__":14656},"blog\u002Fblog\u002Ffirst-post.md","First Post - Who Am I?",{"name":9,"website":10,"img":11},{"type":13,"value":14600,"toc":14635},[14601,14605,14608,14611,14614,14618,14621,14625,14628,14632],[16,14602,14604],{"id":14603},"just-starting","Just Starting",[21,14606,14607],{},"Hey, I am Vantol, I am a Jamaican - FullStack Developer - and Educator in the Jamaican school system. (I teach Math to kids! Help Me! Please!) LOL!",[21,14609,14610],{},"I have been coding for about 10 years, and I have experimented with most programming languages but I have come to love Javascript - Node.js, Vue.js, Nuxt.js, React, MongoDB\u002FPostgres in my stack. I have been playing with Django Rest Framework of recently and might make something with one day.",[21,14612,14613],{},"In 2021, my focused shifted to making me a concept of grow along with skills and ideas that can change the world.",[32,14615,14617],{"id":14616},"starting-a-new-path","Starting a new path",[21,14619,14620],{},"Basically moving forward in 2022 will be more about Open Source, Content Creation (from Youtube and Twitch Stream) and Npm packages.",[32,14622,14624],{"id":14623},"focus","Focus",[21,14626,14627],{},"'Evolve with Innovation' has always been a slogan for me, and what better way to evolve but to make myself better as a person, father and friend. Outreach and giving back is something I have always wanted to do, so i created Zhyjenae. Zhyjenae is about helping young persons (whether in Jamaica or worldwide) appreciate coding and the doors in which it can open.",[32,14629,14631],{"id":14630},"continued-faith","Continued Faith",[21,14633,14634],{},"It's important that I state I am a Christian and increasing my faith in the Father is important and I want to show that in my coding going forward. In times like these its important to my trust in Him so I can be better in every, shape or form.",{"title":56,"searchDepth":111,"depth":111,"links":14636},[14637,14638,14639],{"id":14616,"depth":114,"text":14617},{"id":14623,"depth":114,"text":14624},{"id":14630,"depth":114,"text":14631},"2021-11-14","Hey, I am Vantol, I am a Jamaican. Get to know me!",{"type":13,"value":14643},[14644,14646],[16,14645,14604],{"id":14603},[21,14647,14607],{},"https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1636919448\u002FDSC_0991_snlozn.jpg",[122,123],"Buy a coffee Meme",{},"\u002Fblog\u002Ffirst-post",{"title":14597,"description":14641},"blog\u002Ffirst-post","Me","gU8LLHpsi2enw1rOK7IKIn01SlAUqMESrO7y_S24NjY",{"id":14658,"title":14659,"author":14660,"body":14661,"canonical":116,"categories":116,"date":15676,"description":15677,"excerpt":15678,"extension":119,"img":15726,"keywords":15727,"language":3788,"meta":15730,"navigation":130,"ogImage":116,"path":15731,"rating":498,"readTime":133,"seo":15732,"stem":15733,"tag":8316,"__hash__":15734},"blog\u002Fblog\u002Ffood-truck-p1.md","Building a Real App with Nitro (Part 1): Foundations of a Food Truck API",{"name":9,"img":11,"website":327},{"type":13,"value":14662,"toc":15660},[14663,14665,14683,14690,14707,14713,14727,14733,14735,14739,14746,14750,14764,14767,14778,14780,14784,14787,14801,14806,14808,14812,14846,14849,14855,14858,14860,14864,14871,14876,15051,15054,15056,15060,15064,15118,15121,15127,15130,15137,15139,15143,15148,15231,15234,15236,15240,15243,15248,15310,15313,15357,15360,15362,15366,15370,15505,15512,15515,15517,15521,15527,15559,15562,15564,15568,15571,15587,15591,15607,15609,15613,15618,15635,15637,15639,15646,15654,15657],[16,14664,333],{"id":332},[21,14666,14667,14668,14671,14672,14675,14676,14678,14679,14682],{},"Most backend tutorials stop at ",[156,14669,14670],{},"“Hello World”",". In this ",[151,14673,14674],{},"two-part series",", we’ll go further by using ",[151,14677,4189],{}," to build an ",[151,14680,14681],{},"actual backend application"," — a food truck \u002F coffee shop API that could realistically power a real product.",[21,14684,14685,14686,14689],{},"In ",[151,14687,14688],{},"Part 1",", we’ll focus on:",[383,14691,14692,14695,14698,14701,14704],{},[386,14693,14694],{},"Project setup",[386,14696,14697],{},"Application structure",[386,14699,14700],{},"Core API routes (menu, orders, hours)",[386,14702,14703],{},"Runtime configuration",[386,14705,14706],{},"Local development",[21,14708,14685,14709,14712],{},[151,14710,14711],{},"Part 2",", we’ll expand this into:",[383,14714,14715,14718,14721,14724],{},[386,14716,14717],{},"Order creation and validation",[386,14719,14720],{},"Persistence (in-memory → storage)",[386,14722,14723],{},"Middleware and plugins",[386,14725,14726],{},"Deployment-ready architecture",[21,14728,14729,14730,165],{},"This series assumes basic JavaScript\u002FTypeScript knowledge but ",[151,14731,14732],{},"no prior Nitro experience",[16,14734,4373],{"id":4372},[32,14736,14738],{"id":14737},"the-app-were-building","The App We’re Building",[21,14740,14741,14742,14745],{},"Our fictional app: ",[151,14743,14744],{},"BrewStop"," ☕🚚 — a mobile coffee truck.",[2808,14747,14749],{"id":14748},"core-features-backend","Core Features (Backend)",[383,14751,14752,14755,14758,14761],{},[386,14753,14754],{},"Fetch menu items",[386,14756,14757],{},"View opening hours",[386,14759,14760],{},"Place orders",[386,14762,14763],{},"Track order status",[21,14765,14766],{},"Nitro will serve as:",[383,14768,14769,14772,14775],{},[386,14770,14771],{},"The API server",[386,14773,14774],{},"The runtime abstraction layer",[386,14776,14777],{},"The deployment target (Node, serverless, edge)",[73,14779],{},[32,14781,14783],{"id":14782},"why-nitro-for-this-app","Why Nitro for This App?",[21,14785,14786],{},"Nitro is perfect for this use case because:",[383,14788,14789,14792,14795,14798],{},[386,14790,14791],{},"It’s lightweight and fast",[386,14793,14794],{},"It supports filesystem routing",[386,14796,14797],{},"It deploys anywhere",[386,14799,14800],{},"It works great with frontend frameworks later",[25,14802,14803],{"type":363},[21,14804,14805],{},"Nitro is the same server engine powering Nuxt — meaning anything we build here can later scale into a fullstack Nuxt app.",[73,14807],{},[32,14809,14811],{"id":14810},"step-1-create-the-nitro-project","Step 1: Create the Nitro Project",[48,14813,14815],{"className":839,"code":14814,"language":841,"meta":56,"style":56},"npx create-nitro-app brewstop-api\ncd brewstop-api\nnpm install\nnpm run dev\n",[40,14816,14817,14826,14832,14838],{"__ignoreMap":56},[449,14818,14819,14821,14823],{"class":451,"line":452},[449,14820,8898],{"class":721},[449,14822,10567],{"class":691},[449,14824,14825],{"class":691}," brewstop-api\n",[449,14827,14828,14830],{"class":451,"line":114},[449,14829,4490],{"class":714},[449,14831,14825],{"class":691},[449,14833,14834,14836],{"class":451,"line":111},[449,14835,3703],{"class":721},[449,14837,4512],{"class":691},[449,14839,14840,14842,14844],{"class":451,"line":468},[449,14841,3703],{"class":721},[449,14843,5296],{"class":691},[449,14845,5299],{"class":691},[21,14847,14848],{},"Your project structure will look like this:",[48,14850,14853],{"className":14851,"code":14852,"language":53},[51],"server\u002F\n  api\u002F\n  routes\u002F\n  plugins\u002F\nnitro.config.ts\n",[40,14854,14852],{"__ignoreMap":56},[21,14856,14857],{},"Nitro automatically wires everything together.",[73,14859],{},[32,14861,14863],{"id":14862},"step-2-define-the-menu-data","Step 2: Define the Menu Data",[21,14865,14866,14867,14870],{},"For now, we’ll start with ",[151,14868,14869],{},"in-memory data"," (we’ll improve this in Part 2).",[21,14872,4698,14873,3070],{},[40,14874,14875],{},"server\u002Fdata\u002Fmenu.ts",[48,14877,14879],{"className":670,"code":14878,"language":673,"meta":56,"style":56},"export interface MenuItem {\n  id: string\n  name: string\n  price: number\n  category: \"coffee\" | \"tea\" | \"food\"\n}\n\nexport const menu: MenuItem[] = [\n  { id: \"latte\", name: \"Latte\", price: 4.5, category: \"coffee\" },\n  { id: \"espresso\", name: \"Espresso\", price: 3, category: \"coffee\" },\n  { id: \"chai\", name: \"Chai Tea\", price: 4, category: \"tea\" },\n  { id: \"croissant\", name: \"Croissant\", price: 3.5, category: \"food\" }\n]\n",[40,14880,14881,14892,14901,14909,14918,14938,14942,14946,14965,14985,15005,15025,15046],{"__ignoreMap":56},[449,14882,14883,14885,14888,14890],{"class":451,"line":452},[449,14884,4629],{"class":680},[449,14886,14887],{"class":680}," interface",[449,14889,11336],{"class":721},[449,14891,1501],{"class":684},[449,14893,14894,14896,14898],{"class":451,"line":114},[449,14895,6067],{"class":1491},[449,14897,3070],{"class":680},[449,14899,14900],{"class":714}," string\n",[449,14902,14903,14905,14907],{"class":451,"line":111},[449,14904,6078],{"class":1491},[449,14906,3070],{"class":680},[449,14908,14900],{"class":714},[449,14910,14911,14913,14915],{"class":451,"line":468},[449,14912,6090],{"class":1491},[449,14914,3070],{"class":680},[449,14916,14917],{"class":714}," number\n",[449,14919,14920,14923,14925,14928,14930,14933,14935],{"class":451,"line":474},[449,14921,14922],{"class":1491},"  category",[449,14924,3070],{"class":680},[449,14926,14927],{"class":691}," \"coffee\"",[449,14929,4405],{"class":680},[449,14931,14932],{"class":691}," \"tea\"",[449,14934,4405],{"class":680},[449,14936,14937],{"class":691}," \"food\"\n",[449,14939,14940],{"class":451,"line":480},[449,14941,4604],{"class":684},[449,14943,14944],{"class":451,"line":486},[449,14945,700],{"emptyLinePlaceholder":130},[449,14947,14948,14950,14952,14955,14957,14959,14961,14963],{"class":451,"line":492},[449,14949,4629],{"class":680},[449,14951,10801],{"class":680},[449,14953,14954],{"class":714}," menu",[449,14956,3070],{"class":680},[449,14958,11336],{"class":721},[449,14960,5012],{"class":684},[449,14962,5015],{"class":680},[449,14964,4791],{"class":684},[449,14966,14967,14969,14971,14973,14975,14977,14979,14981,14983],{"class":451,"line":498},[449,14968,4796],{"class":684},[449,14970,11771],{"class":691},[449,14972,4802],{"class":684},[449,14974,4805],{"class":691},[449,14976,4808],{"class":684},[449,14978,4811],{"class":714},[449,14980,4814],{"class":684},[449,14982,4817],{"class":691},[449,14984,4820],{"class":684},[449,14986,14987,14989,14991,14993,14995,14997,14999,15001,15003],{"class":451,"line":133},[449,14988,4796],{"class":684},[449,14990,11798],{"class":691},[449,14992,4802],{"class":684},[449,14994,4832],{"class":691},[449,14996,4808],{"class":684},[449,14998,4850],{"class":714},[449,15000,4814],{"class":684},[449,15002,4817],{"class":691},[449,15004,4820],{"class":684},[449,15006,15007,15009,15011,15013,15015,15017,15019,15021,15023],{"class":451,"line":509},[449,15008,4796],{"class":684},[449,15010,11878],{"class":691},[449,15012,4802],{"class":684},[449,15014,4879],{"class":691},[449,15016,4808],{"class":684},[449,15018,4874],{"class":714},[449,15020,4814],{"class":684},[449,15022,4889],{"class":691},[449,15024,4820],{"class":684},[449,15026,15027,15029,15031,15033,15035,15037,15039,15041,15043],{"class":451,"line":318},[449,15028,4796],{"class":684},[449,15030,11931],{"class":691},[449,15032,4802],{"class":684},[449,15034,4855],{"class":691},[449,15036,4808],{"class":684},[449,15038,4860],{"class":714},[449,15040,4814],{"class":684},[449,15042,4865],{"class":691},[449,15044,15045],{"class":684}," }\n",[449,15047,15048],{"class":451,"line":520},[449,15049,15050],{"class":684},"]\n",[21,15052,15053],{},"This shared file keeps our types and data clean.",[73,15055],{},[32,15057,15059],{"id":15058},"step-3-create-the-menu-api-route","Step 3: Create the Menu API Route",[21,15061,4698,15062,3070],{},[40,15063,12161],{},[48,15065,15067],{"className":670,"code":15066,"language":673,"meta":56,"style":56},"import { menu } from \"..\u002Fdata\u002Fmenu\"\n\nexport default defineEventHandler(() => {\n  return {\n    items: menu\n  }\n})\n",[40,15068,15069,15081,15085,15099,15105,15110,15114],{"__ignoreMap":56},[449,15070,15071,15073,15076,15078],{"class":451,"line":452},[449,15072,681],{"class":680},[449,15074,15075],{"class":684}," { menu } ",[449,15077,688],{"class":680},[449,15079,15080],{"class":691}," \"..\u002Fdata\u002Fmenu\"\n",[449,15082,15083],{"class":451,"line":114},[449,15084,700],{"emptyLinePlaceholder":130},[449,15086,15087,15089,15091,15093,15095,15097],{"class":451,"line":111},[449,15088,4629],{"class":680},[449,15090,4632],{"class":680},[449,15092,4715],{"class":721},[449,15094,4718],{"class":684},[449,15096,1498],{"class":680},[449,15098,1501],{"class":684},[449,15100,15101,15103],{"class":451,"line":468},[449,15102,4727],{"class":680},[449,15104,1501],{"class":684},[449,15106,15107],{"class":451,"line":474},[449,15108,15109],{"class":684},"    items: menu\n",[449,15111,15112],{"class":451,"line":480},[449,15113,4599],{"class":684},[449,15115,15116],{"class":451,"line":486},[449,15117,4113],{"class":684},[21,15119,15120],{},"Now visit:",[48,15122,15125],{"className":15123,"code":15124,"language":53},[51],"http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fmenu\n",[40,15126,15124],{"__ignoreMap":56},[21,15128,15129],{},"You’ll receive structured JSON instantly.",[21,15131,15132,15133,15136],{},"Filesystem routing means no router config, no boilerplate — your file structure ",[156,15134,15135],{},"is"," your API.\n::",[73,15138],{},[32,15140,15142],{"id":15141},"step-4-add-business-hours","Step 4: Add Business Hours",[21,15144,4698,15145,3070],{},[40,15146,15147],{},"server\u002Fapi\u002Fhours.get.ts",[48,15149,15151],{"className":670,"code":15150,"language":673,"meta":56,"style":56},"export default defineEventHandler(() => {\n  return {\n    open: \"08:00\",\n    close: \"16:00\",\n    days: [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\"]\n  }\n})\n",[40,15152,15153,15167,15173,15183,15193,15223,15227],{"__ignoreMap":56},[449,15154,15155,15157,15159,15161,15163,15165],{"class":451,"line":452},[449,15156,4629],{"class":680},[449,15158,4632],{"class":680},[449,15160,4715],{"class":721},[449,15162,4718],{"class":684},[449,15164,1498],{"class":680},[449,15166,1501],{"class":684},[449,15168,15169,15171],{"class":451,"line":114},[449,15170,4727],{"class":680},[449,15172,1501],{"class":684},[449,15174,15175,15178,15181],{"class":451,"line":111},[449,15176,15177],{"class":684},"    open: ",[449,15179,15180],{"class":691},"\"08:00\"",[449,15182,779],{"class":684},[449,15184,15185,15188,15191],{"class":451,"line":468},[449,15186,15187],{"class":684},"    close: ",[449,15189,15190],{"class":691},"\"16:00\"",[449,15192,779],{"class":684},[449,15194,15195,15198,15201,15203,15206,15208,15211,15213,15216,15218,15221],{"class":451,"line":474},[449,15196,15197],{"class":684},"    days: [",[449,15199,15200],{"class":691},"\"Monday\"",[449,15202,349],{"class":684},[449,15204,15205],{"class":691},"\"Tuesday\"",[449,15207,349],{"class":684},[449,15209,15210],{"class":691},"\"Wednesday\"",[449,15212,349],{"class":684},[449,15214,15215],{"class":691},"\"Thursday\"",[449,15217,349],{"class":684},[449,15219,15220],{"class":691},"\"Friday\"",[449,15222,15050],{"class":684},[449,15224,15225],{"class":451,"line":480},[449,15226,4599],{"class":684},[449,15228,15229],{"class":451,"line":486},[449,15230,4113],{"class":684},[21,15232,15233],{},"This allows the frontend to determine if orders are allowed.",[73,15235],{},[32,15237,15239],{"id":15238},"step-5-runtime-configuration","Step 5: Runtime Configuration",[21,15241,15242],{},"Let’s make the truck name configurable.",[21,15244,4616,15245,3070],{},[40,15246,15247],{},"nitro.config.ts",[48,15249,15251],{"className":670,"code":15250,"language":673,"meta":56,"style":56},"import { defineNitroConfig } from \"nitro\u002Fconfig\"\n\nexport default defineNitroConfig({\n  runtimeConfig: {\n    public: {\n      truckName: \"BrewStop Coffee\"\n    }\n  }\n})\n",[40,15252,15253,15265,15269,15280,15285,15290,15298,15302,15306],{"__ignoreMap":56},[449,15254,15255,15257,15260,15262],{"class":451,"line":452},[449,15256,681],{"class":680},[449,15258,15259],{"class":684}," { defineNitroConfig } ",[449,15261,688],{"class":680},[449,15263,15264],{"class":691}," \"nitro\u002Fconfig\"\n",[449,15266,15267],{"class":451,"line":114},[449,15268,700],{"emptyLinePlaceholder":130},[449,15270,15271,15273,15275,15278],{"class":451,"line":111},[449,15272,4629],{"class":680},[449,15274,4632],{"class":680},[449,15276,15277],{"class":721}," defineNitroConfig",[449,15279,1391],{"class":684},[449,15281,15282],{"class":451,"line":468},[449,15283,15284],{"class":684},"  runtimeConfig: {\n",[449,15286,15287],{"class":451,"line":474},[449,15288,15289],{"class":684},"    public: {\n",[449,15291,15292,15295],{"class":451,"line":480},[449,15293,15294],{"class":684},"      truckName: ",[449,15296,15297],{"class":691},"\"BrewStop Coffee\"\n",[449,15299,15300],{"class":451,"line":486},[449,15301,7555],{"class":684},[449,15303,15304],{"class":451,"line":492},[449,15305,4599],{"class":684},[449,15307,15308],{"class":451,"line":498},[449,15309,4113],{"class":684},[21,15311,15312],{},"Use it in an endpoint:",[48,15314,15316],{"className":670,"code":15315,"language":673,"meta":56,"style":56},"export default defineEventHandler(() => {\n  const config = useRuntimeConfig()\n  return { name: config.public.truckName }\n})\n",[40,15317,15318,15332,15346,15353],{"__ignoreMap":56},[449,15319,15320,15322,15324,15326,15328,15330],{"class":451,"line":452},[449,15321,4629],{"class":680},[449,15323,4632],{"class":680},[449,15325,4715],{"class":721},[449,15327,4718],{"class":684},[449,15329,1498],{"class":680},[449,15331,1501],{"class":684},[449,15333,15334,15336,15339,15341,15344],{"class":451,"line":114},[449,15335,5049],{"class":680},[449,15337,15338],{"class":714}," config",[449,15340,718],{"class":680},[449,15342,15343],{"class":721}," useRuntimeConfig",[449,15345,11220],{"class":684},[449,15347,15348,15350],{"class":451,"line":111},[449,15349,4727],{"class":680},[449,15351,15352],{"class":684}," { name: config.public.truckName }\n",[449,15354,15355],{"class":451,"line":468},[449,15356,4113],{"class":684},[21,15358,15359],{},"Environment overrides can be added later without code changes.",[73,15361],{},[32,15363,15365],{"id":15364},"step-6-create-orders-initial-version","Step 6: Create Orders (Initial Version)",[21,15367,4698,15368,3070],{},[40,15369,4954],{},[48,15371,15373],{"className":670,"code":15372,"language":673,"meta":56,"style":56},"let orders: any[] = []\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event)\n\n  const order = {\n    id: crypto.randomUUID(),\n    items: body.items,\n    status: \"pending\",\n    createdAt: Date.now()\n  }\n\n  orders.push(order)\n  return order\n})\n",[40,15374,15375,15392,15396,15418,15433,15437,15447,15455,15460,15468,15477,15481,15485,15494,15501],{"__ignoreMap":56},[449,15376,15377,15379,15381,15383,15385,15387,15389],{"class":451,"line":452},[449,15378,5533],{"class":680},[449,15380,5004],{"class":684},[449,15382,3070],{"class":680},[449,15384,5009],{"class":714},[449,15386,5012],{"class":684},[449,15388,5015],{"class":680},[449,15390,15391],{"class":684}," []\n",[449,15393,15394],{"class":451,"line":114},[449,15395,700],{"emptyLinePlaceholder":130},[449,15397,15398,15400,15402,15404,15406,15408,15410,15412,15414,15416],{"class":451,"line":111},[449,15399,4629],{"class":680},[449,15401,4632],{"class":680},[449,15403,4715],{"class":721},[449,15405,725],{"class":684},[449,15407,1485],{"class":680},[449,15409,1913],{"class":684},[449,15411,2991],{"class":1491},[449,15413,1758],{"class":684},[449,15415,1498],{"class":680},[449,15417,1501],{"class":684},[449,15419,15420,15422,15424,15426,15428,15430],{"class":451,"line":468},[449,15421,5049],{"class":680},[449,15423,5052],{"class":714},[449,15425,718],{"class":680},[449,15427,1654],{"class":680},[449,15429,5059],{"class":721},[449,15431,15432],{"class":684},"(event)\n",[449,15434,15435],{"class":451,"line":474},[449,15436,700],{"emptyLinePlaceholder":130},[449,15438,15439,15441,15443,15445],{"class":451,"line":480},[449,15440,5049],{"class":680},[449,15442,5193],{"class":714},[449,15444,718],{"class":680},[449,15446,1501],{"class":684},[449,15448,15449,15451,15453],{"class":451,"line":486},[449,15450,5202],{"class":684},[449,15452,5205],{"class":721},[449,15454,1624],{"class":684},[449,15456,15457],{"class":451,"line":492},[449,15458,15459],{"class":684},"    items: body.items,\n",[449,15461,15462,15464,15466],{"class":451,"line":498},[449,15463,5217],{"class":684},[449,15465,5220],{"class":691},[449,15467,779],{"class":684},[449,15469,15470,15473,15475],{"class":451,"line":133},[449,15471,15472],{"class":684},"    createdAt: Date.",[449,15474,4757],{"class":721},[449,15476,11220],{"class":684},[449,15478,15479],{"class":451,"line":509},[449,15480,4599],{"class":684},[449,15482,15483],{"class":451,"line":318},[449,15484,700],{"emptyLinePlaceholder":130},[449,15486,15487,15489,15491],{"class":451,"line":520},[449,15488,5254],{"class":684},[449,15490,5257],{"class":721},[449,15492,15493],{"class":684},"(order)\n",[449,15495,15496,15498],{"class":451,"line":526},[449,15497,4727],{"class":680},[449,15499,15500],{"class":684}," order\n",[449,15502,15503],{"class":451,"line":532},[449,15504,4113],{"class":684},[21,15506,15507,15508,15511],{},"This gives us a basic order flow we’ll ",[151,15509,15510],{},"refactor and harden"," in Part 2.",[21,15513,15514],{},"This approach is fine for development — but not production. We’ll address persistence and validation next.\n::",[73,15516],{},[32,15518,15520],{"id":15519},"step-7-testing-the-api","Step 7: Testing the API",[21,15522,15523,15524,15526],{},"You can test with ",[40,15525,4396],{},", Postman, or any frontend:",[48,15528,15530],{"className":839,"code":15529,"language":841,"meta":56,"style":56},"curl -X POST http:\u002F\u002Flocalhost:3000\u002Fapi\u002Forders \\\n  -H \"Content-Type: application\u002Fjson\" \\\n  -d '{\"items\":[\"latte\",\"croissant\"]}'\n",[40,15531,15532,15544,15552],{"__ignoreMap":56},[449,15533,15534,15536,15538,15540,15542],{"class":451,"line":452},[449,15535,4396],{"class":721},[449,15537,14251],{"class":714},[449,15539,14254],{"class":691},[449,15541,14293],{"class":691},[449,15543,14296],{"class":714},[449,15545,15546,15548,15550],{"class":451,"line":114},[449,15547,14301],{"class":714},[449,15549,14304],{"class":691},[449,15551,14296],{"class":714},[449,15553,15554,15556],{"class":451,"line":111},[449,15555,14311],{"class":714},[449,15557,15558],{"class":691}," '{\"items\":[\"latte\",\"croissant\"]}'\n",[21,15560,15561],{},"You’ll receive a created order response immediately.",[73,15563],{},[32,15565,15567],{"id":15566},"what-weve-built-so-far","What We’ve Built So Far",[21,15569,15570],{},"By the end of Part 1, we have:",[383,15572,15573,15576,15579,15581,15584],{},[386,15574,15575],{},"A working Nitro backend",[386,15577,15578],{},"Menu and hours endpoints",[386,15580,6920],{},[386,15582,15583],{},"Runtime config",[386,15585,15586],{},"Clean file structure",[2808,15588,15590],{"id":15589},"current-api-endpoints","Current API Endpoints",[383,15592,15593,15597,15602],{},[386,15594,6873,15595],{},[40,15596,14432],{},[386,15598,6873,15599],{},[40,15600,15601],{},"\u002Fapi\u002Fhours",[386,15603,6914,15604,15606],{},[40,15605,6917],{},"\n::",[73,15608],{},[16,15610,15612],{"id":15611},"whats-coming-in-part-2","What’s Coming in Part 2",[21,15614,14685,15615,15617],{},[151,15616,14711],{},", we’ll level this up:",[383,15619,15620,15623,15626,15629,15632],{},[386,15621,15622],{},"Input validation",[386,15624,15625],{},"Order status updates",[386,15627,15628],{},"Storage using UnJS tools",[386,15630,15631],{},"Middleware (logging & auth)",[386,15633,15634],{},"Deployment-ready output",[73,15636],{},[16,15638,3681],{"id":3680},[21,15640,15641,15642,15645],{},"Nitro makes it incredibly easy to go from ",[151,15643,15644],{},"idea → working backend"," with almost zero friction. In just a few files, we’ve built the foundation of a real food truck application — something that could power a production system with the right enhancements.",[21,15647,14685,15648,15650,15651,165],{},[151,15649,14711],{},", we’ll turn this prototype into a ",[151,15652,15653],{},"robust, scalable backend",[21,15655,15656],{},"👉 Stay tuned — BrewStop is just getting started ☕🚀",[3728,15658,15659],{},"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":15661},[15662,15665,15666,15667,15668,15669,15670,15671,15672,15673],{"id":14737,"depth":114,"text":14738,"children":15663},[15664],{"id":14748,"depth":111,"text":14749},{"id":14782,"depth":114,"text":14783},{"id":14810,"depth":114,"text":14811},{"id":14862,"depth":114,"text":14863},{"id":15058,"depth":114,"text":15059},{"id":15141,"depth":114,"text":15142},{"id":15238,"depth":114,"text":15239},{"id":15364,"depth":114,"text":15365},{"id":15519,"depth":114,"text":15520},{"id":15566,"depth":114,"text":15567,"children":15674},[15675],{"id":15589,"depth":111,"text":15590},"2026-01-21","Part 1 of a hands-on series where we use Nitro to build a real backend for a food truck \u002F coffee shop application — from project setup to core API routes.",{"type":13,"value":15679},[15680,15682,15692,15696,15708,15712,15722],[16,15681,333],{"id":332},[21,15683,14667,15684,14671,15686,14675,15688,14678,15690,14682],{},[156,15685,14670],{},[151,15687,14674],{},[151,15689,4189],{},[151,15691,14681],{},[21,15693,14685,15694,14689],{},[151,15695,14688],{},[383,15697,15698,15700,15702,15704,15706],{},[386,15699,14694],{},[386,15701,14697],{},[386,15703,14700],{},[386,15705,14703],{},[386,15707,14706],{},[21,15709,14685,15710,14712],{},[151,15711,14711],{},[383,15713,15714,15716,15718,15720],{},[386,15715,14717],{},[386,15717,14720],{},[386,15719,14723],{},[386,15721,14726],{},[21,15723,14729,15724,165],{},[151,15725,14732],{},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1620589125156-fd5028c5e05b?q=80&w=3287&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",[7076,15728,7079,15729,7080,3784],"backend","food truck",{},"\u002Fblog\u002Ffood-truck-p1",{"title":14659,"description":15677},"blog\u002Ffood-truck-p1","GxqdOxUneV8DH-GPzbVxo9BoNcHlG80DP35vcL5ShQ4",{"id":15736,"title":15737,"author":15738,"body":15739,"canonical":116,"categories":116,"date":15676,"description":17100,"excerpt":17101,"extension":119,"img":15726,"keywords":17133,"language":3788,"meta":17136,"navigation":130,"ogImage":116,"path":17137,"rating":133,"readTime":133,"seo":17138,"stem":17139,"tag":17140,"__hash__":17141},"blog\u002Fblog\u002Ffood-truck-p2.md","Building a Real App with Nitro (Part 2): Persistence, Validation, and Production Readiness",{"name":9,"img":11,"website":327},{"type":13,"value":15740,"toc":17081},[15741,15743,15754,15763,15778,15781,15783,15787,15790,15810,15817,15819,15823,15830,15834,15848,15852,15857,15926,15929,15940,15943,15945,15949,15953,16097,16100,16102,16106,16109,16114,16196,16199,16273,16276,16278,16282,16284,16288,16406,16408,16412,16594,16597,16602,16604,16608,16613,16722,16725,16727,16731,16734,16738,16773,16778,16914,16917,16919,16923,16926,16939,16946,16957,16969,16972,16974,16978,16982,17005,17008,17022,17024,17026,17035,17051,17058,17061,17075,17078],[16,15742,333],{"id":332},[21,15744,14685,15745,15747,15748,15750,15751,15753],{},[151,15746,14688],{},", we built the foundation of ",[151,15749,14744],{},", a food truck \u002F coffee shop backend powered by ",[151,15752,4189],{},". We created menu endpoints, business hours, and a basic order flow.",[21,15755,14685,15756,15758,15759,15762],{},[151,15757,14711],{},", we’ll turn that prototype into something ",[151,15760,15761],{},"production-ready"," by adding:",[383,15764,15765,15767,15770,15772,15775],{},[386,15766,15622],{},[386,15768,15769],{},"Persistent storage (using UnJS tools)",[386,15771,15625],{},[386,15773,15774],{},"Middleware & plugins",[386,15776,15777],{},"Better structure for deployment",[21,15779,15780],{},"This is where Nitro really shines — small additions, big capability.",[16,15782,4373],{"id":4372},[32,15784,15786],{"id":15785},"recap-where-we-left-off","Recap: Where We Left Off",[21,15788,15789],{},"At the end of Part 1, we had:",[383,15791,15792,15797,15802,15807],{},[386,15793,15794],{},[40,15795,15796],{},"GET \u002Fapi\u002Fmenu",[386,15798,15799],{},[40,15800,15801],{},"GET \u002Fapi\u002Fhours",[386,15803,15804],{},[40,15805,15806],{},"POST \u002Fapi\u002Forders",[386,15808,15809],{},"In-memory order storage",[21,15811,15812,15813,15816],{},"The biggest limitation? ",[151,15814,15815],{},"Orders disappear on restart"," and inputs are not validated. Let’s fix that.",[73,15818],{},[32,15820,15822],{"id":15821},"step-1-add-persistent-storage-with-unstorage","Step 1: Add Persistent Storage with Unstorage",[21,15824,15825,15826,15829],{},"Nitro integrates beautifully with ",[151,15827,15828],{},"Unstorage",", a key-value storage system from the UnJS ecosystem.",[2808,15831,15833],{"id":15832},"install-unstorage","Install Unstorage",[48,15835,15837],{"className":839,"code":15836,"language":841,"meta":56,"style":56},"npm install unstorage\n",[40,15838,15839],{"__ignoreMap":56},[449,15840,15841,15843,15845],{"class":451,"line":452},[449,15842,3703],{"class":721},[449,15844,3706],{"class":691},[449,15846,15847],{"class":691}," unstorage\n",[2808,15849,15851],{"id":15850},"create-a-storage-utility","Create a Storage Utility",[21,15853,4698,15854,3070],{},[40,15855,15856],{},"server\u002Futils\u002Fstorage.ts",[48,15858,15860],{"className":670,"code":15859,"language":673,"meta":56,"style":56},"import { createStorage } from \"unstorage\"\nimport fsDriver from \"unstorage\u002Fdrivers\u002Ffs\"\n\nexport const storage = createStorage({\n  driver: fsDriver({ base: \".\u002F.data\" })\n})\n",[40,15861,15862,15874,15886,15890,15906,15922],{"__ignoreMap":56},[449,15863,15864,15866,15869,15871],{"class":451,"line":452},[449,15865,681],{"class":680},[449,15867,15868],{"class":684}," { createStorage } ",[449,15870,688],{"class":680},[449,15872,15873],{"class":691}," \"unstorage\"\n",[449,15875,15876,15878,15881,15883],{"class":451,"line":114},[449,15877,681],{"class":680},[449,15879,15880],{"class":684}," fsDriver ",[449,15882,688],{"class":680},[449,15884,15885],{"class":691}," \"unstorage\u002Fdrivers\u002Ffs\"\n",[449,15887,15888],{"class":451,"line":111},[449,15889,700],{"emptyLinePlaceholder":130},[449,15891,15892,15894,15896,15899,15901,15904],{"class":451,"line":468},[449,15893,4629],{"class":680},[449,15895,10801],{"class":680},[449,15897,15898],{"class":714}," storage",[449,15900,718],{"class":680},[449,15902,15903],{"class":721}," createStorage",[449,15905,1391],{"class":684},[449,15907,15908,15911,15914,15917,15920],{"class":451,"line":474},[449,15909,15910],{"class":684},"  driver: ",[449,15912,15913],{"class":721},"fsDriver",[449,15915,15916],{"class":684},"({ base: ",[449,15918,15919],{"class":691},"\".\u002F.data\"",[449,15921,10958],{"class":684},[449,15923,15924],{"class":451,"line":480},[449,15925,4113],{"class":684},[21,15927,15928],{},"This gives us:",[383,15930,15931,15934,15937],{},[386,15932,15933],{},"Persistent storage",[386,15935,15936],{},"Zero database setup",[386,15938,15939],{},"Easy swap to Redis, Cloudflare KV, etc.",[21,15941,15942],{},"You can switch storage drivers without rewriting your application logic.\n::",[73,15944],{},[32,15946,15948],{"id":15947},"step-2-refactor-orders-to-use-storage","Step 2: Refactor Orders to Use Storage",[21,15950,4616,15951,3070],{},[40,15952,4954],{},[48,15954,15956],{"className":670,"code":15955,"language":673,"meta":56,"style":56},"import { storage } from \"..\u002Futils\u002Fstorage\"\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event)\n\n  const order = {\n    id: crypto.randomUUID(),\n    items: body.items,\n    status: \"pending\",\n    createdAt: Date.now()\n  }\n\n  await storage.setItem(`orders:${order.id}`, order)\n  return order\n})\n",[40,15957,15958,15970,15974,15996,16010,16014,16024,16032,16036,16044,16052,16056,16060,16087,16093],{"__ignoreMap":56},[449,15959,15960,15962,15965,15967],{"class":451,"line":452},[449,15961,681],{"class":680},[449,15963,15964],{"class":684}," { storage } ",[449,15966,688],{"class":680},[449,15968,15969],{"class":691}," \"..\u002Futils\u002Fstorage\"\n",[449,15971,15972],{"class":451,"line":114},[449,15973,700],{"emptyLinePlaceholder":130},[449,15975,15976,15978,15980,15982,15984,15986,15988,15990,15992,15994],{"class":451,"line":111},[449,15977,4629],{"class":680},[449,15979,4632],{"class":680},[449,15981,4715],{"class":721},[449,15983,725],{"class":684},[449,15985,1485],{"class":680},[449,15987,1913],{"class":684},[449,15989,2991],{"class":1491},[449,15991,1758],{"class":684},[449,15993,1498],{"class":680},[449,15995,1501],{"class":684},[449,15997,15998,16000,16002,16004,16006,16008],{"class":451,"line":468},[449,15999,5049],{"class":680},[449,16001,5052],{"class":714},[449,16003,718],{"class":680},[449,16005,1654],{"class":680},[449,16007,5059],{"class":721},[449,16009,15432],{"class":684},[449,16011,16012],{"class":451,"line":474},[449,16013,700],{"emptyLinePlaceholder":130},[449,16015,16016,16018,16020,16022],{"class":451,"line":480},[449,16017,5049],{"class":680},[449,16019,5193],{"class":714},[449,16021,718],{"class":680},[449,16023,1501],{"class":684},[449,16025,16026,16028,16030],{"class":451,"line":486},[449,16027,5202],{"class":684},[449,16029,5205],{"class":721},[449,16031,1624],{"class":684},[449,16033,16034],{"class":451,"line":492},[449,16035,15459],{"class":684},[449,16037,16038,16040,16042],{"class":451,"line":498},[449,16039,5217],{"class":684},[449,16041,5220],{"class":691},[449,16043,779],{"class":684},[449,16045,16046,16048,16050],{"class":451,"line":133},[449,16047,15472],{"class":684},[449,16049,4757],{"class":721},[449,16051,11220],{"class":684},[449,16053,16054],{"class":451,"line":509},[449,16055,4599],{"class":684},[449,16057,16058],{"class":451,"line":318},[449,16059,700],{"emptyLinePlaceholder":130},[449,16061,16062,16064,16067,16070,16072,16075,16077,16079,16082,16084],{"class":451,"line":520},[449,16063,2099],{"class":680},[449,16065,16066],{"class":684}," storage.",[449,16068,16069],{"class":721},"setItem",[449,16071,725],{"class":684},[449,16073,16074],{"class":691},"`orders:${",[449,16076,14066],{"class":684},[449,16078,165],{"class":691},[449,16080,16081],{"class":684},"id",[449,16083,1517],{"class":691},[449,16085,16086],{"class":684},", order)\n",[449,16088,16089,16091],{"class":451,"line":526},[449,16090,4727],{"class":680},[449,16092,15500],{"class":684},[449,16094,16095],{"class":451,"line":532},[449,16096,4113],{"class":684},[21,16098,16099],{},"Now orders persist across restarts.",[73,16101],{},[32,16103,16105],{"id":16104},"step-3-input-validation","Step 3: Input Validation",[21,16107,16108],{},"Never trust user input — even for coffee ☕.",[21,16110,4698,16111,3070],{},[40,16112,16113],{},"server\u002Futils\u002Fvalidation.ts",[48,16115,16117],{"className":670,"code":16116,"language":673,"meta":56,"style":56},"export function validateOrder(body: any) {\n  if (!body?.items || !Array.isArray(body.items)) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Invalid order format\"\n    })\n  }\n}\n",[40,16118,16119,16139,16161,16169,16177,16184,16188,16192],{"__ignoreMap":56},[449,16120,16121,16123,16125,16128,16130,16133,16135,16137],{"class":451,"line":452},[449,16122,4629],{"class":680},[449,16124,5432],{"class":680},[449,16126,16127],{"class":721}," validateOrder",[449,16129,725],{"class":684},[449,16131,16132],{"class":1491},"body",[449,16134,3070],{"class":680},[449,16136,5009],{"class":714},[449,16138,3417],{"class":684},[449,16140,16141,16143,16145,16147,16150,16152,16154,16156,16158],{"class":451,"line":114},[449,16142,5079],{"class":680},[449,16144,1913],{"class":684},[449,16146,5084],{"class":680},[449,16148,16149],{"class":684},"body?.items ",[449,16151,5090],{"class":680},[449,16153,12644],{"class":680},[449,16155,12647],{"class":684},[449,16157,12650],{"class":721},[449,16159,16160],{"class":684},"(body.items)) {\n",[449,16162,16163,16165,16167],{"class":451,"line":111},[449,16164,5109],{"class":680},[449,16166,5112],{"class":721},[449,16168,1391],{"class":684},[449,16170,16171,16173,16175],{"class":451,"line":468},[449,16172,5119],{"class":684},[449,16174,5122],{"class":714},[449,16176,779],{"class":684},[449,16178,16179,16181],{"class":451,"line":474},[449,16180,5129],{"class":684},[449,16182,16183],{"class":691},"\"Invalid order format\"\n",[449,16185,16186],{"class":451,"line":480},[449,16187,13841],{"class":684},[449,16189,16190],{"class":451,"line":486},[449,16191,4599],{"class":684},[449,16193,16194],{"class":451,"line":492},[449,16195,4604],{"class":684},[21,16197,16198],{},"Use it in your endpoint:",[48,16200,16202],{"className":670,"code":16201,"language":673,"meta":56,"style":56},"import { validateOrder } from \"..\u002Futils\u002Fvalidation\"\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event)\n  validateOrder(body)\n  \u002F\u002F continue logic\n})\n",[40,16203,16204,16216,16220,16242,16256,16264,16269],{"__ignoreMap":56},[449,16205,16206,16208,16211,16213],{"class":451,"line":452},[449,16207,681],{"class":680},[449,16209,16210],{"class":684}," { validateOrder } ",[449,16212,688],{"class":680},[449,16214,16215],{"class":691}," \"..\u002Futils\u002Fvalidation\"\n",[449,16217,16218],{"class":451,"line":114},[449,16219,700],{"emptyLinePlaceholder":130},[449,16221,16222,16224,16226,16228,16230,16232,16234,16236,16238,16240],{"class":451,"line":111},[449,16223,4629],{"class":680},[449,16225,4632],{"class":680},[449,16227,4715],{"class":721},[449,16229,725],{"class":684},[449,16231,1485],{"class":680},[449,16233,1913],{"class":684},[449,16235,2991],{"class":1491},[449,16237,1758],{"class":684},[449,16239,1498],{"class":680},[449,16241,1501],{"class":684},[449,16243,16244,16246,16248,16250,16252,16254],{"class":451,"line":468},[449,16245,5049],{"class":680},[449,16247,5052],{"class":714},[449,16249,718],{"class":680},[449,16251,1654],{"class":680},[449,16253,5059],{"class":721},[449,16255,15432],{"class":684},[449,16257,16258,16261],{"class":451,"line":474},[449,16259,16260],{"class":721},"  validateOrder",[449,16262,16263],{"class":684},"(body)\n",[449,16265,16266],{"class":451,"line":480},[449,16267,16268],{"class":705},"  \u002F\u002F continue logic\n",[449,16270,16271],{"class":451,"line":486},[449,16272,4113],{"class":684},[21,16274,16275],{},"Validation protects your API from malformed requests and accidental misuse.\n::",[73,16277],{},[32,16279,16281],{"id":16280},"step-4-fetch-orders-update-status","Step 4: Fetch Orders & Update Status",[2808,16283,13926],{"id":13925},[21,16285,4698,16286,3070],{},[40,16287,13931],{},[48,16289,16291],{"className":670,"code":16290,"language":673,"meta":56,"style":56},"import { storage } from \"..\u002Futils\u002Fstorage\"\n\nexport default defineEventHandler(async () => {\n  const keys = await storage.getKeys(\"orders:\")\n  const orders = await Promise.all(\n    keys.map((key) => storage.getItem(key))\n  )\n  return orders\n})\n",[40,16292,16293,16303,16307,16325,16348,16366,16390,16395,16402],{"__ignoreMap":56},[449,16294,16295,16297,16299,16301],{"class":451,"line":452},[449,16296,681],{"class":680},[449,16298,15964],{"class":684},[449,16300,688],{"class":680},[449,16302,15969],{"class":691},[449,16304,16305],{"class":451,"line":114},[449,16306,700],{"emptyLinePlaceholder":130},[449,16308,16309,16311,16313,16315,16317,16319,16321,16323],{"class":451,"line":111},[449,16310,4629],{"class":680},[449,16312,4632],{"class":680},[449,16314,4715],{"class":721},[449,16316,725],{"class":684},[449,16318,1485],{"class":680},[449,16320,2090],{"class":684},[449,16322,1498],{"class":680},[449,16324,1501],{"class":684},[449,16326,16327,16329,16332,16334,16336,16338,16341,16343,16346],{"class":451,"line":468},[449,16328,5049],{"class":680},[449,16330,16331],{"class":714}," keys",[449,16333,718],{"class":680},[449,16335,1654],{"class":680},[449,16337,16066],{"class":684},[449,16339,16340],{"class":721},"getKeys",[449,16342,725],{"class":684},[449,16344,16345],{"class":691},"\"orders:\"",[449,16347,11211],{"class":684},[449,16349,16350,16352,16354,16356,16358,16360,16362,16364],{"class":451,"line":474},[449,16351,5049],{"class":680},[449,16353,5004],{"class":714},[449,16355,718],{"class":680},[449,16357,1654],{"class":680},[449,16359,5160],{"class":714},[449,16361,165],{"class":684},[449,16363,5815],{"class":721},[449,16365,5566],{"class":684},[449,16367,16368,16371,16373,16375,16378,16380,16382,16384,16387],{"class":451,"line":480},[449,16369,16370],{"class":684},"    keys.",[449,16372,6397],{"class":721},[449,16374,5163],{"class":684},[449,16376,16377],{"class":1491},"key",[449,16379,1758],{"class":684},[449,16381,1498],{"class":680},[449,16383,16066],{"class":684},[449,16385,16386],{"class":721},"getItem",[449,16388,16389],{"class":684},"(key))\n",[449,16391,16392],{"class":451,"line":486},[449,16393,16394],{"class":684},"  )\n",[449,16396,16397,16399],{"class":451,"line":492},[449,16398,4727],{"class":680},[449,16400,16401],{"class":684}," orders\n",[449,16403,16404],{"class":451,"line":498},[449,16405,4113],{"class":684},[2808,16407,13417],{"id":13416},[21,16409,4698,16410,3070],{},[40,16411,13422],{},[48,16413,16415],{"className":670,"code":16414,"language":673,"meta":56,"style":56},"import { storage } from \"..\u002F..\u002Futils\u002Fstorage\"\n\nexport default defineEventHandler(async (event) => {\n  const id = event.context.params?.id\n  const body = await readBody(event)\n\n  const order = await storage.getItem(`orders:${id}`)\n  if (!order) {\n    throw createError({ statusCode: 404, statusMessage: \"Order not found\" })\n  }\n\n  const updated = { ...order, status: body.status }\n  await storage.setItem(`orders:${id}`, updated)\n\n  return updated\n})\n",[40,16416,16417,16428,16432,16454,16465,16479,16483,16507,16518,16536,16540,16544,16560,16579,16583,16590],{"__ignoreMap":56},[449,16418,16419,16421,16423,16425],{"class":451,"line":452},[449,16420,681],{"class":680},[449,16422,15964],{"class":684},[449,16424,688],{"class":680},[449,16426,16427],{"class":691}," \"..\u002F..\u002Futils\u002Fstorage\"\n",[449,16429,16430],{"class":451,"line":114},[449,16431,700],{"emptyLinePlaceholder":130},[449,16433,16434,16436,16438,16440,16442,16444,16446,16448,16450,16452],{"class":451,"line":111},[449,16435,4629],{"class":680},[449,16437,4632],{"class":680},[449,16439,4715],{"class":721},[449,16441,725],{"class":684},[449,16443,1485],{"class":680},[449,16445,1913],{"class":684},[449,16447,2991],{"class":1491},[449,16449,1758],{"class":684},[449,16451,1498],{"class":680},[449,16453,1501],{"class":684},[449,16455,16456,16458,16460,16462],{"class":451,"line":468},[449,16457,5049],{"class":680},[449,16459,12322],{"class":714},[449,16461,718],{"class":680},[449,16463,16464],{"class":684}," event.context.params?.id\n",[449,16466,16467,16469,16471,16473,16475,16477],{"class":451,"line":474},[449,16468,5049],{"class":680},[449,16470,5052],{"class":714},[449,16472,718],{"class":680},[449,16474,1654],{"class":680},[449,16476,5059],{"class":721},[449,16478,15432],{"class":684},[449,16480,16481],{"class":451,"line":480},[449,16482,700],{"emptyLinePlaceholder":130},[449,16484,16485,16487,16489,16491,16493,16495,16497,16499,16501,16503,16505],{"class":451,"line":486},[449,16486,5049],{"class":680},[449,16488,5193],{"class":714},[449,16490,718],{"class":680},[449,16492,1654],{"class":680},[449,16494,16066],{"class":684},[449,16496,16386],{"class":721},[449,16498,725],{"class":684},[449,16500,16074],{"class":691},[449,16502,16081],{"class":684},[449,16504,1517],{"class":691},[449,16506,11211],{"class":684},[449,16508,16509,16511,16513,16515],{"class":451,"line":492},[449,16510,5079],{"class":680},[449,16512,1913],{"class":684},[449,16514,5084],{"class":680},[449,16516,16517],{"class":684},"order) {\n",[449,16519,16520,16522,16524,16527,16529,16532,16534],{"class":451,"line":498},[449,16521,5109],{"class":680},[449,16523,5112],{"class":721},[449,16525,16526],{"class":684},"({ statusCode: ",[449,16528,12452],{"class":714},[449,16530,16531],{"class":684},", statusMessage: ",[449,16533,13777],{"class":691},[449,16535,10958],{"class":684},[449,16537,16538],{"class":451,"line":133},[449,16539,4599],{"class":684},[449,16541,16542],{"class":451,"line":509},[449,16543,700],{"emptyLinePlaceholder":130},[449,16545,16546,16548,16551,16553,16555,16557],{"class":451,"line":318},[449,16547,5049],{"class":680},[449,16549,16550],{"class":714}," updated",[449,16552,718],{"class":680},[449,16554,14124],{"class":684},[449,16556,14127],{"class":680},[449,16558,16559],{"class":684},"order, status: body.status }\n",[449,16561,16562,16564,16566,16568,16570,16572,16574,16576],{"class":451,"line":520},[449,16563,2099],{"class":680},[449,16565,16066],{"class":684},[449,16567,16069],{"class":721},[449,16569,725],{"class":684},[449,16571,16074],{"class":691},[449,16573,16081],{"class":684},[449,16575,1517],{"class":691},[449,16577,16578],{"class":684},", updated)\n",[449,16580,16581],{"class":451,"line":526},[449,16582,700],{"emptyLinePlaceholder":130},[449,16584,16585,16587],{"class":451,"line":532},[449,16586,4727],{"class":680},[449,16588,16589],{"class":684}," updated\n",[449,16591,16592],{"class":451,"line":2790},[449,16593,4113],{"class":684},[21,16595,16596],{},"Now BrewStop supports a real workflow:",[383,16598,16599],{},[386,16600,16601],{},"pending → preparing → ready → completed",[73,16603],{},[32,16605,16607],{"id":16606},"step-5-add-middleware-logging","Step 5: Add Middleware (Logging)",[21,16609,4698,16610,3070],{},[40,16611,16612],{},"server\u002Fplugins\u002Flogger.ts",[48,16614,16616],{"className":670,"code":16615,"language":673,"meta":56,"style":56},"export default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook(\"request\", (event) => {\n    console.log(\n      `[${new Date().toISOString()}] ${event.method} ${event.path}`\n    )\n  })\n})\n",[40,16617,16618,16638,16661,16669,16708,16713,16718],{"__ignoreMap":56},[449,16619,16620,16622,16624,16627,16629,16632,16634,16636],{"class":451,"line":452},[449,16621,4629],{"class":680},[449,16623,4632],{"class":680},[449,16625,16626],{"class":721}," defineNitroPlugin",[449,16628,5163],{"class":684},[449,16630,16631],{"class":1491},"nitroApp",[449,16633,1758],{"class":684},[449,16635,1498],{"class":680},[449,16637,1501],{"class":684},[449,16639,16640,16643,16646,16648,16651,16653,16655,16657,16659],{"class":451,"line":114},[449,16641,16642],{"class":684},"  nitroApp.hooks.",[449,16644,16645],{"class":721},"hook",[449,16647,725],{"class":684},[449,16649,16650],{"class":691},"\"request\"",[449,16652,3500],{"class":684},[449,16654,2991],{"class":1491},[449,16656,1758],{"class":684},[449,16658,1498],{"class":680},[449,16660,1501],{"class":684},[449,16662,16663,16665,16667],{"class":451,"line":111},[449,16664,3002],{"class":684},[449,16666,1675],{"class":721},[449,16668,5566],{"class":684},[449,16670,16671,16674,16676,16678,16680,16682,16685,16688,16690,16692,16695,16698,16700,16702,16705],{"class":451,"line":468},[449,16672,16673],{"class":691},"      `[${",[449,16675,5230],{"class":680},[449,16677,5233],{"class":721},[449,16679,5236],{"class":691},[449,16681,5239],{"class":721},[449,16683,16684],{"class":691},"()",[449,16686,16687],{"class":691},"}] ${",[449,16689,2991],{"class":684},[449,16691,165],{"class":691},[449,16693,16694],{"class":684},"method",[449,16696,16697],{"class":691},"} ${",[449,16699,2991],{"class":684},[449,16701,165],{"class":691},[449,16703,16704],{"class":684},"path",[449,16706,16707],{"class":691},"}`\n",[449,16709,16710],{"class":451,"line":474},[449,16711,16712],{"class":684},"    )\n",[449,16714,16715],{"class":451,"line":480},[449,16716,16717],{"class":684},"  })\n",[449,16719,16720],{"class":451,"line":486},[449,16721,4113],{"class":684},[21,16723,16724],{},"This gives you centralized request logging without cluttering endpoints.",[73,16726],{},[32,16728,16730],{"id":16729},"step-6-simple-api-key-protection","Step 6: Simple API Key Protection",[21,16732,16733],{},"Let’s protect order updates.",[21,16735,4616,16736,3070],{},[40,16737,15247],{},[48,16739,16741],{"className":670,"code":16740,"language":673,"meta":56,"style":56},"export default defineNitroConfig({\n  runtimeConfig: {\n    apiKey: \"super-secret-key\"\n  }\n})\n",[40,16742,16743,16753,16757,16765,16769],{"__ignoreMap":56},[449,16744,16745,16747,16749,16751],{"class":451,"line":452},[449,16746,4629],{"class":680},[449,16748,4632],{"class":680},[449,16750,15277],{"class":721},[449,16752,1391],{"class":684},[449,16754,16755],{"class":451,"line":114},[449,16756,15284],{"class":684},[449,16758,16759,16762],{"class":451,"line":111},[449,16760,16761],{"class":684},"    apiKey: ",[449,16763,16764],{"class":691},"\"super-secret-key\"\n",[449,16766,16767],{"class":451,"line":468},[449,16768,4599],{"class":684},[449,16770,16771],{"class":451,"line":474},[449,16772,4113],{"class":684},[21,16774,4698,16775,3070],{},[40,16776,16777],{},"server\u002Fplugins\u002Fauth.ts",[48,16779,16781],{"className":670,"code":16780,"language":673,"meta":56,"style":56},"export default defineNitroPlugin(() => {\n  addEventHandler((event) => {\n    if (event.path.startsWith(\"\u002Fapi\u002Forders\") && event.method !== \"GET\") {\n      const key = event.headers.get(\"x-api-key\")\n      if (key !== useRuntimeConfig().apiKey) {\n        throw createError({ statusCode: 401, statusMessage: \"Unauthorized\" })\n      }\n    }\n  })\n})\n",[40,16782,16783,16797,16812,16843,16864,16879,16898,16902,16906,16910],{"__ignoreMap":56},[449,16784,16785,16787,16789,16791,16793,16795],{"class":451,"line":452},[449,16786,4629],{"class":680},[449,16788,4632],{"class":680},[449,16790,16626],{"class":721},[449,16792,4718],{"class":684},[449,16794,1498],{"class":680},[449,16796,1501],{"class":684},[449,16798,16799,16802,16804,16806,16808,16810],{"class":451,"line":114},[449,16800,16801],{"class":721},"  addEventHandler",[449,16803,5163],{"class":684},[449,16805,2991],{"class":1491},[449,16807,1758],{"class":684},[449,16809,1498],{"class":680},[449,16811,1501],{"class":684},[449,16813,16814,16816,16819,16822,16824,16827,16829,16832,16835,16838,16841],{"class":451,"line":111},[449,16815,12820],{"class":680},[449,16817,16818],{"class":684}," (event.path.",[449,16820,16821],{"class":721},"startsWith",[449,16823,725],{"class":684},[449,16825,16826],{"class":691},"\"\u002Fapi\u002Forders\"",[449,16828,1758],{"class":684},[449,16830,16831],{"class":680},"&&",[449,16833,16834],{"class":684}," event.method ",[449,16836,16837],{"class":680},"!==",[449,16839,16840],{"class":691}," \"GET\"",[449,16842,3417],{"class":684},[449,16844,16845,16847,16850,16852,16855,16857,16859,16862],{"class":451,"line":468},[449,16846,14077],{"class":680},[449,16848,16849],{"class":714}," key",[449,16851,718],{"class":680},[449,16853,16854],{"class":684}," event.headers.",[449,16856,12420],{"class":721},[449,16858,725],{"class":684},[449,16860,16861],{"class":691},"\"x-api-key\"",[449,16863,11211],{"class":684},[449,16865,16866,16869,16872,16874,16876],{"class":451,"line":474},[449,16867,16868],{"class":680},"      if",[449,16870,16871],{"class":684}," (key ",[449,16873,16837],{"class":680},[449,16875,15343],{"class":721},[449,16877,16878],{"class":684},"().apiKey) {\n",[449,16880,16881,16884,16886,16888,16891,16893,16896],{"class":451,"line":480},[449,16882,16883],{"class":680},"        throw",[449,16885,5112],{"class":721},[449,16887,16526],{"class":684},[449,16889,16890],{"class":714},"401",[449,16892,16531],{"class":684},[449,16894,16895],{"class":691},"\"Unauthorized\"",[449,16897,10958],{"class":684},[449,16899,16900],{"class":451,"line":486},[449,16901,8797],{"class":684},[449,16903,16904],{"class":451,"line":492},[449,16905,7555],{"class":684},[449,16907,16908],{"class":451,"line":498},[449,16909,16717],{"class":684},[449,16911,16912],{"class":451,"line":133},[449,16913,4113],{"class":684},[21,16915,16916],{},"This is intentionally simple — Nitro plugins scale well to JWT, OAuth, or third-party auth later.\n::",[73,16918],{},[32,16920,16922],{"id":16921},"step-7-deployment-readiness","Step 7: Deployment Readiness",[21,16924,16925],{},"Build your app:",[48,16927,16929],{"className":839,"code":16928,"language":841,"meta":56,"style":56},"npm run build\n",[40,16930,16931],{"__ignoreMap":56},[449,16932,16933,16935,16937],{"class":451,"line":452},[449,16934,3703],{"class":721},[449,16936,5296],{"class":691},[449,16938,6810],{"class":691},[21,16940,16941,16942,16945],{},"Nitro outputs a ",[40,16943,16944],{},".output"," directory that can run on:",[383,16947,16948,16951,16954],{},[386,16949,16950],{},"Node.js servers",[386,16952,16953],{},"Serverless platforms",[386,16955,16956],{},"Edge runtimes",[48,16958,16960],{"className":839,"code":16959,"language":841,"meta":56,"style":56},"node .output\u002Fserver\u002Findex.mjs\n",[40,16961,16962],{"__ignoreMap":56},[449,16963,16964,16967],{"class":451,"line":452},[449,16965,16966],{"class":721},"node",[449,16968,6819],{"class":691},[21,16970,16971],{},"No changes needed.",[73,16973],{},[32,16975,16977],{"id":16976},"final-api-overview","Final API Overview",[2808,16979,16981],{"id":16980},"brewstop-api-final","BrewStop API (Final)",[383,16983,16984,16988,16992,16996,17000],{},[386,16985,6873,16986],{},[40,16987,14432],{},[386,16989,6873,16990],{},[40,16991,15601],{},[386,16993,6873,16994],{},[40,16995,6917],{},[386,16997,6914,16998],{},[40,16999,6917],{},[386,17001,17002,17003,15606],{},"PATCH ",[40,17004,14480],{},[21,17006,17007],{},"This backend can now realistically power:",[383,17009,17010,17013,17016,17019],{},[386,17011,17012],{},"A POS system",[386,17014,17015],{},"A mobile app",[386,17017,17018],{},"A Nuxt frontend",[386,17020,17021],{},"An admin dashboard",[73,17023],{},[16,17025,3681],{"id":3680},[21,17027,17028,17029,17032,17033,3070],{},"In just ",[151,17030,17031],{},"two parts",", we’ve built a real backend using ",[151,17034,4189],{},[383,17036,17037,17039,17042,17045,17048],{},[386,17038,15933],{},[386,17040,17041],{},"Validation",[386,17043,17044],{},"Middleware",[386,17046,17047],{},"Secure endpoints",[386,17049,17050],{},"Multi-runtime deployment",[21,17052,17053,17054,17057],{},"Nitro proves that you don’t need heavy frameworks or complex infrastructure to build ",[151,17055,17056],{},"serious applications",". With composable UnJS tools and clean architecture, you can ship fast — and scale later.",[21,17059,17060],{},"Next steps?",[383,17062,17063,17066,17069,17072],{},[386,17064,17065],{},"Add a frontend (Nuxt or Vite)",[386,17067,17068],{},"Add payments",[386,17070,17071],{},"Deploy to edge",[386,17073,17074],{},"Replace storage with a database",[21,17076,17077],{},"BrewStop is production-ready — and this is only the beginning. ☕🚀",[3728,17079,17080],{},"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 .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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":56,"searchDepth":111,"depth":111,"links":17082},[17083,17084,17088,17089,17090,17094,17095,17096,17097],{"id":15785,"depth":114,"text":15786},{"id":15821,"depth":114,"text":15822,"children":17085},[17086,17087],{"id":15832,"depth":111,"text":15833},{"id":15850,"depth":111,"text":15851},{"id":15947,"depth":114,"text":15948},{"id":16104,"depth":114,"text":16105},{"id":16280,"depth":114,"text":16281,"children":17091},[17092,17093],{"id":13925,"depth":111,"text":13926},{"id":13416,"depth":111,"text":13417},{"id":16606,"depth":114,"text":16607},{"id":16729,"depth":114,"text":16730},{"id":16921,"depth":114,"text":16922},{"id":16976,"depth":114,"text":16977,"children":17098},[17099],{"id":16980,"depth":111,"text":16981},"Part 2 of our Nitro series where we level up the BrewStop food truck backend with validation, storage, middleware, and deployment-ready architecture.",{"type":13,"value":17102},[17103,17105,17113,17119,17131],[16,17104,333],{"id":332},[21,17106,14685,17107,15747,17109,15750,17111,15753],{},[151,17108,14688],{},[151,17110,14744],{},[151,17112,4189],{},[21,17114,14685,17115,15758,17117,15762],{},[151,17116,14711],{},[151,17118,15761],{},[383,17120,17121,17123,17125,17127,17129],{},[386,17122,15622],{},[386,17124,15769],{},[386,17126,15625],{},[386,17128,15774],{},[386,17130,15777],{},[21,17132,15780],{},[7076,15728,7079,17134,17135,15729],"unjs","serverless",{},"\u002Fblog\u002Ffood-truck-p2",{"title":15737,"description":17100},"blog\u002Ffood-truck-p2","“Application Development”","prrsHwI_79_BIGLnC3EEU-7AfHq-Js5POFW1zkdLKVc",{"id":17143,"title":17144,"author":17145,"body":17147,"canonical":116,"categories":116,"date":18112,"description":18113,"excerpt":18114,"extension":119,"img":18128,"keywords":18129,"language":18137,"meta":18138,"navigation":130,"ogImage":116,"path":18139,"rating":474,"readTime":318,"seo":18140,"stem":18141,"tag":18142,"__hash__":18143},"blog\u002Fblog\u002Fgithub-actions-deployment-difficulties.md","The Hard Truth About GitHub Actions and Continuous Deployment",{"name":9,"img":17146,"website":327},"https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1767533048\u002FPXL_20251010_202726442_2_hhudfr.jpg",{"type":13,"value":17148,"toc":18098},[17149,17151,17161,17164,17167,17172,17174,17178,17185,17188,17192,17195,17235,17242,17246,17249,17337,17340,17342,17346,17349,17353,17356,17382,17398,17402,17405,17431,17434,17436,17440,17443,17447,17510,17514,17517,17554,17557,17559,17563,17566,17570,17573,17576,17594,17601,17603,17607,17610,17614,17617,17638,17642,17645,17659,17663,17666,17681,17685,17688,17702,17706,17709,17723,17725,17729,17732,18057,18060,18068,18070,18072,18075,18078,18092,18095],[16,17150,333],{"id":332},[21,17152,17153,17154,17157,17158],{},"Every tutorial makes it look effortless. A few lines of YAML, a ",[40,17155,17156],{},"git push",", and your application magically deploys to production. ",[151,17159,17160],{},"If only it were that simple.",[21,17162,17163],{},"The reality of GitHub Actions and continuous deployment is far messier. After years of building, breaking, and rebuilding deployment pipelines, I can tell you this: CI\u002FCD is not a solved problem. It is a continuous negotiation between your code, your infrastructure, your tooling, and the infinite ways they can fail silently until 2 AM.",[21,17165,17166],{},"This post is not a tutorial. It is a field report from the trenches — an honest look at what actually goes wrong, why it goes wrong, and what you need to understand before you treat deployment as an afterthought.",[25,17168,17169],{"type":27},[21,17170,17171],{},"If you are new to CI\u002FCD, do not let this discourage you. Let it prepare you. The difficulties are real, but they are surmountable with the right mental model.",[73,17173],{},[16,17175,17177],{"id":17176},"the-illusion-of-simplicity","The Illusion of Simplicity",[21,17179,17180,17181,17184],{},"GitHub Actions sells you a dream: ",[156,17182,17183],{},"\"Automate your workflow from idea to production.\""," The marketplace is full of reusable actions. The syntax looks declarative and friendly. You copy a starter workflow, tweak a few values, and it works — once.",[21,17186,17187],{},"Then reality sets in.",[32,17189,17191],{"id":17190},"yaml-is-a-bad-programming-language","YAML Is a Bad Programming Language",[21,17193,17194],{},"YAML was designed for configuration, not logic. Yet GitHub Actions forces you to express conditional behavior, matrix builds, job dependencies, and environment-specific logic in a format that:",[383,17196,17197,17200,17212,17229],{},[386,17198,17199],{},"Fails silently on indentation errors",[386,17201,17202,17203,349,17206,1542,17208,17211],{},"Has no type system — ",[40,17204,17205],{},"\"true\"",[40,17207,2169],{},[40,17209,17210],{},"True"," are different things",[386,17213,17214,17215,17218,17219,349,17222,349,17225,17228],{},"Uses ",[40,17216,17217],{},"${{ }}"," expression syntax that behaves differently in ",[40,17220,17221],{},"env",[40,17223,17224],{},"with",[40,17226,17227],{},"if",", and shell contexts",[386,17230,17231,17232,17234],{},"Provides no local testing — every iteration requires a ",[40,17233,17156],{}," and a wait",[427,17236,17239],{"icon":17237,"title":17238},"🐛","The Debugging Cycle",[21,17240,17241],{},"A single missing quote in a workflow file can cost you twenty minutes: push, wait for runner allocation, watch it fail, read the logs, fix, repeat. There is no debugger. There is no REPL. There is only patience.",[32,17243,17245],{"id":17244},"the-matrix-multiplication-problem","The Matrix Multiplication Problem",[21,17247,17248],{},"Matrix builds are powerful until they multiply your problems:",[48,17250,17254],{"className":17251,"code":17252,"language":17253,"meta":56,"style":56},"language-yaml shiki shiki-themes github-light github-dark","strategy:\n  matrix:\n    node: [18, 20, 22]\n    os: [ubuntu-latest, windows-latest, macos-latest]\n    database: [postgres, mysql, sqlite]\n","yaml",[40,17255,17256,17265,17272,17295,17317],{"__ignoreMap":56},[449,17257,17258,17262],{"class":451,"line":452},[449,17259,17261],{"class":17260},"s9eBZ","strategy",[449,17263,17264],{"class":684},":\n",[449,17266,17267,17270],{"class":451,"line":114},[449,17268,17269],{"class":17260},"  matrix",[449,17271,17264],{"class":684},[449,17273,17274,17277,17280,17283,17285,17288,17290,17293],{"class":451,"line":111},[449,17275,17276],{"class":17260},"    node",[449,17278,17279],{"class":684},": [",[449,17281,17282],{"class":714},"18",[449,17284,349],{"class":684},[449,17286,17287],{"class":714},"20",[449,17289,349],{"class":684},[449,17291,17292],{"class":714},"22",[449,17294,15050],{"class":684},[449,17296,17297,17300,17302,17305,17307,17310,17312,17315],{"class":451,"line":468},[449,17298,17299],{"class":17260},"    os",[449,17301,17279],{"class":684},[449,17303,17304],{"class":691},"ubuntu-latest",[449,17306,349],{"class":684},[449,17308,17309],{"class":691},"windows-latest",[449,17311,349],{"class":684},[449,17313,17314],{"class":691},"macos-latest",[449,17316,15050],{"class":684},[449,17318,17319,17322,17324,17326,17328,17331,17333,17335],{"class":451,"line":474},[449,17320,17321],{"class":17260},"    database",[449,17323,17279],{"class":684},[449,17325,14588],{"class":691},[449,17327,349],{"class":684},[449,17329,17330],{"class":691},"mysql",[449,17332,349],{"class":684},[449,17334,14589],{"class":691},[449,17336,15050],{"class":684},[21,17338,17339],{},"Congratulations, you now have 27 jobs. One flaky test on one OS with one database version will fail randomly, blocking your entire pipeline. You will spend hours chasing Heisenbugs that only reproduce on GitHub's runners, never locally.",[73,17341],{},[16,17343,17345],{"id":17344},"secrets-and-environment-hell","Secrets and Environment Hell",[21,17347,17348],{},"Managing secrets in GitHub Actions is straightforward until it is not.",[32,17350,17352],{"id":17351},"the-secret-sprawl","The Secret Sprawl",[21,17354,17355],{},"Every repository has secrets. Every environment (staging, production, review apps) has its own set. Then you have organization secrets, repository secrets, and environment secrets — each with different precedence rules. You need:",[383,17357,17358,17364,17370,17376],{},[386,17359,17360,17363],{},[40,17361,17362],{},"DATABASE_URL"," — but different for staging and production",[386,17365,17366,17369],{},[40,17367,17368],{},"API_KEY"," — rotated quarterly, now invalidating old workflow runs",[386,17371,17372,17375],{},[40,17373,17374],{},"SSH_PRIVATE_KEY"," — for deployment, but only certain branches should deploy",[386,17377,17378,17381],{},[40,17379,17380],{},"SLACK_WEBHOOK"," — for notifications, except on fork PRs",[25,17383,17385],{"type":17384},"error",[21,17386,17387,17388,17391,17392,17394,17395,17397],{},"Secrets do not interpolate the way you expect. ",[40,17389,17390],{},"${{ secrets.MY_SECRET }}"," in an ",[40,17393,17221],{}," block behaves differently than in a ",[40,17396,17224],{}," block. Fork PRs cannot access secrets by design — which breaks workflows that need them for testing.",[32,17399,17401],{"id":17400},"the-deployment-permission-labyrinth","The Deployment Permission Labyrinth",[21,17403,17404],{},"GitHub's permission model is granular to a fault:",[383,17406,17407,17413,17419,17425],{},[386,17408,17409,17412],{},[40,17410,17411],{},"contents: write"," — for pushing tags and releases",[386,17414,17415,17418],{},[40,17416,17417],{},"id-token: write"," — for OIDC authentication with cloud providers",[386,17420,17421,17424],{},[40,17422,17423],{},"packages: write"," — for publishing container images",[386,17426,17427,17430],{},[40,17428,17429],{},"actions: write"," — for caching and artifact management",[21,17432,17433],{},"Getting these wrong means cryptic 403 errors from AWS, Vercel, or Docker Hub with no indication of which permission is missing. You will add permissions one by one, pushing and waiting, until something works — without understanding why.",[73,17435],{},[16,17437,17439],{"id":17438},"the-flakiness-tax","The Flakiness Tax",[21,17441,17442],{},"Flaky tests are the silent killer of CI\u002FCD confidence. A test that passes 90% of the time teaches your team to ignore failures. Eventually, a real failure is missed because everyone assumes \"it's just flaky.\"",[32,17444,17446],{"id":17445},"sources-of-flakiness","Sources of Flakiness",[547,17448,17449,17459],{},[550,17450,17451],{},[553,17452,17453,17456],{},[556,17454,17455],{},"Source",[556,17457,17458],{},"Why It Happens",[569,17460,17461,17469,17477,17485,17499],{},[553,17462,17463,17466],{},[574,17464,17465],{},"Timing issues",[574,17467,17468],{},"Network requests, animations, database migrations finish in different orders",[553,17470,17471,17474],{},[574,17472,17473],{},"Shared state",[574,17475,17476],{},"Tests leak data into databases, filesystems, or caches",[553,17478,17479,17482],{},[574,17480,17481],{},"External services",[574,17483,17484],{},"Third-party APIs rate-limit or return different responses",[553,17486,17487,17490],{},[574,17488,17489],{},"Runner variance",[574,17491,17492,17493,17495,17496,17498],{},"GitHub's ",[40,17494,17304],{}," today is not ",[40,17497,17304],{}," six months ago",[553,17500,17501,17504],{},[574,17502,17503],{},"Caching bugs",[574,17505,17506,17509],{},[40,17507,17508],{},"actions\u002Fcache"," restores corrupted or stale dependencies",[32,17511,17513],{"id":17512},"the-retry-trap","The Retry Trap",[21,17515,17516],{},"The easiest fix is the worst fix:",[48,17518,17520],{"className":17251,"code":17519,"language":17253,"meta":56,"style":56},"- name: Run tests\n  run: npm test\n  retries: 3\n",[40,17521,17522,17534,17544],{"__ignoreMap":56},[449,17523,17524,17527,17529,17531],{"class":451,"line":452},[449,17525,17526],{"class":684},"- ",[449,17528,12920],{"class":17260},[449,17530,1482],{"class":684},[449,17532,17533],{"class":691},"Run tests\n",[449,17535,17536,17539,17541],{"class":451,"line":114},[449,17537,17538],{"class":17260},"  run",[449,17540,1482],{"class":684},[449,17542,17543],{"class":691},"npm test\n",[449,17545,17546,17549,17551],{"class":451,"line":111},[449,17547,17548],{"class":17260},"  retries",[449,17550,1482],{"class":684},[449,17552,17553],{"class":714},"3\n",[21,17555,17556],{},"You have not fixed the flakiness. You have institutionalized it. Your tests are now slower, your feedback loop is degraded, and your team trusts the pipeline less every day.",[73,17558],{},[16,17560,17562],{"id":17561},"deployment-is-not-the-end","Deployment Is Not the End",[21,17564,17565],{},"Getting code onto a server is only half the battle. The other half is knowing whether it actually works.",[32,17567,17569],{"id":17568},"the-green-checkmark-lie","The \"Green Checkmark\" Lie",[21,17571,17572],{},"A successful workflow run means the deployment step completed — not that your application is healthy. Your health checks, smoke tests, and rollback procedures matter more than the deployment itself.",[21,17574,17575],{},"We have seen pipelines that:",[383,17577,17578,17581,17584,17591],{},[386,17579,17580],{},"Deploy successfully but serve 500 errors due to missing environment variables",[386,17582,17583],{},"Pass all tests but break in production because the test database schema differs from production",[386,17585,17586,17587,17590],{},"Deploy to the wrong region because ",[40,17588,17589],{},"AWS_REGION"," was set in the wrong scope",[386,17592,17593],{},"Roll forward instead of back because rollback procedures were never tested",[427,17595,17598],{"icon":17596,"title":17597},"🔥","The Untested Rollback",[21,17599,17600],{},"The most dangerous code in your repository is not your application. It is your rollback script — which you have never actually run under pressure.",[73,17602],{},[16,17604,17606],{"id":17605},"what-you-actually-need","What You Actually Need",[21,17608,17609],{},"After years of fighting with GitHub Actions, here is what we have learned about what a reliable CI\u002FCD setup actually requires.",[32,17611,17613],{"id":17612},"_1-treat-pipelines-as-production-code","1. Treat Pipelines as Production Code",[21,17615,17616],{},"Your workflow files deserve the same rigor as your application code:",[383,17618,17619,17629,17632,17635],{},[386,17620,17621,17622,17625,17626],{},"Version pin your actions: ",[40,17623,17624],{},"actions\u002Fcheckout@v4.1.1",", not ",[40,17627,17628],{},"@v4",[386,17630,17631],{},"Use reusable workflows to reduce duplication and centralize logic",[386,17633,17634],{},"Document why each step exists, not just what it does",[386,17636,17637],{},"Review workflow changes in pull requests with the same scrutiny as application changes",[32,17639,17641],{"id":17640},"_2-test-your-deployment-not-just-your-code","2. Test Your Deployment, Not Just Your Code",[21,17643,17644],{},"A deployment pipeline should verify the deployment, not just execute it:",[383,17646,17647,17650,17653,17656],{},[386,17648,17649],{},"Health checks after every deploy",[386,17651,17652],{},"Smoke tests against the production environment",[386,17654,17655],{},"Canary or blue-green deployments for critical services",[386,17657,17658],{},"Automated rollbacks when error rates spike",[32,17660,17662],{"id":17661},"_3-isolate-and-reproduce","3. Isolate and Reproduce",[21,17664,17665],{},"When a pipeline fails, you need to reproduce it locally:",[383,17667,17668,17675,17678],{},[386,17669,17670,17671,17674],{},"Use ",[40,17672,17673],{},"act"," to run workflows locally (with caveats — it is not perfect)",[386,17676,17677],{},"Containerize your build environment so local and CI behave identically",[386,17679,17680],{},"Log everything. The cost of storage is nothing compared to the cost of debugging a failure you cannot see.",[32,17682,17684],{"id":17683},"_4-own-your-observability","4. Own Your Observability",[21,17686,17687],{},"If you cannot answer these questions in under 60 seconds, your observability is insufficient:",[383,17689,17690,17693,17696,17699],{},[386,17691,17692],{},"When did this specific commit last deploy to production?",[386,17694,17695],{},"Which workflow run introduced this regression?",[386,17697,17698],{},"How long does the average deployment take, and is it trending up?",[386,17700,17701],{},"What is the failure rate of our pipeline by job and by step?",[32,17703,17705],{"id":17704},"_5-accept-that-failure-is-normal","5. Accept That Failure Is Normal",[21,17707,17708],{},"Build for failure, not for perfection:",[383,17710,17711,17714,17717,17720],{},[386,17712,17713],{},"Every deployment should be rollback-able in under five minutes",[386,17715,17716],{},"Every secret should be rotatable without a code change",[386,17718,17719],{},"Every environment should be rebuildable from scratch in under an hour",[386,17721,17722],{},"Every team member should know how to pause deployments and who to call",[73,17724],{},[16,17726,17728],{"id":17727},"a-realistic-starting-point","A Realistic Starting Point",[21,17730,17731],{},"If you are building a GitHub Actions pipeline today, here is a minimal, defensible structure:",[48,17733,17735],{"className":17251,"code":17734,"language":17253,"meta":56,"style":56},"name: Deploy\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-node@v4\n        with:\n          node-version-file: '.nvmrc'\n          cache: 'npm'\n      - run: npm ci\n      - run: npm run test:ci\n      - run: npm run lint\n      - run: npm run typecheck\n\n  deploy:\n    needs: test\n    if: github.ref == 'refs\u002Fheads\u002Fmain'\n    runs-on: ubuntu-latest\n    environment: production\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - name: Deploy\n        run: .\u002Fscripts\u002Fdeploy.sh\n      - name: Health Check\n        run: .\u002Fscripts\u002Fhealth-check.sh\n",[40,17736,17737,17746,17750,17756,17763,17775,17782,17792,17796,17803,17813,17823,17827,17834,17841,17851,17858,17871,17882,17889,17899,17909,17920,17931,17942,17953,17957,17964,17974,17983,17991,18001,18007,18017,18027,18037,18048],{"__ignoreMap":56},[449,17738,17739,17741,17743],{"class":451,"line":452},[449,17740,12920],{"class":17260},[449,17742,1482],{"class":684},[449,17744,17745],{"class":691},"Deploy\n",[449,17747,17748],{"class":451,"line":114},[449,17749,700],{"emptyLinePlaceholder":130},[449,17751,17752,17754],{"class":451,"line":111},[449,17753,3492],{"class":714},[449,17755,17264],{"class":684},[449,17757,17758,17761],{"class":451,"line":468},[449,17759,17760],{"class":17260},"  push",[449,17762,17264],{"class":684},[449,17764,17765,17768,17770,17773],{"class":451,"line":474},[449,17766,17767],{"class":17260},"    branches",[449,17769,17279],{"class":684},[449,17771,17772],{"class":691},"main",[449,17774,15050],{"class":684},[449,17776,17777,17780],{"class":451,"line":480},[449,17778,17779],{"class":17260},"  pull_request",[449,17781,17264],{"class":684},[449,17783,17784,17786,17788,17790],{"class":451,"line":486},[449,17785,17767],{"class":17260},[449,17787,17279],{"class":684},[449,17789,17772],{"class":691},[449,17791,15050],{"class":684},[449,17793,17794],{"class":451,"line":492},[449,17795,700],{"emptyLinePlaceholder":130},[449,17797,17798,17801],{"class":451,"line":498},[449,17799,17800],{"class":17260},"concurrency",[449,17802,17264],{"class":684},[449,17804,17805,17808,17810],{"class":451,"line":133},[449,17806,17807],{"class":17260},"  group",[449,17809,1482],{"class":684},[449,17811,17812],{"class":691},"${{ github.workflow }}-${{ github.ref }}\n",[449,17814,17815,17818,17820],{"class":451,"line":509},[449,17816,17817],{"class":17260},"  cancel-in-progress",[449,17819,1482],{"class":684},[449,17821,17822],{"class":714},"true\n",[449,17824,17825],{"class":451,"line":318},[449,17826,700],{"emptyLinePlaceholder":130},[449,17828,17829,17832],{"class":451,"line":520},[449,17830,17831],{"class":17260},"jobs",[449,17833,17264],{"class":684},[449,17835,17836,17839],{"class":451,"line":526},[449,17837,17838],{"class":17260},"  test",[449,17840,17264],{"class":684},[449,17842,17843,17846,17848],{"class":451,"line":532},[449,17844,17845],{"class":17260},"    runs-on",[449,17847,1482],{"class":684},[449,17849,17850],{"class":691},"ubuntu-latest\n",[449,17852,17853,17856],{"class":451,"line":2790},[449,17854,17855],{"class":17260},"    steps",[449,17857,17264],{"class":684},[449,17859,17860,17863,17866,17868],{"class":451,"line":3511},[449,17861,17862],{"class":684},"      - ",[449,17864,17865],{"class":17260},"uses",[449,17867,1482],{"class":684},[449,17869,17870],{"class":691},"actions\u002Fcheckout@v4\n",[449,17872,17873,17875,17877,17879],{"class":451,"line":3526},[449,17874,17862],{"class":684},[449,17876,17865],{"class":17260},[449,17878,1482],{"class":684},[449,17880,17881],{"class":691},"actions\u002Fsetup-node@v4\n",[449,17883,17884,17887],{"class":451,"line":3531},[449,17885,17886],{"class":17260},"        with",[449,17888,17264],{"class":684},[449,17890,17891,17894,17896],{"class":451,"line":3536},[449,17892,17893],{"class":17260},"          node-version-file",[449,17895,1482],{"class":684},[449,17897,17898],{"class":691},"'.nvmrc'\n",[449,17900,17901,17904,17906],{"class":451,"line":4009},[449,17902,17903],{"class":17260},"          cache",[449,17905,1482],{"class":684},[449,17907,17908],{"class":691},"'npm'\n",[449,17910,17911,17913,17915,17917],{"class":451,"line":4015},[449,17912,17862],{"class":684},[449,17914,12051],{"class":17260},[449,17916,1482],{"class":684},[449,17918,17919],{"class":691},"npm ci\n",[449,17921,17922,17924,17926,17928],{"class":451,"line":4021},[449,17923,17862],{"class":684},[449,17925,12051],{"class":17260},[449,17927,1482],{"class":684},[449,17929,17930],{"class":691},"npm run test:ci\n",[449,17932,17933,17935,17937,17939],{"class":451,"line":4027},[449,17934,17862],{"class":684},[449,17936,12051],{"class":17260},[449,17938,1482],{"class":684},[449,17940,17941],{"class":691},"npm run lint\n",[449,17943,17944,17946,17948,17950],{"class":451,"line":4032},[449,17945,17862],{"class":684},[449,17947,12051],{"class":17260},[449,17949,1482],{"class":684},[449,17951,17952],{"class":691},"npm run typecheck\n",[449,17954,17955],{"class":451,"line":4038},[449,17956,700],{"emptyLinePlaceholder":130},[449,17958,17959,17962],{"class":451,"line":4044},[449,17960,17961],{"class":17260},"  deploy",[449,17963,17264],{"class":684},[449,17965,17966,17969,17971],{"class":451,"line":4049},[449,17967,17968],{"class":17260},"    needs",[449,17970,1482],{"class":684},[449,17972,17973],{"class":691},"test\n",[449,17975,17976,17978,17980],{"class":451,"line":4054},[449,17977,12820],{"class":17260},[449,17979,1482],{"class":684},[449,17981,17982],{"class":691},"github.ref == 'refs\u002Fheads\u002Fmain'\n",[449,17984,17985,17987,17989],{"class":451,"line":4059},[449,17986,17845],{"class":17260},[449,17988,1482],{"class":684},[449,17990,17850],{"class":691},[449,17992,17993,17996,17998],{"class":451,"line":4065},[449,17994,17995],{"class":17260},"    environment",[449,17997,1482],{"class":684},[449,17999,18000],{"class":691},"production\n",[449,18002,18003,18005],{"class":451,"line":4070},[449,18004,17855],{"class":17260},[449,18006,17264],{"class":684},[449,18008,18009,18011,18013,18015],{"class":451,"line":4075},[449,18010,17862],{"class":684},[449,18012,17865],{"class":17260},[449,18014,1482],{"class":684},[449,18016,17870],{"class":691},[449,18018,18019,18021,18023,18025],{"class":451,"line":4081},[449,18020,17862],{"class":684},[449,18022,12920],{"class":17260},[449,18024,1482],{"class":684},[449,18026,17745],{"class":691},[449,18028,18029,18032,18034],{"class":451,"line":4086},[449,18030,18031],{"class":17260},"        run",[449,18033,1482],{"class":684},[449,18035,18036],{"class":691},".\u002Fscripts\u002Fdeploy.sh\n",[449,18038,18039,18041,18043,18045],{"class":451,"line":4092},[449,18040,17862],{"class":684},[449,18042,12920],{"class":17260},[449,18044,1482],{"class":684},[449,18046,18047],{"class":691},"Health Check\n",[449,18049,18050,18052,18054],{"class":451,"line":4098},[449,18051,18031],{"class":17260},[449,18053,1482],{"class":684},[449,18055,18056],{"class":691},".\u002Fscripts\u002Fhealth-check.sh\n",[21,18058,18059],{},"This is not fancy. It is boring. Boring is the goal.",[25,18061,18062],{"type":363},[21,18063,18064,18065,18067],{},"Notice the ",[40,18066,17800],{}," block — it prevents multiple deployments from running simultaneously, which is a common source of race conditions and partial deployments.",[73,18069],{},[16,18071,3681],{"id":3680},[21,18073,18074],{},"GitHub Actions and continuous deployment are not easy because deployment is not easy. The YAML is awkward, the failure modes are opaque, and the gap between \"it works in the tutorial\" and \"it works in production\" is wide and deep.",[21,18076,18077],{},"The developers who succeed are not the ones who copy the most sophisticated workflows. They are the ones who:",[383,18079,18080,18083,18086,18089],{},[386,18081,18082],{},"Understand every line in their pipeline",[386,18084,18085],{},"Test their deployments, not just their code",[386,18087,18088],{},"Build for failure and know how to recover",[386,18090,18091],{},"Treat CI\u002FCD as a first-class engineering concern, not an afterthought",[21,18093,18094],{},"If you are struggling with GitHub Actions, you are not doing it wrong. You are experiencing what everyone experiences. Keep going. Document what you learn. And remember: every green checkmark in production was earned by someone who survived a lot of red ones first.",[3728,18096,18097],{},"html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":56,"searchDepth":111,"depth":111,"links":18099},[18100,18101,18102,18103,18104,18105,18106,18107,18108,18109,18110,18111],{"id":17190,"depth":114,"text":17191},{"id":17244,"depth":114,"text":17245},{"id":17351,"depth":114,"text":17352},{"id":17400,"depth":114,"text":17401},{"id":17445,"depth":114,"text":17446},{"id":17512,"depth":114,"text":17513},{"id":17568,"depth":114,"text":17569},{"id":17612,"depth":114,"text":17613},{"id":17640,"depth":114,"text":17641},{"id":17661,"depth":114,"text":17662},{"id":17683,"depth":114,"text":17684},{"id":17704,"depth":114,"text":17705},"2026-06-10","CI\u002FCD pipelines look simple in tutorials. In practice, GitHub Actions is a maze of YAML syntax, secret management, flaky tests, and deployment failures that only show up in production.",{"type":13,"value":18115},[18116,18118,18124,18126],[16,18117,333],{"id":332},[21,18119,17153,18120,17157,18122],{},[40,18121,17156],{},[151,18123,17160],{},[21,18125,17163],{},[21,18127,17166],{},"https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1775009589\u002Fgithub-actions_cd_dificulties.png",[18130,18131,18132,18133,17253,18134,18135,18136],"github actions","ci\u002Fcd","continuous deployment","devops","automation","deployment pipeline","github secrets","YAML",{},"\u002Fblog\u002Fgithub-actions-deployment-difficulties",{"title":17144,"description":18113},"blog\u002Fgithub-actions-deployment-difficulties","DevOps","AM8FjLRy_DQs5OJz7fseuMw6itevwfvy2ZIvJQGctqc",{"id":18145,"title":18146,"author":18147,"body":18148,"canonical":116,"categories":116,"date":8300,"description":20682,"excerpt":116,"extension":119,"img":20683,"keywords":20684,"language":3788,"meta":20691,"navigation":130,"ogImage":116,"path":20692,"rating":116,"readTime":318,"seo":20693,"stem":20694,"tag":3793,"__hash__":20695},"blog\u002Fblog\u002Fllms-and-ai-harness-integration.md","LLMs and Their Integration with AI Harnesses: A Comprehensive Guide",{"name":9,"img":11,"website":327},{"type":13,"value":18149,"toc":20614},[18150,18153,18155,18158,18161,18164,18166,18232,18235,18239,18242,18268,18272,18304,18308,18328,18331,18335,18338,18376,18380,18383,18415,18418,18422,18425,18451,18455,18458,18484,18488,18491,18517,18520,18524,18529,18543,18548,18559,18563,18567,18581,18585,18596,18600,18604,18618,18622,18633,18637,18641,18655,18659,18670,18674,18678,18692,18696,18707,18711,18715,18729,18733,18747,18750,18754,18760,18765,18776,18781,18792,18797,18808,18812,18817,18821,18832,18836,18847,18851,18862,18866,18871,18875,18886,18890,18901,18905,18916,18920,18925,18930,18936,18940,18945,18949,18960,18964,18970,18973,18977,18997,19001,19021,19025,19045,19049,19065,19068,19088,19091,19095,19100,19111,19116,19129,19133,19137,19148,19152,19166,19170,19174,19185,19189,19203,19207,19211,19222,19226,19240,19244,19248,19259,19263,19277,19280,19284,19290,19296,19300,19311,19315,19326,19330,19335,19340,19344,19355,19359,19370,19374,19379,19384,19388,19399,19403,19414,19418,19423,19428,19432,19443,19447,19458,19462,19467,19472,19476,19487,19491,19501,19504,19508,19513,19527,19532,19702,19706,19711,19728,19732,19925,19929,19934,19948,19952,20146,20150,20155,20169,20173,20422,20425,20429,20435,20439,20452,20456,20467,20471,20476,20481,20495,20500,20511,20515,20520,20524,20538,20542,20547,20551,20565,20569,20574,20579,20593,20595,20598,20601,20604,20606,20611],[16,18151,18146],{"id":18152},"llms-and-their-integration-with-ai-harnesses-a-comprehensive-guide",[32,18154,333],{"id":332},[21,18156,18157],{},"Large Language Models (LLMs) have revolutionized the field of artificial intelligence, enabling applications that can understand, generate, and reason with human language at unprecedented scale. However, deploying these powerful models in production environments requires more than just model weights and inference code.",[21,18159,18160],{},"Enter AI harnesses - the infrastructure and tooling layer that bridges the gap between raw LLM capabilities and practical, production-ready applications.",[21,18162,18163],{},"In this comprehensive guide, we'll explore what AI harnesses are, why they're essential for LLM integration, and how to design and implement effective harness architectures for your applications.",[32,18165,7106],{"id":7105},[816,18167,18168,18174,18180,18186,18192,18198,18204,18210,18216,18222,18228],{},[386,18169,18170],{},[3861,18171,18173],{"href":18172},"#understanding-large-language-models","Understanding Large Language Models",[386,18175,18176],{},[3861,18177,18179],{"href":18178},"#what-are-ai-harnesses","What Are AI Harnesses?",[386,18181,18182],{},[3861,18183,18185],{"href":18184},"#why-ai-harnesses-matter-for-llm-integration","Why AI Harnesses Matter for LLM Integration",[386,18187,18188],{},[3861,18189,18191],{"href":18190},"#core-components-of-an-ai-harness","Core Components of an AI Harness",[386,18193,18194],{},[3861,18195,18197],{"href":18196},"#integration-patterns-and-approaches","Integration Patterns and Approaches",[386,18199,18200],{},[3861,18201,18203],{"href":18202},"#benefits-of-ai-harness-integration","Benefits of AI Harness Integration",[386,18205,18206],{},[3861,18207,18209],{"href":18208},"#technical-considerations-and-challenges","Technical Considerations and Challenges",[386,18211,18212],{},[3861,18213,18215],{"href":18214},"#use-cases-and-real-world-examples","Use Cases and Real-World Examples",[386,18217,18218],{},[3861,18219,18221],{"href":18220},"#best-practices-for-llm-integration","Best Practices for LLM Integration",[386,18223,18224],{},[3861,18225,18227],{"href":18226},"#future-trends-in-ai-harness-design","Future Trends in AI Harness Design",[386,18229,18230],{},[3861,18231,3681],{"href":7155},[32,18233,18173],{"id":18234},"understanding-large-language-models",[2808,18236,18238],{"id":18237},"what-are-llms","What Are LLMs?",[21,18240,18241],{},"Large Language Models are deep learning models trained on vast amounts of text data to understand and generate human language. Key characteristics include:",[383,18243,18244,18250,18256,18262],{},[386,18245,18246,18249],{},[151,18247,18248],{},"Scale",": Models with billions to trillions of parameters",[386,18251,18252,18255],{},[151,18253,18254],{},"Autoregressive",": Predict next token based on context",[386,18257,18258,18261],{},[151,18259,18260],{},"Transformer Architecture",": Self-attention mechanisms for context understanding",[386,18263,18264,18267],{},[151,18265,18266],{},"Pre-training",": Learned from massive text corpora before fine-tuning",[2808,18269,18271],{"id":18270},"common-llm-families","Common LLM Families",[383,18273,18274,18280,18286,18292,18298],{},[386,18275,18276,18279],{},[151,18277,18278],{},"GPT Series",": OpenAI's ChatGPT, GPT-3, GPT-4",[386,18281,18282,18285],{},[151,18283,18284],{},"Llama Series",": Meta's open-source models",[386,18287,18288,18291],{},[151,18289,18290],{},"Claude Series",": Anthropic's conversational AI",[386,18293,18294,18297],{},[151,18295,18296],{},"Gemini Series",": Google's multimodal models",[386,18299,18300,18303],{},[151,18301,18302],{},"Mistral Series",": Efficient open-source models",[2808,18305,18307],{"id":18306},"llm-capabilities","LLM Capabilities",[383,18309,18310,18313,18316,18319,18322,18325],{},[386,18311,18312],{},"Text generation and completion",[386,18314,18315],{},"Summarization and translation",[386,18317,18318],{},"Question answering and reasoning",[386,18320,18321],{},"Code generation and debugging",[386,18323,18324],{},"Content creation and editing",[386,18326,18327],{},"Conversational AI and chatbots",[32,18329,18179],{"id":18330},"what-are-ai-harnesses",[2808,18332,18334],{"id":18333},"definition","Definition",[21,18336,18337],{},"An AI harness is a comprehensive infrastructure layer that provides:",[383,18339,18340,18346,18352,18358,18364,18370],{},[386,18341,18342,18345],{},[151,18343,18344],{},"Model Management",": Deployment, scaling, and monitoring of LLM services",[386,18347,18348,18351],{},[151,18349,18350],{},"API Layer",": Standardized interfaces for LLM interactions",[386,18353,18354,18357],{},[151,18355,18356],{},"Caching and Optimization",": Performance tuning and cost reduction",[386,18359,18360,18363],{},[151,18361,18362],{},"Security and Compliance",": Access control and data protection",[386,18365,18366,18369],{},[151,18367,18368],{},"Observability",": Logging, metrics, and debugging tools",[386,18371,18372,18375],{},[151,18373,18374],{},"Integration Layer",": Connectors to external systems and data sources",[2808,18377,18379],{"id":18378},"key-differentiators","Key Differentiators",[21,18381,18382],{},"Unlike traditional ML pipelines, AI harnesses are specifically designed for:",[383,18384,18385,18391,18397,18403,18409],{},[386,18386,18387,18390],{},[151,18388,18389],{},"Conversational Workflows",": Multi-turn interactions and context management",[386,18392,18393,18396],{},[151,18394,18395],{},"Prompt Engineering",": Systematic optimization of model inputs",[386,18398,18399,18402],{},[151,18400,18401],{},"Cost Management",": Token usage tracking and budget control",[386,18404,18405,18408],{},[151,18406,18407],{},"Latency Optimization",": Real-time response generation",[386,18410,18411,18414],{},[151,18412,18413],{},"Multi-modal Support",": Integration with vision, audio, and other modalities",[32,18416,18185],{"id":18417},"why-ai-harnesses-matter-for-llm-integration",[2808,18419,18421],{"id":18420},"_1-production-readiness","1. Production Readiness",[21,18423,18424],{},"LLMs trained in research environments face significant challenges when deployed in production:",[383,18426,18427,18433,18439,18445],{},[386,18428,18429,18432],{},[151,18430,18431],{},"Scalability",": Handling concurrent requests at scale",[386,18434,18435,18438],{},[151,18436,18437],{},"Reliability",": Ensuring consistent performance under load",[386,18440,18441,18444],{},[151,18442,18443],{},"Security",": Protecting sensitive data and model intellectual property",[386,18446,18447,18450],{},[151,18448,18449],{},"Compliance",": Meeting regulatory requirements",[2808,18452,18454],{"id":18453},"_2-cost-efficiency","2. Cost Efficiency",[21,18456,18457],{},"AI harnesses optimize LLM usage through:",[383,18459,18460,18466,18472,18478],{},[386,18461,18462,18465],{},[151,18463,18464],{},"Token Caching",": Reducing redundant API calls",[386,18467,18468,18471],{},[151,18469,18470],{},"Model Selection",": Choosing the right model for each use case",[386,18473,18474,18477],{},[151,18475,18476],{},"Load Balancing",": Distributing requests efficiently",[386,18479,18480,18483],{},[151,18481,18482],{},"Auto-scaling",": Adjusting resources based on demand",[2808,18485,18487],{"id":18486},"_3-developer-experience","3. Developer Experience",[21,18489,18490],{},"Harnesses provide developers with:",[383,18492,18493,18499,18505,18511],{},[386,18494,18495,18498],{},[151,18496,18497],{},"Consistent APIs",": Unified interfaces across different LLM providers",[386,18500,18501,18504],{},[151,18502,18503],{},"Error Handling",": Robust error recovery and retry logic",[386,18506,18507,18510],{},[151,18508,18509],{},"Debugging Tools",": Visibility into model interactions",[386,18512,18513,18516],{},[151,18514,18515],{},"Testing Frameworks",": Unit and integration testing for LLM applications",[32,18518,18191],{"id":18519},"core-components-of-an-ai-harness",[2808,18521,18523],{"id":18522},"_1-model-management-layer","1. Model Management Layer",[21,18525,18526,3070],{},[151,18527,18528],{},"Features",[383,18530,18531,18534,18537,18540],{},[386,18532,18533],{},"Model versioning and deployment",[386,18535,18536],{},"A\u002FB testing capabilities",[386,18538,18539],{},"Model performance monitoring",[386,18541,18542],{},"Automated rollbacks",[21,18544,18545,3070],{},[151,18546,18547],{},"Technologies",[383,18549,18550,18553,18556],{},[386,18551,18552],{},"Container orchestration (Kubernetes)",[386,18554,18555],{},"Model registries (Hugging Face, MLflow)",[386,18557,18558],{},"CI\u002FCD pipelines for model updates",[2808,18560,18562],{"id":18561},"_2-api-gateway-and-routing","2. API Gateway and Routing",[21,18564,18565,3070],{},[151,18566,18528],{},[383,18568,18569,18572,18575,18578],{},[386,18570,18571],{},"Request validation and authentication",[386,18573,18574],{},"Rate limiting and throttling",[386,18576,18577],{},"Load balancing across model instances",[386,18579,18580],{},"Request\u002Fresponse transformation",[21,18582,18583,3070],{},[151,18584,18547],{},[383,18586,18587,18590,18593],{},[386,18588,18589],{},"API gateways (Kong, AWS API Gateway)",[386,18591,18592],{},"Service mesh (Istio, Linkerd)",[386,18594,18595],{},"Circuit breakers and retries",[2808,18597,18599],{"id":18598},"_3-caching-and-optimization-layer","3. Caching and Optimization Layer",[21,18601,18602,3070],{},[151,18603,18528],{},[383,18605,18606,18609,18612,18615],{},[386,18607,18608],{},"Response caching for frequent queries",[386,18610,18611],{},"Prompt caching for repeated inputs",[386,18613,18614],{},"Token usage optimization",[386,18616,18617],{},"Cost tracking and budgeting",[21,18619,18620,3070],{},[151,18621,18547],{},[383,18623,18624,18627,18630],{},[386,18625,18626],{},"In-memory caches (Redis, Memcached)",[386,18628,18629],{},"Vector databases (Pinecone, Weaviate)",[386,18631,18632],{},"Tokenizers and embedding caches",[2808,18634,18636],{"id":18635},"_4-observability-and-monitoring","4. Observability and Monitoring",[21,18638,18639,3070],{},[151,18640,18528],{},[383,18642,18643,18646,18649,18652],{},[386,18644,18645],{},"Request tracing and logging",[386,18647,18648],{},"Performance metrics and alerting",[386,18650,18651],{},"Usage analytics and cost tracking",[386,18653,18654],{},"Error classification and reporting",[21,18656,18657,3070],{},[151,18658,18547],{},[383,18660,18661,18664,18667],{},[386,18662,18663],{},"Observability platforms (Datadog, New Relic)",[386,18665,18666],{},"Logging systems (ELK stack, Splunk)",[386,18668,18669],{},"Metrics collection (Prometheus, Grafana)",[2808,18671,18673],{"id":18672},"_5-security-and-compliance","5. Security and Compliance",[21,18675,18676,3070],{},[151,18677,18528],{},[383,18679,18680,18683,18686,18689],{},[386,18681,18682],{},"Authentication and authorization",[386,18684,18685],{},"Data encryption and masking",[386,18687,18688],{},"Access control policies",[386,18690,18691],{},"Audit logging and compliance reporting",[21,18693,18694,3070],{},[151,18695,18547],{},[383,18697,18698,18701,18704],{},[386,18699,18700],{},"Identity providers (Okta, Auth0)",[386,18702,18703],{},"Encryption libraries (OpenSSL, libsodium)",[386,18705,18706],{},"Compliance frameworks (SOC 2, GDPR)",[2808,18708,18710],{"id":18709},"_6-integration-layer","6. Integration Layer",[21,18712,18713,3070],{},[151,18714,18528],{},[383,18716,18717,18720,18723,18726],{},[386,18718,18719],{},"Connectors to external APIs",[386,18721,18722],{},"Database integrations",[386,18724,18725],{},"Message queue support",[386,18727,18728],{},"Webhook handling",[21,18730,18731,3070],{},[151,18732,18547],{},[383,18734,18735,18738,18741,18744],{},[386,18736,18737],{},"REST and gRPC clients",[386,18739,18740],{},"Database drivers (SQLAlchemy, Prisma)",[386,18742,18743],{},"Message brokers (RabbitMQ, Kafka)",[386,18745,18746],{},"Webhook frameworks (Express, FastAPI)",[32,18748,18197],{"id":18749},"integration-patterns-and-approaches",[2808,18751,18753],{"id":18752},"_1-direct-api-integration","1. Direct API Integration",[21,18755,18756,18759],{},[151,18757,18758],{},"Pattern",": Connect directly to LLM provider APIs",[21,18761,18762,3070],{},[151,18763,18764],{},"Use Cases",[383,18766,18767,18770,18773],{},[386,18768,18769],{},"Simple text generation",[386,18771,18772],{},"Quick prototyping",[386,18774,18775],{},"Low-volume applications",[21,18777,18778,3070],{},[151,18779,18780],{},"Pros",[383,18782,18783,18786,18789],{},[386,18784,18785],{},"Simple implementation",[386,18787,18788],{},"Direct access to latest models",[386,18790,18791],{},"No additional infrastructure",[21,18793,18794,3070],{},[151,18795,18796],{},"Cons",[383,18798,18799,18802,18805],{},[386,18800,18801],{},"Provider lock-in",[386,18803,18804],{},"Limited control over performance",[386,18806,18807],{},"Higher costs at scale",[2808,18809,18811],{"id":18810},"_2-managed-llm-services","2. Managed LLM Services",[21,18813,18814,18816],{},[151,18815,18758],{},": Use managed LLM platforms (OpenAI, Anthropic, etc.)",[21,18818,18819,3070],{},[151,18820,18764],{},[383,18822,18823,18826,18829],{},[386,18824,18825],{},"Production applications",[386,18827,18828],{},"Enterprise deployments",[386,18830,18831],{},"Multi-model support",[21,18833,18834,3070],{},[151,18835,18780],{},[383,18837,18838,18841,18844],{},[386,18839,18840],{},"Managed infrastructure",[386,18842,18843],{},"Built-in monitoring",[386,18845,18846],{},"Enterprise features",[21,18848,18849,3070],{},[151,18850,18796],{},[383,18852,18853,18856,18859],{},[386,18854,18855],{},"Vendor dependencies",[386,18857,18858],{},"Potential vendor lock-in",[386,18860,18861],{},"Limited customization",[2808,18863,18865],{"id":18864},"_3-self-hosted-llm-solutions","3. Self-Hosted LLM Solutions",[21,18867,18868,18870],{},[151,18869,18758],{},": Run LLMs on your own infrastructure",[21,18872,18873,3070],{},[151,18874,18764],{},[383,18876,18877,18880,18883],{},[386,18878,18879],{},"Privacy-sensitive applications",[386,18881,18882],{},"Offline deployments",[386,18884,18885],{},"Custom model fine-tuning",[21,18887,18888,3070],{},[151,18889,18780],{},[383,18891,18892,18895,18898],{},[386,18893,18894],{},"Full control",[386,18896,18897],{},"Cost optimization",[386,18899,18900],{},"Data privacy",[21,18902,18903,3070],{},[151,18904,18796],{},[383,18906,18907,18910,18913],{},[386,18908,18909],{},"High infrastructure costs",[386,18911,18912],{},"Complex maintenance",[386,18914,18915],{},"Requires expertise",[2808,18917,18919],{"id":18918},"_4-hybrid-integration-pattern","4. Hybrid Integration Pattern",[21,18921,18922,18924],{},[151,18923,18758],{},": Combine multiple approaches based on use case",[21,18926,18927,3070],{},[151,18928,18929],{},"Architecture",[48,18931,18934],{"className":18932,"code":18933,"language":53},[51],"┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐\n│   Direct API    │    │ Managed Service │    │ Self-Hosted     │\n│   Integration   │    │   Integration   │    │   Integration   │\n└─────────┬───────┘    └─────────┬───────┘    └─────────┬───────┘\n          │                      │                      │\n          └──────────────────────┼──────────────────────┘\n                                 │\n                    ┌────────────▼────────────┐\n                    │    AI Harness Layer     │\n                    │  (Routing, Caching,     │\n                    │   Monitoring, Security) │\n                    └─────────────────────────┘\n",[40,18935,18933],{"__ignoreMap":56},[2808,18937,18939],{"id":18938},"_5-event-driven-integration","5. Event-Driven Integration",[21,18941,18942,18944],{},[151,18943,18758],{},": Use message queues for asynchronous LLM processing",[21,18946,18947,3070],{},[151,18948,18764],{},[383,18950,18951,18954,18957],{},[386,18952,18953],{},"Batch processing",[386,18955,18956],{},"Real-time analytics",[386,18958,18959],{},"Background tasks",[21,18961,18962,3070],{},[151,18963,18929],{},[48,18965,18968],{"className":18966,"code":18967,"language":53},[51],"┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐\n│   Request Queue │───▶│   LLM Processor  │───▶│   Response DB   │\n│   (RabbitMQ)    │    │   (Kubernetes)   │    │   (PostgreSQL)   │\n└─────────────────┘    └─────────────────┘    └─────────────────┘\n",[40,18969,18967],{"__ignoreMap":56},[32,18971,18203],{"id":18972},"benefits-of-ai-harness-integration",[2808,18974,18976],{"id":18975},"_1-improved-performance","1. Improved Performance",[383,18978,18979,18985,18991],{},[386,18980,18981,18984],{},[151,18982,18983],{},"Faster Response Times",": Caching and optimization reduce latency",[386,18986,18987,18990],{},[151,18988,18989],{},"Higher Throughput",": Efficient load balancing handles concurrent requests",[386,18992,18993,18996],{},[151,18994,18995],{},"Better Resource Utilization",": Auto-scaling adjusts to demand",[2808,18998,19000],{"id":18999},"_2-enhanced-reliability","2. Enhanced Reliability",[383,19002,19003,19009,19015],{},[386,19004,19005,19008],{},[151,19006,19007],{},"High Availability",": Redundancy and failover mechanisms",[386,19010,19011,19014],{},[151,19012,19013],{},"Error Recovery",": Automatic retries and circuit breakers",[386,19016,19017,19020],{},[151,19018,19019],{},"Health Monitoring",": Proactive issue detection and resolution",[2808,19022,19024],{"id":19023},"_3-cost-optimization","3. Cost Optimization",[383,19026,19027,19033,19039],{},[386,19028,19029,19032],{},[151,19030,19031],{},"Reduced Token Usage",": Intelligent caching and prompt optimization",[386,19034,19035,19038],{},[151,19036,19037],{},"Efficient Resource Allocation",": Auto-scaling based on demand",[386,19040,19041,19044],{},[151,19042,19043],{},"Budget Control",": Spending limits and cost tracking",[2808,19046,19048],{"id":19047},"_4-better-developer-experience","4. Better Developer Experience",[383,19050,19051,19056,19060],{},[386,19052,19053,19055],{},[151,19054,18497],{},": Unified interfaces across providers",[386,19057,19058,18510],{},[151,19059,18509],{},[386,19061,19062,19064],{},[151,19063,18515],{},": Comprehensive testing capabilities",[2808,19066,18673],{"id":19067},"_5-security-and-compliance-1",[383,19069,19070,19076,19082],{},[386,19071,19072,19075],{},[151,19073,19074],{},"Data Protection",": Encryption and access control",[386,19077,19078,19081],{},[151,19079,19080],{},"Audit Trails",": Comprehensive logging and monitoring",[386,19083,19084,19087],{},[151,19085,19086],{},"Regulatory Compliance",": Support for industry standards",[32,19089,18209],{"id":19090},"technical-considerations-and-challenges",[2808,19092,19094],{"id":19093},"_1-latency-and-performance","1. Latency and Performance",[21,19096,19097,3070],{},[151,19098,19099],{},"Challenges",[383,19101,19102,19105,19108],{},[386,19103,19104],{},"LLM inference latency",[386,19106,19107],{},"Network overhead",[386,19109,19110],{},"Token processing costs",[21,19112,19113,3070],{},[151,19114,19115],{},"Solutions",[383,19117,19118,19121,19124,19127],{},[386,19119,19120],{},"Response caching",[386,19122,19123],{},"Model optimization",[386,19125,19126],{},"Edge computing",[386,19128,18953],{},[2808,19130,19132],{"id":19131},"_2-cost-management","2. Cost Management",[21,19134,19135,3070],{},[151,19136,19099],{},[383,19138,19139,19142,19145],{},[386,19140,19141],{},"Unpredictable token costs",[386,19143,19144],{},"Model selection complexity",[386,19146,19147],{},"Scaling expenses",[21,19149,19150,3070],{},[151,19151,19115],{},[383,19153,19154,19157,19160,19163],{},[386,19155,19156],{},"Token usage tracking",[386,19158,19159],{},"Cost-per-query monitoring",[386,19161,19162],{},"Model tiering strategies",[386,19164,19165],{},"Budget controls",[2808,19167,19169],{"id":19168},"_3-context-management","3. Context Management",[21,19171,19172,3070],{},[151,19173,19099],{},[383,19175,19176,19179,19182],{},[386,19177,19178],{},"Conversation context preservation",[386,19180,19181],{},"Token limits",[386,19183,19184],{},"Memory efficiency",[21,19186,19187,3070],{},[151,19188,19115],{},[383,19190,19191,19194,19197,19200],{},[386,19192,19193],{},"Vector databases for context storage",[386,19195,19196],{},"Context summarization",[386,19198,19199],{},"Sliding window techniques",[386,19201,19202],{},"Efficient tokenization",[2808,19204,19206],{"id":19205},"_4-security-and-privacy","4. Security and Privacy",[21,19208,19209,3070],{},[151,19210,19099],{},[383,19212,19213,19216,19219],{},[386,19214,19215],{},"Data leakage risks",[386,19217,19218],{},"Model theft",[386,19220,19221],{},"Compliance requirements",[21,19223,19224,3070],{},[151,19225,19115],{},[383,19227,19228,19231,19234,19237],{},[386,19229,19230],{},"Data encryption",[386,19232,19233],{},"Access control",[386,19235,19236],{},"Audit logging",[386,19238,19239],{},"Differential privacy",[2808,19241,19243],{"id":19242},"_5-monitoring-and-observability","5. Monitoring and Observability",[21,19245,19246,3070],{},[151,19247,19099],{},[383,19249,19250,19253,19256],{},[386,19251,19252],{},"Distributed system complexity",[386,19254,19255],{},"LLM-specific metrics",[386,19257,19258],{},"Error classification",[21,19260,19261,3070],{},[151,19262,19115],{},[383,19264,19265,19268,19271,19274],{},[386,19266,19267],{},"Custom metrics",[386,19269,19270],{},"LLM-specific logging",[386,19272,19273],{},"Anomaly detection",[386,19275,19276],{},"Alerting systems",[32,19278,18215],{"id":19279},"use-cases-and-real-world-examples",[2808,19281,19283],{"id":19282},"_1-customer-support-chatbots","1. Customer Support Chatbots",[21,19285,19286,19289],{},[151,19287,19288],{},"Application",": Automated customer service",[21,19291,19292,19295],{},[151,19293,19294],{},"Integration Pattern",": Hybrid (direct API + managed services)",[21,19297,19298,3070],{},[151,19299,18528],{},[383,19301,19302,19305,19308],{},[386,19303,19304],{},"Multi-turn conversations",[386,19306,19307],{},"Knowledge base integration",[386,19309,19310],{},"Escalation to human agents",[21,19312,19313,3070],{},[151,19314,9064],{},[383,19316,19317,19320,19323],{},[386,19318,19319],{},"24\u002F7 availability",[386,19321,19322],{},"Consistent responses",[386,19324,19325],{},"Reduced operational costs",[2808,19327,19329],{"id":19328},"_2-code-generation-and-review","2. Code Generation and Review",[21,19331,19332,19334],{},[151,19333,19288],{},": Developer productivity tools",[21,19336,19337,19339],{},[151,19338,19294],{},": Self-hosted + managed services",[21,19341,19342,3070],{},[151,19343,18528],{},[383,19345,19346,19349,19352],{},[386,19347,19348],{},"Code completion",[386,19350,19351],{},"Bug detection",[386,19353,19354],{},"Code optimization",[21,19356,19357,3070],{},[151,19358,9064],{},[383,19360,19361,19364,19367],{},[386,19362,19363],{},"Faster development",[386,19365,19366],{},"Improved code quality",[386,19368,19369],{},"Reduced debugging time",[2808,19371,19373],{"id":19372},"_3-content-generation-and-marketing","3. Content Generation and Marketing",[21,19375,19376,19378],{},[151,19377,19288],{},": Marketing automation",[21,19380,19381,19383],{},[151,19382,19294],{},": Managed services",[21,19385,19386,3070],{},[151,19387,18528],{},[383,19389,19390,19393,19396],{},[386,19391,19392],{},"Blog post generation",[386,19394,19395],{},"Social media content",[386,19397,19398],{},"Email campaigns",[21,19400,19401,3070],{},[151,19402,9064],{},[383,19404,19405,19408,19411],{},[386,19406,19407],{},"Scalable content creation",[386,19409,19410],{},"Consistent brand voice",[386,19412,19413],{},"Faster campaign deployment",[2808,19415,19417],{"id":19416},"_4-data-analysis-and-insights","4. Data Analysis and Insights",[21,19419,19420,19422],{},[151,19421,19288],{},": Business intelligence",[21,19424,19425,19427],{},[151,19426,19294],{},": Event-driven integration",[21,19429,19430,3070],{},[151,19431,18528],{},[383,19433,19434,19437,19440],{},[386,19435,19436],{},"Natural language queries",[386,19438,19439],{},"Data summarization",[386,19441,19442],{},"Pattern detection",[21,19444,19445,3070],{},[151,19446,9064],{},[383,19448,19449,19452,19455],{},[386,19450,19451],{},"Democratized data access",[386,19453,19454],{},"Faster insights",[386,19456,19457],{},"Reduced analyst workload",[2808,19459,19461],{"id":19460},"_5-educational-ai-tutors","5. Educational AI Tutors",[21,19463,19464,19466],{},[151,19465,19288],{},": Personalized learning",[21,19468,19469,19471],{},[151,19470,19294],{},": Hybrid with self-hosted models",[21,19473,19474,3070],{},[151,19475,18528],{},[383,19477,19478,19481,19484],{},[386,19479,19480],{},"Adaptive learning paths",[386,19482,19483],{},"Personalized feedback",[386,19485,19486],{},"Progress tracking",[21,19488,19489,3070],{},[151,19490,9064],{},[383,19492,19493,19496,19498],{},[386,19494,19495],{},"Customized education",[386,19497,19319],{},[386,19499,19500],{},"Scalable learning",[32,19502,18221],{"id":19503},"best-practices-for-llm-integration",[2808,19505,19507],{"id":19506},"_1-design-for-scalability","1. Design for Scalability",[21,19509,19510,3070],{},[151,19511,19512],{},"Principles",[383,19514,19515,19518,19521,19524],{},[386,19516,19517],{},"Stateless design",[386,19519,19520],{},"Horizontal scaling",[386,19522,19523],{},"Load balancing",[386,19525,19526],{},"Circuit breakers",[21,19528,19529,3070],{},[151,19530,19531],{},"Implementation",[48,19533,19537],{"className":19534,"code":19535,"language":19536,"meta":56,"style":56},"language-python shiki shiki-themes github-light github-dark","# Example: Scalable LLM integration\nclass LLMIntegration:\n    def __init__(self, config):\n        self.config = config\n        self.client_pool = []\n        self.load_balancer = LoadBalancer()\n        self.circuit_breaker = CircuitBreaker()\n    \n    async def generate(self, prompt, context=None):\n        # Use circuit breaker for resilience\n        if not self.circuit_breaker.can_execute():\n            raise CircuitBreakerError(\"Service unavailable\")\n        \n        # Use load balancer for distribution\n        client = self.load_balancer.get_client()\n        return await client.generate(prompt, context)\n","python",[40,19538,19539,19544,19554,19565,19578,19589,19601,19613,19617,19639,19644,19658,19671,19675,19680,19692],{"__ignoreMap":56},[449,19540,19541],{"class":451,"line":452},[449,19542,19543],{"class":705},"# Example: Scalable LLM integration\n",[449,19545,19546,19549,19552],{"class":451,"line":114},[449,19547,19548],{"class":680},"class",[449,19550,19551],{"class":721}," LLMIntegration",[449,19553,17264],{"class":684},[449,19555,19556,19559,19562],{"class":451,"line":111},[449,19557,19558],{"class":680},"    def",[449,19560,19561],{"class":714}," __init__",[449,19563,19564],{"class":684},"(self, config):\n",[449,19566,19567,19570,19573,19575],{"class":451,"line":468},[449,19568,19569],{"class":714},"        self",[449,19571,19572],{"class":684},".config ",[449,19574,5015],{"class":680},[449,19576,19577],{"class":684}," config\n",[449,19579,19580,19582,19585,19587],{"class":451,"line":474},[449,19581,19569],{"class":714},[449,19583,19584],{"class":684},".client_pool ",[449,19586,5015],{"class":680},[449,19588,15391],{"class":684},[449,19590,19591,19593,19596,19598],{"class":451,"line":480},[449,19592,19569],{"class":714},[449,19594,19595],{"class":684},".load_balancer ",[449,19597,5015],{"class":680},[449,19599,19600],{"class":684}," LoadBalancer()\n",[449,19602,19603,19605,19608,19610],{"class":451,"line":486},[449,19604,19569],{"class":714},[449,19606,19607],{"class":684},".circuit_breaker ",[449,19609,5015],{"class":680},[449,19611,19612],{"class":684}," CircuitBreaker()\n",[449,19614,19615],{"class":451,"line":492},[449,19616,9155],{"class":684},[449,19618,19619,19622,19625,19628,19631,19633,19636],{"class":451,"line":498},[449,19620,19621],{"class":680},"    async",[449,19623,19624],{"class":680}," def",[449,19626,19627],{"class":721}," generate",[449,19629,19630],{"class":684},"(self, prompt, context",[449,19632,5015],{"class":680},[449,19634,19635],{"class":714},"None",[449,19637,19638],{"class":684},"):\n",[449,19640,19641],{"class":451,"line":133},[449,19642,19643],{"class":705},"        # Use circuit breaker for resilience\n",[449,19645,19646,19649,19652,19655],{"class":451,"line":509},[449,19647,19648],{"class":680},"        if",[449,19650,19651],{"class":680}," not",[449,19653,19654],{"class":714}," self",[449,19656,19657],{"class":684},".circuit_breaker.can_execute():\n",[449,19659,19660,19663,19666,19669],{"class":451,"line":318},[449,19661,19662],{"class":680},"            raise",[449,19664,19665],{"class":684}," CircuitBreakerError(",[449,19667,19668],{"class":691},"\"Service unavailable\"",[449,19670,11211],{"class":684},[449,19672,19673],{"class":451,"line":520},[449,19674,5774],{"class":684},[449,19676,19677],{"class":451,"line":526},[449,19678,19679],{"class":705},"        # Use load balancer for distribution\n",[449,19681,19682,19685,19687,19689],{"class":451,"line":532},[449,19683,19684],{"class":684},"        client ",[449,19686,5015],{"class":680},[449,19688,19654],{"class":714},[449,19690,19691],{"class":684},".load_balancer.get_client()\n",[449,19693,19694,19697,19699],{"class":451,"line":2790},[449,19695,19696],{"class":680},"        return",[449,19698,1654],{"class":680},[449,19700,19701],{"class":684}," client.generate(prompt, context)\n",[2808,19703,19705],{"id":19704},"_2-implement-comprehensive-monitoring","2. Implement Comprehensive Monitoring",[21,19707,19708,3070],{},[151,19709,19710],{},"Metrics to Track",[383,19712,19713,19716,19719,19722,19725],{},[386,19714,19715],{},"Request latency",[386,19717,19718],{},"Token usage",[386,19720,19721],{},"Error rates",[386,19723,19724],{},"Model performance",[386,19726,19727],{},"Cost tracking",[21,19729,19730,3070],{},[151,19731,19531],{},[48,19733,19735],{"className":19534,"code":19734,"language":19536,"meta":56,"style":56},"# Example: Monitoring integration\nimport prometheus_client\n\nclass LLMMonitor:\n    request_latency = prometheus_client.Histogram(\n        'llm_request_latency_seconds', 'LLM request latency'\n    )\n    token_usage = prometheus_client.Counter(\n        'llm_token_usage_total', 'Total tokens used'\n    )\n    error_rate = prometheus_client.Gauge(\n        'llm_error_rate', 'LLM error rate'\n    )\n    \n    @request_latency.time()\n    async def generate_with_monitoring(self, prompt):\n        # Generate response\n        response = await self.llm_client.generate(prompt)\n        \n        # Track metrics\n        self.token_usage.inc(response.token_count)\n        self.error_rate.set(0 if response.success else 1)\n        \n        return response\n",[40,19736,19737,19742,19749,19753,19762,19772,19782,19786,19796,19806,19810,19820,19830,19834,19838,19845,19857,19862,19876,19880,19885,19892,19914,19918],{"__ignoreMap":56},[449,19738,19739],{"class":451,"line":452},[449,19740,19741],{"class":705},"# Example: Monitoring integration\n",[449,19743,19744,19746],{"class":451,"line":114},[449,19745,681],{"class":680},[449,19747,19748],{"class":684}," prometheus_client\n",[449,19750,19751],{"class":451,"line":111},[449,19752,700],{"emptyLinePlaceholder":130},[449,19754,19755,19757,19760],{"class":451,"line":468},[449,19756,19548],{"class":680},[449,19758,19759],{"class":721}," LLMMonitor",[449,19761,17264],{"class":684},[449,19763,19764,19767,19769],{"class":451,"line":474},[449,19765,19766],{"class":684},"    request_latency ",[449,19768,5015],{"class":680},[449,19770,19771],{"class":684}," prometheus_client.Histogram(\n",[449,19773,19774,19777,19779],{"class":451,"line":480},[449,19775,19776],{"class":691},"        'llm_request_latency_seconds'",[449,19778,349],{"class":684},[449,19780,19781],{"class":691},"'LLM request latency'\n",[449,19783,19784],{"class":451,"line":486},[449,19785,16712],{"class":684},[449,19787,19788,19791,19793],{"class":451,"line":492},[449,19789,19790],{"class":684},"    token_usage ",[449,19792,5015],{"class":680},[449,19794,19795],{"class":684}," prometheus_client.Counter(\n",[449,19797,19798,19801,19803],{"class":451,"line":498},[449,19799,19800],{"class":691},"        'llm_token_usage_total'",[449,19802,349],{"class":684},[449,19804,19805],{"class":691},"'Total tokens used'\n",[449,19807,19808],{"class":451,"line":133},[449,19809,16712],{"class":684},[449,19811,19812,19815,19817],{"class":451,"line":509},[449,19813,19814],{"class":684},"    error_rate ",[449,19816,5015],{"class":680},[449,19818,19819],{"class":684}," prometheus_client.Gauge(\n",[449,19821,19822,19825,19827],{"class":451,"line":318},[449,19823,19824],{"class":691},"        'llm_error_rate'",[449,19826,349],{"class":684},[449,19828,19829],{"class":691},"'LLM error rate'\n",[449,19831,19832],{"class":451,"line":520},[449,19833,16712],{"class":684},[449,19835,19836],{"class":451,"line":526},[449,19837,9155],{"class":684},[449,19839,19840,19843],{"class":451,"line":532},[449,19841,19842],{"class":721},"    @request_latency.time",[449,19844,11220],{"class":684},[449,19846,19847,19849,19851,19854],{"class":451,"line":2790},[449,19848,19621],{"class":680},[449,19850,19624],{"class":680},[449,19852,19853],{"class":721}," generate_with_monitoring",[449,19855,19856],{"class":684},"(self, prompt):\n",[449,19858,19859],{"class":451,"line":3511},[449,19860,19861],{"class":705},"        # Generate response\n",[449,19863,19864,19867,19869,19871,19873],{"class":451,"line":3526},[449,19865,19866],{"class":684},"        response ",[449,19868,5015],{"class":680},[449,19870,1654],{"class":680},[449,19872,19654],{"class":714},[449,19874,19875],{"class":684},".llm_client.generate(prompt)\n",[449,19877,19878],{"class":451,"line":3531},[449,19879,5774],{"class":684},[449,19881,19882],{"class":451,"line":3536},[449,19883,19884],{"class":705},"        # Track metrics\n",[449,19886,19887,19889],{"class":451,"line":4009},[449,19888,19569],{"class":714},[449,19890,19891],{"class":684},".token_usage.inc(response.token_count)\n",[449,19893,19894,19896,19899,19901,19904,19907,19909,19912],{"class":451,"line":4015},[449,19895,19569],{"class":714},[449,19897,19898],{"class":684},".error_rate.set(",[449,19900,9911],{"class":714},[449,19902,19903],{"class":680}," if",[449,19905,19906],{"class":684}," response.success ",[449,19908,5721],{"class":680},[449,19910,19911],{"class":714}," 1",[449,19913,11211],{"class":684},[449,19915,19916],{"class":451,"line":4021},[449,19917,5774],{"class":684},[449,19919,19920,19922],{"class":451,"line":4027},[449,19921,19696],{"class":680},[449,19923,19924],{"class":684}," response\n",[2808,19926,19928],{"id":19927},"_3-optimize-costs-and-performance","3. Optimize Costs and Performance",[21,19930,19931,3070],{},[151,19932,19933],{},"Strategies",[383,19935,19936,19939,19942,19945],{},[386,19937,19938],{},"Implement caching",[386,19940,19941],{},"Use efficient prompts",[386,19943,19944],{},"Batch requests",[386,19946,19947],{},"Monitor usage",[21,19949,19950,3070],{},[151,19951,19531],{},[48,19953,19955],{"className":19534,"code":19954,"language":19536,"meta":56,"style":56},"# Example: Cost optimization\nclass CostOptimizedLLM:\n    def __init__(self, config):\n        self.config = config\n        self.cache = {}\n        self.prompt_optimizer = PromptOptimizer()\n    \n    async def generate(self, prompt, context=None):\n        # Check cache first\n        cache_key = self._generate_cache_key(prompt, context)\n        if cache_key in self.cache:\n            return self.cache[cache_key]\n        \n        # Optimize prompt\n        optimized_prompt = self.prompt_optimizer.optimize(prompt)\n        \n        # Generate response\n        response = await self.llm_client.generate(optimized_prompt, context)\n        \n        # Cache response\n        self.cache[cache_key] = response\n        \n        return response\n",[40,19956,19957,19962,19971,19979,19989,20001,20013,20017,20033,20038,20050,20065,20074,20078,20083,20095,20099,20103,20116,20120,20125,20136,20140],{"__ignoreMap":56},[449,19958,19959],{"class":451,"line":452},[449,19960,19961],{"class":705},"# Example: Cost optimization\n",[449,19963,19964,19966,19969],{"class":451,"line":114},[449,19965,19548],{"class":680},[449,19967,19968],{"class":721}," CostOptimizedLLM",[449,19970,17264],{"class":684},[449,19972,19973,19975,19977],{"class":451,"line":111},[449,19974,19558],{"class":680},[449,19976,19561],{"class":714},[449,19978,19564],{"class":684},[449,19980,19981,19983,19985,19987],{"class":451,"line":468},[449,19982,19569],{"class":714},[449,19984,19572],{"class":684},[449,19986,5015],{"class":680},[449,19988,19577],{"class":684},[449,19990,19991,19993,19996,19998],{"class":451,"line":474},[449,19992,19569],{"class":714},[449,19994,19995],{"class":684},".cache ",[449,19997,5015],{"class":680},[449,19999,20000],{"class":684}," {}\n",[449,20002,20003,20005,20008,20010],{"class":451,"line":480},[449,20004,19569],{"class":714},[449,20006,20007],{"class":684},".prompt_optimizer ",[449,20009,5015],{"class":680},[449,20011,20012],{"class":684}," PromptOptimizer()\n",[449,20014,20015],{"class":451,"line":486},[449,20016,9155],{"class":684},[449,20018,20019,20021,20023,20025,20027,20029,20031],{"class":451,"line":492},[449,20020,19621],{"class":680},[449,20022,19624],{"class":680},[449,20024,19627],{"class":721},[449,20026,19630],{"class":684},[449,20028,5015],{"class":680},[449,20030,19635],{"class":714},[449,20032,19638],{"class":684},[449,20034,20035],{"class":451,"line":498},[449,20036,20037],{"class":705},"        # Check cache first\n",[449,20039,20040,20043,20045,20047],{"class":451,"line":133},[449,20041,20042],{"class":684},"        cache_key ",[449,20044,5015],{"class":680},[449,20046,19654],{"class":714},[449,20048,20049],{"class":684},"._generate_cache_key(prompt, context)\n",[449,20051,20052,20054,20057,20060,20062],{"class":451,"line":509},[449,20053,19648],{"class":680},[449,20055,20056],{"class":684}," cache_key ",[449,20058,20059],{"class":680},"in",[449,20061,19654],{"class":714},[449,20063,20064],{"class":684},".cache:\n",[449,20066,20067,20069,20071],{"class":451,"line":318},[449,20068,5633],{"class":680},[449,20070,19654],{"class":714},[449,20072,20073],{"class":684},".cache[cache_key]\n",[449,20075,20076],{"class":451,"line":520},[449,20077,5774],{"class":684},[449,20079,20080],{"class":451,"line":526},[449,20081,20082],{"class":705},"        # Optimize prompt\n",[449,20084,20085,20088,20090,20092],{"class":451,"line":532},[449,20086,20087],{"class":684},"        optimized_prompt ",[449,20089,5015],{"class":680},[449,20091,19654],{"class":714},[449,20093,20094],{"class":684},".prompt_optimizer.optimize(prompt)\n",[449,20096,20097],{"class":451,"line":2790},[449,20098,5774],{"class":684},[449,20100,20101],{"class":451,"line":3511},[449,20102,19861],{"class":705},[449,20104,20105,20107,20109,20111,20113],{"class":451,"line":3526},[449,20106,19866],{"class":684},[449,20108,5015],{"class":680},[449,20110,1654],{"class":680},[449,20112,19654],{"class":714},[449,20114,20115],{"class":684},".llm_client.generate(optimized_prompt, context)\n",[449,20117,20118],{"class":451,"line":3531},[449,20119,5774],{"class":684},[449,20121,20122],{"class":451,"line":3536},[449,20123,20124],{"class":705},"        # Cache response\n",[449,20126,20127,20129,20132,20134],{"class":451,"line":4009},[449,20128,19569],{"class":714},[449,20130,20131],{"class":684},".cache[cache_key] ",[449,20133,5015],{"class":680},[449,20135,19924],{"class":684},[449,20137,20138],{"class":451,"line":4015},[449,20139,5774],{"class":684},[449,20141,20142,20144],{"class":451,"line":4021},[449,20143,19696],{"class":680},[449,20145,19924],{"class":684},[2808,20147,20149],{"id":20148},"_4-ensure-security-and-privacy","4. Ensure Security and Privacy",[21,20151,20152,3070],{},[151,20153,20154],{},"Best Practices",[383,20156,20157,20160,20163,20166],{},[386,20158,20159],{},"Encrypt sensitive data",[386,20161,20162],{},"Implement access control",[386,20164,20165],{},"Use secure authentication",[386,20167,20168],{},"Log access patterns",[21,20170,20171,3070],{},[151,20172,19531],{},[48,20174,20176],{"className":19534,"code":20175,"language":19536,"meta":56,"style":56},"# Example: Security implementation\nclass SecureLLMIntegration:\n    def __init__(self, config):\n        self.config = config\n        self.auth = AuthManager(config)\n        self.encryption = EncryptionManager(config)\n        self.audit = AuditLogger(config)\n    \n    async def generate(self, prompt, context=None):\n        # Authenticate request\n        user = await self.auth.authenticate(request)\n        \n        # Check permissions\n        if not self.auth.has_permission(user, 'generate_llm'):\n            raise PermissionError(\"Insufficient permissions\")\n        \n        # Encrypt sensitive data\n        encrypted_prompt = self.encryption.encrypt(prompt)\n        \n        # Generate response\n        response = await self.llm_client.generate(encrypted_prompt, context)\n        \n        # Decrypt response\n        decrypted_response = self.encryption.decrypt(response)\n        \n        # Log access\n        await self.audit.log_access(user, 'generate_llm', prompt, response)\n        \n        return decrypted_response\n",[40,20177,20178,20183,20192,20200,20210,20222,20234,20246,20250,20266,20271,20285,20289,20294,20310,20324,20328,20333,20345,20349,20353,20366,20370,20375,20387,20391,20396,20411,20415],{"__ignoreMap":56},[449,20179,20180],{"class":451,"line":452},[449,20181,20182],{"class":705},"# Example: Security implementation\n",[449,20184,20185,20187,20190],{"class":451,"line":114},[449,20186,19548],{"class":680},[449,20188,20189],{"class":721}," SecureLLMIntegration",[449,20191,17264],{"class":684},[449,20193,20194,20196,20198],{"class":451,"line":111},[449,20195,19558],{"class":680},[449,20197,19561],{"class":714},[449,20199,19564],{"class":684},[449,20201,20202,20204,20206,20208],{"class":451,"line":468},[449,20203,19569],{"class":714},[449,20205,19572],{"class":684},[449,20207,5015],{"class":680},[449,20209,19577],{"class":684},[449,20211,20212,20214,20217,20219],{"class":451,"line":474},[449,20213,19569],{"class":714},[449,20215,20216],{"class":684},".auth ",[449,20218,5015],{"class":680},[449,20220,20221],{"class":684}," AuthManager(config)\n",[449,20223,20224,20226,20229,20231],{"class":451,"line":480},[449,20225,19569],{"class":714},[449,20227,20228],{"class":684},".encryption ",[449,20230,5015],{"class":680},[449,20232,20233],{"class":684}," EncryptionManager(config)\n",[449,20235,20236,20238,20241,20243],{"class":451,"line":486},[449,20237,19569],{"class":714},[449,20239,20240],{"class":684},".audit ",[449,20242,5015],{"class":680},[449,20244,20245],{"class":684}," AuditLogger(config)\n",[449,20247,20248],{"class":451,"line":492},[449,20249,9155],{"class":684},[449,20251,20252,20254,20256,20258,20260,20262,20264],{"class":451,"line":498},[449,20253,19621],{"class":680},[449,20255,19624],{"class":680},[449,20257,19627],{"class":721},[449,20259,19630],{"class":684},[449,20261,5015],{"class":680},[449,20263,19635],{"class":714},[449,20265,19638],{"class":684},[449,20267,20268],{"class":451,"line":133},[449,20269,20270],{"class":705},"        # Authenticate request\n",[449,20272,20273,20276,20278,20280,20282],{"class":451,"line":509},[449,20274,20275],{"class":684},"        user ",[449,20277,5015],{"class":680},[449,20279,1654],{"class":680},[449,20281,19654],{"class":714},[449,20283,20284],{"class":684},".auth.authenticate(request)\n",[449,20286,20287],{"class":451,"line":318},[449,20288,5774],{"class":684},[449,20290,20291],{"class":451,"line":520},[449,20292,20293],{"class":705},"        # Check permissions\n",[449,20295,20296,20298,20300,20302,20305,20308],{"class":451,"line":526},[449,20297,19648],{"class":680},[449,20299,19651],{"class":680},[449,20301,19654],{"class":714},[449,20303,20304],{"class":684},".auth.has_permission(user, ",[449,20306,20307],{"class":691},"'generate_llm'",[449,20309,19638],{"class":684},[449,20311,20312,20314,20317,20319,20322],{"class":451,"line":532},[449,20313,19662],{"class":680},[449,20315,20316],{"class":714}," PermissionError",[449,20318,725],{"class":684},[449,20320,20321],{"class":691},"\"Insufficient permissions\"",[449,20323,11211],{"class":684},[449,20325,20326],{"class":451,"line":2790},[449,20327,5774],{"class":684},[449,20329,20330],{"class":451,"line":3511},[449,20331,20332],{"class":705},"        # Encrypt sensitive data\n",[449,20334,20335,20338,20340,20342],{"class":451,"line":3526},[449,20336,20337],{"class":684},"        encrypted_prompt ",[449,20339,5015],{"class":680},[449,20341,19654],{"class":714},[449,20343,20344],{"class":684},".encryption.encrypt(prompt)\n",[449,20346,20347],{"class":451,"line":3531},[449,20348,5774],{"class":684},[449,20350,20351],{"class":451,"line":3536},[449,20352,19861],{"class":705},[449,20354,20355,20357,20359,20361,20363],{"class":451,"line":4009},[449,20356,19866],{"class":684},[449,20358,5015],{"class":680},[449,20360,1654],{"class":680},[449,20362,19654],{"class":714},[449,20364,20365],{"class":684},".llm_client.generate(encrypted_prompt, context)\n",[449,20367,20368],{"class":451,"line":4015},[449,20369,5774],{"class":684},[449,20371,20372],{"class":451,"line":4021},[449,20373,20374],{"class":705},"        # Decrypt response\n",[449,20376,20377,20380,20382,20384],{"class":451,"line":4027},[449,20378,20379],{"class":684},"        decrypted_response ",[449,20381,5015],{"class":680},[449,20383,19654],{"class":714},[449,20385,20386],{"class":684},".encryption.decrypt(response)\n",[449,20388,20389],{"class":451,"line":4032},[449,20390,5774],{"class":684},[449,20392,20393],{"class":451,"line":4038},[449,20394,20395],{"class":705},"        # Log access\n",[449,20397,20398,20401,20403,20406,20408],{"class":451,"line":4044},[449,20399,20400],{"class":680},"        await",[449,20402,19654],{"class":714},[449,20404,20405],{"class":684},".audit.log_access(user, ",[449,20407,20307],{"class":691},[449,20409,20410],{"class":684},", prompt, response)\n",[449,20412,20413],{"class":451,"line":4049},[449,20414,5774],{"class":684},[449,20416,20417,20419],{"class":451,"line":4054},[449,20418,19696],{"class":680},[449,20420,20421],{"class":684}," decrypted_response\n",[32,20423,18227],{"id":20424},"future-trends-in-ai-harness-design",[2808,20426,20428],{"id":20427},"_1-edge-ai-harnesses","1. Edge AI Harnesses",[21,20430,20431,20434],{},[151,20432,20433],{},"Trend",": Moving LLM inference to edge devices",[21,20436,20437,3070],{},[151,20438,9064],{},[383,20440,20441,20444,20447,20450],{},[386,20442,20443],{},"Reduced latency",[386,20445,20446],{},"Offline capabilities",[386,20448,20449],{},"Improved privacy",[386,20451,18897],{},[21,20453,20454,3070],{},[151,20455,18547],{},[383,20457,20458,20461,20464],{},[386,20459,20460],{},"Edge computing platforms",[386,20462,20463],{},"Quantized models",[386,20465,20466],{},"Efficient inference engines",[2808,20468,20470],{"id":20469},"_2-multi-modal-harnesses","2. Multi-Modal Harnesses",[21,20472,20473,20475],{},[151,20474,20433],{},": Integrating vision, audio, and other modalities",[21,20477,20478,3070],{},[151,20479,20480],{},"Capabilities",[383,20482,20483,20486,20489,20492],{},[386,20484,20485],{},"Image understanding",[386,20487,20488],{},"Speech recognition",[386,20490,20491],{},"Sensor data processing",[386,20493,20494],{},"Cross-modal reasoning",[21,20496,20497,3070],{},[151,20498,20499],{},"Applications",[383,20501,20502,20505,20508],{},[386,20503,20504],{},"Computer vision",[386,20506,20507],{},"Voice assistants",[386,20509,20510],{},"IoT analytics",[2808,20512,20514],{"id":20513},"_3-federated-learning-harnesses","3. Federated Learning Harnesses",[21,20516,20517,20519],{},[151,20518,20433],{},": Training models across distributed devices",[21,20521,20522,3070],{},[151,20523,9064],{},[383,20525,20526,20529,20532,20535],{},[386,20527,20528],{},"Privacy preservation",[386,20530,20531],{},"Data diversity",[386,20533,20534],{},"Reduced data transfer",[386,20536,20537],{},"Collaborative learning",[2808,20539,20541],{"id":20540},"_4-ai-harness-orchestration","4. AI Harness Orchestration",[21,20543,20544,20546],{},[151,20545,20433],{},": Automated management of complex harness architectures",[21,20548,20549,3070],{},[151,20550,18528],{},[383,20552,20553,20556,20559,20562],{},[386,20554,20555],{},"Self-healing systems",[386,20557,20558],{},"Auto-optimization",[386,20560,20561],{},"Resource management",[386,20563,20564],{},"Performance tuning",[2808,20566,20568],{"id":20567},"_5-sustainable-ai-harnesses","5. Sustainable AI Harnesses",[21,20570,20571,20573],{},[151,20572,20433],{},": Environmentally conscious AI infrastructure",[21,20575,20576,3070],{},[151,20577,20578],{},"Focus Areas",[383,20580,20581,20584,20587,20590],{},[386,20582,20583],{},"Energy efficiency",[386,20585,20586],{},"Carbon footprint tracking",[386,20588,20589],{},"Sustainable hardware",[386,20591,20592],{},"Green computing practices",[32,20594,3681],{"id":3680},[21,20596,20597],{},"AI harnesses are essential infrastructure for integrating LLMs into production applications. They provide the foundation for scalable, reliable, and cost-effective LLM deployment while addressing critical challenges like security, privacy, and performance.",[21,20599,20600],{},"By understanding the components, integration patterns, and best practices outlined in this guide, you can design and implement effective AI harnesses that unlock the full potential of LLMs while maintaining operational excellence.",[21,20602,20603],{},"The future of AI harness design promises even more sophisticated capabilities, from edge AI to multi-modal integration and federated learning. As these trends mature, AI harnesses will continue to evolve, enabling increasingly powerful and accessible AI applications for everyone.",[73,20605],{},[21,20607,20608],{},[156,20609,20610],{},"Whether you're building a simple chatbot or a complex enterprise AI system, investing in a robust AI harness will pay dividends in terms of reliability, cost efficiency, and developer experience. The key is to start with a solid foundation and evolve your harness as your needs grow.",[3728,20612,20613],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":56,"searchDepth":111,"depth":111,"links":20615},[20616,20617,20618,20623,20627,20632,20640,20647,20654,20661,20668,20674,20681],{"id":332,"depth":114,"text":333},{"id":7105,"depth":114,"text":7106},{"id":18234,"depth":114,"text":18173,"children":20619},[20620,20621,20622],{"id":18237,"depth":111,"text":18238},{"id":18270,"depth":111,"text":18271},{"id":18306,"depth":111,"text":18307},{"id":18330,"depth":114,"text":18179,"children":20624},[20625,20626],{"id":18333,"depth":111,"text":18334},{"id":18378,"depth":111,"text":18379},{"id":18417,"depth":114,"text":18185,"children":20628},[20629,20630,20631],{"id":18420,"depth":111,"text":18421},{"id":18453,"depth":111,"text":18454},{"id":18486,"depth":111,"text":18487},{"id":18519,"depth":114,"text":18191,"children":20633},[20634,20635,20636,20637,20638,20639],{"id":18522,"depth":111,"text":18523},{"id":18561,"depth":111,"text":18562},{"id":18598,"depth":111,"text":18599},{"id":18635,"depth":111,"text":18636},{"id":18672,"depth":111,"text":18673},{"id":18709,"depth":111,"text":18710},{"id":18749,"depth":114,"text":18197,"children":20641},[20642,20643,20644,20645,20646],{"id":18752,"depth":111,"text":18753},{"id":18810,"depth":111,"text":18811},{"id":18864,"depth":111,"text":18865},{"id":18918,"depth":111,"text":18919},{"id":18938,"depth":111,"text":18939},{"id":18972,"depth":114,"text":18203,"children":20648},[20649,20650,20651,20652,20653],{"id":18975,"depth":111,"text":18976},{"id":18999,"depth":111,"text":19000},{"id":19023,"depth":111,"text":19024},{"id":19047,"depth":111,"text":19048},{"id":19067,"depth":111,"text":18673},{"id":19090,"depth":114,"text":18209,"children":20655},[20656,20657,20658,20659,20660],{"id":19093,"depth":111,"text":19094},{"id":19131,"depth":111,"text":19132},{"id":19168,"depth":111,"text":19169},{"id":19205,"depth":111,"text":19206},{"id":19242,"depth":111,"text":19243},{"id":19279,"depth":114,"text":18215,"children":20662},[20663,20664,20665,20666,20667],{"id":19282,"depth":111,"text":19283},{"id":19328,"depth":111,"text":19329},{"id":19372,"depth":111,"text":19373},{"id":19416,"depth":111,"text":19417},{"id":19460,"depth":111,"text":19461},{"id":19503,"depth":114,"text":18221,"children":20669},[20670,20671,20672,20673],{"id":19506,"depth":111,"text":19507},{"id":19704,"depth":111,"text":19705},{"id":19927,"depth":111,"text":19928},{"id":20148,"depth":111,"text":20149},{"id":20424,"depth":114,"text":18227,"children":20675},[20676,20677,20678,20679,20680],{"id":20427,"depth":111,"text":20428},{"id":20469,"depth":111,"text":20470},{"id":20513,"depth":111,"text":20514},{"id":20540,"depth":111,"text":20541},{"id":20567,"depth":111,"text":20568},{"id":3680,"depth":114,"text":3681},"An overview of Large Language Models and the harness infrastructure that makes them production-ready — covering model management, API routing, caching, observability, security, and integration patterns from direct API to self-hosted and event-driven approaches.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1677442136019-21780ecad995",[3781,3782,20685,20686,20687,20688,20689,20690],"harness","integration","infrastructure","observability","caching","multi-modal",{},"\u002Fblog\u002Fllms-and-ai-harness-integration",{"title":18146,"description":20682},"blog\u002Fllms-and-ai-harness-integration","PZkVKCP5DRZkv_ppF3rcJL8_3X34zCWNX_Dzey0KcWk",{"id":20697,"title":20698,"author":20699,"body":20700,"canonical":116,"categories":116,"date":20873,"description":20874,"excerpt":20875,"extension":119,"img":20886,"keywords":20887,"language":3788,"meta":20893,"navigation":130,"ogImage":116,"path":20894,"rating":474,"readTime":480,"seo":20895,"stem":20896,"tag":20897,"__hash__":20898},"blog\u002Fblog\u002Fmps-new-begins.md","Mashed Potato Studios: Crafting Digital Experiences That Actually Work",{"name":9,"img":11,"website":327},{"type":13,"value":20701,"toc":20865},[20702,20704,20716,20718,20722,20725,20730,20734,20737,20757,20760,20764,20771,20785,20790,20794,20797,20862],[16,20703,333],{"id":332},[21,20705,20706,20707,20709,20710,20715],{},"At ",[151,20708,164],{},", we believe great digital products should be simple, effective, and memorable—just like comfort food done right. Our website, ",[3861,20711,20714],{"href":20712,"rel":20713},"https:\u002F\u002Fmashedpotatostudios.com",[3865],"mashedpotatostudios.com",", reflects who we are: a creative digital studio focused on building solutions that make sense for real people and real businesses.",[16,20717,4373],{"id":4372},[32,20719,20721],{"id":20720},"who-we-are","Who We Are",[21,20723,20724],{},"Mashed Potato Studios is a creative and technology-driven studio that helps businesses transform ideas into polished digital experiences. From concept to launch, we focus on clarity, usability, and performance—cutting out the unnecessary fluff and delivering what actually matters.",[25,20726,20727],{"type":363},[21,20728,20729],{},"Mashed Potato Studios works with startups, growing businesses, and established brands looking to level up their digital presence.",[32,20731,20733],{"id":20732},"what-we-do","What We Do",[21,20735,20736],{},"Our services are designed to meet clients where they are, whether they’re starting from scratch or refining an existing product.",[2805,20738,20739,20743],{},[2808,20740,20742],{"id":20741},"core-focus-areas","Core Focus Areas",[383,20744,20745,20748,20751,20754],{},[386,20746,20747],{},"Website design & development",[386,20749,20750],{},"Web applications & SaaS platforms",[386,20752,20753],{},"Branding & digital identity",[386,20755,20756],{},"Performance, scalability, and user experience",[21,20758,20759],{},"We combine clean design with modern development practices to ensure every project is fast, accessible, and future-ready.",[32,20761,20763],{"id":20762},"why-the-website-matters","Why the Website Matters",[21,20765,20766,20767,20770],{},"Your website is often the ",[151,20768,20769],{},"first impression"," of your brand. At Mashed Potato Studios, we design websites that:",[383,20772,20773,20776,20779,20782],{},[386,20774,20775],{},"Communicate value clearly",[386,20777,20778],{},"Load fast and work across all devices",[386,20780,20781],{},"Convert visitors into customers",[386,20783,20784],{},"Grow with your business",[25,20786,20787],{"type":27},[21,20788,20789],{},"A slow, cluttered, or outdated website can cost you credibility—and customers.",[32,20791,20793],{"id":20792},"our-approach-to-development","Our Approach to Development",[21,20795,20796],{},"We build with modern tools and best practices, focusing on maintainability and long-term success.",[48,20798,20801],{"className":670,"code":20799,"filename":20800,"language":673,"meta":56,"style":56},"const studioPhilosophy = {\n  design: \"Clean and purposeful\",\n  code: \"Scalable and maintainable\",\n  clients: \"Partners, not just projects\",\n};\n\nconsole.log(studioPhilosophy);\n","philosophy.ts",[40,20802,20803,20814,20824,20834,20844,20849,20853],{"__ignoreMap":56},[449,20804,20805,20807,20810,20812],{"class":451,"line":452},[449,20806,711],{"class":680},[449,20808,20809],{"class":714}," studioPhilosophy",[449,20811,718],{"class":680},[449,20813,1501],{"class":684},[449,20815,20816,20819,20822],{"class":451,"line":114},[449,20817,20818],{"class":684},"  design: ",[449,20820,20821],{"class":691},"\"Clean and purposeful\"",[449,20823,779],{"class":684},[449,20825,20826,20829,20832],{"class":451,"line":111},[449,20827,20828],{"class":684},"  code: ",[449,20830,20831],{"class":691},"\"Scalable and maintainable\"",[449,20833,779],{"class":684},[449,20835,20836,20839,20842],{"class":451,"line":468},[449,20837,20838],{"class":684},"  clients: ",[449,20840,20841],{"class":691},"\"Partners, not just projects\"",[449,20843,779],{"class":684},[449,20845,20846],{"class":451,"line":474},[449,20847,20848],{"class":684},"};\n",[449,20850,20851],{"class":451,"line":480},[449,20852,700],{"emptyLinePlaceholder":130},[449,20854,20855,20857,20859],{"class":451,"line":486},[449,20856,1672],{"class":684},[449,20858,1675],{"class":721},[449,20860,20861],{"class":684},"(studioPhilosophy);\n",[3728,20863,20864],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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);}",{"title":56,"searchDepth":111,"depth":111,"links":20866},[20867,20868,20871,20872],{"id":20720,"depth":114,"text":20721},{"id":20732,"depth":114,"text":20733,"children":20869},[20870],{"id":20741,"depth":111,"text":20742},{"id":20762,"depth":114,"text":20763},{"id":20792,"depth":114,"text":20793},"2026-01-04","An inside look at Mashed Potato Studios—our mission, services, and how we help brands turn ideas into powerful digital products.",{"type":13,"value":20876},[20877,20879],[16,20878,333],{"id":332},[21,20880,20706,20881,20709,20883,20715],{},[151,20882,164],{},[3861,20884,20714],{"href":20712,"rel":20885},[3865],"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1553877522-43269d4ea984",[20888,20889,20890,20891,20892],"web development","branding","digital studio","startups","saas",{},"\u002Fblog\u002Fmps-new-begins",{"title":20698,"description":20874},"blog\u002Fmps-new-begins","Business","RTbMI38IFrp9Zg8xRFdCHiPkiqz_jlaEe9outeJD4n0",{"id":20900,"title":20901,"author":20902,"body":20903,"canonical":116,"categories":116,"date":20948,"description":20949,"excerpt":20950,"extension":119,"img":4159,"keywords":20956,"language":4162,"meta":20957,"navigation":130,"ogImage":116,"path":20958,"rating":20959,"readTime":111,"seo":20960,"stem":20961,"tag":20962,"__hash__":20963},"blog\u002Fblog\u002Fnew-vue-default.md","The New Vue Default",{"name":9,"website":10,"img":11},{"type":13,"value":20904,"toc":20946},[20905,20908,20911,20917,20920,20940,20943],[16,20906,20901],{"id":20907},"the-new-vue-default",[21,20909,20910],{},"Important notice: Vue will become default version on February 7, 2022.",[20912,20913,20914],"note",{},[21,20915,20916],{},"This was a major milestone for the Vue ecosystem, shifting from a library-first to a framework-first mindset.",[21,20918,20919],{},"I am actually excited about the current direction and vision that Vue is headed. Being a progressive framework means that implementation of how code is written is paramount for developer experience (DX). Look at how the new vue is implemented.",[427,20921,20924],{"icon":20922,"title":20923},"⚡️","Quick Install",[48,20925,20927],{"className":839,"code":20926,"language":841,"meta":56,"style":56},"npm install init vue@latest\n",[40,20928,20929],{"__ignoreMap":56},[449,20930,20931,20933,20935,20937],{"class":451,"line":452},[449,20932,3703],{"class":721},[449,20934,3706],{"class":691},[449,20936,4478],{"class":691},[449,20938,20939],{"class":691}," vue@latest\n",[21,20941,20942],{},"This provides a refreshing CLI experience that runs on Vite tooling under the hood. Integrating all the relevant tools from the community make the experience worth while..... just wish there was an turn off the bloatware (for more experienced persons who wanna get up and running quickly).",[3728,20944,20945],{},"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 .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);}",{"title":56,"searchDepth":111,"depth":111,"links":20947},[],"2022-02-01","From a library to a Framework",{"type":13,"value":20951},[20952,20954],[16,20953,20901],{"id":20907},[21,20955,20910],{},[122,123,124,125,4161,127],{},"\u002Fblog\u002Fnew-vue-default",4.8,{"title":20901,"description":20949},"blog\u002Fnew-vue-default","Framework","_ciURD2dpRkcxF78RbONV7kr3yG4Lj1VuIUZBl4WBf8",{"id":20965,"title":20966,"author":20967,"body":20968,"canonical":116,"categories":116,"date":21000,"description":21001,"excerpt":21002,"extension":119,"img":120,"keywords":21008,"language":4162,"meta":21009,"navigation":130,"ogImage":116,"path":21010,"rating":132,"readTime":474,"seo":21011,"stem":21012,"tag":21013,"__hash__":21014},"blog\u002Fblog\u002Fnew-website.md","My New Website",{"name":9,"website":10,"img":11},{"type":13,"value":20969,"toc":20996},[20970,20973,20976,20979,20984,20987,20991],[32,20971,20972],{"id":3612},"Performance",[21,20974,20975],{},"Nuxt.js provides some unique advantages which allowed for it to be the main choice when creating my website.",[21,20977,20978],{},"Vue.js is my framework of choice in most cases. Nuxt.js provides an \"intutitve\" developer experience (DX).",[3830,20980,20981],{},[21,20982,20983],{},"Nuxt.js has zero configuration, allowing for file-system routing, components auto-import and seo friendly for indexing.",[21,20985,20986],{},"Paramount to this application using the power modules- Content, that allows for my web app to using MDX for this blog section.",[32,20988,20990],{"id":20989},"future-updates","Future Updates",[2805,20992,20993],{},[21,20994,20995],{},"Addition of Youtube videos section will added shortly, Integration of Twitch API....",{"title":56,"searchDepth":111,"depth":111,"links":20997},[20998,20999],{"id":3612,"depth":114,"text":20972},{"id":20989,"depth":114,"text":20990},"2021-12-01","Built with Nuxt.js 2",{"type":13,"value":21003},[21004,21006],[32,21005,20972],{"id":3612},[21,21007,20975],{},[122,123,124,125,4161,127],{},"\u002Fblog\u002Fnew-website",{"title":20966,"description":21001},"blog\u002Fnew-website","New Stack","QZONtmh4tVLWktddB0aVeaOkILpE6S43GCANap-Q3KM",{"id":21016,"title":21017,"author":21018,"body":21019,"canonical":116,"categories":116,"date":21230,"description":21231,"excerpt":116,"extension":119,"img":21232,"keywords":21233,"language":3788,"meta":21238,"navigation":130,"ogImage":116,"path":21239,"rating":474,"readTime":474,"seo":21240,"stem":21241,"tag":3793,"__hash__":21242},"blog\u002Fblog\u002Fnew-year-new-code.md","New Year, New Stack: Embracing TanStack Start & AI Integrations in 2026",{"name":9,"img":11,"website":327},{"type":13,"value":21020,"toc":21222},[21021,21023,21038,21040,21044,21050,21053,21058,21062,21067,21070,21084,21101,21104,21108,21111,21114,21128,21135,21138,21142,21152,21220],[16,21022,333],{"id":332},[21,21024,21025,21026,21029,21030,21033,21034,21037],{},"A new year always brings fresh energy—and in tech, that energy often shows up as better tools, smarter workflows, and new ways of thinking. As we step into ",[151,21027,21028],{},"2026",", this is the year of embracing modern frameworks like ",[151,21031,21032],{},"TanStack Start"," and leaning fully into ",[151,21035,21036],{},"AI integrations"," to build faster, more intelligent digital experiences.",[16,21039,4373],{"id":4372},[32,21041,21043],{"id":21042},"why-a-new-stack-matters","Why a New Stack Matters",[21,21045,21046,21047,165],{},"The web is evolving quickly. Users expect instant feedback, seamless navigation, and personalized experiences. To meet those expectations, developers need tools that are ",[151,21048,21049],{},"performant, flexible, and future-proof",[21,21051,21052],{},"That’s where modern frameworks come in.",[25,21054,21055],{"type":363},[21,21056,21057],{},"Choosing the right framework isn’t about hype—it’s about long-term maintainability and developer experience.",[32,21059,21061],{"id":21060},"tanstack-start-a-fresh-way-to-build","TanStack Start: A Fresh Way to Build",[21,21063,21064,21066],{},[151,21065,21032],{}," represents a shift toward a more unified, type-safe, and scalable approach to building full-stack applications. It brings together ideas we already love—routing, data fetching, and server-first thinking—into a cohesive system.",[21,21068,21069],{},"Why it’s exciting:",[383,21071,21072,21075,21078,21081],{},[386,21073,21074],{},"End-to-end type safety",[386,21076,21077],{},"Powerful data management with TanStack Query",[386,21079,21080],{},"Server-first architecture without unnecessary complexity",[386,21082,21083],{},"Freedom from heavy abstractions",[2805,21085,21086,21090],{},[2808,21087,21089],{"id":21088},"what-tanstack-start-encourages","What TanStack Start Encourages",[383,21091,21092,21095,21098],{},[386,21093,21094],{},"Thinking in data, not just pages",[386,21096,21097],{},"Clear separation of concerns",[386,21099,21100],{},"Performance by default",[21,21102,21103],{},"It’s less about fighting the framework and more about building with confidence.",[32,21105,21107],{"id":21106},"ai-integrations-from-nice-to-have-to-essential","AI Integrations: From “Nice-to-Have” to Essential",[21,21109,21110],{},"AI is no longer experimental—it’s becoming foundational.",[21,21112,21113],{},"In 2026, AI integrations are helping teams:",[383,21115,21116,21119,21122,21125],{},[386,21117,21118],{},"Automate repetitive workflows",[386,21120,21121],{},"Enhance user experiences with personalization",[386,21123,21124],{},"Provide smarter search and recommendations",[386,21126,21127],{},"Improve developer productivity",[427,21129,21132],{"icon":21130,"title":21131},"🤖","AI as a Teammate",[21,21133,21134],{},"The best AI tools don’t replace developers—they amplify what developers can do.",[21,21136,21137],{},"From intelligent chat assistants to backend automation, AI is reshaping how products are built and how users interact with them.",[32,21139,21141],{"id":21140},"building-smarter-not-harder","Building Smarter, Not Harder",[21,21143,21144,21145,21148,21149,165],{},"The real power comes from combining ",[151,21146,21147],{},"modern frameworks"," with ",[151,21150,21151],{},"AI-driven features",[48,21153,21156],{"className":670,"code":21154,"filename":21155,"language":673,"meta":56,"style":56},"const newYearStack = {\n  framework: \"TanStack Start\",\n  ai: \"Integrated at every layer\",\n  goal: \"Faster builds, smarter products\",\n};\n\nconsole.log(\"Welcome to 2026 🚀\");\n","future.ts",[40,21157,21158,21169,21179,21189,21199,21203,21207],{"__ignoreMap":56},[449,21159,21160,21162,21165,21167],{"class":451,"line":452},[449,21161,711],{"class":680},[449,21163,21164],{"class":714}," newYearStack",[449,21166,718],{"class":680},[449,21168,1501],{"class":684},[449,21170,21171,21174,21177],{"class":451,"line":114},[449,21172,21173],{"class":684},"  framework: ",[449,21175,21176],{"class":691},"\"TanStack Start\"",[449,21178,779],{"class":684},[449,21180,21181,21184,21187],{"class":451,"line":111},[449,21182,21183],{"class":684},"  ai: ",[449,21185,21186],{"class":691},"\"Integrated at every layer\"",[449,21188,779],{"class":684},[449,21190,21191,21194,21197],{"class":451,"line":468},[449,21192,21193],{"class":684},"  goal: ",[449,21195,21196],{"class":691},"\"Faster builds, smarter products\"",[449,21198,779],{"class":684},[449,21200,21201],{"class":451,"line":474},[449,21202,20848],{"class":684},[449,21204,21205],{"class":451,"line":480},[449,21206,700],{"emptyLinePlaceholder":130},[449,21208,21209,21211,21213,21215,21218],{"class":451,"line":486},[449,21210,1672],{"class":684},[449,21212,1675],{"class":721},[449,21214,725],{"class":684},[449,21216,21217],{"class":691},"\"Welcome to 2026 🚀\"",[449,21219,740],{"class":684},[3728,21221,20864],{},{"title":56,"searchDepth":111,"depth":111,"links":21223},[21224,21225,21228,21229],{"id":21042,"depth":114,"text":21043},{"id":21060,"depth":114,"text":21061,"children":21226},[21227],{"id":21088,"depth":111,"text":21089},{"id":21106,"depth":114,"text":21107},{"id":21140,"depth":114,"text":21141},"2026-01-01","A New Year reflection on adopting modern frameworks like TanStack Start and integrating AI to build faster, smarter digital products.","https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1767533051\u002FPXL_20251114_022510096_kq4fyh.jpg",[21234,21235,21236,20888,21237],"tanstack","tanstack start","ai integration",2026,{},"\u002Fblog\u002Fnew-year-new-code",{"title":21017,"description":21231},"blog\u002Fnew-year-new-code","HRNYSokixOaSOra7wi-C5D-PN0FTgyqYLz-TF3johDs",{"id":21244,"title":21245,"author":21246,"body":21247,"canonical":116,"categories":116,"date":21911,"description":21912,"excerpt":21913,"extension":119,"img":21929,"keywords":21930,"language":3788,"meta":21931,"navigation":130,"ogImage":116,"path":21932,"rating":474,"readTime":133,"seo":21933,"stem":21934,"tag":3793,"__hash__":21935},"blog\u002Fblog\u002Fnitro-build.md","How to Use Nitro: Build and Deploy Modern Servers Anywhere",{"name":9,"img":11,"website":327},{"type":13,"value":21248,"toc":21891},[21249,21251,21263,21270,21272,21276,21281,21309,21316,21323,21325,21329,21333,21338,21364,21367,21373,21381,21385,21388,21400,21410,21412,21416,21427,21431,21437,21471,21480,21489,21491,21495,21504,21508,21573,21579,21581,21585,21593,21597,21649,21652,21688,21698,21700,21704,21716,21723,21725,21729,21736,21748,21757,21760,21787,21793,21795,21799,21810,21813,21815,21819,21831,21834,21845,21847,21849,21854,21886,21889],[16,21250,333],{"id":332},[21,21252,21253,21254,21257,21258,21260,21261],{},"Modern web applications increasingly require flexible, performant backends that can run ",[151,21255,21256],{},"anywhere from edge servers to traditional Node hosts",". That’s where ",[151,21259,4189],{}," comes in — a lightweight, runtime-agnostic server toolkit that lets you build APIs, server logic, and backend functionality with minimal configuration and deploy it across platforms seamlessly. Nitro works standalone or as the server engine under frameworks like Nuxt and Vite-based apps. ",[9909,21262,9912],{"index":9911},[21,21264,21265,21266,21269],{},"In this post, we’ll explore how to ",[151,21267,21268],{},"use Nitro",", walk through practical examples, and show you how to build and deploy real server-side logic using this powerful tool.",[16,21271,4373],{"id":4372},[32,21273,21275],{"id":21274},"what-is-nitro-and-why-use-it","What Is Nitro and Why Use It?",[21,21277,9924,21278,21280],{},[151,21279,4189],{}," is a server engine and framework designed to make backend development simple, portable, and efficient. It gives you:",[383,21282,21283,21289,21295,21301,21304],{},[386,21284,21285,21288],{},[151,21286,21287],{},"Out-of-the-box server routing"," with filesystem-based handlers",[386,21290,21291,21294],{},[151,21292,21293],{},"Multi-runtime support"," — Node, Bun, Deno, and more",[386,21296,21297,21300],{},[151,21298,21299],{},"Zero-config deployment"," for many platforms",[386,21302,21303],{},"Hot reloading and developer experience conveniences",[386,21305,21306,21307],{},"Minimal overhead — compiled output is very small ",[9909,21308,9938],{"index":4799},[21,21310,21311,21312,21315],{},"Instead of wrestling with server setups or environment differences, Nitro lets you focus on your ",[151,21313,21314],{},"handlers and business logic"," while it handles the rest.",[25,21317,21318],{"type":363},[21,21319,21320,21321],{},"Nitro works both as a standalone server toolkit and as the server engine powering full-stack frameworks like Nuxt — giving you flexibility to choose how you build your backend. ",[9909,21322,9950],{"index":4827},[73,21324],{},[32,21326,21328],{"id":21327},"getting-started-with-nitro","Getting Started with Nitro",[2808,21330,21332],{"id":21331},"step-1-create-a-new-nitro-project","Step 1: Create a New Nitro Project",[21,21334,21335,21336],{},"Nitro provides a starter template to help you bootstrap quickly (and you can even try it in your browser with an online playground). ",[9909,21337,9997],{"index":4850},[48,21339,21341],{"className":839,"code":21340,"language":841,"meta":56,"style":56},"npx create-nitro-app nitro-app\ncd nitro-app\nnpm install\n",[40,21342,21343,21352,21358],{"__ignoreMap":56},[449,21344,21345,21347,21349],{"class":451,"line":452},[449,21346,8898],{"class":721},[449,21348,10567],{"class":691},[449,21350,21351],{"class":691}," nitro-app\n",[449,21353,21354,21356],{"class":451,"line":114},[449,21355,4490],{"class":714},[449,21357,21351],{"class":691},[449,21359,21360,21362],{"class":451,"line":111},[449,21361,3703],{"class":721},[449,21363,4512],{"class":691},[21,21365,21366],{},"Once installed, you’ll have a Nitro project with a directory structure like:",[48,21368,21371],{"className":21369,"code":21370,"language":53},[51],"server\u002Froutes\u002F\nserver\u002Fapi\u002F\nserver\u002Futils\u002F\nserver\u002Fplugins\u002F\nnitro.config.ts\n",[40,21372,21370],{"__ignoreMap":56},[21,21374,21375,21376,827],{},"These folders are where you’ll place your routes, logic, utilities, and plugins. (",[3861,21377,4189],{"href":21378,"rel":21379,"title":21380},"https:\u002F\u002Fnitro.build\u002Fguide?utm_source=chatgpt.com",[3865],"Docs - Nitro",[2808,21382,21384],{"id":21383},"step-2-run-the-development-server","Step 2: Run the Development Server",[21,21386,21387],{},"Nitro comes with a built-in development server that supports hot reload:",[48,21389,21390],{"className":839,"code":3854,"language":841,"meta":56,"style":56},[40,21391,21392],{"__ignoreMap":56},[449,21393,21394,21396,21398],{"class":451,"line":452},[449,21395,3703],{"class":721},[449,21397,5296],{"class":691},[449,21399,5299],{"class":691},[21,21401,21402,21403,21406,21407,827],{},"By default, Nitro starts on ",[40,21404,21405],{},"http:\u002F\u002Flocalhost:3000\u002F",". You’ll see your handlers and static files served automatically. (",[3861,21408,4189],{"href":21378,"rel":21409,"title":21380},[3865],[73,21411],{},[32,21413,21415],{"id":21414},"filesystem-routing-api-routes-handlers","Filesystem Routing: API Routes & Handlers",[21,21417,21418,21419,21422,21423,21426],{},"Just like many modern server frameworks, Nitro uses ",[151,21420,21421],{},"filesystem routing",": files you create under ",[40,21424,21425],{},"server\u002Froutes\u002F"," automatically become endpoints.",[2808,21428,21430],{"id":21429},"example-create-a-simple-api-route","Example: Create a Simple API Route",[21,21432,21433,21434,3070],{},"Create a file named ",[40,21435,21436],{},"server\u002Fapi\u002Fhello.ts",[48,21438,21440],{"className":670,"code":21439,"language":673,"meta":56,"style":56},"export default defineEventHandler(() => {\n  return { message: \"Hello from Nitro!\" }\n})\n",[40,21441,21442,21456,21467],{"__ignoreMap":56},[449,21443,21444,21446,21448,21450,21452,21454],{"class":451,"line":452},[449,21445,4629],{"class":680},[449,21447,4632],{"class":680},[449,21449,4715],{"class":721},[449,21451,4718],{"class":684},[449,21453,1498],{"class":680},[449,21455,1501],{"class":684},[449,21457,21458,21460,21462,21465],{"class":451,"line":114},[449,21459,4727],{"class":680},[449,21461,12132],{"class":684},[449,21463,21464],{"class":691},"\"Hello from Nitro!\"",[449,21466,15045],{"class":684},[449,21468,21469],{"class":451,"line":111},[449,21470,4113],{"class":684},[21,21472,7226,21473,21476,21477,827],{},[40,21474,21475],{},"http:\u002F\u002Flocalhost:3000\u002Fapi\u002Fhello"," in the browser to see the JSON response. Nitro does the routing for you based on file paths. (",[3861,21478,4189],{"href":21378,"rel":21479,"title":21380},[3865],[21,21481,21482,21483,21485,21486,10064],{},"Nitro routes can be nested — create folders within ",[40,21484,21425],{}," to structure your API and handlers naturally. (",[3861,21487,4189],{"href":21378,"rel":21488,"title":21380},[3865],[73,21490],{},[32,21492,21494],{"id":21493},"using-utility-functions","Using Utility Functions",[21,21496,21497,21498,827],{},"Nitro auto-imports useful utilities that help you build more expressive handlers without boilerplate. For example, parsing query parameters, reading cookies, or handling headers can be done with helpers from h3 (Nitro’s HTTP utility library under the hood). (",[3861,21499,21503],{"href":21500,"rel":21501,"title":21502},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002F3.x\u002Fguide\u002Fconcepts\u002Fserver-engine?utm_source=chatgpt.com",[3865],"Server Engine · Nuxt Concepts v3","Nuxt",[2808,21505,21507],{"id":21506},"example-reading-query-parameters","Example: Reading Query Parameters",[48,21509,21511],{"className":670,"code":21510,"language":673,"meta":56,"style":56},"export default defineEventHandler((event) => {\n  const { name } = getQuery(event)\n  return { greeting: `Hello ${name ?? \"World\"}` }\n})\n",[40,21512,21513,21531,21549,21569],{"__ignoreMap":56},[449,21514,21515,21517,21519,21521,21523,21525,21527,21529],{"class":451,"line":452},[449,21516,4629],{"class":680},[449,21518,4632],{"class":680},[449,21520,4715],{"class":721},[449,21522,5163],{"class":684},[449,21524,2991],{"class":1491},[449,21526,1758],{"class":684},[449,21528,1498],{"class":680},[449,21530,1501],{"class":684},[449,21532,21533,21535,21537,21539,21542,21544,21547],{"class":451,"line":114},[449,21534,5049],{"class":680},[449,21536,14124],{"class":684},[449,21538,12920],{"class":714},[449,21540,21541],{"class":684}," } ",[449,21543,5015],{"class":680},[449,21545,21546],{"class":721}," getQuery",[449,21548,15432],{"class":684},[449,21550,21551,21553,21556,21559,21561,21564,21567],{"class":451,"line":111},[449,21552,4727],{"class":680},[449,21554,21555],{"class":684}," { greeting: ",[449,21557,21558],{"class":691},"`Hello ${",[449,21560,12920],{"class":684},[449,21562,21563],{"class":680}," ??",[449,21565,21566],{"class":691}," \"World\"}`",[449,21568,15045],{"class":684},[449,21570,21571],{"class":451,"line":468},[449,21572,4113],{"class":684},[21,21574,7226,21575,21578],{},[40,21576,21577],{},"\u002Fapi\u002Fhello?name=Vantol"," and Nitro will handle parsing automatically.",[73,21580],{},[32,21582,21584],{"id":21583},"configuration-with-nitroconfigts","Configuration with nitro.config.ts",[21,21586,21587,21588,827],{},"Nitro can be customized via a configuration file. You might want to set runtime config values or adjust build behavior. (",[3861,21589,4189],{"href":21590,"rel":21591,"title":21592},"https:\u002F\u002Fnitro.build\u002Fguide\u002Fconfiguration\u002F?utm_source=chatgpt.com",[3865],"Configuration - Nitro",[2808,21594,21596],{"id":21595},"example-runtime-config","Example: Runtime Config",[48,21598,21600],{"className":670,"code":21599,"language":673,"meta":56,"style":56},"import { defineNitroConfig } from \"nitro\u002Fconfig\";\n\nexport default defineNitroConfig({\n  runtimeConfig: {\n    apiToken: \"dev_token\"\n  }\n});\n",[40,21601,21602,21615,21619,21629,21633,21641,21645],{"__ignoreMap":56},[449,21603,21604,21606,21608,21610,21613],{"class":451,"line":452},[449,21605,681],{"class":680},[449,21607,15259],{"class":684},[449,21609,688],{"class":680},[449,21611,21612],{"class":691}," \"nitro\u002Fconfig\"",[449,21614,695],{"class":684},[449,21616,21617],{"class":451,"line":114},[449,21618,700],{"emptyLinePlaceholder":130},[449,21620,21621,21623,21625,21627],{"class":451,"line":111},[449,21622,4629],{"class":680},[449,21624,4632],{"class":680},[449,21626,15277],{"class":721},[449,21628,1391],{"class":684},[449,21630,21631],{"class":451,"line":468},[449,21632,15284],{"class":684},[449,21634,21635,21638],{"class":451,"line":474},[449,21636,21637],{"class":684},"    apiToken: ",[449,21639,21640],{"class":691},"\"dev_token\"\n",[449,21642,21643],{"class":451,"line":480},[449,21644,4599],{"class":684},[449,21646,21647],{"class":451,"line":486},[449,21648,784],{"class":684},[21,21650,21651],{},"Then, within handlers you can access this config:",[48,21653,21655],{"className":670,"code":21654,"language":673,"meta":56,"style":56},"export default defineEventHandler((event) => {\n  return useRuntimeConfig().apiToken;\n});\n",[40,21656,21657,21675,21684],{"__ignoreMap":56},[449,21658,21659,21661,21663,21665,21667,21669,21671,21673],{"class":451,"line":452},[449,21660,4629],{"class":680},[449,21662,4632],{"class":680},[449,21664,4715],{"class":721},[449,21666,5163],{"class":684},[449,21668,2991],{"class":1491},[449,21670,1758],{"class":684},[449,21672,1498],{"class":680},[449,21674,1501],{"class":684},[449,21676,21677,21679,21681],{"class":451,"line":114},[449,21678,4727],{"class":680},[449,21680,15343],{"class":721},[449,21682,21683],{"class":684},"().apiToken;\n",[449,21685,21686],{"class":451,"line":111},[449,21687,784],{"class":684},[21,21689,21690,21691,21694,21695,827],{},"You can override these values with environment variables prefixed with ",[40,21692,21693],{},"NITRO_"," for different environments (development, staging, production). (",[3861,21696,4189],{"href":21590,"rel":21697,"title":21592},[3865],[73,21699],{},[32,21701,21703],{"id":21702},"serving-static-files-and-custom-renderers","Serving Static Files and Custom Renderers",[21,21705,21706,21707,21710,21711,827],{},"Nitro can serve static files like HTML, images, and assets from unconfigured folders. It also supports fallback renderers — for example serving an ",[40,21708,21709],{},"index.html"," for all non-API routes in single-page applications. (",[3861,21712,4189],{"href":21713,"rel":21714,"title":21715},"https:\u002F\u002Fv3.nitro.build\u002Fdocs\u002Frenderer?utm_source=chatgpt.com",[3865],"Nitro Renderer - Nitro",[21,21717,21718,21719,21722],{},"This makes Nitro useful for ",[151,21720,21721],{},"hybrid applications"," that combine server APIs with frontend PWAs or SPAs.",[73,21724],{},[32,21726,21728],{"id":21727},"deploying-your-nitro-app","Deploying Your Nitro App",[21,21730,21731,21732,21735],{},"One of Nitro’s strengths is its ability to ",[151,21733,21734],{},"deploy to multiple environments with minimal changes",". After building your app:",[48,21737,21738],{"className":839,"code":16928,"language":841,"meta":56,"style":56},[40,21739,21740],{"__ignoreMap":56},[449,21741,21742,21744,21746],{"class":451,"line":452},[449,21743,3703],{"class":721},[449,21745,5296],{"class":691},[449,21747,6810],{"class":691},[21,21749,21750,21751,21753,21754,827],{},"Nitro outputs a production-ready server in the ",[40,21752,16944],{}," directory that can run on various targets. (",[3861,21755,4189],{"href":21378,"rel":21756,"title":21380},[3865],[21,21758,21759],{},"You can deploy to:",[383,21761,21762,21770,21776],{},[386,21763,21764,21766,21767],{},[151,21765,16950],{}," — run ",[40,21768,21769],{},".output\u002Fserver\u002Findex.mjs",[386,21771,21772,21775],{},[151,21773,21774],{},"Edge platforms"," — Cloudflare Workers, Deno Deploy with presets",[386,21777,21778,21781,21782,827],{},[151,21779,21780],{},"Serverless providers"," — AWS Lambda, Vercel, Netlify, etc. (",[3861,21783,4189],{"href":21784,"rel":21785,"title":21786},"https:\u002F\u002Fnitro.build\u002Fdeploy?utm_source=chatgpt.com",[3865],"Deploy - Nitro",[21,21788,21789,21790,827],{},"Nitro often autodetects the hosting provider and configures itself accordingly — meaning fewer environment-specific tweaks. (",[3861,21791,4189],{"href":21784,"rel":21792,"title":21786},[3865],[73,21794],{},[32,21796,21798],{"id":21797},"advanced-plugins-middleware","Advanced: Plugins & Middleware",[21,21800,21801,21802,21805,21806,21809],{},"If you need to extend Nitro’s behavior, you can create your own ",[151,21803,21804],{},"plugins"," in ",[40,21807,21808],{},"server\u002Fplugins\u002F",". These let you hook into the lifecycle of requests, register middleware, or augment server behavior in powerful ways.",[21,21811,21812],{},"This pattern helps modularize cross-cutting concerns like authentication, logging, or error reporting.",[73,21814],{},[32,21816,21818],{"id":21817},"example-full-api-frontend-integration","Example: Full API + Frontend Integration",[21,21820,21821,21822,21825,21826,827],{},"You can integrate Nitro with frontend frameworks like Vite to create ",[151,21823,21824],{},"full-stack apps"," where your UI and backend live in one project. Nitro handles server APIs and static assets, while Vite serves HMR and frontend bundling. (",[3861,21827,4189],{"href":21828,"rel":21829,"title":21830},"https:\u002F\u002Fv3.nitro.build\u002Fdocs\u002Fquick-start?utm_source=chatgpt.com",[3865],"Quick Start - Nitro",[21,21832,21833],{},"This setup enables:",[383,21835,21836,21839,21842],{},[386,21837,21838],{},"Server APIs without CORS complexity",[386,21840,21841],{},"Sharing code between backend and frontend",[386,21843,21844],{},"Fast development workflows with hot reload",[73,21846],{},[16,21848,3681],{"id":3680},[21,21850,21851,21853],{},[151,21852,4189],{}," is an incredibly flexible and powerful tool for modern backend development. Whether you’re building APIs, hybrid apps, or full-stack projects, Nitro offers:",[383,21855,21856,21861,21866,21871,21876],{},[386,21857,21858],{},[151,21859,21860],{},"Filesystem routing and simple handlers",[386,21862,21863],{},[151,21864,21865],{},"Configurable runtime environments",[386,21867,21868],{},[151,21869,21870],{},"Cross-runtime deployment",[386,21872,21873],{},[151,21874,21875],{},"Out-of-the-box utility functions and developer experience",[386,21877,21878,1913,21881,827],{},[151,21879,21880],{},"Easy integration with frontend frameworks",[3861,21882,4189],{"href":21883,"rel":21884,"title":21885},"https:\u002F\u002Fnitro.build\u002F?utm_source=chatgpt.com",[3865],"Nitro - Next Generation Server Toolkit",[21,21887,21888],{},"With these capabilities, Nitro enables developers to build fast, scalable, and portable server apps without sacrificing simplicity — making it a strong choice for teams building modern web applications. 🚀",[3728,21890,15659],{},{"title":56,"searchDepth":111,"depth":111,"links":21892},[21893,21894,21898,21901,21904,21907,21908,21909,21910],{"id":21274,"depth":114,"text":21275},{"id":21327,"depth":114,"text":21328,"children":21895},[21896,21897],{"id":21331,"depth":111,"text":21332},{"id":21383,"depth":111,"text":21384},{"id":21414,"depth":114,"text":21415,"children":21899},[21900],{"id":21429,"depth":111,"text":21430},{"id":21493,"depth":114,"text":21494,"children":21902},[21903],{"id":21506,"depth":111,"text":21507},{"id":21583,"depth":114,"text":21584,"children":21905},[21906],{"id":21595,"depth":111,"text":21596},{"id":21702,"depth":114,"text":21703},{"id":21727,"depth":114,"text":21728},{"id":21797,"depth":114,"text":21798},{"id":21817,"depth":114,"text":21818},"2026-01-20","An in-depth look at Nitro — what it is, how to use it to build servers and APIs, and practical steps to get started with real examples.",{"type":13,"value":21914},[21915,21917,21925],[16,21916,333],{"id":332},[21,21918,21253,21919,21257,21921,21260,21923],{},[151,21920,21256],{},[151,21922,4189],{},[9909,21924,9912],{"index":9911},[21,21926,21265,21927,21269],{},[151,21928,21268],{},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1498050108023-c5249f4df085",[7076,17135,127,7080,15728],{},"\u002Fblog\u002Fnitro-build",{"title":21245,"description":21912},"blog\u002Fnitro-build","rDn6LRPhkhACMYyhkv_Kcexsf6uRlCOFgdqKwauBfhg",{"id":21937,"title":21938,"author":21939,"body":21940,"canonical":116,"categories":116,"date":22539,"description":22540,"excerpt":22541,"extension":119,"img":22561,"keywords":22562,"language":3788,"meta":22564,"navigation":130,"ogImage":116,"path":22565,"rating":474,"readTime":492,"seo":22566,"stem":22567,"tag":3793,"__hash__":22568},"blog\u002Fblog\u002Fnitro-plugins.md","Extending Nitro with Plugins: How to Build and Ideas for Real World Use",{"name":9,"img":11,"website":327},{"type":13,"value":21941,"toc":22521},[21942,21944,21953,21956,21967,21969,21973,21981,21985,21995,21998,22043,22051,22055,22064,22067,22152,22155,22240,22244,22247,22254,22257,22268,22271,22273,22280,22283,22391,22394,22396,22403,22406,22409,22412,22423,22425,22432,22435,22438,22476,22478,22485,22488,22491,22493,22497,22502,22504,22506,22509,22512,22518],[16,21943,333],{"id":332},[21,21945,21946,21947,21950,21951],{},"Nitro is a lightweight, runtime-agnostic server framework that lets you build fast APIs and backends deployable anywhere. But what happens when you need more than basic routes? That’s where ",[151,21948,21949],{},"Nitro plugins"," come in — a powerful way to hook into Nitro’s runtime and extend its behavior.:contentReference",[449,21952,9938],{"index":4799},[21,21954,21955],{},"In this post we’ll cover:",[383,21957,21958,21961,21964],{},[386,21959,21960],{},"What Nitro plugins are",[386,21962,21963],{},"How to create one",[386,21965,21966],{},"Useful plugin ideas you can build for real applications",[16,21968,4373],{"id":4372},[32,21970,21972],{"id":21971},"what-are-nitro-plugins","What Are Nitro Plugins?",[21,21974,21975,21976,21978,21979],{},"Nitro plugins allow you to extend Nitro’s runtime behavior in a modular and reusable way. They’re executed when the server starts and can hook into internal lifecycle events, letting you react to incoming requests, errors, responses, and more. Nitro registers plugins automatically from your ",[40,21977,21808],{}," directory or ones you list in the Nitro config.:contentReference",[449,21980,9950],{"index":4827},[32,21982,21984],{"id":21983},"how-to-create-a-simple-nitro-plugin","How to Create a Simple Nitro Plugin",[21,21986,21987,21988,21991,21992,21994],{},"Creating a Nitro plugin starts with the ",[40,21989,21990],{},"defineNitroPlugin"," function. Plugins receive the ",[40,21993,16631],{}," context — the runtime environment you can interact with.",[21,21996,21997],{},"For example, a simple plugin that logs when the server starts could look like:",[48,21999,22001],{"className":670,"code":22000,"language":673,"meta":56,"style":56},"\u002F\u002F server\u002Fplugins\u002Flogger.ts\nexport default defineNitroPlugin((nitroApp) => {\n  console.log(\"Nitro server initialized!\")\n})\n",[40,22002,22003,22008,22026,22039],{"__ignoreMap":56},[449,22004,22005],{"class":451,"line":452},[449,22006,22007],{"class":705},"\u002F\u002F server\u002Fplugins\u002Flogger.ts\n",[449,22009,22010,22012,22014,22016,22018,22020,22022,22024],{"class":451,"line":114},[449,22011,4629],{"class":680},[449,22013,4632],{"class":680},[449,22015,16626],{"class":721},[449,22017,5163],{"class":684},[449,22019,16631],{"class":1491},[449,22021,1758],{"class":684},[449,22023,1498],{"class":680},[449,22025,1501],{"class":684},[449,22027,22028,22030,22032,22034,22037],{"class":451,"line":111},[449,22029,5443],{"class":684},[449,22031,1675],{"class":721},[449,22033,725],{"class":684},[449,22035,22036],{"class":691},"\"Nitro server initialized!\"",[449,22038,11211],{"class":684},[449,22040,22041],{"class":451,"line":468},[449,22042,4113],{"class":684},[21,22044,22045,22046,22048,22049,827],{},"Nitro runs your plugin as part of server initialization. You can even register plugins from custom directories by adding them to ",[40,22047,15247],{},".([nitro.build]",[449,22050,4827],{},[32,22052,22054],{"id":22053},"using-nitro-lifecycle-hooks","Using Nitro Lifecycle Hooks",[21,22056,22057,22058,22061,22062,827],{},"Plugins become especially powerful when you use ",[151,22059,22060],{},"lifecycle hooks",". Nitro exposes several event hooks that let you react to server behavior — from incoming requests to errors and responses.([nitro.build]",[449,22063,4827],{},[21,22065,22066],{},"Here’s a plugin that logs every request URL:",[48,22068,22070],{"className":670,"code":22069,"language":673,"meta":56,"style":56},"\u002F\u002F server\u002Fplugins\u002Frequest-logger.ts\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook(\"request\", (event) => {\n    console.log(`[Request] ${event.method} ${event.path}`)\n  })\n})\n",[40,22071,22072,22077,22095,22115,22144,22148],{"__ignoreMap":56},[449,22073,22074],{"class":451,"line":452},[449,22075,22076],{"class":705},"\u002F\u002F server\u002Fplugins\u002Frequest-logger.ts\n",[449,22078,22079,22081,22083,22085,22087,22089,22091,22093],{"class":451,"line":114},[449,22080,4629],{"class":680},[449,22082,4632],{"class":680},[449,22084,16626],{"class":721},[449,22086,5163],{"class":684},[449,22088,16631],{"class":1491},[449,22090,1758],{"class":684},[449,22092,1498],{"class":680},[449,22094,1501],{"class":684},[449,22096,22097,22099,22101,22103,22105,22107,22109,22111,22113],{"class":451,"line":111},[449,22098,16642],{"class":684},[449,22100,16645],{"class":721},[449,22102,725],{"class":684},[449,22104,16650],{"class":691},[449,22106,3500],{"class":684},[449,22108,2991],{"class":1491},[449,22110,1758],{"class":684},[449,22112,1498],{"class":680},[449,22114,1501],{"class":684},[449,22116,22117,22119,22121,22123,22126,22128,22130,22132,22134,22136,22138,22140,22142],{"class":451,"line":468},[449,22118,3002],{"class":684},[449,22120,1675],{"class":721},[449,22122,725],{"class":684},[449,22124,22125],{"class":691},"`[Request] ${",[449,22127,2991],{"class":684},[449,22129,165],{"class":691},[449,22131,16694],{"class":684},[449,22133,16697],{"class":691},[449,22135,2991],{"class":684},[449,22137,165],{"class":691},[449,22139,16704],{"class":684},[449,22141,1517],{"class":691},[449,22143,11211],{"class":684},[449,22145,22146],{"class":451,"line":474},[449,22147,16717],{"class":684},[449,22149,22150],{"class":451,"line":480},[449,22151,4113],{"class":684},[21,22153,22154],{},"And you can also hook into errors to capture and log them:",[48,22156,22158],{"className":670,"code":22157,"language":673,"meta":56,"style":56},"\u002F\u002F server\u002Fplugins\u002Ferror-catcher.ts\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook(\"error\", (error, { event }) => {\n    console.error(`Error at ${event.path}:`, error)\n  })\n})\n",[40,22159,22160,22165,22183,22209,22232,22236],{"__ignoreMap":56},[449,22161,22162],{"class":451,"line":452},[449,22163,22164],{"class":705},"\u002F\u002F server\u002Fplugins\u002Ferror-catcher.ts\n",[449,22166,22167,22169,22171,22173,22175,22177,22179,22181],{"class":451,"line":114},[449,22168,4629],{"class":680},[449,22170,4632],{"class":680},[449,22172,16626],{"class":721},[449,22174,5163],{"class":684},[449,22176,16631],{"class":1491},[449,22178,1758],{"class":684},[449,22180,1498],{"class":680},[449,22182,1501],{"class":684},[449,22184,22185,22187,22189,22191,22194,22196,22198,22201,22203,22205,22207],{"class":451,"line":111},[449,22186,16642],{"class":684},[449,22188,16645],{"class":721},[449,22190,725],{"class":684},[449,22192,22193],{"class":691},"\"error\"",[449,22195,3500],{"class":684},[449,22197,17384],{"class":1491},[449,22199,22200],{"class":684},", { ",[449,22202,2991],{"class":1491},[449,22204,1495],{"class":684},[449,22206,1498],{"class":680},[449,22208,1501],{"class":684},[449,22210,22211,22213,22215,22217,22220,22222,22224,22226,22229],{"class":451,"line":468},[449,22212,3002],{"class":684},[449,22214,17384],{"class":721},[449,22216,725],{"class":684},[449,22218,22219],{"class":691},"`Error at ${",[449,22221,2991],{"class":684},[449,22223,165],{"class":691},[449,22225,16704],{"class":684},[449,22227,22228],{"class":691},"}:`",[449,22230,22231],{"class":684},", error)\n",[449,22233,22234],{"class":451,"line":474},[449,22235,16717],{"class":684},[449,22237,22238],{"class":451,"line":480},[449,22239,4113],{"class":684},[32,22241,22243],{"id":22242},"plugin-ideas-you-can-build-today","Plugin Ideas You Can Build Today",[21,22245,22246],{},"Here are a handful of practical Nitro plugin ideas you could build into your app — especially handy for real projects like an e-commerce backend, food truck API, or admin dashboard API.",[2808,22248,22250,22251],{"id":22249},"_1-request-metrics-analytics-plugin","📊 1. ",[151,22252,22253],{},"Request Metrics & Analytics Plugin",[21,22255,22256],{},"Track performance of your endpoints in real time.",[383,22258,22259,22262,22265],{},[386,22260,22261],{},"Count hits per route",[386,22263,22264],{},"Record timing and latency",[386,22266,22267],{},"Write structured logs or push to analytics back end",[21,22269,22270],{},"Example benefit: track how many orders per hour your API gets and surface performance bottlenecks.",[73,22272],{},[2808,22274,22276,22277],{"id":22275},"_2-api-key-token-validation-plugin","🔐 2. ",[151,22278,22279],{},"API Key \u002F Token Validation Plugin",[21,22281,22282],{},"Centralize API key checks before your route handlers run.",[48,22284,22286],{"className":670,"code":22285,"language":673,"meta":56,"style":56},"\u002F\u002F server\u002Fplugins\u002Fapi-auth.ts\nexport default defineNitroPlugin((nitroApp) => {\n  nitroApp.hooks.hook(\"request\", (event) => {\n    const key = event.headers.get(\"x-api-key\")\n    if (!isValidKey(key)) {\n      throw createError({ statusCode: 401, statusMessage: \"Unauthorized\" })\n    }\n  })\n})\n",[40,22287,22288,22293,22311,22331,22349,22363,22379,22383,22387],{"__ignoreMap":56},[449,22289,22290],{"class":451,"line":452},[449,22291,22292],{"class":705},"\u002F\u002F server\u002Fplugins\u002Fapi-auth.ts\n",[449,22294,22295,22297,22299,22301,22303,22305,22307,22309],{"class":451,"line":114},[449,22296,4629],{"class":680},[449,22298,4632],{"class":680},[449,22300,16626],{"class":721},[449,22302,5163],{"class":684},[449,22304,16631],{"class":1491},[449,22306,1758],{"class":684},[449,22308,1498],{"class":680},[449,22310,1501],{"class":684},[449,22312,22313,22315,22317,22319,22321,22323,22325,22327,22329],{"class":451,"line":111},[449,22314,16642],{"class":684},[449,22316,16645],{"class":721},[449,22318,725],{"class":684},[449,22320,16650],{"class":691},[449,22322,3500],{"class":684},[449,22324,2991],{"class":1491},[449,22326,1758],{"class":684},[449,22328,1498],{"class":680},[449,22330,1501],{"class":684},[449,22332,22333,22335,22337,22339,22341,22343,22345,22347],{"class":451,"line":468},[449,22334,12761],{"class":680},[449,22336,16849],{"class":714},[449,22338,718],{"class":680},[449,22340,16854],{"class":684},[449,22342,12420],{"class":721},[449,22344,725],{"class":684},[449,22346,16861],{"class":691},[449,22348,11211],{"class":684},[449,22350,22351,22353,22355,22357,22360],{"class":451,"line":474},[449,22352,12820],{"class":680},[449,22354,1913],{"class":684},[449,22356,5084],{"class":680},[449,22358,22359],{"class":721},"isValidKey",[449,22361,22362],{"class":684},"(key)) {\n",[449,22364,22365,22367,22369,22371,22373,22375,22377],{"class":451,"line":480},[449,22366,12832],{"class":680},[449,22368,5112],{"class":721},[449,22370,16526],{"class":684},[449,22372,16890],{"class":714},[449,22374,16531],{"class":684},[449,22376,16895],{"class":691},[449,22378,10958],{"class":684},[449,22380,22381],{"class":451,"line":486},[449,22382,7555],{"class":684},[449,22384,22385],{"class":451,"line":492},[449,22386,16717],{"class":684},[449,22388,22389],{"class":451,"line":498},[449,22390,4113],{"class":684},[21,22392,22393],{},"This saves you from adding auth logic inside each route and ensures consistent security.",[73,22395],{},[2808,22397,22399,22400],{"id":22398},"_3-response-caching-plugin","📦 3. ",[151,22401,22402],{},"Response Caching Plugin",[21,22404,22405],{},"A plugin that caches successful GET responses in memory or Redis to speed up repeat calls.",[21,22407,22408],{},"Useful for endpoints like menus or business hours that don’t change often.",[21,22410,22411],{},"Example concept:",[383,22413,22414,22417,22420],{},[386,22415,22416],{},"Check cache on request",[386,22418,22419],{},"Bypass expensive data work if present",[386,22421,22422],{},"Store responses with TTL",[73,22424],{},[2808,22426,22428,22429],{"id":22427},"_4-request-body-sanitizer-plugin","📥 4. ",[151,22430,22431],{},"Request Body Sanitizer Plugin",[21,22433,22434],{},"Automatically sanitize or validate JSON bodies before they hit route handlers.",[21,22436,22437],{},"This helps enforce stricter API contracts and cleaner data at the router level.",[48,22439,22441],{"className":670,"code":22440,"language":673,"meta":56,"style":56},"nitroApp.hooks.hook(\"request\", (event) => {\n  sanitize(event.body)\n})\n",[40,22442,22443,22464,22472],{"__ignoreMap":56},[449,22444,22445,22448,22450,22452,22454,22456,22458,22460,22462],{"class":451,"line":452},[449,22446,22447],{"class":684},"nitroApp.hooks.",[449,22449,16645],{"class":721},[449,22451,725],{"class":684},[449,22453,16650],{"class":691},[449,22455,3500],{"class":684},[449,22457,2991],{"class":1491},[449,22459,1758],{"class":684},[449,22461,1498],{"class":680},[449,22463,1501],{"class":684},[449,22465,22466,22469],{"class":451,"line":114},[449,22467,22468],{"class":721},"  sanitize",[449,22470,22471],{"class":684},"(event.body)\n",[449,22473,22474],{"class":451,"line":111},[449,22475,4113],{"class":684},[73,22477],{},[2808,22479,22481,22482],{"id":22480},"_5-feature-flag-config-plugin","🛠 5. ",[151,22483,22484],{},"Feature Flag & Config Plugin",[21,22486,22487],{},"Load feature flags or custom runtime config from environment, remote store, or a file, and broadcast them server-wide.",[21,22489,22490],{},"Example: enable\u002Fdisable special holiday menus, seasonal pricing, or debug logging without code changes.",[73,22492],{},[32,22494,22496],{"id":22495},"when-plugins-run","When Plugins Run",[21,22498,22499,22500,827],{},"Nitro plugins execute once during server startup and the hooks you register can fire at various points during request processing — giving you centralized control over behavior, logging, auth, or data flow.([v3.nitro.build]",[449,22501,4799],{},[73,22503],{},[16,22505,3681],{"id":3680},[21,22507,22508],{},"Nitro’s plugin system gives you the flexibility to extend your server logic beyond basic routes — letting you implement logging, auth, caching, monitoring, and more in reusable packages.",[21,22510,22511],{},"If you’re building an API that needs observability, security, or business logic centralized in one place, plugins make Nitro even more powerful without complicating your routes.🚀",[21,22513,22514,22515,5084],{},"Let me know if you want ",[151,22516,22517],{},"tutorial examples for those plugin ideas",[3728,22519,22520],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":56,"searchDepth":111,"depth":111,"links":22522},[22523,22524,22525,22526,22538],{"id":21971,"depth":114,"text":21972},{"id":21983,"depth":114,"text":21984},{"id":22053,"depth":114,"text":22054},{"id":22242,"depth":114,"text":22243,"children":22527},[22528,22530,22532,22534,22536],{"id":22249,"depth":111,"text":22529},"📊 1. Request Metrics & Analytics Plugin",{"id":22275,"depth":111,"text":22531},"🔐 2. API Key \u002F Token Validation Plugin",{"id":22398,"depth":111,"text":22533},"📦 3. Response Caching Plugin",{"id":22427,"depth":111,"text":22535},"📥 4. Request Body Sanitizer Plugin",{"id":22480,"depth":111,"text":22537},"🛠 5. Feature Flag & Config Plugin",{"id":22495,"depth":114,"text":22496},"2026-01-29","Learn how to create Nitro plugins to extend your server’s behavior, with examples and practical plugin ideas you can build into your application.",{"type":13,"value":22542},[22543,22545,22551,22553],[16,22544,333],{"id":332},[21,22546,21946,22547,21950,22549],{},[151,22548,21949],{},[449,22550,9938],{"index":4799},[21,22552,21955],{},[383,22554,22555,22557,22559],{},[386,22556,21960],{},[386,22558,21963],{},[386,22560,21966],{},"https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1769836111\u002FNitroFull_ww4pbg.png",[7076,22563,7078,7080,3784],"nitro plugin",{},"\u002Fblog\u002Fnitro-plugins",{"title":21938,"description":22540},"blog\u002Fnitro-plugins","1TtyjRpEqgljjXADT7cjszOkC3kzSLiBPCJnttW7QoY",{"id":22570,"title":22571,"author":22572,"body":22573,"canonical":116,"categories":116,"date":22854,"description":22855,"excerpt":22856,"extension":119,"img":22561,"keywords":22884,"language":3788,"meta":22885,"navigation":130,"ogImage":116,"path":22886,"rating":474,"readTime":318,"seo":22887,"stem":22888,"tag":3793,"__hash__":22889},"blog\u002Fblog\u002Fnitro-tutorial.md","Building Real Nitro Plugins: Step-by-Step Tutorials You Can Use Today",{"name":9,"img":11,"website":327},{"type":13,"value":22574,"toc":22843},[22575,22577,22588,22591,22604,22614,22618,22621,22661,22665,22668,22671,22677,22680,22684,22687,22690,22693,22696,22699,22702,22706,22709,22715,22718,22724,22726,22730,22733,22736,22739,22743,22746,22749,22755,22758,22761,22767,22769,22773,22776,22779,22782,22786,22789,22792,22795,22799,22802,22805,22808,22812,22815,22818,22821,22824,22827,22830,22833,22835,22838,22841],[16,22576,333],{"id":332},[21,22578,22579,22580,22583,22584,22587],{},"In the previous post, we explored ",[151,22581,22582],{},"what Nitro plugins are"," and why they’re one of Nitro’s most powerful features. In this tutorial-focused follow-up, we’ll ",[151,22585,22586],{},"build real Nitro plugins step by step"," — the kind you’d actually use in production.",[21,22589,22590],{},"By the end, you’ll know how to create plugins for:",[383,22592,22593,22596,22599,22601],{},[386,22594,22595],{},"Request logging",[386,22597,22598],{},"API key authentication",[386,22600,19120],{},[386,22602,22603],{},"Feature flags and runtime configuration",[21,22605,22606,22607,22609,22610,22613],{},"All examples use ",[151,22608,3788],{}," and work in ",[151,22611,22612],{},"Nuxt + Nitro"," or standalone Nitro apps.",[16,22615,22617],{"id":22616},"project-setup","Project Setup",[21,22619,22620],{},"Assuming a Nitro or Nuxt project, your folder structure should include:",[48,22622,22624],{"className":443,"code":22623,"language":445,"meta":56,"style":56},"server\u002F\n  plugins\u002F\n    logger.ts\n    auth.ts\n    cache.ts\n    feature-flags.ts\nNitro automatically loads plugins from server\u002Fplugins\u002F.\n",[40,22625,22626,22631,22636,22641,22646,22651,22656],{"__ignoreMap":56},[449,22627,22628],{"class":451,"line":452},[449,22629,22630],{},"server\u002F\n",[449,22632,22633],{"class":451,"line":114},[449,22634,22635],{},"  plugins\u002F\n",[449,22637,22638],{"class":451,"line":111},[449,22639,22640],{},"    logger.ts\n",[449,22642,22643],{"class":451,"line":468},[449,22644,22645],{},"    auth.ts\n",[449,22647,22648],{"class":451,"line":474},[449,22649,22650],{},"    cache.ts\n",[449,22652,22653],{"class":451,"line":480},[449,22654,22655],{},"    feature-flags.ts\n",[449,22657,22658],{"class":451,"line":486},[449,22659,22660],{},"Nitro automatically loads plugins from server\u002Fplugins\u002F.\n",[16,22662,22664],{"id":22663},"tutorial-1-request-logger-plugin","Tutorial 1: Request Logger Plugin",[21,22666,22667],{},"Goal\nLog every incoming request with method, path, and timing.",[21,22669,22670],{},"Step 1: Create the Plugin\n\u002F\u002F server\u002Fplugins\u002Flogger.ts\nexport default defineNitroPlugin((nitroApp) => {\nnitroApp.hooks.hook(\"request\", (event) => {\nconst start = Date.now()",[48,22672,22675],{"className":22673,"code":22674,"language":53},[51],"event.node.res.on(\"finish\", () => {\n  const duration = Date.now() - start\n  console.log(\n    `[${event.method}] ${event.path} - ${duration}ms`\n  )\n})\n",[40,22676,22674],{"__ignoreMap":56},[21,22678,22679],{},"})\n})",[32,22681,22683],{"id":22682},"what-this-does","What This Does",[21,22685,22686],{},"Hooks into every request",[21,22688,22689],{},"Measures request duration",[21,22691,22692],{},"Logs after the response finishes",[21,22694,22695],{},"When to Use\nDebugging slow endpoints",[21,22697,22698],{},"Monitoring API usage",[21,22700,22701],{},"Lightweight analytics",[32,22703,22705],{"id":22704},"tutorial-2-api-key-authentication-plugin","Tutorial 2: API Key Authentication Plugin",[21,22707,22708],{},"Goal\nProtect your API by validating an API key before routes run.",[21,22710,22711,22712,827],{},"Step 1: Define a Validator\nconst VALID_KEYS = new Set(",[449,22713,22714],{},"\"dev-key-123\", \"prod-key-456\"",[21,22716,22717],{},"function isValidKey(key?: string | null) {\nreturn key && VALID_KEYS.has(key)\n}\nStep 2: Create the Plugin\n\u002F\u002F server\u002Fplugins\u002Fauth.ts\nexport default defineNitroPlugin((nitroApp) => {\nnitroApp.hooks.hook(\"request\", (event) => {\nconst apiKey = event.headers.get(\"x-api-key\")",[48,22719,22722],{"className":22720,"code":22721,"language":53},[51],"if (!isValidKey(apiKey)) {\n  throw createError({\n    statusCode: 401,\n    statusMessage: \"Invalid API Key\"\n  })\n}\n",[40,22723,22721],{"__ignoreMap":56},[21,22725,22679],{},[32,22727,22729],{"id":22728},"why-this-is-powerful","Why This Is Powerful",[21,22731,22732],{},"No auth logic in individual routes",[21,22734,22735],{},"Centralized, reusable security",[21,22737,22738],{},"Easy to extend with JWT or OAuth later",[32,22740,22742],{"id":22741},"tutorial-3-simple-response-caching-plugin","Tutorial 3: Simple Response Caching Plugin",[21,22744,22745],{},"Goal\nCache GET responses for frequently accessed endpoints (menus, products, settings).",[21,22747,22748],{},"Step 1: Create an In-Memory Cache\nconst cache = new Map\u003Cstring, { value: any; expires: number }>()\nconst TTL = 60_000 \u002F\u002F 60 seconds\nStep 2: Build the Plugin\n\u002F\u002F server\u002Fplugins\u002Fcache.ts\nexport default defineNitroPlugin((nitroApp) => {\nnitroApp.hooks.hook(\"request\", async (event) => {\nif (event.method !== \"GET\") return",[48,22750,22753],{"className":22751,"code":22752,"language":53},[51],"const key = event.path\nconst cached = cache.get(key)\n\nif (cached && cached.expires > Date.now()) {\n  return cached.value\n}\n",[40,22754,22752],{"__ignoreMap":56},[21,22756,22757],{},"})",[21,22759,22760],{},"nitroApp.hooks.hook(\"afterResponse\", (event, response) => {\nif (event.method !== \"GET\") return",[48,22762,22765],{"className":22763,"code":22764,"language":53},[51],"cache.set(event.path, {\n  value: response,\n  expires: Date.now() + TTL\n})\n",[40,22766,22764],{"__ignoreMap":56},[21,22768,22679],{},[32,22770,22772],{"id":22771},"real-world-use-case","Real-World Use Case",[21,22774,22775],{},"Food truck menu API",[21,22777,22778],{},"Business hours endpoint",[21,22780,22781],{},"Pricing data",[32,22783,22785],{"id":22784},"tutorial-4-feature-flags-plugin","Tutorial 4: Feature Flags Plugin",[21,22787,22788],{},"Goal\nEnable or disable features at runtime without touching routes.",[21,22790,22791],{},"Step 1: Define Flags\nconst flags = {\nseasonalMenu: true,\nenableDiscounts: false\n}\nStep 2: Attach Flags to Runtime\n\u002F\u002F server\u002Fplugins\u002Ffeature-flags.ts\nexport default defineNitroPlugin((nitroApp) => {\nnitroApp.hooks.hook(\"request\", (event) => {\nevent.context.features = flags\n})\n})\nStep 3: Use in a Route\nexport default defineEventHandler((event) => {\nif (event.context.features.seasonalMenu) {\nreturn { menu: \"Winter Specials\" }\n}",[21,22793,22794],{},"return { menu: \"Standard Menu\" }\n})",[32,22796,22798],{"id":22797},"why-this-matters","Why This Matters",[21,22800,22801],{},"Toggle features instantly",[21,22803,22804],{},"Safe rollouts",[21,22806,22807],{},"Environment-based behavior",[32,22809,22811],{"id":22810},"combining-plugins-for-a-real-app","Combining Plugins for a Real App",[21,22813,22814],{},"For a coffee shop or food truck API, you might use:",[21,22816,22817],{},"Plugin\tPurpose\nLogger\tTrack traffic & performance\nAuth\tSecure admin endpoints\nCache\tSpeed up menu reads\nFeature Flags\tEnable promotions\nAll without bloating your route handlers.",[32,22819,20154],{"id":22820},"best-practices",[21,22822,22823],{},"Keep plugins focused on one responsibility",[21,22825,22826],{},"Avoid heavy computation in request hooks",[21,22828,22829],{},"Use plugins for cross-cutting concerns, not business logic",[21,22831,22832],{},"Prefer plugins over middleware duplication\n::",[16,22834,3681],{"id":3680},[21,22836,22837],{},"Nitro plugins give you framework-level power without framework-level complexity. Instead of repeating logic in routes, plugins let you build reusable, centralized behaviors that scale with your app.",[21,22839,22840],{},"If you’re serious about building maintainable APIs — whether for Nuxt apps, serverless deployments, or edge runtimes — mastering Nitro plugins is a game changer. 🚀",[3728,22842,4140],{},{"title":56,"searchDepth":111,"depth":111,"links":22844},[22845,22846,22847,22848,22849,22850,22851,22852,22853],{"id":22682,"depth":114,"text":22683},{"id":22704,"depth":114,"text":22705},{"id":22728,"depth":114,"text":22729},{"id":22741,"depth":114,"text":22742},{"id":22771,"depth":114,"text":22772},{"id":22784,"depth":114,"text":22785},{"id":22797,"depth":114,"text":22798},{"id":22810,"depth":114,"text":22811},{"id":22820,"depth":114,"text":20154},"2026-01-30","Hands-on Nitro plugin tutorials showing how to build logging, authentication, caching, and feature flag plugins for real-world applications.",{"type":13,"value":22857},[22858,22860,22866,22868,22878],[16,22859,333],{"id":332},[21,22861,22579,22862,22583,22864,22587],{},[151,22863,22582],{},[151,22865,22586],{},[21,22867,22590],{},[383,22869,22870,22872,22874,22876],{},[386,22871,22595],{},[386,22873,22598],{},[386,22875,19120],{},[386,22877,22603],{},[21,22879,22606,22880,22609,22882,22613],{},[151,22881,3788],{},[151,22883,22612],{},[7076,21804,7078,7079,3784],{},"\u002Fblog\u002Fnitro-tutorial",{"title":22571,"description":22855},"blog\u002Fnitro-tutorial","i4pPWumhRzJriakSE9zevlb2qtVUOftePtjEXoFGaY0",{"id":22891,"title":22892,"author":22893,"body":22894,"canonical":116,"categories":116,"date":23621,"description":23622,"excerpt":23623,"extension":119,"img":21929,"keywords":23637,"language":3788,"meta":23642,"navigation":130,"ogImage":116,"path":23643,"rating":474,"readTime":133,"seo":23644,"stem":23645,"tag":3793,"__hash__":23646},"blog\u002Fblog\u002Fnuxt-42-steady-movements-to-5.md","Evaluating Nuxt 4.2: How the Latest Stable Release Empowers Modern Developers",{"name":9,"img":11,"website":327},{"type":13,"value":22895,"toc":23600},[22896,22898,22909,22915,22917,22921,22928,22942,22945,22950,22952,22956,22966,22969,22971,22982,22986,23109,23112,23114,23118,23121,23137,23140,23143,23145,23149,23152,23159,23163,23174,23178,23189,23192,23194,23198,23205,23240,23243,23254,23257,23259,23263,23269,23280,23283,23285,23289,23296,23300,23311,23345,23348,23350,23354,23357,23360,23531,23534,23545,23548,23550,23554,23557,23577,23580,23582,23584,23587,23594,23597],[16,22897,333],{"id":332},[21,22899,22900,22901,22904,22905,22908],{},"Nuxt has consistently positioned itself as one of the most developer-friendly frameworks in the Vue ecosystem. With the release of ",[151,22902,22903],{},"Nuxt 4",", the framework doubled down on clarity, performance, and full-stack flexibility. The latest stable release, ",[151,22906,22907],{},"Nuxt 4.2",", continues that trajectory—not by reinventing the framework, but by polishing the parts developers interact with every single day.",[21,22910,22911,22912,165],{},"This post evaluates Nuxt 4.2 from a practical standpoint: what’s new, why it matters, and how these changes help developers build ",[151,22913,22914],{},"faster, more resilient, and more maintainable applications",[16,22916,4373],{"id":4372},[32,22918,22920],{"id":22919},"nuxt-42-in-context","Nuxt 4.2 in Context",[21,22922,22923,22924,22927],{},"Nuxt 4.2 is best understood as a ",[151,22925,22926],{},"developer-experience release",". Rather than introducing breaking changes or radical APIs, it focuses on:",[383,22929,22930,22933,22936,22939],{},[386,22931,22932],{},"More control over async behavior",[386,22934,22935],{},"Better debugging ergonomics",[386,22937,22938],{},"Smarter performance optimizations",[386,22940,22941],{},"Stronger alignment with Vite and Nitro",[21,22943,22944],{},"These improvements target real-world pain points that surface as applications grow.",[25,22946,22947],{"type":363},[21,22948,22949],{},"Nuxt 4.2 is a stable minor release, meaning it’s safe to adopt for production apps already running on Nuxt 4.",[73,22951],{},[32,22953,22955],{"id":22954},"abortable-data-fetching-a-real-world-necessity","Abortable Data Fetching: A Real-World Necessity",[21,22957,22958,22959,22962,22963,165],{},"One of the most impactful additions in Nuxt 4.2 is ",[151,22960,22961],{},"native support for aborting async data requests"," using ",[40,22964,22965],{},"AbortController",[21,22967,22968],{},"In dynamic interfaces—search inputs, filters, route transitions—users often trigger multiple requests in quick succession. Without cancellation, older requests can overwrite newer state.",[2808,22970,22798],{"id":22797},[383,22972,22973,22976,22979],{},[386,22974,22975],{},"Prevents race conditions",[386,22977,22978],{},"Eliminates unnecessary network traffic",[386,22980,22981],{},"Keeps UI state predictable",[32,22983,22985],{"id":22984},"code-example","Code Example",[48,22987,22990],{"className":670,"code":22988,"filename":22989,"language":673,"meta":56,"style":56},"\u003Cscript setup lang=\"ts\">\nconst controller = new AbortController()\n\nconst { data } = await useAsyncData('users', () =>\n  $fetch('\u002Fapi\u002Fusers', {\n    signal: controller.signal\n  })\n)\n\n\u002F\u002F Cancel the request if the user navigates away\ncontroller.abort()\n\u003C\u002Fscript>\n","abortable-fetch.ts",[40,22991,22992,23007,23023,23027,23055,23067,23072,23076,23080,23084,23089,23099],{"__ignoreMap":56},[449,22993,22994,22996,22999,23001,23004],{"class":451,"line":452},[449,22995,5546],{"class":680},[449,22997,22998],{"class":684},"script setup lang",[449,23000,5015],{"class":680},[449,23002,23003],{"class":691},"\"ts\"",[449,23005,23006],{"class":680},">\n",[449,23008,23009,23011,23014,23016,23018,23021],{"class":451,"line":114},[449,23010,711],{"class":680},[449,23012,23013],{"class":714}," controller",[449,23015,718],{"class":680},[449,23017,1343],{"class":680},[449,23019,23020],{"class":721}," AbortController",[449,23022,11220],{"class":684},[449,23024,23025],{"class":451,"line":111},[449,23026,700],{"emptyLinePlaceholder":130},[449,23028,23029,23031,23033,23035,23037,23039,23041,23044,23046,23049,23052],{"class":451,"line":468},[449,23030,711],{"class":680},[449,23032,14124],{"class":684},[449,23034,1755],{"class":714},[449,23036,21541],{"class":684},[449,23038,5015],{"class":680},[449,23040,1654],{"class":680},[449,23042,23043],{"class":721}," useAsyncData",[449,23045,725],{"class":684},[449,23047,23048],{"class":691},"'users'",[449,23050,23051],{"class":684},", () ",[449,23053,23054],{"class":680},"=>\n",[449,23056,23057,23060,23062,23065],{"class":451,"line":474},[449,23058,23059],{"class":721},"  $fetch",[449,23061,725],{"class":684},[449,23063,23064],{"class":691},"'\u002Fapi\u002Fusers'",[449,23066,768],{"class":684},[449,23068,23069],{"class":451,"line":480},[449,23070,23071],{"class":684},"    signal: controller.signal\n",[449,23073,23074],{"class":451,"line":486},[449,23075,16717],{"class":684},[449,23077,23078],{"class":451,"line":492},[449,23079,11211],{"class":684},[449,23081,23082],{"class":451,"line":498},[449,23083,700],{"emptyLinePlaceholder":130},[449,23085,23086],{"class":451,"line":133},[449,23087,23088],{"class":705},"\u002F\u002F Cancel the request if the user navigates away\n",[449,23090,23091,23094,23097],{"class":451,"line":509},[449,23092,23093],{"class":684},"controller.",[449,23095,23096],{"class":721},"abort",[449,23098,11220],{"class":684},[449,23100,23101,23104,23107],{"class":451,"line":318},[449,23102,23103],{"class":680},"\u003C\u002F",[449,23105,23106],{"class":684},"script",[449,23108,23006],{"class":680},[21,23110,23111],{},"This feature brings Nuxt’s data handling closer to native browser behavior—something developers have wanted for a long time.",[73,23113],{},[32,23115,23117],{"id":23116},"improved-error-handling-during-development","Improved Error Handling During Development",[21,23119,23120],{},"Debugging is a daily activity, and Nuxt 4.2 makes it less disruptive. When an error occurs during development, Nuxt now displays:",[383,23122,23123,23130],{},[386,23124,23125,23126,23129],{},"Your ",[151,23127,23128],{},"custom error page"," (what users would see)",[386,23131,23132,23133,23136],{},"A ",[151,23134,23135],{},"toggleable technical overlay"," with stack traces and diagnostics",[21,23138,23139],{},"This dual-view approach keeps developers grounded in the user experience while still providing deep technical insight.",[21,23141,23142],{},"Poor error visibility often leads to longer debugging sessions and fragile fixes—Nuxt 4.2 directly addresses this.\n::",[73,23144],{},[32,23146,23148],{"id":23147},"performance-improvements-that-scale","Performance Improvements That Scale",[21,23150,23151],{},"Performance optimizations in Nuxt 4.2 are subtle but powerful, especially for production-grade applications.",[21,23153,23154,23155,23158],{},"One standout improvement is ",[151,23156,23157],{},"async data handler extraction"," (experimental). Nuxt can now move certain async logic into build-time chunks, preventing unnecessary JavaScript from being shipped to the browser.",[2808,23160,23162],{"id":23161},"developer-impact","Developer Impact",[383,23164,23165,23168,23171],{},[386,23166,23167],{},"Smaller client bundles",[386,23169,23170],{},"Faster initial page loads",[386,23172,23173],{},"Better SEO and Lighthouse scores",[2808,23175,23177],{"id":23176},"performance-wins-in-practice","Performance Wins in Practice",[383,23179,23180,23183,23186],{},[386,23181,23182],{},"Reduced client-side JavaScript",[386,23184,23185],{},"Faster cold starts",[386,23187,23188],{},"Improved perceived performance for users\n::",[21,23190,23191],{},"These gains matter most at scale—when milliseconds start adding up across thousands of users.",[73,23193],{},[32,23195,23197],{"id":23196},"vite-environment-api-preparing-for-the-future","Vite Environment API: Preparing for the Future",[21,23199,23200,23201,23204],{},"Nuxt 4.2 introduces optional support for Vite’s new ",[151,23202,23203],{},"Environment API",", allowing more control over environment-specific behavior.",[48,23206,23208],{"className":670,"code":23207,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  experimental: {\n    viteEnvironmentApi: true\n  }\n})\n",[40,23209,23210,23220,23225,23232,23236],{"__ignoreMap":56},[449,23211,23212,23214,23216,23218],{"class":451,"line":452},[449,23213,4629],{"class":680},[449,23215,4632],{"class":680},[449,23217,4635],{"class":721},[449,23219,1391],{"class":684},[449,23221,23222],{"class":451,"line":114},[449,23223,23224],{"class":684},"  experimental: {\n",[449,23226,23227,23230],{"class":451,"line":111},[449,23228,23229],{"class":684},"    viteEnvironmentApi: ",[449,23231,17822],{"class":714},[449,23233,23234],{"class":451,"line":468},[449,23235,4599],{"class":684},[449,23237,23238],{"class":451,"line":474},[449,23239,4113],{"class":684},[21,23241,23242],{},"This is particularly valuable for teams managing:",[383,23244,23245,23248,23251],{},[386,23246,23247],{},"Multiple deployment stages",[386,23249,23250],{},"Complex environment variables",[386,23252,23253],{},"Shared infrastructure across apps",[21,23255,23256],{},"While still experimental, it signals Nuxt’s commitment to evolving alongside Vite rather than lagging behind it.",[73,23258],{},[32,23260,23262],{"id":23261},"a-more-modular-nitro-architecture","A More Modular Nitro Architecture",[21,23264,23265,23266,23268],{},"Behind the scenes, Nuxt 4.2 improves how ",[151,23267,4189],{},"—Nuxt’s server engine—is integrated. By extracting server integration into a dedicated package, Nuxt gains:",[383,23270,23271,23274,23277],{},[386,23272,23273],{},"Cleaner separation between frontend and backend concerns",[386,23275,23276],{},"More flexibility for future runtime targets",[386,23278,23279],{},"Improved long-term maintainability",[21,23281,23282],{},"Most developers won’t notice this day to day, but it lays the groundwork for future innovation without breaking existing apps.",[73,23284],{},[32,23286,23288],{"id":23287},"typescript-tooling-improvements","TypeScript Tooling Improvements",[21,23290,23291,23292,23295],{},"TypeScript users benefit from experimental tooling enhancements in Nuxt 4.2. These improvements focus on ",[151,23293,23294],{},"editor intelligence",", making large codebases easier to navigate.",[2808,23297,23299],{"id":23298},"what-developers-gain","What Developers Gain",[383,23301,23302,23305,23308],{},[386,23303,23304],{},"Better go-to-definition support",[386,23306,23307],{},"Smarter component renaming",[386,23309,23310],{},"Improved navigation between server routes and client fetches",[48,23312,23314],{"className":670,"code":23313,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  experimental: {\n    typescriptPlugin: true\n  }\n})\n",[40,23315,23316,23326,23330,23337,23341],{"__ignoreMap":56},[449,23317,23318,23320,23322,23324],{"class":451,"line":452},[449,23319,4629],{"class":680},[449,23321,4632],{"class":680},[449,23323,4635],{"class":721},[449,23325,1391],{"class":684},[449,23327,23328],{"class":451,"line":114},[449,23329,23224],{"class":684},[449,23331,23332,23335],{"class":451,"line":111},[449,23333,23334],{"class":684},"    typescriptPlugin: ",[449,23336,17822],{"class":714},[449,23338,23339],{"class":451,"line":468},[449,23340,4599],{"class":684},[449,23342,23343],{"class":451,"line":474},[449,23344,4113],{"class":684},[21,23346,23347],{},"For teams working in TypeScript-first environments, these changes reduce cognitive overhead and make refactoring safer.",[73,23349],{},[32,23351,23353],{"id":23352},"practical-pattern-search-with-request-cancellation","Practical Pattern: Search With Request Cancellation",[21,23355,23356],{},"Here’s a real-world pattern enabled by Nuxt 4.2’s abortable requests:",[32,23358,22985],{"id":23359},"code-example-1",[48,23361,23364],{"className":670,"code":23362,"filename":23363,"language":673,"meta":56,"style":56},"\u003Cscript setup lang=\"ts\">\nconst query = ref('')\nlet controller = new AbortController()\n\nwatch(query, async () => {\n  controller.abort()\n  controller = new AbortController()\n\n  await useAsyncData(\n    ['search', query.value],\n    () =>\n      $fetch(`\u002Fapi\u002Fsearch?q=${query.value}`, {\n        signal: controller.signal\n      })\n  )\n})\n\u003C\u002Fscript>\n","search-pattern.ts",[40,23365,23366,23378,23397,23412,23416,23432,23441,23454,23458,23466,23477,23484,23506,23511,23515,23519,23523],{"__ignoreMap":56},[449,23367,23368,23370,23372,23374,23376],{"class":451,"line":452},[449,23369,5546],{"class":680},[449,23371,22998],{"class":684},[449,23373,5015],{"class":680},[449,23375,23003],{"class":691},[449,23377,23006],{"class":680},[449,23379,23380,23382,23385,23387,23390,23392,23395],{"class":451,"line":114},[449,23381,711],{"class":680},[449,23383,23384],{"class":714}," query",[449,23386,718],{"class":680},[449,23388,23389],{"class":721}," ref",[449,23391,725],{"class":684},[449,23393,23394],{"class":691},"''",[449,23396,11211],{"class":684},[449,23398,23399,23401,23404,23406,23408,23410],{"class":451,"line":111},[449,23400,5533],{"class":680},[449,23402,23403],{"class":684}," controller ",[449,23405,5015],{"class":680},[449,23407,1343],{"class":680},[449,23409,23020],{"class":721},[449,23411,11220],{"class":684},[449,23413,23414],{"class":451,"line":468},[449,23415,700],{"emptyLinePlaceholder":130},[449,23417,23418,23421,23424,23426,23428,23430],{"class":451,"line":474},[449,23419,23420],{"class":721},"watch",[449,23422,23423],{"class":684},"(query, ",[449,23425,1485],{"class":680},[449,23427,2090],{"class":684},[449,23429,1498],{"class":680},[449,23431,1501],{"class":684},[449,23433,23434,23437,23439],{"class":451,"line":480},[449,23435,23436],{"class":684},"  controller.",[449,23438,23096],{"class":721},[449,23440,11220],{"class":684},[449,23442,23443,23446,23448,23450,23452],{"class":451,"line":486},[449,23444,23445],{"class":684},"  controller ",[449,23447,5015],{"class":680},[449,23449,1343],{"class":680},[449,23451,23020],{"class":721},[449,23453,11220],{"class":684},[449,23455,23456],{"class":451,"line":492},[449,23457,700],{"emptyLinePlaceholder":130},[449,23459,23460,23462,23464],{"class":451,"line":498},[449,23461,2099],{"class":680},[449,23463,23043],{"class":721},[449,23465,5566],{"class":684},[449,23467,23468,23471,23474],{"class":451,"line":133},[449,23469,23470],{"class":684},"    [",[449,23472,23473],{"class":691},"'search'",[449,23475,23476],{"class":684},", query.value],\n",[449,23478,23479,23482],{"class":451,"line":509},[449,23480,23481],{"class":684},"    () ",[449,23483,23054],{"class":680},[449,23485,23486,23489,23491,23494,23497,23499,23502,23504],{"class":451,"line":318},[449,23487,23488],{"class":721},"      $fetch",[449,23490,725],{"class":684},[449,23492,23493],{"class":691},"`\u002Fapi\u002Fsearch?q=${",[449,23495,23496],{"class":684},"query",[449,23498,165],{"class":691},[449,23500,23501],{"class":684},"value",[449,23503,1517],{"class":691},[449,23505,768],{"class":684},[449,23507,23508],{"class":451,"line":520},[449,23509,23510],{"class":684},"        signal: controller.signal\n",[449,23512,23513],{"class":451,"line":526},[449,23514,5787],{"class":684},[449,23516,23517],{"class":451,"line":532},[449,23518,16394],{"class":684},[449,23520,23521],{"class":451,"line":2790},[449,23522,4113],{"class":684},[449,23524,23525,23527,23529],{"class":451,"line":3511},[449,23526,23103],{"class":680},[449,23528,23106],{"class":684},[449,23530,23006],{"class":680},[21,23532,23533],{},"This approach:",[383,23535,23536,23539,23542],{},[386,23537,23538],{},"Cancels outdated requests automatically",[386,23540,23541],{},"Keeps results in sync with user input",[386,23543,23544],{},"Improves perceived responsiveness",[21,23546,23547],{},"It’s a small API change with a huge real-world payoff.",[73,23549],{},[32,23551,23553],{"id":23552},"who-benefits-most-from-nuxt-42","Who Benefits Most From Nuxt 4.2?",[21,23555,23556],{},"Nuxt 4.2 shines for:",[383,23558,23559,23565,23571,23574],{},[386,23560,23561,23562],{},"Teams building ",[151,23563,23564],{},"interactive, data-driven UIs",[386,23566,23567,23568],{},"Developers optimizing for ",[151,23569,23570],{},"performance and SEO",[386,23572,23573],{},"TypeScript-heavy projects",[386,23575,23576],{},"Full-stack Nuxt apps using Nitro",[21,23578,23579],{},"If you’re already on Nuxt 4, upgrading to 4.2 is a low-risk way to gain immediate DX and performance benefits.\n::",[73,23581],{},[32,23583,3681],{"id":3680},[21,23585,23586],{},"Nuxt 4.2 is a thoughtful, developer-focused release. It doesn’t chase trends—it solves real problems. By improving async control, debugging clarity, performance, and tooling, Nuxt 4.2 makes everyday development smoother and production applications more robust.",[21,23588,23589,23590,23593],{},"For developers who value ",[151,23591,23592],{},"clarity, performance, and long-term maintainability",", Nuxt 4.2 is not just an upgrade—it’s a refinement of everything that makes Nuxt a pleasure to use.",[21,23595,23596],{},"If you’re building serious Vue applications in 2026, Nuxt 4.2 deserves a place at the center of your stack.",[3728,23598,23599],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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);}",{"title":56,"searchDepth":111,"depth":111,"links":23601},[23602,23603,23606,23607,23608,23612,23613,23614,23617,23618,23619,23620],{"id":22919,"depth":114,"text":22920},{"id":22954,"depth":114,"text":22955,"children":23604},[23605],{"id":22797,"depth":111,"text":22798},{"id":22984,"depth":114,"text":22985},{"id":23116,"depth":114,"text":23117},{"id":23147,"depth":114,"text":23148,"children":23609},[23610,23611],{"id":23161,"depth":111,"text":23162},{"id":23176,"depth":111,"text":23177},{"id":23196,"depth":114,"text":23197},{"id":23261,"depth":114,"text":23262},{"id":23287,"depth":114,"text":23288,"children":23615},[23616],{"id":23298,"depth":111,"text":23299},{"id":23352,"depth":114,"text":23353},{"id":23359,"depth":114,"text":22985},{"id":23552,"depth":114,"text":23553},{"id":3680,"depth":114,"text":3681},"2026-01-09","A deep evaluation of Nuxt 4.2, exploring new features, performance improvements, and why this release makes building modern Vue applications faster and more reliable.",{"type":13,"value":23624},[23625,23627,23633],[16,23626,333],{"id":332},[21,23628,22900,23629,22904,23631,22908],{},[151,23630,22903],{},[151,23632,22907],{},[21,23634,22911,23635,165],{},[151,23636,22914],{},[23638,23639,23640,7076,127,23641],"nuxt 4","nuxt 4.2","vue","web performance",{},"\u002Fblog\u002Fnuxt-42-steady-movements-to-5",{"title":22892,"description":23622},"blog\u002Fnuxt-42-steady-movements-to-5","xF7X0JRedqolAzpj1780Epatp6IDaI6ADDC3FG4FqCQ",{"id":23648,"title":23649,"author":23650,"body":23651,"canonical":116,"categories":116,"date":7042,"description":28560,"excerpt":28561,"extension":119,"img":7074,"keywords":28589,"language":3788,"meta":28595,"navigation":130,"ogImage":116,"path":28596,"rating":474,"readTime":532,"seo":28597,"stem":28598,"tag":3793,"__hash__":28599},"blog\u002Fblog\u002Fnuxt-auth-guide.md","Complete Guide to Authentication in Nuxt 4: From Basics to Production",{"name":9,"img":11,"website":327},{"type":13,"value":23652,"toc":28541},[23653,23655,23658,23663,23686,23689,23691,23695,23698,23764,23769,23771,23773,23776,23804,23807,23838,23853,23855,23859,23862,23866,23992,24000,24007,24030,24032,24036,24039,24044,24518,24524,24526,24530,24533,24538,24930,24935,25094,25096,25100,25103,25108,25480,25484,25489,25785,25789,25794,25846,25850,25855,26144,26146,26150,26153,26157,26511,26517,26519,26523,26526,26533,26538,26563,26567,26695,26700,26799,26801,26805,26810,27464,27469,28050,28052,28056,28059,28311,28313,28317,28320,28370,28372,28376,28450,28452,28454,28457,28489,28492,28518,28521,28535,28538],[16,23654,333],{"id":332},[21,23656,23657],{},"Every production application needs authentication. Yet it's one of the most commonly botched parts of web development—poorly implemented auth leads to security vulnerabilities, frustrated users, and late-night debugging sessions.",[21,23659,23660,23661,3070],{},"In this comprehensive guide, we'll cover everything you need to know about authentication in ",[151,23662,22903],{},[383,23664,23665,23668,23671,23674,23677,23680,23683],{},[386,23666,23667],{},"Authentication strategies and when to use each",[386,23669,23670],{},"Implementing session-based auth",[386,23672,23673],{},"JWT authentication flow",[386,23675,23676],{},"OAuth with GitHub and Google",[386,23678,23679],{},"Protecting API routes",[386,23681,23682],{},"Secure password handling",[386,23684,23685],{},"Type-safe auth with TypeScript",[21,23687,23688],{},"Let's build a complete auth system for our BrewStop application.",[16,23690,4373],{"id":4372},[32,23692,23694],{"id":23693},"understanding-authentication-strategies","Understanding Authentication Strategies",[21,23696,23697],{},"Before we write code, let's understand the three main authentication strategies:",[547,23699,23700,23714],{},[550,23701,23702],{},[553,23703,23704,23707,23710,23712],{},[556,23705,23706],{},"Strategy",[556,23708,23709],{},"Best For",[556,23711,18780],{},[556,23713,18796],{},[569,23715,23716,23732,23748],{},[553,23717,23718,23723,23726,23729],{},[574,23719,23720],{},[151,23721,23722],{},"Session",[574,23724,23725],{},"Traditional web apps",[574,23727,23728],{},"Simple, familiar UX",[574,23730,23731],{},"CSRF concerns, server state",[553,23733,23734,23739,23742,23745],{},[574,23735,23736],{},[151,23737,23738],{},"JWT",[574,23740,23741],{},"SPAs, APIs, mobile",[574,23743,23744],{},"Stateless, scalable",[574,23746,23747],{},"Token management complexity",[553,23749,23750,23755,23758,23761],{},[574,23751,23752],{},[151,23753,23754],{},"OAuth",[574,23756,23757],{},"Social login",[574,23759,23760],{},"Frictionless, trusted",[574,23762,23763],{},"Third-party dependency",[25,23765,23766],{"type":363},[21,23767,23768],{},"For most Nuxt applications, a hybrid approach works best: JWT for API authentication, OAuth for social login, and session-like behavior via cookies.",[73,23770],{},[32,23772,10547],{"id":10546},[21,23774,23775],{},"Let's create a fresh Nuxt project with authentication in mind.",[48,23777,23779],{"className":839,"code":23778,"filename":4384,"language":841,"meta":56,"style":56},"npx nuxi@latest init brewstop-auth\ncd brewstop-auth\nnpm install\n",[40,23780,23781,23792,23798],{"__ignoreMap":56},[449,23782,23783,23785,23787,23789],{"class":451,"line":452},[449,23784,8898],{"class":721},[449,23786,4475],{"class":691},[449,23788,4478],{"class":691},[449,23790,23791],{"class":691}," brewstop-auth\n",[449,23793,23794,23796],{"class":451,"line":114},[449,23795,4490],{"class":714},[449,23797,23791],{"class":691},[449,23799,23800,23802],{"class":451,"line":111},[449,23801,3703],{"class":721},[449,23803,4512],{"class":691},[21,23805,23806],{},"We'll need some additional packages:",[48,23808,23810],{"className":839,"code":23809,"filename":4384,"language":841,"meta":56,"style":56},"npm install @sidebase\u002Fnuxt-auth next-auth@beta jose\nnpm install -D @types\u002Fnode\n",[40,23811,23812,23827],{"__ignoreMap":56},[449,23813,23814,23816,23818,23821,23824],{"class":451,"line":452},[449,23815,3703],{"class":721},[449,23817,3706],{"class":691},[449,23819,23820],{"class":691}," @sidebase\u002Fnuxt-auth",[449,23822,23823],{"class":691}," next-auth@beta",[449,23825,23826],{"class":691}," jose\n",[449,23828,23829,23831,23833,23835],{"class":451,"line":114},[449,23830,3703],{"class":721},[449,23832,3706],{"class":691},[449,23834,10606],{"class":714},[449,23836,23837],{"class":691}," @types\u002Fnode\n",[427,23839,23842],{"icon":23840,"title":23841},"🔐","Security First",[21,23843,23844,23845,23848,23849,23852],{},"We're using ",[40,23846,23847],{},"@sidebase\u002Fnuxt-auth"," which provides first-class support for NextAuth.js in Nuxt, and ",[40,23850,23851],{},"jose"," for JWT operations.",[73,23854],{},[32,23856,23858],{"id":23857},"step-2-configure-nuxt-auth","Step 2: Configure Nuxt Auth",[21,23860,23861],{},"Add the auth module to your Nuxt configuration.",[21,23863,4698,23864,3070],{},[40,23865,4619],{},[48,23867,23869],{"className":670,"code":23868,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  modules: [\"@sidebase\u002Fnuxt-auth\"],\n  \n  auth: {\n    provider: {\n      type: \"authjs\",\n      defaultProvider: \"credentials\",\n    },\n    globalAppMiddleware: true,\n  },\n  \n  runtimeConfig: {\n    authSecret: process.env.AUTH_SECRET,\n    public: {\n      authOrigin: process.env.AUTH_ORIGIN || \"http:\u002F\u002Flocalhost:3000\",\n    },\n  },\n});\n",[40,23870,23871,23881,23891,23895,23900,23905,23915,23925,23929,23938,23942,23946,23950,23960,23964,23980,23984,23988],{"__ignoreMap":56},[449,23872,23873,23875,23877,23879],{"class":451,"line":452},[449,23874,4629],{"class":680},[449,23876,4632],{"class":680},[449,23878,4635],{"class":721},[449,23880,1391],{"class":684},[449,23882,23883,23886,23889],{"class":451,"line":114},[449,23884,23885],{"class":684},"  modules: [",[449,23887,23888],{"class":691},"\"@sidebase\u002Fnuxt-auth\"",[449,23890,1469],{"class":684},[449,23892,23893],{"class":451,"line":111},[449,23894,4661],{"class":684},[449,23896,23897],{"class":451,"line":468},[449,23898,23899],{"class":684},"  auth: {\n",[449,23901,23902],{"class":451,"line":474},[449,23903,23904],{"class":684},"    provider: {\n",[449,23906,23907,23910,23913],{"class":451,"line":480},[449,23908,23909],{"class":684},"      type: ",[449,23911,23912],{"class":691},"\"authjs\"",[449,23914,779],{"class":684},[449,23916,23917,23920,23923],{"class":451,"line":486},[449,23918,23919],{"class":684},"      defaultProvider: ",[449,23921,23922],{"class":691},"\"credentials\"",[449,23924,779],{"class":684},[449,23926,23927],{"class":451,"line":492},[449,23928,1458],{"class":684},[449,23930,23931,23934,23936],{"class":451,"line":498},[449,23932,23933],{"class":684},"    globalAppMiddleware: ",[449,23935,2169],{"class":714},[449,23937,779],{"class":684},[449,23939,23940],{"class":451,"line":133},[449,23941,1474],{"class":684},[449,23943,23944],{"class":451,"line":509},[449,23945,4661],{"class":684},[449,23947,23948],{"class":451,"line":318},[449,23949,15284],{"class":684},[449,23951,23952,23955,23958],{"class":451,"line":520},[449,23953,23954],{"class":684},"    authSecret: process.env.",[449,23956,23957],{"class":714},"AUTH_SECRET",[449,23959,779],{"class":684},[449,23961,23962],{"class":451,"line":526},[449,23963,15289],{"class":684},[449,23965,23966,23969,23972,23975,23978],{"class":451,"line":532},[449,23967,23968],{"class":684},"      authOrigin: process.env.",[449,23970,23971],{"class":714},"AUTH_ORIGIN",[449,23973,23974],{"class":680}," ||",[449,23976,23977],{"class":691}," \"http:\u002F\u002Flocalhost:3000\"",[449,23979,779],{"class":684},[449,23981,23982],{"class":451,"line":2790},[449,23983,1458],{"class":684},[449,23985,23986],{"class":451,"line":3511},[449,23987,1474],{"class":684},[449,23989,23990],{"class":451,"line":3526},[449,23991,784],{"class":684},[25,23993,23994],{"type":27},[21,23995,23996,23997,23999],{},"Never commit your ",[40,23998,23957],{},". Use environment variables for all secrets.",[21,24001,24002,24003,24006],{},"Create a ",[40,24004,24005],{},".env"," file:",[48,24008,24010],{"className":839,"code":24009,"filename":24005,"language":841,"meta":56,"style":56},"AUTH_SECRET=your-super-secret-key-change-in-production\nAUTH_ORIGIN=http:\u002F\u002Flocalhost:3000\n",[40,24011,24012,24021],{"__ignoreMap":56},[449,24013,24014,24016,24018],{"class":451,"line":452},[449,24015,23957],{"class":684},[449,24017,5015],{"class":680},[449,24019,24020],{"class":691},"your-super-secret-key-change-in-production\n",[449,24022,24023,24025,24027],{"class":451,"line":114},[449,24024,23971],{"class":684},[449,24026,5015],{"class":680},[449,24028,24029],{"class":691},"http:\u002F\u002Flocalhost:3000\n",[73,24031],{},[32,24033,24035],{"id":24034},"step-3-create-the-authentication-backend","Step 3: Create the Authentication Backend",[21,24037,24038],{},"We'll build a credentials-based auth system with proper password hashing.",[21,24040,4698,24041,3070],{},[40,24042,24043],{},"server\u002Futils\u002Fauth.ts",[48,24045,24047],{"className":670,"code":24046,"filename":24043,"language":673,"meta":56,"style":56},"import { SignJWT, jwtVerify } from \"jose\";\nimport { hash, verify } from \"@node-rs\u002Fargon2\";\n\nconst secret = new TextEncoder().encode(\n  process.env.AUTH_SECRET || \"fallback-secret\"\n);\n\nexport async function hashPassword(password: string): Promise\u003Cstring> {\n  return hash(password, {\n    memoryCost: 19456,\n    timeCost: 2,\n    outputLen: 32,\n    parallelism: 1,\n  });\n}\n\nexport async function verifyPassword(hash: string, password: string): Promise\u003Cboolean> {\n  return verify(hash, password);\n}\n\nexport async function createToken(userId: string, email: string): Promise\u003Cstring> {\n  return new SignJWT({ userId, email })\n    .setProtectedHeader({ alg: \"HS256\" })\n    .setIssuedAt()\n    .setExpirationTime(\"7d\")\n    .sign(secret);\n}\n\nexport async function verifyToken(token: string): Promise\u003C{ userId: string; email: string } | null> {\n  try {\n    const { payload } = await jwtVerify(token, secret);\n    return payload as { userId: string; email: string };\n  } catch {\n    return null;\n  }\n}\n",[40,24048,24049,24063,24077,24081,24102,24114,24118,24122,24156,24166,24176,24185,24195,24204,24208,24212,24216,24257,24267,24271,24275,24316,24328,24343,24352,24366,24376,24380,24384,24438,24445,24466,24493,24502,24510,24514],{"__ignoreMap":56},[449,24050,24051,24053,24056,24058,24061],{"class":451,"line":452},[449,24052,681],{"class":680},[449,24054,24055],{"class":684}," { SignJWT, jwtVerify } ",[449,24057,688],{"class":680},[449,24059,24060],{"class":691}," \"jose\"",[449,24062,695],{"class":684},[449,24064,24065,24067,24070,24072,24075],{"class":451,"line":114},[449,24066,681],{"class":680},[449,24068,24069],{"class":684}," { hash, verify } ",[449,24071,688],{"class":680},[449,24073,24074],{"class":691}," \"@node-rs\u002Fargon2\"",[449,24076,695],{"class":684},[449,24078,24079],{"class":451,"line":111},[449,24080,700],{"emptyLinePlaceholder":130},[449,24082,24083,24085,24088,24090,24092,24095,24097,24100],{"class":451,"line":468},[449,24084,711],{"class":680},[449,24086,24087],{"class":714}," secret",[449,24089,718],{"class":680},[449,24091,1343],{"class":680},[449,24093,24094],{"class":721}," TextEncoder",[449,24096,5236],{"class":684},[449,24098,24099],{"class":721},"encode",[449,24101,5566],{"class":684},[449,24103,24104,24107,24109,24111],{"class":451,"line":474},[449,24105,24106],{"class":684},"  process.env.",[449,24108,23957],{"class":714},[449,24110,23974],{"class":680},[449,24112,24113],{"class":691}," \"fallback-secret\"\n",[449,24115,24116],{"class":451,"line":480},[449,24117,740],{"class":684},[449,24119,24120],{"class":451,"line":486},[449,24121,700],{"emptyLinePlaceholder":130},[449,24123,24124,24126,24128,24130,24133,24135,24138,24140,24142,24144,24146,24148,24150,24153],{"class":451,"line":492},[449,24125,4629],{"class":680},[449,24127,5596],{"class":680},[449,24129,5432],{"class":680},[449,24131,24132],{"class":721}," hashPassword",[449,24134,725],{"class":684},[449,24136,24137],{"class":1491},"password",[449,24139,3070],{"class":680},[449,24141,6083],{"class":714},[449,24143,827],{"class":684},[449,24145,3070],{"class":680},[449,24147,5160],{"class":721},[449,24149,5546],{"class":684},[449,24151,24152],{"class":714},"string",[449,24154,24155],{"class":684},"> {\n",[449,24157,24158,24160,24163],{"class":451,"line":498},[449,24159,4727],{"class":680},[449,24161,24162],{"class":721}," hash",[449,24164,24165],{"class":684},"(password, {\n",[449,24167,24168,24171,24174],{"class":451,"line":133},[449,24169,24170],{"class":684},"    memoryCost: ",[449,24172,24173],{"class":714},"19456",[449,24175,779],{"class":684},[449,24177,24178,24181,24183],{"class":451,"line":509},[449,24179,24180],{"class":684},"    timeCost: ",[449,24182,4827],{"class":714},[449,24184,779],{"class":684},[449,24186,24187,24190,24193],{"class":451,"line":318},[449,24188,24189],{"class":684},"    outputLen: ",[449,24191,24192],{"class":714},"32",[449,24194,779],{"class":684},[449,24196,24197,24200,24202],{"class":451,"line":520},[449,24198,24199],{"class":684},"    parallelism: ",[449,24201,4799],{"class":714},[449,24203,779],{"class":684},[449,24205,24206],{"class":451,"line":526},[449,24207,13106],{"class":684},[449,24209,24210],{"class":451,"line":532},[449,24211,4604],{"class":684},[449,24213,24214],{"class":451,"line":2790},[449,24215,700],{"emptyLinePlaceholder":130},[449,24217,24218,24220,24222,24224,24227,24229,24232,24234,24236,24238,24240,24242,24244,24246,24248,24250,24252,24255],{"class":451,"line":3511},[449,24219,4629],{"class":680},[449,24221,5596],{"class":680},[449,24223,5432],{"class":680},[449,24225,24226],{"class":721}," verifyPassword",[449,24228,725],{"class":684},[449,24230,24231],{"class":1491},"hash",[449,24233,3070],{"class":680},[449,24235,6083],{"class":714},[449,24237,349],{"class":684},[449,24239,24137],{"class":1491},[449,24241,3070],{"class":680},[449,24243,6083],{"class":714},[449,24245,827],{"class":684},[449,24247,3070],{"class":680},[449,24249,5160],{"class":721},[449,24251,5546],{"class":684},[449,24253,24254],{"class":714},"boolean",[449,24256,24155],{"class":684},[449,24258,24259,24261,24264],{"class":451,"line":3526},[449,24260,4727],{"class":680},[449,24262,24263],{"class":721}," verify",[449,24265,24266],{"class":684},"(hash, password);\n",[449,24268,24269],{"class":451,"line":3531},[449,24270,4604],{"class":684},[449,24272,24273],{"class":451,"line":3536},[449,24274,700],{"emptyLinePlaceholder":130},[449,24276,24277,24279,24281,24283,24286,24288,24291,24293,24295,24297,24300,24302,24304,24306,24308,24310,24312,24314],{"class":451,"line":4009},[449,24278,4629],{"class":680},[449,24280,5596],{"class":680},[449,24282,5432],{"class":680},[449,24284,24285],{"class":721}," createToken",[449,24287,725],{"class":684},[449,24289,24290],{"class":1491},"userId",[449,24292,3070],{"class":680},[449,24294,6083],{"class":714},[449,24296,349],{"class":684},[449,24298,24299],{"class":1491},"email",[449,24301,3070],{"class":680},[449,24303,6083],{"class":714},[449,24305,827],{"class":684},[449,24307,3070],{"class":680},[449,24309,5160],{"class":721},[449,24311,5546],{"class":684},[449,24313,24152],{"class":714},[449,24315,24155],{"class":684},[449,24317,24318,24320,24322,24325],{"class":451,"line":4015},[449,24319,4727],{"class":680},[449,24321,1343],{"class":680},[449,24323,24324],{"class":721}," SignJWT",[449,24326,24327],{"class":684},"({ userId, email })\n",[449,24329,24330,24332,24335,24338,24341],{"class":451,"line":4021},[449,24331,10963],{"class":684},[449,24333,24334],{"class":721},"setProtectedHeader",[449,24336,24337],{"class":684},"({ alg: ",[449,24339,24340],{"class":691},"\"HS256\"",[449,24342,10958],{"class":684},[449,24344,24345,24347,24350],{"class":451,"line":4027},[449,24346,10963],{"class":684},[449,24348,24349],{"class":721},"setIssuedAt",[449,24351,11220],{"class":684},[449,24353,24354,24356,24359,24361,24364],{"class":451,"line":4032},[449,24355,10963],{"class":684},[449,24357,24358],{"class":721},"setExpirationTime",[449,24360,725],{"class":684},[449,24362,24363],{"class":691},"\"7d\"",[449,24365,11211],{"class":684},[449,24367,24368,24370,24373],{"class":451,"line":4038},[449,24369,10963],{"class":684},[449,24371,24372],{"class":721},"sign",[449,24374,24375],{"class":684},"(secret);\n",[449,24377,24378],{"class":451,"line":4044},[449,24379,4604],{"class":684},[449,24381,24382],{"class":451,"line":4049},[449,24383,700],{"emptyLinePlaceholder":130},[449,24385,24386,24388,24390,24392,24395,24397,24400,24402,24404,24406,24408,24410,24413,24415,24417,24419,24422,24424,24426,24428,24430,24433,24436],{"class":451,"line":4054},[449,24387,4629],{"class":680},[449,24389,5596],{"class":680},[449,24391,5432],{"class":680},[449,24393,24394],{"class":721}," verifyToken",[449,24396,725],{"class":684},[449,24398,24399],{"class":1491},"token",[449,24401,3070],{"class":680},[449,24403,6083],{"class":714},[449,24405,827],{"class":684},[449,24407,3070],{"class":680},[449,24409,5160],{"class":721},[449,24411,24412],{"class":684},"\u003C{ ",[449,24414,24290],{"class":1491},[449,24416,3070],{"class":680},[449,24418,6083],{"class":714},[449,24420,24421],{"class":684},"; ",[449,24423,24299],{"class":1491},[449,24425,3070],{"class":680},[449,24427,6083],{"class":714},[449,24429,21541],{"class":684},[449,24431,24432],{"class":680},"|",[449,24434,24435],{"class":714}," null",[449,24437,24155],{"class":684},[449,24439,24440,24443],{"class":451,"line":4059},[449,24441,24442],{"class":680},"  try",[449,24444,1501],{"class":684},[449,24446,24447,24449,24451,24454,24456,24458,24460,24463],{"class":451,"line":4065},[449,24448,12761],{"class":680},[449,24450,14124],{"class":684},[449,24452,24453],{"class":714},"payload",[449,24455,21541],{"class":684},[449,24457,5015],{"class":680},[449,24459,1654],{"class":680},[449,24461,24462],{"class":721}," jwtVerify",[449,24464,24465],{"class":684},"(token, secret);\n",[449,24467,24468,24470,24473,24475,24477,24479,24481,24483,24485,24487,24489,24491],{"class":451,"line":4070},[449,24469,1506],{"class":680},[449,24471,24472],{"class":684}," payload ",[449,24474,5065],{"class":680},[449,24476,14124],{"class":684},[449,24478,24290],{"class":1491},[449,24480,3070],{"class":680},[449,24482,6083],{"class":714},[449,24484,24421],{"class":684},[449,24486,24299],{"class":1491},[449,24488,3070],{"class":680},[449,24490,6083],{"class":714},[449,24492,1520],{"class":684},[449,24494,24495,24498,24500],{"class":451,"line":4075},[449,24496,24497],{"class":684},"  } ",[449,24499,5747],{"class":680},[449,24501,1501],{"class":684},[449,24503,24504,24506,24508],{"class":451,"line":4081},[449,24505,1506],{"class":680},[449,24507,24435],{"class":714},[449,24509,695],{"class":684},[449,24511,24512],{"class":451,"line":4086},[449,24513,4599],{"class":684},[449,24515,24516],{"class":451,"line":4092},[449,24517,4604],{"class":684},[427,24519,24521],{"icon":11395,"title":24520},"Password Security",[21,24522,24523],{},"We're using Argon2id—the winner of the Password Hashing Competition. It's memory-hard and resistant to GPU attacks.",[73,24525],{},[32,24527,24529],{"id":24528},"step-4-create-users-database","Step 4: Create Users Database",[21,24531,24532],{},"For simplicity, we'll use a file-based approach (replace with Drizzle\u002FPrisma in production).",[21,24534,4698,24535,3070],{},[40,24536,24537],{},"server\u002Futils\u002Fdb.ts",[48,24539,24541],{"className":670,"code":24540,"filename":24537,"language":673,"meta":56,"style":56},"import { db } from \".\u002Fdb\";\nimport { users } from \".\u002Fschema\";\nimport { eq } from \"drizzle-orm\";\nimport { hashPassword, verifyPassword } from \".\u002Fauth\";\n\ninterface CreateUserInput {\n  email: string;\n  password: string;\n  name: string;\n}\n\nexport async function createUser(input: CreateUserInput) {\n  const hashedPassword = await hashPassword(input.password);\n  \n  await db.insert(users).values({\n    id: crypto.randomUUID(),\n    email: input.email,\n    password: hashedPassword,\n    name: input.name,\n  });\n}\n\nexport async function findUserByEmail(email: string) {\n  return db.select().from(users).where(eq(users.email, email)).get();\n}\n\nexport async function validateUserCredentials(email: string, password: string) {\n  const user = await findUserByEmail(email);\n  \n  if (!user) {\n    return null;\n  }\n  \n  const isValid = await verifyPassword(user.password, password);\n  \n  if (!isValid) {\n    return null;\n  }\n  \n  return { id: user.id, email: user.email, name: user.name };\n}\n",[40,24542,24543,24556,24569,24581,24595,24599,24608,24619,24630,24640,24644,24648,24670,24686,24690,24705,24713,24718,24723,24728,24732,24736,24740,24761,24788,24792,24796,24825,24841,24845,24856,24864,24868,24872,24888,24892,24903,24911,24915,24919,24926],{"__ignoreMap":56},[449,24544,24545,24547,24549,24551,24554],{"class":451,"line":452},[449,24546,681],{"class":680},[449,24548,11728],{"class":684},[449,24550,688],{"class":680},[449,24552,24553],{"class":691}," \".\u002Fdb\"",[449,24555,695],{"class":684},[449,24557,24558,24560,24563,24565,24567],{"class":451,"line":114},[449,24559,681],{"class":680},[449,24561,24562],{"class":684}," { users } ",[449,24564,688],{"class":680},[449,24566,11464],{"class":691},[449,24568,695],{"class":684},[449,24570,24571,24573,24575,24577,24579],{"class":451,"line":111},[449,24572,681],{"class":680},[449,24574,12534],{"class":684},[449,24576,688],{"class":680},[449,24578,10783],{"class":691},[449,24580,695],{"class":684},[449,24582,24583,24585,24588,24590,24593],{"class":451,"line":468},[449,24584,681],{"class":680},[449,24586,24587],{"class":684}," { hashPassword, verifyPassword } ",[449,24589,688],{"class":680},[449,24591,24592],{"class":691}," \".\u002Fauth\"",[449,24594,695],{"class":684},[449,24596,24597],{"class":451,"line":474},[449,24598,700],{"emptyLinePlaceholder":130},[449,24600,24601,24603,24606],{"class":451,"line":480},[449,24602,4961],{"class":680},[449,24604,24605],{"class":721}," CreateUserInput",[449,24607,1501],{"class":684},[449,24609,24610,24613,24615,24617],{"class":451,"line":486},[449,24611,24612],{"class":1491},"  email",[449,24614,3070],{"class":680},[449,24616,6083],{"class":714},[449,24618,695],{"class":684},[449,24620,24621,24624,24626,24628],{"class":451,"line":492},[449,24622,24623],{"class":1491},"  password",[449,24625,3070],{"class":680},[449,24627,6083],{"class":714},[449,24629,695],{"class":684},[449,24631,24632,24634,24636,24638],{"class":451,"line":498},[449,24633,6078],{"class":1491},[449,24635,3070],{"class":680},[449,24637,6083],{"class":714},[449,24639,695],{"class":684},[449,24641,24642],{"class":451,"line":133},[449,24643,4604],{"class":684},[449,24645,24646],{"class":451,"line":509},[449,24647,700],{"emptyLinePlaceholder":130},[449,24649,24650,24652,24654,24656,24659,24661,24664,24666,24668],{"class":451,"line":318},[449,24651,4629],{"class":680},[449,24653,5596],{"class":680},[449,24655,5432],{"class":680},[449,24657,24658],{"class":721}," createUser",[449,24660,725],{"class":684},[449,24662,24663],{"class":1491},"input",[449,24665,3070],{"class":680},[449,24667,24605],{"class":721},[449,24669,3417],{"class":684},[449,24671,24672,24674,24677,24679,24681,24683],{"class":451,"line":520},[449,24673,5049],{"class":680},[449,24675,24676],{"class":714}," hashedPassword",[449,24678,718],{"class":680},[449,24680,1654],{"class":680},[449,24682,24132],{"class":721},[449,24684,24685],{"class":684},"(input.password);\n",[449,24687,24688],{"class":451,"line":526},[449,24689,4661],{"class":684},[449,24691,24692,24694,24696,24698,24701,24703],{"class":451,"line":532},[449,24693,2099],{"class":680},[449,24695,12042],{"class":684},[449,24697,12089],{"class":721},[449,24699,24700],{"class":684},"(users).",[449,24702,12094],{"class":721},[449,24704,1391],{"class":684},[449,24706,24707,24709,24711],{"class":451,"line":2790},[449,24708,5202],{"class":684},[449,24710,5205],{"class":721},[449,24712,1624],{"class":684},[449,24714,24715],{"class":451,"line":3511},[449,24716,24717],{"class":684},"    email: input.email,\n",[449,24719,24720],{"class":451,"line":3526},[449,24721,24722],{"class":684},"    password: hashedPassword,\n",[449,24724,24725],{"class":451,"line":3531},[449,24726,24727],{"class":684},"    name: input.name,\n",[449,24729,24730],{"class":451,"line":3536},[449,24731,13106],{"class":684},[449,24733,24734],{"class":451,"line":4009},[449,24735,4604],{"class":684},[449,24737,24738],{"class":451,"line":4015},[449,24739,700],{"emptyLinePlaceholder":130},[449,24741,24742,24744,24746,24748,24751,24753,24755,24757,24759],{"class":451,"line":4021},[449,24743,4629],{"class":680},[449,24745,5596],{"class":680},[449,24747,5432],{"class":680},[449,24749,24750],{"class":721}," findUserByEmail",[449,24752,725],{"class":684},[449,24754,24299],{"class":1491},[449,24756,3070],{"class":680},[449,24758,6083],{"class":714},[449,24760,3417],{"class":684},[449,24762,24763,24765,24767,24769,24771,24773,24775,24777,24779,24781,24784,24786],{"class":451,"line":4027},[449,24764,4727],{"class":680},[449,24766,12042],{"class":684},[449,24768,12228],{"class":721},[449,24770,5236],{"class":684},[449,24772,688],{"class":721},[449,24774,24700],{"class":684},[449,24776,12237],{"class":721},[449,24778,725],{"class":684},[449,24780,12800],{"class":721},[449,24782,24783],{"class":684},"(users.email, email)).",[449,24785,12420],{"class":721},[449,24787,1322],{"class":684},[449,24789,24790],{"class":451,"line":4032},[449,24791,4604],{"class":684},[449,24793,24794],{"class":451,"line":4038},[449,24795,700],{"emptyLinePlaceholder":130},[449,24797,24798,24800,24802,24804,24807,24809,24811,24813,24815,24817,24819,24821,24823],{"class":451,"line":4044},[449,24799,4629],{"class":680},[449,24801,5596],{"class":680},[449,24803,5432],{"class":680},[449,24805,24806],{"class":721}," validateUserCredentials",[449,24808,725],{"class":684},[449,24810,24299],{"class":1491},[449,24812,3070],{"class":680},[449,24814,6083],{"class":714},[449,24816,349],{"class":684},[449,24818,24137],{"class":1491},[449,24820,3070],{"class":680},[449,24822,6083],{"class":714},[449,24824,3417],{"class":684},[449,24826,24827,24829,24832,24834,24836,24838],{"class":451,"line":4049},[449,24828,5049],{"class":680},[449,24830,24831],{"class":714}," user",[449,24833,718],{"class":680},[449,24835,1654],{"class":680},[449,24837,24750],{"class":721},[449,24839,24840],{"class":684},"(email);\n",[449,24842,24843],{"class":451,"line":4054},[449,24844,4661],{"class":684},[449,24846,24847,24849,24851,24853],{"class":451,"line":4059},[449,24848,5079],{"class":680},[449,24850,1913],{"class":684},[449,24852,5084],{"class":680},[449,24854,24855],{"class":684},"user) {\n",[449,24857,24858,24860,24862],{"class":451,"line":4065},[449,24859,1506],{"class":680},[449,24861,24435],{"class":714},[449,24863,695],{"class":684},[449,24865,24866],{"class":451,"line":4070},[449,24867,4599],{"class":684},[449,24869,24870],{"class":451,"line":4075},[449,24871,4661],{"class":684},[449,24873,24874,24876,24879,24881,24883,24885],{"class":451,"line":4081},[449,24875,5049],{"class":680},[449,24877,24878],{"class":714}," isValid",[449,24880,718],{"class":680},[449,24882,1654],{"class":680},[449,24884,24226],{"class":721},[449,24886,24887],{"class":684},"(user.password, password);\n",[449,24889,24890],{"class":451,"line":4086},[449,24891,4661],{"class":684},[449,24893,24894,24896,24898,24900],{"class":451,"line":4092},[449,24895,5079],{"class":680},[449,24897,1913],{"class":684},[449,24899,5084],{"class":680},[449,24901,24902],{"class":684},"isValid) {\n",[449,24904,24905,24907,24909],{"class":451,"line":4098},[449,24906,1506],{"class":680},[449,24908,24435],{"class":714},[449,24910,695],{"class":684},[449,24912,24913],{"class":451,"line":4104},[449,24914,4599],{"class":684},[449,24916,24917],{"class":451,"line":4110},[449,24918,4661],{"class":684},[449,24920,24921,24923],{"class":451,"line":5784},[449,24922,4727],{"class":680},[449,24924,24925],{"class":684}," { id: user.id, email: user.email, name: user.name };\n",[449,24927,24928],{"class":451,"line":5790},[449,24929,4604],{"class":684},[21,24931,24932,24933,3070],{},"Create the users schema in ",[40,24934,10752],{},[48,24936,24938],{"className":670,"code":24937,"filename":10752,"language":673,"meta":56,"style":56},"import { sqliteTable, text, integer } from \"drizzle-orm\u002Fsqlite-core\";\n\nexport const users = sqliteTable(\"users\", {\n  id: text(\"id\").primaryKey(),\n  email: text(\"email\").notNull().unique(),\n  password: text(\"password\").notNull(),\n  name: text(\"name\").notNull(),\n  createdAt: integer(\"created_at\", { mode: \"timestamp\" }).notNull(),\n});\n\nexport type User = typeof users.$inferSelect;\n",[40,24939,24940,24953,24957,24977,24993,25016,25034,25050,25070,25074,25078],{"__ignoreMap":56},[449,24941,24942,24944,24947,24949,24951],{"class":451,"line":452},[449,24943,681],{"class":680},[449,24945,24946],{"class":684}," { sqliteTable, text, integer } ",[449,24948,688],{"class":680},[449,24950,10769],{"class":691},[449,24952,695],{"class":684},[449,24954,24955],{"class":451,"line":114},[449,24956,700],{"emptyLinePlaceholder":130},[449,24958,24959,24961,24963,24966,24968,24970,24972,24975],{"class":451,"line":111},[449,24960,4629],{"class":680},[449,24962,10801],{"class":680},[449,24964,24965],{"class":714}," users",[449,24967,718],{"class":680},[449,24969,10809],{"class":721},[449,24971,725],{"class":684},[449,24973,24974],{"class":691},"\"users\"",[449,24976,768],{"class":684},[449,24978,24979,24981,24983,24985,24987,24989,24991],{"class":451,"line":468},[449,24980,10821],{"class":684},[449,24982,53],{"class":721},[449,24984,725],{"class":684},[449,24986,10828],{"class":691},[449,24988,374],{"class":684},[449,24990,10833],{"class":721},[449,24992,1624],{"class":684},[449,24994,24995,24998,25000,25002,25005,25007,25009,25011,25014],{"class":451,"line":474},[449,24996,24997],{"class":684},"  email: ",[449,24999,53],{"class":721},[449,25001,725],{"class":684},[449,25003,25004],{"class":691},"\"email\"",[449,25006,374],{"class":684},[449,25008,10851],{"class":721},[449,25010,5236],{"class":684},[449,25012,25013],{"class":721},"unique",[449,25015,1624],{"class":684},[449,25017,25018,25021,25023,25025,25028,25030,25032],{"class":451,"line":480},[449,25019,25020],{"class":684},"  password: ",[449,25022,53],{"class":721},[449,25024,725],{"class":684},[449,25026,25027],{"class":691},"\"password\"",[449,25029,374],{"class":684},[449,25031,10851],{"class":721},[449,25033,1624],{"class":684},[449,25035,25036,25038,25040,25042,25044,25046,25048],{"class":451,"line":486},[449,25037,1396],{"class":684},[449,25039,53],{"class":721},[449,25041,725],{"class":684},[449,25043,10846],{"class":691},[449,25045,374],{"class":684},[449,25047,10851],{"class":721},[449,25049,1624],{"class":684},[449,25051,25052,25054,25056,25058,25060,25062,25064,25066,25068],{"class":451,"line":492},[449,25053,10943],{"class":684},[449,25055,10915],{"class":721},[449,25057,725],{"class":684},[449,25059,10950],{"class":691},[449,25061,10923],{"class":684},[449,25063,10955],{"class":691},[449,25065,10929],{"class":684},[449,25067,10851],{"class":721},[449,25069,1624],{"class":684},[449,25071,25072],{"class":451,"line":498},[449,25073,784],{"class":684},[449,25075,25076],{"class":451,"line":133},[449,25077,700],{"emptyLinePlaceholder":130},[449,25079,25080,25082,25084,25087,25089,25091],{"class":451,"line":509},[449,25081,4629],{"class":680},[449,25083,11333],{"class":680},[449,25085,25086],{"class":721}," User",[449,25088,718],{"class":680},[449,25090,11341],{"class":680},[449,25092,25093],{"class":684}," users.$inferSelect;\n",[73,25095],{},[32,25097,25099],{"id":25098},"step-5-create-authentication-api-routes","Step 5: Create Authentication API Routes",[2808,25101,25102],{"id":1356},"Register",[21,25104,4698,25105,3070],{},[40,25106,25107],{},"server\u002Fapi\u002Fauth\u002Fregister.post.ts",[48,25109,25111],{"className":670,"code":25110,"filename":25107,"language":673,"meta":56,"style":56},"import { createUser, findUserByEmail } from \"..\u002F..\u002Futils\u002Fdb\";\nimport { createToken } from \"..\u002F..\u002Futils\u002Fauth\";\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event);\n  \n  \u002F\u002F Validate input\n  if (!body.email || !body.password || !body.name) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Email, password, and name are required\",\n    });\n  }\n  \n  \u002F\u002F Check if user exists\n  const existing = await findUserByEmail(body.email);\n  \n  if (existing) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"User already exists\",\n    });\n  }\n  \n  \u002F\u002F Create user\n  await createUser({\n    email: body.email,\n    password: body.password,\n    name: body.name,\n  });\n  \n  \u002F\u002F Generate token\n  const token = await createToken(body.email, body.email);\n  \n  setCookie(event, \"auth_token\", token, {\n    httpOnly: true,\n    secure: process.env.NODE_ENV === \"production\",\n    sameSite: \"lax\",\n    maxAge: 60 * 60 * 24 * 7, \u002F\u002F 7 days\n  });\n  \n  return { success: true, email: body.email };\n});\n",[40,25112,25113,25127,25141,25145,25167,25181,25185,25189,25214,25222,25230,25239,25243,25247,25251,25256,25272,25276,25283,25291,25299,25308,25312,25316,25320,25325,25333,25338,25343,25348,25352,25356,25361,25377,25381,25394,25403,25418,25428,25456,25460,25464,25476],{"__ignoreMap":56},[449,25114,25115,25117,25120,25122,25125],{"class":451,"line":452},[449,25116,681],{"class":680},[449,25118,25119],{"class":684}," { createUser, findUserByEmail } ",[449,25121,688],{"class":680},[449,25123,25124],{"class":691}," \"..\u002F..\u002Futils\u002Fdb\"",[449,25126,695],{"class":684},[449,25128,25129,25131,25134,25136,25139],{"class":451,"line":114},[449,25130,681],{"class":680},[449,25132,25133],{"class":684}," { createToken } ",[449,25135,688],{"class":680},[449,25137,25138],{"class":691}," \"..\u002F..\u002Futils\u002Fauth\"",[449,25140,695],{"class":684},[449,25142,25143],{"class":451,"line":111},[449,25144,700],{"emptyLinePlaceholder":130},[449,25146,25147,25149,25151,25153,25155,25157,25159,25161,25163,25165],{"class":451,"line":468},[449,25148,4629],{"class":680},[449,25150,4632],{"class":680},[449,25152,4715],{"class":721},[449,25154,725],{"class":684},[449,25156,1485],{"class":680},[449,25158,1913],{"class":684},[449,25160,2991],{"class":1491},[449,25162,1758],{"class":684},[449,25164,1498],{"class":680},[449,25166,1501],{"class":684},[449,25168,25169,25171,25173,25175,25177,25179],{"class":451,"line":474},[449,25170,5049],{"class":680},[449,25172,5052],{"class":714},[449,25174,718],{"class":680},[449,25176,1654],{"class":680},[449,25178,5059],{"class":721},[449,25180,12619],{"class":684},[449,25182,25183],{"class":451,"line":480},[449,25184,4661],{"class":684},[449,25186,25187],{"class":451,"line":486},[449,25188,12628],{"class":705},[449,25190,25191,25193,25195,25197,25200,25202,25204,25207,25209,25211],{"class":451,"line":492},[449,25192,5079],{"class":680},[449,25194,1913],{"class":684},[449,25196,5084],{"class":680},[449,25198,25199],{"class":684},"body.email ",[449,25201,5090],{"class":680},[449,25203,12644],{"class":680},[449,25205,25206],{"class":684},"body.password ",[449,25208,5090],{"class":680},[449,25210,12644],{"class":680},[449,25212,25213],{"class":684},"body.name) {\n",[449,25215,25216,25218,25220],{"class":451,"line":498},[449,25217,5109],{"class":680},[449,25219,5112],{"class":721},[449,25221,1391],{"class":684},[449,25223,25224,25226,25228],{"class":451,"line":133},[449,25225,5119],{"class":684},[449,25227,5122],{"class":714},[449,25229,779],{"class":684},[449,25231,25232,25234,25237],{"class":451,"line":509},[449,25233,5129],{"class":684},[449,25235,25236],{"class":691},"\"Email, password, and name are required\"",[449,25238,779],{"class":684},[449,25240,25241],{"class":451,"line":318},[449,25242,3484],{"class":684},[449,25244,25245],{"class":451,"line":520},[449,25246,4599],{"class":684},[449,25248,25249],{"class":451,"line":526},[449,25250,4661],{"class":684},[449,25252,25253],{"class":451,"line":532},[449,25254,25255],{"class":705},"  \u002F\u002F Check if user exists\n",[449,25257,25258,25260,25263,25265,25267,25269],{"class":451,"line":2790},[449,25259,5049],{"class":680},[449,25261,25262],{"class":714}," existing",[449,25264,718],{"class":680},[449,25266,1654],{"class":680},[449,25268,24750],{"class":721},[449,25270,25271],{"class":684},"(body.email);\n",[449,25273,25274],{"class":451,"line":3511},[449,25275,4661],{"class":684},[449,25277,25278,25280],{"class":451,"line":3526},[449,25279,5079],{"class":680},[449,25281,25282],{"class":684}," (existing) {\n",[449,25284,25285,25287,25289],{"class":451,"line":3531},[449,25286,5109],{"class":680},[449,25288,5112],{"class":721},[449,25290,1391],{"class":684},[449,25292,25293,25295,25297],{"class":451,"line":3536},[449,25294,5119],{"class":684},[449,25296,5122],{"class":714},[449,25298,779],{"class":684},[449,25300,25301,25303,25306],{"class":451,"line":4009},[449,25302,5129],{"class":684},[449,25304,25305],{"class":691},"\"User already exists\"",[449,25307,779],{"class":684},[449,25309,25310],{"class":451,"line":4015},[449,25311,3484],{"class":684},[449,25313,25314],{"class":451,"line":4021},[449,25315,4599],{"class":684},[449,25317,25318],{"class":451,"line":4027},[449,25319,4661],{"class":684},[449,25321,25322],{"class":451,"line":4032},[449,25323,25324],{"class":705},"  \u002F\u002F Create user\n",[449,25326,25327,25329,25331],{"class":451,"line":4038},[449,25328,2099],{"class":680},[449,25330,24658],{"class":721},[449,25332,1391],{"class":684},[449,25334,25335],{"class":451,"line":4044},[449,25336,25337],{"class":684},"    email: body.email,\n",[449,25339,25340],{"class":451,"line":4049},[449,25341,25342],{"class":684},"    password: body.password,\n",[449,25344,25345],{"class":451,"line":4054},[449,25346,25347],{"class":684},"    name: body.name,\n",[449,25349,25350],{"class":451,"line":4059},[449,25351,13106],{"class":684},[449,25353,25354],{"class":451,"line":4065},[449,25355,4661],{"class":684},[449,25357,25358],{"class":451,"line":4070},[449,25359,25360],{"class":705},"  \u002F\u002F Generate token\n",[449,25362,25363,25365,25368,25370,25372,25374],{"class":451,"line":4075},[449,25364,5049],{"class":680},[449,25366,25367],{"class":714}," token",[449,25369,718],{"class":680},[449,25371,1654],{"class":680},[449,25373,24285],{"class":721},[449,25375,25376],{"class":684},"(body.email, body.email);\n",[449,25378,25379],{"class":451,"line":4081},[449,25380,4661],{"class":684},[449,25382,25383,25386,25388,25391],{"class":451,"line":4086},[449,25384,25385],{"class":721},"  setCookie",[449,25387,12330],{"class":684},[449,25389,25390],{"class":691},"\"auth_token\"",[449,25392,25393],{"class":684},", token, {\n",[449,25395,25396,25399,25401],{"class":451,"line":4092},[449,25397,25398],{"class":684},"    httpOnly: ",[449,25400,2169],{"class":714},[449,25402,779],{"class":684},[449,25404,25405,25408,25411,25413,25416],{"class":451,"line":4098},[449,25406,25407],{"class":684},"    secure: process.env.",[449,25409,25410],{"class":714},"NODE_ENV",[449,25412,5099],{"class":680},[449,25414,25415],{"class":691}," \"production\"",[449,25417,779],{"class":684},[449,25419,25420,25423,25426],{"class":451,"line":4104},[449,25421,25422],{"class":684},"    sameSite: ",[449,25424,25425],{"class":691},"\"lax\"",[449,25427,779],{"class":684},[449,25429,25430,25433,25436,25438,25441,25443,25446,25448,25451,25453],{"class":451,"line":4110},[449,25431,25432],{"class":684},"    maxAge: ",[449,25434,25435],{"class":714},"60",[449,25437,11453],{"class":680},[449,25439,25440],{"class":714}," 60",[449,25442,11453],{"class":680},[449,25444,25445],{"class":714}," 24",[449,25447,11453],{"class":680},[449,25449,25450],{"class":714}," 7",[449,25452,349],{"class":684},[449,25454,25455],{"class":705},"\u002F\u002F 7 days\n",[449,25457,25458],{"class":451,"line":5784},[449,25459,13106],{"class":684},[449,25461,25462],{"class":451,"line":5790},[449,25463,4661],{"class":684},[449,25465,25466,25468,25471,25473],{"class":451,"line":5796},[449,25467,4727],{"class":680},[449,25469,25470],{"class":684}," { success: ",[449,25472,2169],{"class":714},[449,25474,25475],{"class":684},", email: body.email };\n",[449,25477,25478],{"class":451,"line":5801},[449,25479,784],{"class":684},[2808,25481,25483],{"id":25482},"login","Login",[21,25485,4698,25486,3070],{},[40,25487,25488],{},"server\u002Fapi\u002Fauth\u002Flogin.post.ts",[48,25490,25492],{"className":670,"code":25491,"filename":25488,"language":673,"meta":56,"style":56},"import { validateUserCredentials } from \"..\u002F..\u002Futils\u002Fdb\";\nimport { createToken } from \"..\u002F..\u002Futils\u002Fauth\";\n\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event);\n  \n  if (!body.email || !body.password) {\n    throw createError({\n      statusCode: 400,\n      statusMessage: \"Email and password are required\",\n    });\n  }\n  \n  const user = await validateUserCredentials(body.email, body.password);\n  \n  if (!user) {\n    throw createError({\n      statusCode: 401,\n      statusMessage: \"Invalid credentials\",\n    });\n  }\n  \n  const token = await createToken(user.id, user.email);\n  \n  setCookie(event, \"auth_token\", token, {\n    httpOnly: true,\n    secure: process.env.NODE_ENV === \"production\",\n    sameSite: \"lax\",\n    maxAge: 60 * 60 * 24 * 7, \u002F\u002F 7 days\n  });\n  \n  return { success: true, user: { email: user.email, name: user.name } };\n});\n",[40,25493,25494,25507,25519,25523,25545,25559,25563,25580,25588,25596,25605,25609,25613,25617,25632,25636,25646,25654,25662,25671,25675,25679,25683,25698,25702,25712,25720,25732,25740,25762,25766,25770,25781],{"__ignoreMap":56},[449,25495,25496,25498,25501,25503,25505],{"class":451,"line":452},[449,25497,681],{"class":680},[449,25499,25500],{"class":684}," { validateUserCredentials } ",[449,25502,688],{"class":680},[449,25504,25124],{"class":691},[449,25506,695],{"class":684},[449,25508,25509,25511,25513,25515,25517],{"class":451,"line":114},[449,25510,681],{"class":680},[449,25512,25133],{"class":684},[449,25514,688],{"class":680},[449,25516,25138],{"class":691},[449,25518,695],{"class":684},[449,25520,25521],{"class":451,"line":111},[449,25522,700],{"emptyLinePlaceholder":130},[449,25524,25525,25527,25529,25531,25533,25535,25537,25539,25541,25543],{"class":451,"line":468},[449,25526,4629],{"class":680},[449,25528,4632],{"class":680},[449,25530,4715],{"class":721},[449,25532,725],{"class":684},[449,25534,1485],{"class":680},[449,25536,1913],{"class":684},[449,25538,2991],{"class":1491},[449,25540,1758],{"class":684},[449,25542,1498],{"class":680},[449,25544,1501],{"class":684},[449,25546,25547,25549,25551,25553,25555,25557],{"class":451,"line":474},[449,25548,5049],{"class":680},[449,25550,5052],{"class":714},[449,25552,718],{"class":680},[449,25554,1654],{"class":680},[449,25556,5059],{"class":721},[449,25558,12619],{"class":684},[449,25560,25561],{"class":451,"line":480},[449,25562,4661],{"class":684},[449,25564,25565,25567,25569,25571,25573,25575,25577],{"class":451,"line":486},[449,25566,5079],{"class":680},[449,25568,1913],{"class":684},[449,25570,5084],{"class":680},[449,25572,25199],{"class":684},[449,25574,5090],{"class":680},[449,25576,12644],{"class":680},[449,25578,25579],{"class":684},"body.password) {\n",[449,25581,25582,25584,25586],{"class":451,"line":492},[449,25583,5109],{"class":680},[449,25585,5112],{"class":721},[449,25587,1391],{"class":684},[449,25589,25590,25592,25594],{"class":451,"line":498},[449,25591,5119],{"class":684},[449,25593,5122],{"class":714},[449,25595,779],{"class":684},[449,25597,25598,25600,25603],{"class":451,"line":133},[449,25599,5129],{"class":684},[449,25601,25602],{"class":691},"\"Email and password are required\"",[449,25604,779],{"class":684},[449,25606,25607],{"class":451,"line":509},[449,25608,3484],{"class":684},[449,25610,25611],{"class":451,"line":318},[449,25612,4599],{"class":684},[449,25614,25615],{"class":451,"line":520},[449,25616,4661],{"class":684},[449,25618,25619,25621,25623,25625,25627,25629],{"class":451,"line":526},[449,25620,5049],{"class":680},[449,25622,24831],{"class":714},[449,25624,718],{"class":680},[449,25626,1654],{"class":680},[449,25628,24806],{"class":721},[449,25630,25631],{"class":684},"(body.email, body.password);\n",[449,25633,25634],{"class":451,"line":532},[449,25635,4661],{"class":684},[449,25637,25638,25640,25642,25644],{"class":451,"line":2790},[449,25639,5079],{"class":680},[449,25641,1913],{"class":684},[449,25643,5084],{"class":680},[449,25645,24855],{"class":684},[449,25647,25648,25650,25652],{"class":451,"line":3511},[449,25649,5109],{"class":680},[449,25651,5112],{"class":721},[449,25653,1391],{"class":684},[449,25655,25656,25658,25660],{"class":451,"line":3526},[449,25657,5119],{"class":684},[449,25659,16890],{"class":714},[449,25661,779],{"class":684},[449,25663,25664,25666,25669],{"class":451,"line":3531},[449,25665,5129],{"class":684},[449,25667,25668],{"class":691},"\"Invalid credentials\"",[449,25670,779],{"class":684},[449,25672,25673],{"class":451,"line":3536},[449,25674,3484],{"class":684},[449,25676,25677],{"class":451,"line":4009},[449,25678,4599],{"class":684},[449,25680,25681],{"class":451,"line":4015},[449,25682,4661],{"class":684},[449,25684,25685,25687,25689,25691,25693,25695],{"class":451,"line":4021},[449,25686,5049],{"class":680},[449,25688,25367],{"class":714},[449,25690,718],{"class":680},[449,25692,1654],{"class":680},[449,25694,24285],{"class":721},[449,25696,25697],{"class":684},"(user.id, user.email);\n",[449,25699,25700],{"class":451,"line":4027},[449,25701,4661],{"class":684},[449,25703,25704,25706,25708,25710],{"class":451,"line":4032},[449,25705,25385],{"class":721},[449,25707,12330],{"class":684},[449,25709,25390],{"class":691},[449,25711,25393],{"class":684},[449,25713,25714,25716,25718],{"class":451,"line":4038},[449,25715,25398],{"class":684},[449,25717,2169],{"class":714},[449,25719,779],{"class":684},[449,25721,25722,25724,25726,25728,25730],{"class":451,"line":4044},[449,25723,25407],{"class":684},[449,25725,25410],{"class":714},[449,25727,5099],{"class":680},[449,25729,25415],{"class":691},[449,25731,779],{"class":684},[449,25733,25734,25736,25738],{"class":451,"line":4049},[449,25735,25422],{"class":684},[449,25737,25425],{"class":691},[449,25739,779],{"class":684},[449,25741,25742,25744,25746,25748,25750,25752,25754,25756,25758,25760],{"class":451,"line":4054},[449,25743,25432],{"class":684},[449,25745,25435],{"class":714},[449,25747,11453],{"class":680},[449,25749,25440],{"class":714},[449,25751,11453],{"class":680},[449,25753,25445],{"class":714},[449,25755,11453],{"class":680},[449,25757,25450],{"class":714},[449,25759,349],{"class":684},[449,25761,25455],{"class":705},[449,25763,25764],{"class":451,"line":4059},[449,25765,13106],{"class":684},[449,25767,25768],{"class":451,"line":4065},[449,25769,4661],{"class":684},[449,25771,25772,25774,25776,25778],{"class":451,"line":4070},[449,25773,4727],{"class":680},[449,25775,25470],{"class":684},[449,25777,2169],{"class":714},[449,25779,25780],{"class":684},", user: { email: user.email, name: user.name } };\n",[449,25782,25783],{"class":451,"line":4075},[449,25784,784],{"class":684},[2808,25786,25788],{"id":25787},"logout","Logout",[21,25790,4698,25791,3070],{},[40,25792,25793],{},"server\u002Fapi\u002Fauth\u002Flogout.post.ts",[48,25795,25797],{"className":670,"code":25796,"filename":25793,"language":673,"meta":56,"style":56},"export default defineEventHandler(async (event) => {\n  deleteCookie(event, \"auth_token\");\n  return { success: true };\n});\n",[40,25798,25799,25821,25832,25842],{"__ignoreMap":56},[449,25800,25801,25803,25805,25807,25809,25811,25813,25815,25817,25819],{"class":451,"line":452},[449,25802,4629],{"class":680},[449,25804,4632],{"class":680},[449,25806,4715],{"class":721},[449,25808,725],{"class":684},[449,25810,1485],{"class":680},[449,25812,1913],{"class":684},[449,25814,2991],{"class":1491},[449,25816,1758],{"class":684},[449,25818,1498],{"class":680},[449,25820,1501],{"class":684},[449,25822,25823,25826,25828,25830],{"class":451,"line":114},[449,25824,25825],{"class":721},"  deleteCookie",[449,25827,12330],{"class":684},[449,25829,25390],{"class":691},[449,25831,740],{"class":684},[449,25833,25834,25836,25838,25840],{"class":451,"line":111},[449,25835,4727],{"class":680},[449,25837,25470],{"class":684},[449,25839,2169],{"class":714},[449,25841,1520],{"class":684},[449,25843,25844],{"class":451,"line":468},[449,25845,784],{"class":684},[2808,25847,25849],{"id":25848},"get-current-user","Get Current User",[21,25851,4698,25852,3070],{},[40,25853,25854],{},"server\u002Fapi\u002Fauth\u002Fme.get.ts",[48,25856,25858],{"className":670,"code":25857,"filename":25854,"language":673,"meta":56,"style":56},"import { verifyToken } from \"..\u002F..\u002Futils\u002Fauth\";\nimport { findUserByEmail } from \"..\u002F..\u002Futils\u002Fdb\";\n\nexport default defineEventHandler(async (event) => {\n  const token = getCookie(event, \"auth_token\");\n  \n  if (!token) {\n    throw createError({\n      statusCode: 401,\n      statusMessage: \"Not authenticated\",\n    });\n  }\n  \n  const payload = await verifyToken(token);\n  \n  if (!payload) {\n    throw createError({\n      statusCode: 401,\n      statusMessage: \"Invalid token\",\n    });\n  }\n  \n  const user = await findUserByEmail(payload.email);\n  \n  if (!user) {\n    throw createError({\n      statusCode: 401,\n      statusMessage: \"User not found\",\n    });\n  }\n  \n  return {\n    id: user.id,\n    email: user.email,\n    name: user.name,\n  };\n});\n",[40,25859,25860,25873,25886,25890,25912,25929,25933,25944,25952,25960,25969,25973,25977,25981,25997,26001,26012,26020,26028,26037,26041,26045,26049,26064,26068,26078,26086,26094,26103,26107,26111,26115,26121,26126,26131,26136,26140],{"__ignoreMap":56},[449,25861,25862,25864,25867,25869,25871],{"class":451,"line":452},[449,25863,681],{"class":680},[449,25865,25866],{"class":684}," { verifyToken } ",[449,25868,688],{"class":680},[449,25870,25138],{"class":691},[449,25872,695],{"class":684},[449,25874,25875,25877,25880,25882,25884],{"class":451,"line":114},[449,25876,681],{"class":680},[449,25878,25879],{"class":684}," { findUserByEmail } ",[449,25881,688],{"class":680},[449,25883,25124],{"class":691},[449,25885,695],{"class":684},[449,25887,25888],{"class":451,"line":111},[449,25889,700],{"emptyLinePlaceholder":130},[449,25891,25892,25894,25896,25898,25900,25902,25904,25906,25908,25910],{"class":451,"line":468},[449,25893,4629],{"class":680},[449,25895,4632],{"class":680},[449,25897,4715],{"class":721},[449,25899,725],{"class":684},[449,25901,1485],{"class":680},[449,25903,1913],{"class":684},[449,25905,2991],{"class":1491},[449,25907,1758],{"class":684},[449,25909,1498],{"class":680},[449,25911,1501],{"class":684},[449,25913,25914,25916,25918,25920,25923,25925,25927],{"class":451,"line":474},[449,25915,5049],{"class":680},[449,25917,25367],{"class":714},[449,25919,718],{"class":680},[449,25921,25922],{"class":721}," getCookie",[449,25924,12330],{"class":684},[449,25926,25390],{"class":691},[449,25928,740],{"class":684},[449,25930,25931],{"class":451,"line":480},[449,25932,4661],{"class":684},[449,25934,25935,25937,25939,25941],{"class":451,"line":486},[449,25936,5079],{"class":680},[449,25938,1913],{"class":684},[449,25940,5084],{"class":680},[449,25942,25943],{"class":684},"token) {\n",[449,25945,25946,25948,25950],{"class":451,"line":492},[449,25947,5109],{"class":680},[449,25949,5112],{"class":721},[449,25951,1391],{"class":684},[449,25953,25954,25956,25958],{"class":451,"line":498},[449,25955,5119],{"class":684},[449,25957,16890],{"class":714},[449,25959,779],{"class":684},[449,25961,25962,25964,25967],{"class":451,"line":133},[449,25963,5129],{"class":684},[449,25965,25966],{"class":691},"\"Not authenticated\"",[449,25968,779],{"class":684},[449,25970,25971],{"class":451,"line":509},[449,25972,3484],{"class":684},[449,25974,25975],{"class":451,"line":318},[449,25976,4599],{"class":684},[449,25978,25979],{"class":451,"line":520},[449,25980,4661],{"class":684},[449,25982,25983,25985,25988,25990,25992,25994],{"class":451,"line":526},[449,25984,5049],{"class":680},[449,25986,25987],{"class":714}," payload",[449,25989,718],{"class":680},[449,25991,1654],{"class":680},[449,25993,24394],{"class":721},[449,25995,25996],{"class":684},"(token);\n",[449,25998,25999],{"class":451,"line":532},[449,26000,4661],{"class":684},[449,26002,26003,26005,26007,26009],{"class":451,"line":2790},[449,26004,5079],{"class":680},[449,26006,1913],{"class":684},[449,26008,5084],{"class":680},[449,26010,26011],{"class":684},"payload) {\n",[449,26013,26014,26016,26018],{"class":451,"line":3511},[449,26015,5109],{"class":680},[449,26017,5112],{"class":721},[449,26019,1391],{"class":684},[449,26021,26022,26024,26026],{"class":451,"line":3526},[449,26023,5119],{"class":684},[449,26025,16890],{"class":714},[449,26027,779],{"class":684},[449,26029,26030,26032,26035],{"class":451,"line":3531},[449,26031,5129],{"class":684},[449,26033,26034],{"class":691},"\"Invalid token\"",[449,26036,779],{"class":684},[449,26038,26039],{"class":451,"line":3536},[449,26040,3484],{"class":684},[449,26042,26043],{"class":451,"line":4009},[449,26044,4599],{"class":684},[449,26046,26047],{"class":451,"line":4015},[449,26048,4661],{"class":684},[449,26050,26051,26053,26055,26057,26059,26061],{"class":451,"line":4021},[449,26052,5049],{"class":680},[449,26054,24831],{"class":714},[449,26056,718],{"class":680},[449,26058,1654],{"class":680},[449,26060,24750],{"class":721},[449,26062,26063],{"class":684},"(payload.email);\n",[449,26065,26066],{"class":451,"line":4027},[449,26067,4661],{"class":684},[449,26069,26070,26072,26074,26076],{"class":451,"line":4032},[449,26071,5079],{"class":680},[449,26073,1913],{"class":684},[449,26075,5084],{"class":680},[449,26077,24855],{"class":684},[449,26079,26080,26082,26084],{"class":451,"line":4038},[449,26081,5109],{"class":680},[449,26083,5112],{"class":721},[449,26085,1391],{"class":684},[449,26087,26088,26090,26092],{"class":451,"line":4044},[449,26089,5119],{"class":684},[449,26091,16890],{"class":714},[449,26093,779],{"class":684},[449,26095,26096,26098,26101],{"class":451,"line":4049},[449,26097,5129],{"class":684},[449,26099,26100],{"class":691},"\"User not found\"",[449,26102,779],{"class":684},[449,26104,26105],{"class":451,"line":4054},[449,26106,3484],{"class":684},[449,26108,26109],{"class":451,"line":4059},[449,26110,4599],{"class":684},[449,26112,26113],{"class":451,"line":4065},[449,26114,4661],{"class":684},[449,26116,26117,26119],{"class":451,"line":4070},[449,26118,4727],{"class":680},[449,26120,1501],{"class":684},[449,26122,26123],{"class":451,"line":4075},[449,26124,26125],{"class":684},"    id: user.id,\n",[449,26127,26128],{"class":451,"line":4081},[449,26129,26130],{"class":684},"    email: user.email,\n",[449,26132,26133],{"class":451,"line":4086},[449,26134,26135],{"class":684},"    name: user.name,\n",[449,26137,26138],{"class":451,"line":4092},[449,26139,4764],{"class":684},[449,26141,26142],{"class":451,"line":4098},[449,26143,784],{"class":684},[73,26145],{},[32,26147,26149],{"id":26148},"step-6-protect-api-routes","Step 6: Protect API Routes",[21,26151,26152],{},"Now let's protect our order management endpoints so only authenticated users can access them.",[21,26154,4616,26155,3070],{},[40,26156,13931],{},[48,26158,26160],{"className":670,"code":26159,"filename":13931,"language":673,"meta":56,"style":56},"import { db } from \"..\u002Fdb\";\nimport { orders, orderItems } from \"..\u002Fdb\u002Fschema\";\nimport { verifyToken } from \"..\u002Futils\u002Fauth\";\n\nexport default defineEventHandler(async (event) => {\n  \u002F\u002F Authentication check\n  const token = getCookie(event, \"auth_token\");\n  \n  if (!token) {\n    throw createError({\n      statusCode: 401,\n      statusMessage: \"Authentication required\",\n    });\n  }\n  \n  const payload = await verifyToken(token);\n  \n  if (!payload) {\n    throw createError({\n      statusCode: 401,\n      statusMessage: \"Invalid token\",\n    });\n  }\n  \n  const allOrders = await db\n    .select()\n    .from(orders)\n    .orderBy(orders.createdAt);\n  \n  const ordersWithItems = await Promise.all(\n    allOrders.map(async (order) => {\n      const items = await db\n        .select()\n        .from(orderItems)\n        .where(orderItems.orderId.equals(order.id));\n      return { ...order, items };\n    })\n  );\n  \n  return { orders: ordersWithItems };\n});\n",[40,26161,26162,26174,26186,26199,26203,26225,26230,26246,26250,26260,26268,26276,26285,26289,26293,26297,26311,26315,26325,26333,26341,26349,26353,26357,26361,26373,26381,26389,26397,26401,26419,26439,26451,26459,26467,26479,26489,26493,26497,26501,26507],{"__ignoreMap":56},[449,26163,26164,26166,26168,26170,26172],{"class":451,"line":452},[449,26165,681],{"class":680},[449,26167,11728],{"class":684},[449,26169,688],{"class":680},[449,26171,11733],{"class":691},[449,26173,695],{"class":684},[449,26175,26176,26178,26180,26182,26184],{"class":451,"line":114},[449,26177,681],{"class":680},[449,26179,13955],{"class":684},[449,26181,688],{"class":680},[449,26183,11747],{"class":691},[449,26185,695],{"class":684},[449,26187,26188,26190,26192,26194,26197],{"class":451,"line":111},[449,26189,681],{"class":680},[449,26191,25866],{"class":684},[449,26193,688],{"class":680},[449,26195,26196],{"class":691}," \"..\u002Futils\u002Fauth\"",[449,26198,695],{"class":684},[449,26200,26201],{"class":451,"line":468},[449,26202,700],{"emptyLinePlaceholder":130},[449,26204,26205,26207,26209,26211,26213,26215,26217,26219,26221,26223],{"class":451,"line":474},[449,26206,4629],{"class":680},[449,26208,4632],{"class":680},[449,26210,4715],{"class":721},[449,26212,725],{"class":684},[449,26214,1485],{"class":680},[449,26216,1913],{"class":684},[449,26218,2991],{"class":1491},[449,26220,1758],{"class":684},[449,26222,1498],{"class":680},[449,26224,1501],{"class":684},[449,26226,26227],{"class":451,"line":480},[449,26228,26229],{"class":705},"  \u002F\u002F Authentication check\n",[449,26231,26232,26234,26236,26238,26240,26242,26244],{"class":451,"line":486},[449,26233,5049],{"class":680},[449,26235,25367],{"class":714},[449,26237,718],{"class":680},[449,26239,25922],{"class":721},[449,26241,12330],{"class":684},[449,26243,25390],{"class":691},[449,26245,740],{"class":684},[449,26247,26248],{"class":451,"line":492},[449,26249,4661],{"class":684},[449,26251,26252,26254,26256,26258],{"class":451,"line":498},[449,26253,5079],{"class":680},[449,26255,1913],{"class":684},[449,26257,5084],{"class":680},[449,26259,25943],{"class":684},[449,26261,26262,26264,26266],{"class":451,"line":133},[449,26263,5109],{"class":680},[449,26265,5112],{"class":721},[449,26267,1391],{"class":684},[449,26269,26270,26272,26274],{"class":451,"line":509},[449,26271,5119],{"class":684},[449,26273,16890],{"class":714},[449,26275,779],{"class":684},[449,26277,26278,26280,26283],{"class":451,"line":318},[449,26279,5129],{"class":684},[449,26281,26282],{"class":691},"\"Authentication required\"",[449,26284,779],{"class":684},[449,26286,26287],{"class":451,"line":520},[449,26288,3484],{"class":684},[449,26290,26291],{"class":451,"line":526},[449,26292,4599],{"class":684},[449,26294,26295],{"class":451,"line":532},[449,26296,4661],{"class":684},[449,26298,26299,26301,26303,26305,26307,26309],{"class":451,"line":2790},[449,26300,5049],{"class":680},[449,26302,25987],{"class":714},[449,26304,718],{"class":680},[449,26306,1654],{"class":680},[449,26308,24394],{"class":721},[449,26310,25996],{"class":684},[449,26312,26313],{"class":451,"line":3511},[449,26314,4661],{"class":684},[449,26316,26317,26319,26321,26323],{"class":451,"line":3526},[449,26318,5079],{"class":680},[449,26320,1913],{"class":684},[449,26322,5084],{"class":680},[449,26324,26011],{"class":684},[449,26326,26327,26329,26331],{"class":451,"line":3531},[449,26328,5109],{"class":680},[449,26330,5112],{"class":721},[449,26332,1391],{"class":684},[449,26334,26335,26337,26339],{"class":451,"line":3536},[449,26336,5119],{"class":684},[449,26338,16890],{"class":714},[449,26340,779],{"class":684},[449,26342,26343,26345,26347],{"class":451,"line":4009},[449,26344,5129],{"class":684},[449,26346,26034],{"class":691},[449,26348,779],{"class":684},[449,26350,26351],{"class":451,"line":4015},[449,26352,3484],{"class":684},[449,26354,26355],{"class":451,"line":4021},[449,26356,4599],{"class":684},[449,26358,26359],{"class":451,"line":4027},[449,26360,4661],{"class":684},[449,26362,26363,26365,26367,26369,26371],{"class":451,"line":4032},[449,26364,5049],{"class":680},[449,26366,13990],{"class":714},[449,26368,718],{"class":680},[449,26370,1654],{"class":680},[449,26372,12771],{"class":684},[449,26374,26375,26377,26379],{"class":451,"line":4038},[449,26376,10963],{"class":684},[449,26378,12228],{"class":721},[449,26380,11220],{"class":684},[449,26382,26383,26385,26387],{"class":451,"line":4044},[449,26384,10963],{"class":684},[449,26386,688],{"class":721},[449,26388,13302],{"class":684},[449,26390,26391,26393,26395],{"class":451,"line":4049},[449,26392,10963],{"class":684},[449,26394,14019],{"class":721},[449,26396,14022],{"class":684},[449,26398,26399],{"class":451,"line":4054},[449,26400,4661],{"class":684},[449,26402,26403,26405,26407,26409,26411,26413,26415,26417],{"class":451,"line":4059},[449,26404,5049],{"class":680},[449,26406,14038],{"class":714},[449,26408,718],{"class":680},[449,26410,1654],{"class":680},[449,26412,5160],{"class":714},[449,26414,165],{"class":684},[449,26416,5815],{"class":721},[449,26418,5566],{"class":684},[449,26420,26421,26423,26425,26427,26429,26431,26433,26435,26437],{"class":451,"line":4065},[449,26422,14055],{"class":684},[449,26424,6397],{"class":721},[449,26426,725],{"class":684},[449,26428,1485],{"class":680},[449,26430,1913],{"class":684},[449,26432,14066],{"class":1491},[449,26434,1758],{"class":684},[449,26436,1498],{"class":680},[449,26438,1501],{"class":684},[449,26440,26441,26443,26445,26447,26449],{"class":451,"line":4070},[449,26442,14077],{"class":680},[449,26444,12219],{"class":714},[449,26446,718],{"class":680},[449,26448,1654],{"class":680},[449,26450,12771],{"class":684},[449,26452,26453,26455,26457],{"class":451,"line":4075},[449,26454,14090],{"class":684},[449,26456,12228],{"class":721},[449,26458,11220],{"class":684},[449,26460,26461,26463,26465],{"class":451,"line":4081},[449,26462,14090],{"class":684},[449,26464,688],{"class":721},[449,26466,13362],{"class":684},[449,26468,26469,26471,26473,26475,26477],{"class":451,"line":4086},[449,26470,14090],{"class":684},[449,26472,12237],{"class":721},[449,26474,14111],{"class":684},[449,26476,12414],{"class":721},[449,26478,14116],{"class":684},[449,26480,26481,26483,26485,26487],{"class":451,"line":4092},[449,26482,14121],{"class":680},[449,26484,14124],{"class":684},[449,26486,14127],{"class":680},[449,26488,14130],{"class":684},[449,26490,26491],{"class":451,"line":4098},[449,26492,13841],{"class":684},[449,26494,26495],{"class":451,"line":4104},[449,26496,6436],{"class":684},[449,26498,26499],{"class":451,"line":4110},[449,26500,4661],{"class":684},[449,26502,26503,26505],{"class":451,"line":5784},[449,26504,4727],{"class":680},[449,26506,14149],{"class":684},[449,26508,26509],{"class":451,"line":5790},[449,26510,784],{"class":684},[427,26512,26514],{"icon":14202,"title":26513},"Middleware Pattern",[21,26515,26516],{},"You can extract this auth check into a reusable middleware function to protect multiple routes without repetition.",[73,26518],{},[32,26520,26522],{"id":26521},"step-7-add-oauth-with-github","Step 7: Add OAuth with GitHub",[21,26524,26525],{},"Let's add social login using GitHub as an example.",[21,26527,26528,26529],{},"First, create a GitHub OAuth app at ",[3861,26530,26531],{"href":26531,"rel":26532},"https:\u002F\u002Fgithub.com\u002Fsettings\u002Fdevelopers",[3865],[21,26534,26535,26536,3070],{},"Add credentials to your ",[40,26537,24005],{},[48,26539,26541],{"className":839,"code":26540,"filename":24005,"language":841,"meta":56,"style":56},"GITHUB_CLIENT_ID=your_client_id\nGITHUB_CLIENT_SECRET=your_client_secret\n",[40,26542,26543,26553],{"__ignoreMap":56},[449,26544,26545,26548,26550],{"class":451,"line":452},[449,26546,26547],{"class":684},"GITHUB_CLIENT_ID",[449,26549,5015],{"class":680},[449,26551,26552],{"class":691},"your_client_id\n",[449,26554,26555,26558,26560],{"class":451,"line":114},[449,26556,26557],{"class":684},"GITHUB_CLIENT_SECRET",[449,26559,5015],{"class":680},[449,26561,26562],{"class":691},"your_client_secret\n",[21,26564,4616,26565,3070],{},[40,26566,4619],{},[48,26568,26570],{"className":670,"code":26569,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  auth: {\n    provider: {\n      type: \"authjs\",\n      provider: \"github\",\n    },\n    globalAppMiddleware: true,\n  },\n  \n  runtimeConfig: {\n    authSecret: process.env.AUTH_SECRET,\n    github: {\n      clientId: process.env.GITHUB_CLIENT_ID,\n      clientSecret: process.env.GITHUB_CLIENT_SECRET,\n    },\n    public: {\n      authOrigin: process.env.AUTH_ORIGIN || \"http:\u002F\u002Flocalhost:3000\",\n    },\n  },\n});\n",[40,26571,26572,26582,26586,26590,26598,26608,26612,26620,26624,26628,26632,26640,26645,26654,26663,26667,26671,26683,26687,26691],{"__ignoreMap":56},[449,26573,26574,26576,26578,26580],{"class":451,"line":452},[449,26575,4629],{"class":680},[449,26577,4632],{"class":680},[449,26579,4635],{"class":721},[449,26581,1391],{"class":684},[449,26583,26584],{"class":451,"line":114},[449,26585,23899],{"class":684},[449,26587,26588],{"class":451,"line":111},[449,26589,23904],{"class":684},[449,26591,26592,26594,26596],{"class":451,"line":468},[449,26593,23909],{"class":684},[449,26595,23912],{"class":691},[449,26597,779],{"class":684},[449,26599,26600,26603,26606],{"class":451,"line":474},[449,26601,26602],{"class":684},"      provider: ",[449,26604,26605],{"class":691},"\"github\"",[449,26607,779],{"class":684},[449,26609,26610],{"class":451,"line":480},[449,26611,1458],{"class":684},[449,26613,26614,26616,26618],{"class":451,"line":486},[449,26615,23933],{"class":684},[449,26617,2169],{"class":714},[449,26619,779],{"class":684},[449,26621,26622],{"class":451,"line":492},[449,26623,1474],{"class":684},[449,26625,26626],{"class":451,"line":498},[449,26627,4661],{"class":684},[449,26629,26630],{"class":451,"line":133},[449,26631,15284],{"class":684},[449,26633,26634,26636,26638],{"class":451,"line":509},[449,26635,23954],{"class":684},[449,26637,23957],{"class":714},[449,26639,779],{"class":684},[449,26641,26642],{"class":451,"line":318},[449,26643,26644],{"class":684},"    github: {\n",[449,26646,26647,26650,26652],{"class":451,"line":520},[449,26648,26649],{"class":684},"      clientId: process.env.",[449,26651,26547],{"class":714},[449,26653,779],{"class":684},[449,26655,26656,26659,26661],{"class":451,"line":526},[449,26657,26658],{"class":684},"      clientSecret: process.env.",[449,26660,26557],{"class":714},[449,26662,779],{"class":684},[449,26664,26665],{"class":451,"line":532},[449,26666,1458],{"class":684},[449,26668,26669],{"class":451,"line":2790},[449,26670,15289],{"class":684},[449,26672,26673,26675,26677,26679,26681],{"class":451,"line":3511},[449,26674,23968],{"class":684},[449,26676,23971],{"class":714},[449,26678,23974],{"class":680},[449,26680,23977],{"class":691},[449,26682,779],{"class":684},[449,26684,26685],{"class":451,"line":3526},[449,26686,1458],{"class":684},[449,26688,26689],{"class":451,"line":3531},[449,26690,1474],{"class":684},[449,26692,26693],{"class":451,"line":3536},[449,26694,784],{"class":684},[21,26696,4698,26697,3070],{},[40,26698,26699],{},"server\u002Fapi\u002Fauth\u002F[...].ts",[48,26701,26703],{"className":670,"code":26702,"filename":26699,"language":673,"meta":56,"style":56},"import { NuxtAuthHandler } from \"@sidebase\u002Fnuxt-auth\";\nimport GithubProvider from \"next-auth\u002Fproviders\u002Fgithub\";\n\nexport default NuxtAuthHandler({\n  secret: process.env.AUTH_SECRET,\n  providers: [\n    GithubProvider.default({\n      clientId: process.env.GITHUB_CLIENT_ID,\n      clientSecret: process.env.GITHUB_CLIENT_SECRET,\n    }),\n  ],\n});\n",[40,26704,26705,26719,26733,26737,26748,26757,26762,26771,26779,26787,26791,26795],{"__ignoreMap":56},[449,26706,26707,26709,26712,26714,26717],{"class":451,"line":452},[449,26708,681],{"class":680},[449,26710,26711],{"class":684}," { NuxtAuthHandler } ",[449,26713,688],{"class":680},[449,26715,26716],{"class":691}," \"@sidebase\u002Fnuxt-auth\"",[449,26718,695],{"class":684},[449,26720,26721,26723,26726,26728,26731],{"class":451,"line":114},[449,26722,681],{"class":680},[449,26724,26725],{"class":684}," GithubProvider ",[449,26727,688],{"class":680},[449,26729,26730],{"class":691}," \"next-auth\u002Fproviders\u002Fgithub\"",[449,26732,695],{"class":684},[449,26734,26735],{"class":451,"line":111},[449,26736,700],{"emptyLinePlaceholder":130},[449,26738,26739,26741,26743,26746],{"class":451,"line":468},[449,26740,4629],{"class":680},[449,26742,4632],{"class":680},[449,26744,26745],{"class":721}," NuxtAuthHandler",[449,26747,1391],{"class":684},[449,26749,26750,26753,26755],{"class":451,"line":474},[449,26751,26752],{"class":684},"  secret: process.env.",[449,26754,23957],{"class":714},[449,26756,779],{"class":684},[449,26758,26759],{"class":451,"line":480},[449,26760,26761],{"class":684},"  providers: [\n",[449,26763,26764,26767,26769],{"class":451,"line":486},[449,26765,26766],{"class":684},"    GithubProvider.",[449,26768,10932],{"class":721},[449,26770,1391],{"class":684},[449,26772,26773,26775,26777],{"class":451,"line":492},[449,26774,26649],{"class":684},[449,26776,26547],{"class":714},[449,26778,779],{"class":684},[449,26780,26781,26783,26785],{"class":451,"line":498},[449,26782,26658],{"class":684},[449,26784,26557],{"class":714},[449,26786,779],{"class":684},[449,26788,26789],{"class":451,"line":133},[449,26790,4101],{"class":684},[449,26792,26793],{"class":451,"line":509},[449,26794,4107],{"class":684},[449,26796,26797],{"class":451,"line":318},[449,26798,784],{"class":684},[73,26800],{},[32,26802,26804],{"id":26803},"step-8-create-the-frontend-auth-pages","Step 8: Create the Frontend Auth Pages",[21,26806,4698,26807,3070],{},[40,26808,26809],{},"pages\u002Flogin.vue",[48,26811,26814],{"className":26812,"code":26813,"filename":26809,"language":23640,"meta":56,"style":56},"language-vue shiki shiki-themes github-light github-dark","\u003Ctemplate>\n  \u003Cdiv class=\"auth-page\">\n    \u003Ch1>Login to BrewStop\u003C\u002Fh1>\n    \n    \u003Cform @submit.prevent=\"handleLogin\">\n      \u003Cdiv class=\"form-group\">\n        \u003Clabel>Email\u003C\u002Flabel>\n        \u003Cinput v-model=\"email\" type=\"email\" required \u002F>\n      \u003C\u002Fdiv>\n      \n      \u003Cdiv class=\"form-group\">\n        \u003Clabel>Password\u003C\u002Flabel>\n        \u003Cinput v-model=\"password\" type=\"password\" required \u002F>\n      \u003C\u002Fdiv>\n      \n      \u003Cbutton type=\"submit\" :disabled=\"loading\">\n        {{ loading ? \"Logging in...\" : \"Login\" }}\n      \u003C\u002Fbutton>\n      \n      \u003Cp v-if=\"error\" class=\"error\">{{ error }}\u003C\u002Fp>\n    \u003C\u002Fform>\n    \n    \u003Cp class=\"oauth-separator\">or\u003C\u002Fp>\n    \n    \u003Cbutton @click=\"loginWithGithub\" class=\"github-btn\">\n      Continue with GitHub\n    \u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\nconst email = ref(\"\");\nconst password = ref(\"\");\nconst loading = ref(false);\nconst error = ref(\"\");\n\nasync function handleLogin() {\n  loading.value = true;\n  error.value = \"\";\n  \n  try {\n    await $fetch(\"\u002Fapi\u002Fauth\u002Flogin\", {\n      method: \"POST\",\n      body: { email: email.value, password: password.value },\n    });\n    \n    navigateTo(\"\u002Fdashboard\");\n  } catch (e: any) {\n    error.value = e.data?.statusMessage || \"Login failed\";\n  } finally {\n    loading.value = false;\n  }\n}\n\nasync function loginWithGithub() {\n  await navigateTo(\"\u002Fapi\u002Fauth\u002Fsignin\u002Fgithub\", { external: true });\n}\n\u003C\u002Fscript>\n",[40,26815,26816,26825,26843,26857,26861,26878,26894,26909,26934,26943,26948,26962,26975,26997,27005,27009,27033,27038,27046,27050,27076,27085,27089,27109,27113,27136,27141,27149,27158,27166,27170,27188,27206,27223,27241,27258,27262,27273,27285,27297,27301,27307,27321,27331,27336,27340,27344,27356,27373,27390,27399,27410,27414,27418,27422,27433,27452,27456],{"__ignoreMap":56},[449,26817,26818,26820,26823],{"class":451,"line":452},[449,26819,5546],{"class":684},[449,26821,26822],{"class":17260},"template",[449,26824,23006],{"class":684},[449,26826,26827,26830,26833,26836,26838,26841],{"class":451,"line":114},[449,26828,26829],{"class":684},"  \u003C",[449,26831,26832],{"class":17260},"div",[449,26834,26835],{"class":721}," class",[449,26837,5015],{"class":684},[449,26839,26840],{"class":691},"\"auth-page\"",[449,26842,23006],{"class":684},[449,26844,26845,26848,26850,26853,26855],{"class":451,"line":111},[449,26846,26847],{"class":684},"    \u003C",[449,26849,16],{"class":17260},[449,26851,26852],{"class":684},">Login to BrewStop\u003C\u002F",[449,26854,16],{"class":17260},[449,26856,23006],{"class":684},[449,26858,26859],{"class":451,"line":468},[449,26860,9155],{"class":684},[449,26862,26863,26865,26868,26871,26873,26876],{"class":451,"line":474},[449,26864,26847],{"class":684},[449,26866,26867],{"class":17260},"form",[449,26869,26870],{"class":721}," @submit.prevent",[449,26872,5015],{"class":684},[449,26874,26875],{"class":691},"\"handleLogin\"",[449,26877,23006],{"class":684},[449,26879,26880,26883,26885,26887,26889,26892],{"class":451,"line":480},[449,26881,26882],{"class":684},"      \u003C",[449,26884,26832],{"class":17260},[449,26886,26835],{"class":721},[449,26888,5015],{"class":684},[449,26890,26891],{"class":691},"\"form-group\"",[449,26893,23006],{"class":684},[449,26895,26896,26899,26902,26905,26907],{"class":451,"line":486},[449,26897,26898],{"class":684},"        \u003C",[449,26900,26901],{"class":17260},"label",[449,26903,26904],{"class":684},">Email\u003C\u002F",[449,26906,26901],{"class":17260},[449,26908,23006],{"class":684},[449,26910,26911,26913,26915,26918,26920,26922,26924,26926,26928,26931],{"class":451,"line":492},[449,26912,26898],{"class":684},[449,26914,24663],{"class":17260},[449,26916,26917],{"class":721}," v-model",[449,26919,5015],{"class":684},[449,26921,25004],{"class":691},[449,26923,11333],{"class":721},[449,26925,5015],{"class":684},[449,26927,25004],{"class":691},[449,26929,26930],{"class":721}," required",[449,26932,26933],{"class":684}," \u002F>\n",[449,26935,26936,26939,26941],{"class":451,"line":498},[449,26937,26938],{"class":684},"      \u003C\u002F",[449,26940,26832],{"class":17260},[449,26942,23006],{"class":684},[449,26944,26945],{"class":451,"line":133},[449,26946,26947],{"class":684},"      \n",[449,26949,26950,26952,26954,26956,26958,26960],{"class":451,"line":509},[449,26951,26882],{"class":684},[449,26953,26832],{"class":17260},[449,26955,26835],{"class":721},[449,26957,5015],{"class":684},[449,26959,26891],{"class":691},[449,26961,23006],{"class":684},[449,26963,26964,26966,26968,26971,26973],{"class":451,"line":318},[449,26965,26898],{"class":684},[449,26967,26901],{"class":17260},[449,26969,26970],{"class":684},">Password\u003C\u002F",[449,26972,26901],{"class":17260},[449,26974,23006],{"class":684},[449,26976,26977,26979,26981,26983,26985,26987,26989,26991,26993,26995],{"class":451,"line":520},[449,26978,26898],{"class":684},[449,26980,24663],{"class":17260},[449,26982,26917],{"class":721},[449,26984,5015],{"class":684},[449,26986,25027],{"class":691},[449,26988,11333],{"class":721},[449,26990,5015],{"class":684},[449,26992,25027],{"class":691},[449,26994,26930],{"class":721},[449,26996,26933],{"class":684},[449,26998,26999,27001,27003],{"class":451,"line":526},[449,27000,26938],{"class":684},[449,27002,26832],{"class":17260},[449,27004,23006],{"class":684},[449,27006,27007],{"class":451,"line":532},[449,27008,26947],{"class":684},[449,27010,27011,27013,27016,27018,27020,27023,27026,27028,27031],{"class":451,"line":2790},[449,27012,26882],{"class":684},[449,27014,27015],{"class":17260},"button",[449,27017,11333],{"class":721},[449,27019,5015],{"class":684},[449,27021,27022],{"class":691},"\"submit\"",[449,27024,27025],{"class":721}," :disabled",[449,27027,5015],{"class":684},[449,27029,27030],{"class":691},"\"loading\"",[449,27032,23006],{"class":684},[449,27034,27035],{"class":451,"line":3511},[449,27036,27037],{"class":684},"        {{ loading ? \"Logging in...\" : \"Login\" }}\n",[449,27039,27040,27042,27044],{"class":451,"line":3526},[449,27041,26938],{"class":684},[449,27043,27015],{"class":17260},[449,27045,23006],{"class":684},[449,27047,27048],{"class":451,"line":3531},[449,27049,26947],{"class":684},[449,27051,27052,27054,27056,27059,27061,27063,27065,27067,27069,27072,27074],{"class":451,"line":3536},[449,27053,26882],{"class":684},[449,27055,21],{"class":17260},[449,27057,27058],{"class":721}," v-if",[449,27060,5015],{"class":684},[449,27062,22193],{"class":691},[449,27064,26835],{"class":721},[449,27066,5015],{"class":684},[449,27068,22193],{"class":691},[449,27070,27071],{"class":684},">{{ error }}\u003C\u002F",[449,27073,21],{"class":17260},[449,27075,23006],{"class":684},[449,27077,27078,27081,27083],{"class":451,"line":4009},[449,27079,27080],{"class":684},"    \u003C\u002F",[449,27082,26867],{"class":17260},[449,27084,23006],{"class":684},[449,27086,27087],{"class":451,"line":4015},[449,27088,9155],{"class":684},[449,27090,27091,27093,27095,27097,27099,27102,27105,27107],{"class":451,"line":4021},[449,27092,26847],{"class":684},[449,27094,21],{"class":17260},[449,27096,26835],{"class":721},[449,27098,5015],{"class":684},[449,27100,27101],{"class":691},"\"oauth-separator\"",[449,27103,27104],{"class":684},">or\u003C\u002F",[449,27106,21],{"class":17260},[449,27108,23006],{"class":684},[449,27110,27111],{"class":451,"line":4027},[449,27112,9155],{"class":684},[449,27114,27115,27117,27119,27122,27124,27127,27129,27131,27134],{"class":451,"line":4032},[449,27116,26847],{"class":684},[449,27118,27015],{"class":17260},[449,27120,27121],{"class":721}," @click",[449,27123,5015],{"class":684},[449,27125,27126],{"class":691},"\"loginWithGithub\"",[449,27128,26835],{"class":721},[449,27130,5015],{"class":684},[449,27132,27133],{"class":691},"\"github-btn\"",[449,27135,23006],{"class":684},[449,27137,27138],{"class":451,"line":4038},[449,27139,27140],{"class":684},"      Continue with GitHub\n",[449,27142,27143,27145,27147],{"class":451,"line":4044},[449,27144,27080],{"class":684},[449,27146,27015],{"class":17260},[449,27148,23006],{"class":684},[449,27150,27151,27154,27156],{"class":451,"line":4049},[449,27152,27153],{"class":684},"  \u003C\u002F",[449,27155,26832],{"class":17260},[449,27157,23006],{"class":684},[449,27159,27160,27162,27164],{"class":451,"line":4054},[449,27161,23103],{"class":684},[449,27163,26822],{"class":17260},[449,27165,23006],{"class":684},[449,27167,27168],{"class":451,"line":4059},[449,27169,700],{"emptyLinePlaceholder":130},[449,27171,27172,27174,27176,27179,27182,27184,27186],{"class":451,"line":4065},[449,27173,5546],{"class":684},[449,27175,23106],{"class":17260},[449,27177,27178],{"class":721}," setup",[449,27180,27181],{"class":721}," lang",[449,27183,5015],{"class":684},[449,27185,23003],{"class":691},[449,27187,23006],{"class":684},[449,27189,27190,27192,27195,27197,27199,27201,27204],{"class":451,"line":4070},[449,27191,711],{"class":680},[449,27193,27194],{"class":714}," email",[449,27196,718],{"class":680},[449,27198,23389],{"class":721},[449,27200,725],{"class":684},[449,27202,27203],{"class":691},"\"\"",[449,27205,740],{"class":684},[449,27207,27208,27210,27213,27215,27217,27219,27221],{"class":451,"line":4075},[449,27209,711],{"class":680},[449,27211,27212],{"class":714}," password",[449,27214,718],{"class":680},[449,27216,23389],{"class":721},[449,27218,725],{"class":684},[449,27220,27203],{"class":691},[449,27222,740],{"class":684},[449,27224,27225,27227,27230,27232,27234,27236,27239],{"class":451,"line":4081},[449,27226,711],{"class":680},[449,27228,27229],{"class":714}," loading",[449,27231,718],{"class":680},[449,27233,23389],{"class":721},[449,27235,725],{"class":684},[449,27237,27238],{"class":714},"false",[449,27240,740],{"class":684},[449,27242,27243,27245,27248,27250,27252,27254,27256],{"class":451,"line":4086},[449,27244,711],{"class":680},[449,27246,27247],{"class":714}," error",[449,27249,718],{"class":680},[449,27251,23389],{"class":721},[449,27253,725],{"class":684},[449,27255,27203],{"class":691},[449,27257,740],{"class":684},[449,27259,27260],{"class":451,"line":4092},[449,27261,700],{"emptyLinePlaceholder":130},[449,27263,27264,27266,27268,27271],{"class":451,"line":4098},[449,27265,1485],{"class":680},[449,27267,5432],{"class":680},[449,27269,27270],{"class":721}," handleLogin",[449,27272,5438],{"class":684},[449,27274,27275,27278,27280,27283],{"class":451,"line":4104},[449,27276,27277],{"class":684},"  loading.value ",[449,27279,5015],{"class":680},[449,27281,27282],{"class":714}," true",[449,27284,695],{"class":684},[449,27286,27287,27290,27292,27295],{"class":451,"line":4110},[449,27288,27289],{"class":684},"  error.value ",[449,27291,5015],{"class":680},[449,27293,27294],{"class":691}," \"\"",[449,27296,695],{"class":684},[449,27298,27299],{"class":451,"line":5784},[449,27300,4661],{"class":684},[449,27302,27303,27305],{"class":451,"line":5790},[449,27304,24442],{"class":680},[449,27306,1501],{"class":684},[449,27308,27309,27311,27314,27316,27319],{"class":451,"line":5796},[449,27310,12084],{"class":680},[449,27312,27313],{"class":721}," $fetch",[449,27315,725],{"class":684},[449,27317,27318],{"class":691},"\"\u002Fapi\u002Fauth\u002Flogin\"",[449,27320,768],{"class":684},[449,27322,27323,27326,27329],{"class":451,"line":5801},[449,27324,27325],{"class":684},"      method: ",[449,27327,27328],{"class":691},"\"POST\"",[449,27330,779],{"class":684},[449,27332,27333],{"class":451,"line":5806},[449,27334,27335],{"class":684},"      body: { email: email.value, password: password.value },\n",[449,27337,27338],{"class":451,"line":5821},[449,27339,3484],{"class":684},[449,27341,27342],{"class":451,"line":5826},[449,27343,9155],{"class":684},[449,27345,27346,27349,27351,27354],{"class":451,"line":5848},[449,27347,27348],{"class":721},"    navigateTo",[449,27350,725],{"class":684},[449,27352,27353],{"class":691},"\"\u002Fdashboard\"",[449,27355,740],{"class":684},[449,27357,27358,27360,27362,27364,27367,27369,27371],{"class":451,"line":5882},[449,27359,24497],{"class":684},[449,27361,5747],{"class":680},[449,27363,1913],{"class":684},[449,27365,27366],{"class":1491},"e",[449,27368,3070],{"class":680},[449,27370,5009],{"class":714},[449,27372,3417],{"class":684},[449,27374,27375,27378,27380,27383,27385,27388],{"class":451,"line":5887},[449,27376,27377],{"class":684},"    error.value ",[449,27379,5015],{"class":680},[449,27381,27382],{"class":684}," e.data?.statusMessage ",[449,27384,5090],{"class":680},[449,27386,27387],{"class":691}," \"Login failed\"",[449,27389,695],{"class":684},[449,27391,27392,27394,27397],{"class":451,"line":5901},[449,27393,24497],{"class":684},[449,27395,27396],{"class":680},"finally",[449,27398,1501],{"class":684},[449,27400,27401,27404,27406,27408],{"class":451,"line":5921},[449,27402,27403],{"class":684},"    loading.value ",[449,27405,5015],{"class":680},[449,27407,8916],{"class":714},[449,27409,695],{"class":684},[449,27411,27412],{"class":451,"line":5940},[449,27413,4599],{"class":684},[449,27415,27416],{"class":451,"line":5975},[449,27417,4604],{"class":684},[449,27419,27420],{"class":451,"line":5980},[449,27421,700],{"emptyLinePlaceholder":130},[449,27423,27424,27426,27428,27431],{"class":451,"line":5985},[449,27425,1485],{"class":680},[449,27427,5432],{"class":680},[449,27429,27430],{"class":721}," loginWithGithub",[449,27432,5438],{"class":684},[449,27434,27435,27437,27440,27442,27445,27448,27450],{"class":451,"line":13024},[449,27436,2099],{"class":680},[449,27438,27439],{"class":721}," navigateTo",[449,27441,725],{"class":684},[449,27443,27444],{"class":691},"\"\u002Fapi\u002Fauth\u002Fsignin\u002Fgithub\"",[449,27446,27447],{"class":684},", { external: ",[449,27449,2169],{"class":714},[449,27451,2051],{"class":684},[449,27453,27454],{"class":451,"line":13029},[449,27455,4604],{"class":684},[449,27457,27458,27460,27462],{"class":451,"line":13045},[449,27459,23103],{"class":684},[449,27461,23106],{"class":17260},[449,27463,23006],{"class":684},[21,27465,4698,27466,3070],{},[40,27467,27468],{},"pages\u002Fregister.vue",[48,27470,27472],{"className":26812,"code":27471,"filename":27468,"language":23640,"meta":56,"style":56},"\u003Ctemplate>\n  \u003Cdiv class=\"auth-page\">\n    \u003Ch1>Create an Account\u003C\u002Fh1>\n    \n    \u003Cform @submit.prevent=\"handleRegister\">\n      \u003Cdiv class=\"form-group\">\n        \u003Clabel>Name\u003C\u002Flabel>\n        \u003Cinput v-model=\"name\" type=\"text\" required \u002F>\n      \u003C\u002Fdiv>\n      \n      \u003Cdiv class=\"form-group\">\n        \u003Clabel>Email\u003C\u002Flabel>\n        \u003Cinput v-model=\"email\" type=\"email\" required \u002F>\n      \u003C\u002Fdiv>\n      \n      \u003Cdiv class=\"form-group\">\n        \u003Clabel>Password\u003C\u002Flabel>\n        \u003Cinput v-model=\"password\" type=\"password\" required \u002F>\n      \u003C\u002Fdiv>\n      \n      \u003Cbutton type=\"submit\" :disabled=\"loading\">\n        {{ loading ? \"Creating account...\" : \"Register\" }}\n      \u003C\u002Fbutton>\n      \n      \u003Cp v-if=\"error\" class=\"error\">{{ error }}\u003C\u002Fp>\n    \u003C\u002Fform>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup lang=\"ts\">\nconst name = ref(\"\");\nconst email = ref(\"\");\nconst password = ref(\"\");\nconst loading = ref(false);\nconst error = ref(\"\");\n\nasync function handleRegister() {\n  loading.value = true;\n  error.value = \"\";\n  \n  try {\n    await $fetch(\"\u002Fapi\u002Fauth\u002Fregister\", {\n      method: \"POST\",\n      body: { name: name.value, email: email.value, password: password.value },\n    });\n    \n    navigateTo(\"\u002Fdashboard\");\n  } catch (e: any) {\n    error.value = e.data?.statusMessage || \"Registration failed\";\n  } finally {\n    loading.value = false;\n  }\n}\n\u003C\u002Fscript>\n",[40,27473,27474,27482,27496,27509,27513,27528,27542,27555,27578,27586,27590,27604,27616,27638,27646,27650,27664,27676,27698,27706,27710,27730,27735,27743,27747,27771,27779,27787,27795,27799,27815,27832,27848,27864,27880,27896,27900,27911,27921,27931,27935,27941,27954,27962,27967,27971,27975,27985,28001,28016,28024,28034,28038,28042],{"__ignoreMap":56},[449,27475,27476,27478,27480],{"class":451,"line":452},[449,27477,5546],{"class":684},[449,27479,26822],{"class":17260},[449,27481,23006],{"class":684},[449,27483,27484,27486,27488,27490,27492,27494],{"class":451,"line":114},[449,27485,26829],{"class":684},[449,27487,26832],{"class":17260},[449,27489,26835],{"class":721},[449,27491,5015],{"class":684},[449,27493,26840],{"class":691},[449,27495,23006],{"class":684},[449,27497,27498,27500,27502,27505,27507],{"class":451,"line":111},[449,27499,26847],{"class":684},[449,27501,16],{"class":17260},[449,27503,27504],{"class":684},">Create an Account\u003C\u002F",[449,27506,16],{"class":17260},[449,27508,23006],{"class":684},[449,27510,27511],{"class":451,"line":468},[449,27512,9155],{"class":684},[449,27514,27515,27517,27519,27521,27523,27526],{"class":451,"line":474},[449,27516,26847],{"class":684},[449,27518,26867],{"class":17260},[449,27520,26870],{"class":721},[449,27522,5015],{"class":684},[449,27524,27525],{"class":691},"\"handleRegister\"",[449,27527,23006],{"class":684},[449,27529,27530,27532,27534,27536,27538,27540],{"class":451,"line":480},[449,27531,26882],{"class":684},[449,27533,26832],{"class":17260},[449,27535,26835],{"class":721},[449,27537,5015],{"class":684},[449,27539,26891],{"class":691},[449,27541,23006],{"class":684},[449,27543,27544,27546,27548,27551,27553],{"class":451,"line":486},[449,27545,26898],{"class":684},[449,27547,26901],{"class":17260},[449,27549,27550],{"class":684},">Name\u003C\u002F",[449,27552,26901],{"class":17260},[449,27554,23006],{"class":684},[449,27556,27557,27559,27561,27563,27565,27567,27569,27571,27574,27576],{"class":451,"line":492},[449,27558,26898],{"class":684},[449,27560,24663],{"class":17260},[449,27562,26917],{"class":721},[449,27564,5015],{"class":684},[449,27566,10846],{"class":691},[449,27568,11333],{"class":721},[449,27570,5015],{"class":684},[449,27572,27573],{"class":691},"\"text\"",[449,27575,26930],{"class":721},[449,27577,26933],{"class":684},[449,27579,27580,27582,27584],{"class":451,"line":498},[449,27581,26938],{"class":684},[449,27583,26832],{"class":17260},[449,27585,23006],{"class":684},[449,27587,27588],{"class":451,"line":133},[449,27589,26947],{"class":684},[449,27591,27592,27594,27596,27598,27600,27602],{"class":451,"line":509},[449,27593,26882],{"class":684},[449,27595,26832],{"class":17260},[449,27597,26835],{"class":721},[449,27599,5015],{"class":684},[449,27601,26891],{"class":691},[449,27603,23006],{"class":684},[449,27605,27606,27608,27610,27612,27614],{"class":451,"line":318},[449,27607,26898],{"class":684},[449,27609,26901],{"class":17260},[449,27611,26904],{"class":684},[449,27613,26901],{"class":17260},[449,27615,23006],{"class":684},[449,27617,27618,27620,27622,27624,27626,27628,27630,27632,27634,27636],{"class":451,"line":520},[449,27619,26898],{"class":684},[449,27621,24663],{"class":17260},[449,27623,26917],{"class":721},[449,27625,5015],{"class":684},[449,27627,25004],{"class":691},[449,27629,11333],{"class":721},[449,27631,5015],{"class":684},[449,27633,25004],{"class":691},[449,27635,26930],{"class":721},[449,27637,26933],{"class":684},[449,27639,27640,27642,27644],{"class":451,"line":526},[449,27641,26938],{"class":684},[449,27643,26832],{"class":17260},[449,27645,23006],{"class":684},[449,27647,27648],{"class":451,"line":532},[449,27649,26947],{"class":684},[449,27651,27652,27654,27656,27658,27660,27662],{"class":451,"line":2790},[449,27653,26882],{"class":684},[449,27655,26832],{"class":17260},[449,27657,26835],{"class":721},[449,27659,5015],{"class":684},[449,27661,26891],{"class":691},[449,27663,23006],{"class":684},[449,27665,27666,27668,27670,27672,27674],{"class":451,"line":3511},[449,27667,26898],{"class":684},[449,27669,26901],{"class":17260},[449,27671,26970],{"class":684},[449,27673,26901],{"class":17260},[449,27675,23006],{"class":684},[449,27677,27678,27680,27682,27684,27686,27688,27690,27692,27694,27696],{"class":451,"line":3526},[449,27679,26898],{"class":684},[449,27681,24663],{"class":17260},[449,27683,26917],{"class":721},[449,27685,5015],{"class":684},[449,27687,25027],{"class":691},[449,27689,11333],{"class":721},[449,27691,5015],{"class":684},[449,27693,25027],{"class":691},[449,27695,26930],{"class":721},[449,27697,26933],{"class":684},[449,27699,27700,27702,27704],{"class":451,"line":3531},[449,27701,26938],{"class":684},[449,27703,26832],{"class":17260},[449,27705,23006],{"class":684},[449,27707,27708],{"class":451,"line":3536},[449,27709,26947],{"class":684},[449,27711,27712,27714,27716,27718,27720,27722,27724,27726,27728],{"class":451,"line":4009},[449,27713,26882],{"class":684},[449,27715,27015],{"class":17260},[449,27717,11333],{"class":721},[449,27719,5015],{"class":684},[449,27721,27022],{"class":691},[449,27723,27025],{"class":721},[449,27725,5015],{"class":684},[449,27727,27030],{"class":691},[449,27729,23006],{"class":684},[449,27731,27732],{"class":451,"line":4015},[449,27733,27734],{"class":684},"        {{ loading ? \"Creating account...\" : \"Register\" }}\n",[449,27736,27737,27739,27741],{"class":451,"line":4021},[449,27738,26938],{"class":684},[449,27740,27015],{"class":17260},[449,27742,23006],{"class":684},[449,27744,27745],{"class":451,"line":4027},[449,27746,26947],{"class":684},[449,27748,27749,27751,27753,27755,27757,27759,27761,27763,27765,27767,27769],{"class":451,"line":4032},[449,27750,26882],{"class":684},[449,27752,21],{"class":17260},[449,27754,27058],{"class":721},[449,27756,5015],{"class":684},[449,27758,22193],{"class":691},[449,27760,26835],{"class":721},[449,27762,5015],{"class":684},[449,27764,22193],{"class":691},[449,27766,27071],{"class":684},[449,27768,21],{"class":17260},[449,27770,23006],{"class":684},[449,27772,27773,27775,27777],{"class":451,"line":4038},[449,27774,27080],{"class":684},[449,27776,26867],{"class":17260},[449,27778,23006],{"class":684},[449,27780,27781,27783,27785],{"class":451,"line":4044},[449,27782,27153],{"class":684},[449,27784,26832],{"class":17260},[449,27786,23006],{"class":684},[449,27788,27789,27791,27793],{"class":451,"line":4049},[449,27790,23103],{"class":684},[449,27792,26822],{"class":17260},[449,27794,23006],{"class":684},[449,27796,27797],{"class":451,"line":4054},[449,27798,700],{"emptyLinePlaceholder":130},[449,27800,27801,27803,27805,27807,27809,27811,27813],{"class":451,"line":4059},[449,27802,5546],{"class":684},[449,27804,23106],{"class":17260},[449,27806,27178],{"class":721},[449,27808,27181],{"class":721},[449,27810,5015],{"class":684},[449,27812,23003],{"class":691},[449,27814,23006],{"class":684},[449,27816,27817,27819,27822,27824,27826,27828,27830],{"class":451,"line":4065},[449,27818,711],{"class":680},[449,27820,27821],{"class":714}," name",[449,27823,718],{"class":680},[449,27825,23389],{"class":721},[449,27827,725],{"class":684},[449,27829,27203],{"class":691},[449,27831,740],{"class":684},[449,27833,27834,27836,27838,27840,27842,27844,27846],{"class":451,"line":4070},[449,27835,711],{"class":680},[449,27837,27194],{"class":714},[449,27839,718],{"class":680},[449,27841,23389],{"class":721},[449,27843,725],{"class":684},[449,27845,27203],{"class":691},[449,27847,740],{"class":684},[449,27849,27850,27852,27854,27856,27858,27860,27862],{"class":451,"line":4075},[449,27851,711],{"class":680},[449,27853,27212],{"class":714},[449,27855,718],{"class":680},[449,27857,23389],{"class":721},[449,27859,725],{"class":684},[449,27861,27203],{"class":691},[449,27863,740],{"class":684},[449,27865,27866,27868,27870,27872,27874,27876,27878],{"class":451,"line":4081},[449,27867,711],{"class":680},[449,27869,27229],{"class":714},[449,27871,718],{"class":680},[449,27873,23389],{"class":721},[449,27875,725],{"class":684},[449,27877,27238],{"class":714},[449,27879,740],{"class":684},[449,27881,27882,27884,27886,27888,27890,27892,27894],{"class":451,"line":4086},[449,27883,711],{"class":680},[449,27885,27247],{"class":714},[449,27887,718],{"class":680},[449,27889,23389],{"class":721},[449,27891,725],{"class":684},[449,27893,27203],{"class":691},[449,27895,740],{"class":684},[449,27897,27898],{"class":451,"line":4092},[449,27899,700],{"emptyLinePlaceholder":130},[449,27901,27902,27904,27906,27909],{"class":451,"line":4098},[449,27903,1485],{"class":680},[449,27905,5432],{"class":680},[449,27907,27908],{"class":721}," handleRegister",[449,27910,5438],{"class":684},[449,27912,27913,27915,27917,27919],{"class":451,"line":4104},[449,27914,27277],{"class":684},[449,27916,5015],{"class":680},[449,27918,27282],{"class":714},[449,27920,695],{"class":684},[449,27922,27923,27925,27927,27929],{"class":451,"line":4110},[449,27924,27289],{"class":684},[449,27926,5015],{"class":680},[449,27928,27294],{"class":691},[449,27930,695],{"class":684},[449,27932,27933],{"class":451,"line":5784},[449,27934,4661],{"class":684},[449,27936,27937,27939],{"class":451,"line":5790},[449,27938,24442],{"class":680},[449,27940,1501],{"class":684},[449,27942,27943,27945,27947,27949,27952],{"class":451,"line":5796},[449,27944,12084],{"class":680},[449,27946,27313],{"class":721},[449,27948,725],{"class":684},[449,27950,27951],{"class":691},"\"\u002Fapi\u002Fauth\u002Fregister\"",[449,27953,768],{"class":684},[449,27955,27956,27958,27960],{"class":451,"line":5801},[449,27957,27325],{"class":684},[449,27959,27328],{"class":691},[449,27961,779],{"class":684},[449,27963,27964],{"class":451,"line":5806},[449,27965,27966],{"class":684},"      body: { name: name.value, email: email.value, password: password.value },\n",[449,27968,27969],{"class":451,"line":5821},[449,27970,3484],{"class":684},[449,27972,27973],{"class":451,"line":5826},[449,27974,9155],{"class":684},[449,27976,27977,27979,27981,27983],{"class":451,"line":5848},[449,27978,27348],{"class":721},[449,27980,725],{"class":684},[449,27982,27353],{"class":691},[449,27984,740],{"class":684},[449,27986,27987,27989,27991,27993,27995,27997,27999],{"class":451,"line":5882},[449,27988,24497],{"class":684},[449,27990,5747],{"class":680},[449,27992,1913],{"class":684},[449,27994,27366],{"class":1491},[449,27996,3070],{"class":680},[449,27998,5009],{"class":714},[449,28000,3417],{"class":684},[449,28002,28003,28005,28007,28009,28011,28014],{"class":451,"line":5887},[449,28004,27377],{"class":684},[449,28006,5015],{"class":680},[449,28008,27382],{"class":684},[449,28010,5090],{"class":680},[449,28012,28013],{"class":691}," \"Registration failed\"",[449,28015,695],{"class":684},[449,28017,28018,28020,28022],{"class":451,"line":5901},[449,28019,24497],{"class":684},[449,28021,27396],{"class":680},[449,28023,1501],{"class":684},[449,28025,28026,28028,28030,28032],{"class":451,"line":5921},[449,28027,27403],{"class":684},[449,28029,5015],{"class":680},[449,28031,8916],{"class":714},[449,28033,695],{"class":684},[449,28035,28036],{"class":451,"line":5940},[449,28037,4599],{"class":684},[449,28039,28040],{"class":451,"line":5975},[449,28041,4604],{"class":684},[449,28043,28044,28046,28048],{"class":451,"line":5980},[449,28045,23103],{"class":684},[449,28047,23106],{"class":17260},[449,28049,23006],{"class":684},[73,28051],{},[32,28053,28055],{"id":28054},"step-9-use-auth-composable","Step 9: Use Auth Composable",[21,28057,28058],{},"Nuxt provides a composable for easy auth state management.",[48,28060,28063],{"className":670,"code":28061,"filename":28062,"language":673,"meta":56,"style":56},"export const useAuth = () => {\n  const user = useState(\"auth-user\", () => null);\n  const loading = useState(\"auth-loading\", () => true);\n  \n  const fetchUser = async () => {\n    loading.value = true;\n    try {\n      user.value = await $fetch(\"\u002Fapi\u002Fauth\u002Fme\");\n    } catch {\n      user.value = null;\n    } finally {\n      loading.value = false;\n    }\n  };\n  \n  const logout = async () => {\n    await $fetch(\"\u002Fapi\u002Fauth\u002Flogout\", { method: \"POST\" });\n    user.value = null;\n    navigateTo(\"\u002Flogin\");\n  };\n  \n  return { user, loading, fetchUser, logout };\n};\n","composables\u002FuseAuth.ts",[40,28064,28065,28082,28106,28129,28133,28150,28160,28167,28185,28194,28204,28212,28223,28227,28231,28235,28252,28270,28281,28292,28296,28300,28307],{"__ignoreMap":56},[449,28066,28067,28069,28071,28074,28076,28078,28080],{"class":451,"line":452},[449,28068,4629],{"class":680},[449,28070,10801],{"class":680},[449,28072,28073],{"class":721}," useAuth",[449,28075,718],{"class":680},[449,28077,2090],{"class":684},[449,28079,1498],{"class":680},[449,28081,1501],{"class":684},[449,28083,28084,28086,28088,28090,28093,28095,28098,28100,28102,28104],{"class":451,"line":114},[449,28085,5049],{"class":680},[449,28087,24831],{"class":714},[449,28089,718],{"class":680},[449,28091,28092],{"class":721}," useState",[449,28094,725],{"class":684},[449,28096,28097],{"class":691},"\"auth-user\"",[449,28099,23051],{"class":684},[449,28101,1498],{"class":680},[449,28103,24435],{"class":714},[449,28105,740],{"class":684},[449,28107,28108,28110,28112,28114,28116,28118,28121,28123,28125,28127],{"class":451,"line":111},[449,28109,5049],{"class":680},[449,28111,27229],{"class":714},[449,28113,718],{"class":680},[449,28115,28092],{"class":721},[449,28117,725],{"class":684},[449,28119,28120],{"class":691},"\"auth-loading\"",[449,28122,23051],{"class":684},[449,28124,1498],{"class":680},[449,28126,27282],{"class":714},[449,28128,740],{"class":684},[449,28130,28131],{"class":451,"line":468},[449,28132,4661],{"class":684},[449,28134,28135,28137,28140,28142,28144,28146,28148],{"class":451,"line":474},[449,28136,5049],{"class":680},[449,28138,28139],{"class":721}," fetchUser",[449,28141,718],{"class":680},[449,28143,5596],{"class":680},[449,28145,2090],{"class":684},[449,28147,1498],{"class":680},[449,28149,1501],{"class":684},[449,28151,28152,28154,28156,28158],{"class":451,"line":480},[449,28153,27403],{"class":684},[449,28155,5015],{"class":680},[449,28157,27282],{"class":714},[449,28159,695],{"class":684},[449,28161,28162,28165],{"class":451,"line":486},[449,28163,28164],{"class":680},"    try",[449,28166,1501],{"class":684},[449,28168,28169,28172,28174,28176,28178,28180,28183],{"class":451,"line":492},[449,28170,28171],{"class":684},"      user.value ",[449,28173,5015],{"class":680},[449,28175,1654],{"class":680},[449,28177,27313],{"class":721},[449,28179,725],{"class":684},[449,28181,28182],{"class":691},"\"\u002Fapi\u002Fauth\u002Fme\"",[449,28184,740],{"class":684},[449,28186,28187,28190,28192],{"class":451,"line":498},[449,28188,28189],{"class":684},"    } ",[449,28191,5747],{"class":680},[449,28193,1501],{"class":684},[449,28195,28196,28198,28200,28202],{"class":451,"line":133},[449,28197,28171],{"class":684},[449,28199,5015],{"class":680},[449,28201,24435],{"class":714},[449,28203,695],{"class":684},[449,28205,28206,28208,28210],{"class":451,"line":509},[449,28207,28189],{"class":684},[449,28209,27396],{"class":680},[449,28211,1501],{"class":684},[449,28213,28214,28217,28219,28221],{"class":451,"line":318},[449,28215,28216],{"class":684},"      loading.value ",[449,28218,5015],{"class":680},[449,28220,8916],{"class":714},[449,28222,695],{"class":684},[449,28224,28225],{"class":451,"line":520},[449,28226,7555],{"class":684},[449,28228,28229],{"class":451,"line":526},[449,28230,4764],{"class":684},[449,28232,28233],{"class":451,"line":532},[449,28234,4661],{"class":684},[449,28236,28237,28239,28242,28244,28246,28248,28250],{"class":451,"line":2790},[449,28238,5049],{"class":680},[449,28240,28241],{"class":721}," logout",[449,28243,718],{"class":680},[449,28245,5596],{"class":680},[449,28247,2090],{"class":684},[449,28249,1498],{"class":680},[449,28251,1501],{"class":684},[449,28253,28254,28256,28258,28260,28263,28266,28268],{"class":451,"line":3511},[449,28255,12084],{"class":680},[449,28257,27313],{"class":721},[449,28259,725],{"class":684},[449,28261,28262],{"class":691},"\"\u002Fapi\u002Fauth\u002Flogout\"",[449,28264,28265],{"class":684},", { method: ",[449,28267,27328],{"class":691},[449,28269,2051],{"class":684},[449,28271,28272,28275,28277,28279],{"class":451,"line":3526},[449,28273,28274],{"class":684},"    user.value ",[449,28276,5015],{"class":680},[449,28278,24435],{"class":714},[449,28280,695],{"class":684},[449,28282,28283,28285,28287,28290],{"class":451,"line":3531},[449,28284,27348],{"class":721},[449,28286,725],{"class":684},[449,28288,28289],{"class":691},"\"\u002Flogin\"",[449,28291,740],{"class":684},[449,28293,28294],{"class":451,"line":3536},[449,28295,4764],{"class":684},[449,28297,28298],{"class":451,"line":4009},[449,28299,4661],{"class":684},[449,28301,28302,28304],{"class":451,"line":4015},[449,28303,4727],{"class":680},[449,28305,28306],{"class":684}," { user, loading, fetchUser, logout };\n",[449,28308,28309],{"class":451,"line":4021},[449,28310,20848],{"class":684},[73,28312],{},[32,28314,28316],{"id":28315},"security-checklist","Security Checklist",[21,28318,28319],{},"Before going to production, verify:",[25,28321,28322],{"type":27},[383,28323,28326,28334,28340,28346,28352,28358,28364],{"className":28324},[28325],"contains-task-list",[386,28327,28330,28333],{"className":28328},[28329],"task-list-item",[24663,28331],{"disabled":130,"type":28332},"checkbox"," HTTPS enabled",[386,28335,28337,28339],{"className":28336},[28329],[24663,28338],{"disabled":130,"type":28332}," AUTH_SECRET is set and secure",[386,28341,28343,28345],{"className":28342},[28329],[24663,28344],{"disabled":130,"type":28332}," Passwords are hashed with Argon2 or bcrypt",[386,28347,28349,28351],{"className":28348},[28329],[24663,28350],{"disabled":130,"type":28332}," Cookie has httpOnly, secure, and sameSite flags",[386,28353,28355,28357],{"className":28354},[28329],[24663,28356],{"disabled":130,"type":28332}," Rate limiting on login\u002Fregister endpoints",[386,28359,28361,28363],{"className":28360},[28329],[24663,28362],{"disabled":130,"type":28332}," Email validation implemented",[386,28365,28367,28369],{"className":28366},[28329],[24663,28368],{"disabled":130,"type":28332}," Password strength requirements enforced",[73,28371],{},[32,28373,28375],{"id":28374},"api-routes-summary","API Routes Summary",[547,28377,28378,28388],{},[550,28379,28380],{},[553,28381,28382,28384,28386],{},[556,28383,14416],{},[556,28385,6864],{},[556,28387,923],{},[569,28389,28390,28402,28414,28426,28438],{},[553,28391,28392,28394,28399],{},[574,28393,14452],{},[574,28395,28396],{},[40,28397,28398],{},"\u002Fapi\u002Fauth\u002Fregister",[574,28400,28401],{},"Create new account",[553,28403,28404,28406,28411],{},[574,28405,14452],{},[574,28407,28408],{},[40,28409,28410],{},"\u002Fapi\u002Fauth\u002Flogin",[574,28412,28413],{},"Authenticate user",[553,28415,28416,28418,28423],{},[574,28417,14452],{},[574,28419,28420],{},[40,28421,28422],{},"\u002Fapi\u002Fauth\u002Flogout",[574,28424,28425],{},"Clear session",[553,28427,28428,28430,28435],{},[574,28429,14427],{},[574,28431,28432],{},[40,28433,28434],{},"\u002Fapi\u002Fauth\u002Fme",[574,28436,28437],{},"Get current user",[553,28439,28440,28442,28447],{},[574,28441,14427],{},[574,28443,28444],{},[40,28445,28446],{},"\u002Fapi\u002Fauth\u002F[...]",[574,28448,28449],{},"OAuth handlers",[73,28451],{},[16,28453,3681],{"id":3680},[21,28455,28456],{},"Authentication is complex, but with Nuxt 4 and the right tools, you can build a secure system without reinventing the wheel. We've covered:",[383,28458,28459,28465,28471,28477,28483],{},[386,28460,28461,28464],{},[151,28462,28463],{},"Credentials-based auth"," with secure password hashing",[386,28466,28467,28470],{},[151,28468,28469],{},"JWT tokens"," stored in httpOnly cookies",[386,28472,28473,28476],{},[151,28474,28475],{},"OAuth integration"," with GitHub",[386,28478,28479,28482],{},[151,28480,28481],{},"Protected API routes"," for authenticated endpoints",[386,28484,28485,28488],{},[151,28486,28487],{},"Frontend auth pages"," with proper error handling",[21,28490,28491],{},"The key takeaways:",[816,28493,28494,28500,28506,28512],{},[386,28495,28496,28499],{},[151,28497,28498],{},"Never store plain-text passwords"," — use Argon2 or bcrypt",[386,28501,28502,28505],{},[151,28503,28504],{},"Use httpOnly cookies"," for token storage",[386,28507,28508,28511],{},[151,28509,28510],{},"Validate everything"," on both client and server",[386,28513,28514,28517],{},[151,28515,28516],{},"Use established libraries"," rather than rolling your own",[21,28519,28520],{},"In production, consider adding:",[383,28522,28523,28526,28529,28532],{},[386,28524,28525],{},"Rate limiting on auth endpoints",[386,28527,28528],{},"Email verification flow",[386,28530,28531],{},"Password reset functionality",[386,28533,28534],{},"Two-factor authentication",[21,28536,28537],{},"Now go forth and secure your applications! 🔐",[3728,28539,28540],{},"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":56,"searchDepth":111,"depth":111,"links":28542},[28543,28544,28545,28546,28547,28548,28554,28555,28556,28557,28558,28559],{"id":23693,"depth":114,"text":23694},{"id":10546,"depth":114,"text":10547},{"id":23857,"depth":114,"text":23858},{"id":24034,"depth":114,"text":24035},{"id":24528,"depth":114,"text":24529},{"id":25098,"depth":114,"text":25099,"children":28549},[28550,28551,28552,28553],{"id":1356,"depth":111,"text":25102},{"id":25482,"depth":111,"text":25483},{"id":25787,"depth":111,"text":25788},{"id":25848,"depth":111,"text":25849},{"id":26148,"depth":114,"text":26149},{"id":26521,"depth":114,"text":26522},{"id":26803,"depth":114,"text":26804},{"id":28054,"depth":114,"text":28055},{"id":28315,"depth":114,"text":28316},{"id":28374,"depth":114,"text":28375},"A comprehensive guide to implementing authentication in Nuxt 4. Learn about session management, JWT, OAuth, and securing your API routes with type-safe authentication.",{"type":13,"value":28562},[28563,28565,28567,28571,28587],[16,28564,333],{"id":332},[21,28566,23657],{},[21,28568,23660,28569,3070],{},[151,28570,22903],{},[383,28572,28573,28575,28577,28579,28581,28583,28585],{},[386,28574,23667],{},[386,28576,23670],{},[386,28578,23673],{},[386,28580,23676],{},[386,28582,23679],{},[386,28584,23682],{},[386,28586,23685],{},[21,28588,23688],{},[28590,14535,28591,28592,28593,3608,28594],"nuxt","auth","jwt","session","nuxt auth",{},"\u002Fblog\u002Fnuxt-auth-guide",{"title":23649,"description":28560},"blog\u002Fnuxt-auth-guide","rLYDq6kP6Vru4AFumujpbdQ0qG6tTIuebNlppQZilns",{"id":28601,"title":28602,"author":28603,"body":28604,"canonical":116,"categories":116,"date":29903,"description":29904,"excerpt":29905,"extension":119,"img":29933,"keywords":29934,"language":3788,"meta":29939,"navigation":130,"ogImage":116,"path":29940,"rating":474,"readTime":532,"seo":29941,"stem":29942,"tag":21503,"__hash__":29943},"blog\u002Fblog\u002Fnuxt-layer.md","Mastering Nuxt Layers: A Complete Guide to Scalable Nuxt 3 Architecture",{"name":9,"img":11,"website":327},{"type":13,"value":28605,"toc":29875},[28606,28610,28612,28623,28626,28637,28640,28647,28649,28653,28663,28666,28677,28680,28703,28709,28711,28715,28722,28726,28743,28749,28751,28755,28761,28772,28778,28780,28784,28788,28816,28819,28854,28857,28859,28863,28906,28909,29062,29064,29068,29072,29100,29103,29137,29140,29183,29185,29189,29196,29244,29247,29249,29253,29256,29265,29268,29271,29280,29283,29285,29289,29292,29358,29361,29363,29367,29370,29473,29476,29478,29482,29485,29528,29531,29533,29537,29540,29560,29563,29568,29570,29574,29624,29627,29629,29633,29636,29661,29704,29707,29709,29713,29716,29726,29732,29734,29738,29752,29759,29761,29765,29821,29824,29826,29830,29835,29846,29851,29859,29861,29863,29866,29869,29872],[16,28607,28609],{"id":28608},"mastering-nuxt-layers","Mastering Nuxt Layers",[32,28611,333],{"id":332},[21,28613,28614,28615,28618,28619,28622],{},"Nuxt 3 introduced many powerful features, but ",[151,28616,28617],{},"Nuxt Layers"," stand out as one of the most transformative. Layers allow you to build applications by ",[151,28620,28621],{},"composing reusable Nuxt projects",", instead of maintaining large monolithic codebases or copying boilerplate across repositories.",[21,28624,28625],{},"If you’ve ever wanted to:",[383,28627,28628,28631,28634],{},[386,28629,28630],{},"Reuse layouts, components, and pages across projects",[386,28632,28633],{},"Build a scalable SaaS architecture",[386,28635,28636],{},"Maintain a shared design system or feature set",[21,28638,28639],{},"Then Nuxt Layers are exactly what you’re looking for.",[21,28641,28642,28643,28646],{},"This guide is written as a ",[151,28644,28645],{},"long-form tutorial",". You can follow along step by step and apply everything directly to real projects.",[73,28648],{},[32,28650,28652],{"id":28651},"what-are-nuxt-layers","What Are Nuxt Layers?",[21,28654,23132,28655,28658,28659,28662],{},[151,28656,28657],{},"Nuxt Layer"," is a Nuxt application that is designed to be ",[151,28660,28661],{},"extended",", not deployed on its own.",[21,28664,28665],{},"In simple terms:",[383,28667,28668,28671,28674],{},[386,28669,28670],{},"A layer looks like a normal Nuxt app",[386,28672,28673],{},"Another Nuxt app can extend it",[386,28675,28676],{},"Nuxt merges everything together at runtime",[21,28678,28679],{},"Layers can contain:",[383,28681,28682,28685,28688,28691,28694,28697,28700],{},[386,28683,28684],{},"Pages",[386,28686,28687],{},"Components",[386,28689,28690],{},"Layouts",[386,28692,28693],{},"Composables",[386,28695,28696],{},"Assets",[386,28698,28699],{},"Configuration",[386,28701,28702],{},"Dependencies",[28704,28705,28706],"blockquote",{},[21,28707,28708],{},"Think of layers as inheritance for Nuxt applications.",[73,28710],{},[32,28712,28714],{"id":28713},"why-nuxt-layers-matter","Why Nuxt Layers Matter",[21,28716,28717,28718,28721],{},"Before layers, reuse often meant duplication. Layers introduce a ",[151,28719,28720],{},"first-class architectural pattern"," for sharing functionality.",[2808,28723,28725],{"id":28724},"benefits-of-using-layers","Benefits of Using Layers",[383,28727,28728,28731,28734,28737,28740],{},[386,28729,28730],{},"Reusable features across multiple apps",[386,28732,28733],{},"Cleaner separation of concerns",[386,28735,28736],{},"Faster project setup",[386,28738,28739],{},"Easier long-term maintenance",[386,28741,28742],{},"Ideal for SaaS platforms and white-label apps",[21,28744,28745,28746,165],{},"Layers help teams move faster ",[151,28747,28748],{},"without sacrificing structure",[73,28750],{},[32,28752,28754],{"id":28753},"how-nuxt-layers-work","How Nuxt Layers Work",[21,28756,28757,28758,3070],{},"Nuxt merges layers ",[151,28759,28760],{},"from bottom to top",[816,28762,28763,28766,28769],{},[386,28764,28765],{},"Base layers (design system, shared config)",[386,28767,28768],{},"Feature layers (auth, billing, dashboards)",[386,28770,28771],{},"Final application (branding, custom logic)",[21,28773,28774,28775,165],{},"If multiple layers define the same file, ",[151,28776,28777],{},"the closest one wins",[73,28779],{},[32,28781,28783],{"id":28782},"creating-your-first-nuxt-layer","Creating Your First Nuxt Layer",[2808,28785,28787],{"id":28786},"step-1-create-the-layer","Step 1: Create the Layer",[48,28789,28791],{"className":839,"code":28790,"language":841,"meta":56,"style":56},"npx nuxi init layers\u002Fbase-ui\ncd layers\u002Fbase-ui\nnpm install\n",[40,28792,28793,28804,28810],{"__ignoreMap":56},[449,28794,28795,28797,28799,28801],{"class":451,"line":452},[449,28796,8898],{"class":721},[449,28798,6807],{"class":691},[449,28800,4478],{"class":691},[449,28802,28803],{"class":691}," layers\u002Fbase-ui\n",[449,28805,28806,28808],{"class":451,"line":114},[449,28807,4490],{"class":714},[449,28809,28803],{"class":691},[449,28811,28812,28814],{"class":451,"line":111},[449,28813,3703],{"class":721},[449,28815,4512],{"class":691},[21,28817,28818],{},"This creates a standard Nuxt project structure:",[48,28820,28822],{"className":443,"code":28821,"language":445,"meta":56,"style":56},"layers\u002Fbase-ui\u002F\n├─ components\u002F\n├─ layouts\u002F\n├─ composables\u002F\n├─ assets\u002F\n├─ nuxt.config.ts\n",[40,28823,28824,28829,28834,28839,28844,28849],{"__ignoreMap":56},[449,28825,28826],{"class":451,"line":452},[449,28827,28828],{},"layers\u002Fbase-ui\u002F\n",[449,28830,28831],{"class":451,"line":114},[449,28832,28833],{},"├─ components\u002F\n",[449,28835,28836],{"class":451,"line":111},[449,28837,28838],{},"├─ layouts\u002F\n",[449,28840,28841],{"class":451,"line":468},[449,28842,28843],{},"├─ composables\u002F\n",[449,28845,28846],{"class":451,"line":474},[449,28847,28848],{},"├─ assets\u002F\n",[449,28850,28851],{"class":451,"line":480},[449,28852,28853],{},"├─ nuxt.config.ts\n",[21,28855,28856],{},"You will not run this project directly—it exists to be extended.",[73,28858],{},[2808,28860,28862],{"id":28861},"step-2-configure-the-layer","Step 2: Configure the Layer",[48,28864,28866],{"className":670,"code":28865,"language":673,"meta":56,"style":56},"\u002F\u002F layers\u002Fbase-ui\u002Fnuxt.config.ts\nexport default defineNuxtConfig({\n  components: true,\n  css: ['~\u002Fassets\u002Fmain.css']\n})\n",[40,28867,28868,28873,28883,28892,28902],{"__ignoreMap":56},[449,28869,28870],{"class":451,"line":452},[449,28871,28872],{"class":705},"\u002F\u002F layers\u002Fbase-ui\u002Fnuxt.config.ts\n",[449,28874,28875,28877,28879,28881],{"class":451,"line":114},[449,28876,4629],{"class":680},[449,28878,4632],{"class":680},[449,28880,4635],{"class":721},[449,28882,1391],{"class":684},[449,28884,28885,28888,28890],{"class":451,"line":111},[449,28886,28887],{"class":684},"  components: ",[449,28889,2169],{"class":714},[449,28891,779],{"class":684},[449,28893,28894,28897,28900],{"class":451,"line":468},[449,28895,28896],{"class":684},"  css: [",[449,28898,28899],{"class":691},"'~\u002Fassets\u002Fmain.css'",[449,28901,15050],{"class":684},[449,28903,28904],{"class":451,"line":474},[449,28905,4113],{"class":684},[21,28907,28908],{},"Add a shared component:",[48,28910,28912],{"className":26812,"code":28911,"language":23640,"meta":56,"style":56},"\u003C!-- layers\u002Fbase-ui\u002Fcomponents\u002FBaseButton.vue -->\n\u003Ctemplate>\n  \u003Cbutton class=\"btn\">\n    \u003Cslot \u002F>\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n\n\u003Cstyle scoped>\n.btn {\n  padding: 0.75rem 1.25rem;\n  border-radius: 0.5rem;\n  background: black;\n  color: white;\n}\n\u003C\u002Fstyle>\n",[40,28913,28914,28919,28927,28942,28954,28962,28970,28974,28985,28992,29012,29026,29038,29050,29054],{"__ignoreMap":56},[449,28915,28916],{"class":451,"line":452},[449,28917,28918],{"class":705},"\u003C!-- layers\u002Fbase-ui\u002Fcomponents\u002FBaseButton.vue -->\n",[449,28920,28921,28923,28925],{"class":451,"line":114},[449,28922,5546],{"class":684},[449,28924,26822],{"class":17260},[449,28926,23006],{"class":684},[449,28928,28929,28931,28933,28935,28937,28940],{"class":451,"line":111},[449,28930,26829],{"class":684},[449,28932,27015],{"class":17260},[449,28934,26835],{"class":721},[449,28936,5015],{"class":684},[449,28938,28939],{"class":691},"\"btn\"",[449,28941,23006],{"class":684},[449,28943,28944,28946,28949,28952],{"class":451,"line":468},[449,28945,26847],{"class":684},[449,28947,28948],{"class":17260},"slot",[449,28950,5868],{"class":28951},"s7hpK",[449,28953,23006],{"class":684},[449,28955,28956,28958,28960],{"class":451,"line":474},[449,28957,27153],{"class":684},[449,28959,27015],{"class":17260},[449,28961,23006],{"class":684},[449,28963,28964,28966,28968],{"class":451,"line":480},[449,28965,23103],{"class":684},[449,28967,26822],{"class":17260},[449,28969,23006],{"class":684},[449,28971,28972],{"class":451,"line":486},[449,28973,700],{"emptyLinePlaceholder":130},[449,28975,28976,28978,28980,28983],{"class":451,"line":492},[449,28977,5546],{"class":684},[449,28979,3728],{"class":17260},[449,28981,28982],{"class":721}," scoped",[449,28984,23006],{"class":684},[449,28986,28987,28990],{"class":451,"line":498},[449,28988,28989],{"class":721},".btn",[449,28991,1501],{"class":684},[449,28993,28994,28997,28999,29002,29005,29008,29010],{"class":451,"line":133},[449,28995,28996],{"class":714},"  padding",[449,28998,1482],{"class":684},[449,29000,29001],{"class":714},"0.75",[449,29003,29004],{"class":680},"rem",[449,29006,29007],{"class":714}," 1.25",[449,29009,29004],{"class":680},[449,29011,695],{"class":684},[449,29013,29014,29017,29019,29022,29024],{"class":451,"line":509},[449,29015,29016],{"class":714},"  border-radius",[449,29018,1482],{"class":684},[449,29020,29021],{"class":714},"0.5",[449,29023,29004],{"class":680},[449,29025,695],{"class":684},[449,29027,29028,29031,29033,29036],{"class":451,"line":318},[449,29029,29030],{"class":714},"  background",[449,29032,1482],{"class":684},[449,29034,29035],{"class":714},"black",[449,29037,695],{"class":684},[449,29039,29040,29043,29045,29048],{"class":451,"line":520},[449,29041,29042],{"class":714},"  color",[449,29044,1482],{"class":684},[449,29046,29047],{"class":714},"white",[449,29049,695],{"class":684},[449,29051,29052],{"class":451,"line":526},[449,29053,4604],{"class":684},[449,29055,29056,29058,29060],{"class":451,"line":532},[449,29057,23103],{"class":684},[449,29059,3728],{"class":17260},[449,29061,23006],{"class":684},[73,29063],{},[32,29065,29067],{"id":29066},"using-a-layer-in-a-nuxt-app","Using a Layer in a Nuxt App",[2808,29069,29071],{"id":29070},"step-3-create-the-app","Step 3: Create the App",[48,29073,29075],{"className":839,"code":29074,"language":841,"meta":56,"style":56},"npx nuxi init apps\u002Fweb\ncd apps\u002Fweb\nnpm install\n",[40,29076,29077,29088,29094],{"__ignoreMap":56},[449,29078,29079,29081,29083,29085],{"class":451,"line":452},[449,29080,8898],{"class":721},[449,29082,6807],{"class":691},[449,29084,4478],{"class":691},[449,29086,29087],{"class":691}," apps\u002Fweb\n",[449,29089,29090,29092],{"class":451,"line":114},[449,29091,4490],{"class":714},[449,29093,29087],{"class":691},[449,29095,29096,29098],{"class":451,"line":111},[449,29097,3703],{"class":721},[449,29099,4512],{"class":691},[21,29101,29102],{},"Extend the layer:",[48,29104,29106],{"className":670,"code":29105,"language":673,"meta":56,"style":56},"\u002F\u002F apps\u002Fweb\u002Fnuxt.config.ts\nexport default defineNuxtConfig({\n  extends: ['..\u002Flayers\u002Fbase-ui']\n})\n",[40,29107,29108,29113,29123,29133],{"__ignoreMap":56},[449,29109,29110],{"class":451,"line":452},[449,29111,29112],{"class":705},"\u002F\u002F apps\u002Fweb\u002Fnuxt.config.ts\n",[449,29114,29115,29117,29119,29121],{"class":451,"line":114},[449,29116,4629],{"class":680},[449,29118,4632],{"class":680},[449,29120,4635],{"class":721},[449,29122,1391],{"class":684},[449,29124,29125,29128,29131],{"class":451,"line":111},[449,29126,29127],{"class":684},"  extends: [",[449,29129,29130],{"class":691},"'..\u002Flayers\u002Fbase-ui'",[449,29132,15050],{"class":684},[449,29134,29135],{"class":451,"line":468},[449,29136,4113],{"class":684},[21,29138,29139],{},"Now you can use the shared component without importing it:",[48,29141,29143],{"className":26812,"code":29142,"language":23640,"meta":56,"style":56},"\u003Ctemplate>\n  \u003CBaseButton>\n    Click Me\n  \u003C\u002FBaseButton>\n\u003C\u002Ftemplate>\n",[40,29144,29145,29153,29162,29167,29175],{"__ignoreMap":56},[449,29146,29147,29149,29151],{"class":451,"line":452},[449,29148,5546],{"class":684},[449,29150,26822],{"class":17260},[449,29152,23006],{"class":684},[449,29154,29155,29157,29160],{"class":451,"line":114},[449,29156,26829],{"class":684},[449,29158,29159],{"class":17260},"BaseButton",[449,29161,23006],{"class":684},[449,29163,29164],{"class":451,"line":111},[449,29165,29166],{"class":684},"    Click Me\n",[449,29168,29169,29171,29173],{"class":451,"line":468},[449,29170,27153],{"class":684},[449,29172,29159],{"class":17260},[449,29174,23006],{"class":684},[449,29176,29177,29179,29181],{"class":451,"line":474},[449,29178,23103],{"class":684},[449,29180,26822],{"class":17260},[449,29182,23006],{"class":684},[73,29184],{},[32,29186,29188],{"id":29187},"using-multiple-layers","Using Multiple Layers",[21,29190,29191,29192,29195],{},"You can extend ",[151,29193,29194],{},"multiple layers"," in a single app.",[48,29197,29199],{"className":670,"code":29198,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  extends: [\n    '..\u002Flayers\u002Fbase-ui',\n    '..\u002Flayers\u002Fauth',\n    '..\u002Flayers\u002Fdashboard'\n  ]\n})\n",[40,29200,29201,29211,29216,29223,29230,29235,29240],{"__ignoreMap":56},[449,29202,29203,29205,29207,29209],{"class":451,"line":452},[449,29204,4629],{"class":680},[449,29206,4632],{"class":680},[449,29208,4635],{"class":721},[449,29210,1391],{"class":684},[449,29212,29213],{"class":451,"line":114},[449,29214,29215],{"class":684},"  extends: [\n",[449,29217,29218,29221],{"class":451,"line":111},[449,29219,29220],{"class":691},"    '..\u002Flayers\u002Fbase-ui'",[449,29222,779],{"class":684},[449,29224,29225,29228],{"class":451,"line":468},[449,29226,29227],{"class":691},"    '..\u002Flayers\u002Fauth'",[449,29229,779],{"class":684},[449,29231,29232],{"class":451,"line":474},[449,29233,29234],{"class":691},"    '..\u002Flayers\u002Fdashboard'\n",[449,29236,29237],{"class":451,"line":480},[449,29238,29239],{"class":684},"  ]\n",[449,29241,29242],{"class":451,"line":486},[449,29243,4113],{"class":684},[21,29245,29246],{},"Later layers override earlier ones.",[73,29248],{},[32,29250,29252],{"id":29251},"pages-inside-layers","Pages Inside Layers",[21,29254,29255],{},"Layers can define pages just like an app.",[48,29257,29259],{"className":443,"code":29258,"language":445,"meta":56,"style":56},"layers\u002Fauth\u002Fpages\u002Flogin.vue\n",[40,29260,29261],{"__ignoreMap":56},[449,29262,29263],{"class":451,"line":452},[449,29264,29258],{},[21,29266,29267],{},"That page automatically becomes available in the app.",[21,29269,29270],{},"To override it:",[48,29272,29274],{"className":443,"code":29273,"language":445,"meta":56,"style":56},"apps\u002Fweb\u002Fpages\u002Flogin.vue\n",[40,29275,29276],{"__ignoreMap":56},[449,29277,29278],{"class":451,"line":452},[449,29279,29273],{},[21,29281,29282],{},"Nuxt will use the app’s version instead.",[73,29284],{},[32,29286,29288],{"id":29287},"layouts-in-layers","Layouts in Layers",[21,29290,29291],{},"Shared layouts are ideal for base UI.",[48,29293,29295],{"className":26812,"code":29294,"language":23640,"meta":56,"style":56},"\u003C!-- layers\u002Fbase-ui\u002Flayouts\u002Fdefault.vue -->\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cheader>Base Header\u003C\u002Fheader>\n    \u003Cslot \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[40,29296,29297,29302,29310,29318,29332,29342,29350],{"__ignoreMap":56},[449,29298,29299],{"class":451,"line":452},[449,29300,29301],{"class":705},"\u003C!-- layers\u002Fbase-ui\u002Flayouts\u002Fdefault.vue -->\n",[449,29303,29304,29306,29308],{"class":451,"line":114},[449,29305,5546],{"class":684},[449,29307,26822],{"class":17260},[449,29309,23006],{"class":684},[449,29311,29312,29314,29316],{"class":451,"line":111},[449,29313,26829],{"class":684},[449,29315,26832],{"class":17260},[449,29317,23006],{"class":684},[449,29319,29320,29322,29325,29328,29330],{"class":451,"line":468},[449,29321,26847],{"class":684},[449,29323,29324],{"class":17260},"header",[449,29326,29327],{"class":684},">Base Header\u003C\u002F",[449,29329,29324],{"class":17260},[449,29331,23006],{"class":684},[449,29333,29334,29336,29338,29340],{"class":451,"line":474},[449,29335,26847],{"class":684},[449,29337,28948],{"class":17260},[449,29339,5868],{"class":28951},[449,29341,23006],{"class":684},[449,29343,29344,29346,29348],{"class":451,"line":480},[449,29345,27153],{"class":684},[449,29347,26832],{"class":17260},[449,29349,23006],{"class":684},[449,29351,29352,29354,29356],{"class":451,"line":486},[449,29353,23103],{"class":684},[449,29355,26822],{"class":17260},[449,29357,23006],{"class":684},[21,29359,29360],{},"All extending apps inherit this layout automatically.",[73,29362],{},[32,29364,29366],{"id":29365},"composables-in-layers","Composables in Layers",[21,29368,29369],{},"Layers are perfect for shared logic.",[48,29371,29373],{"className":670,"code":29372,"language":673,"meta":56,"style":56},"\u002F\u002F layers\u002Fauth\u002Fcomposables\u002FuseAuth.ts\nexport const useAuth = () => {\n  const user = useState('user', () => null)\n\n  const login = async () => {\n    user.value = { name: 'Vantol' }\n  }\n\n  return { user, login }\n}\n",[40,29374,29375,29380,29396,29419,29423,29440,29454,29458,29462,29469],{"__ignoreMap":56},[449,29376,29377],{"class":451,"line":452},[449,29378,29379],{"class":705},"\u002F\u002F layers\u002Fauth\u002Fcomposables\u002FuseAuth.ts\n",[449,29381,29382,29384,29386,29388,29390,29392,29394],{"class":451,"line":114},[449,29383,4629],{"class":680},[449,29385,10801],{"class":680},[449,29387,28073],{"class":721},[449,29389,718],{"class":680},[449,29391,2090],{"class":684},[449,29393,1498],{"class":680},[449,29395,1501],{"class":684},[449,29397,29398,29400,29402,29404,29406,29408,29411,29413,29415,29417],{"class":451,"line":111},[449,29399,5049],{"class":680},[449,29401,24831],{"class":714},[449,29403,718],{"class":680},[449,29405,28092],{"class":721},[449,29407,725],{"class":684},[449,29409,29410],{"class":691},"'user'",[449,29412,23051],{"class":684},[449,29414,1498],{"class":680},[449,29416,24435],{"class":714},[449,29418,11211],{"class":684},[449,29420,29421],{"class":451,"line":468},[449,29422,700],{"emptyLinePlaceholder":130},[449,29424,29425,29427,29430,29432,29434,29436,29438],{"class":451,"line":474},[449,29426,5049],{"class":680},[449,29428,29429],{"class":721}," login",[449,29431,718],{"class":680},[449,29433,5596],{"class":680},[449,29435,2090],{"class":684},[449,29437,1498],{"class":680},[449,29439,1501],{"class":684},[449,29441,29442,29444,29446,29449,29452],{"class":451,"line":480},[449,29443,28274],{"class":684},[449,29445,5015],{"class":680},[449,29447,29448],{"class":684}," { name: ",[449,29450,29451],{"class":691},"'Vantol'",[449,29453,15045],{"class":684},[449,29455,29456],{"class":451,"line":486},[449,29457,4599],{"class":684},[449,29459,29460],{"class":451,"line":492},[449,29461,700],{"emptyLinePlaceholder":130},[449,29463,29464,29466],{"class":451,"line":498},[449,29465,4727],{"class":680},[449,29467,29468],{"class":684}," { user, login }\n",[449,29470,29471],{"class":451,"line":133},[449,29472,4604],{"class":684},[21,29474,29475],{},"This composable is globally available.",[73,29477],{},[32,29479,29481],{"id":29480},"runtime-config-and-environment-variables","Runtime Config and Environment Variables",[21,29483,29484],{},"Layers can define runtime configuration:",[48,29486,29488],{"className":670,"code":29487,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  runtimeConfig: {\n    public: {\n      appName: 'Base App'\n    }\n  }\n})\n",[40,29489,29490,29500,29504,29508,29516,29520,29524],{"__ignoreMap":56},[449,29491,29492,29494,29496,29498],{"class":451,"line":452},[449,29493,4629],{"class":680},[449,29495,4632],{"class":680},[449,29497,4635],{"class":721},[449,29499,1391],{"class":684},[449,29501,29502],{"class":451,"line":114},[449,29503,15284],{"class":684},[449,29505,29506],{"class":451,"line":111},[449,29507,15289],{"class":684},[449,29509,29510,29513],{"class":451,"line":468},[449,29511,29512],{"class":684},"      appName: ",[449,29514,29515],{"class":691},"'Base App'\n",[449,29517,29518],{"class":451,"line":474},[449,29519,7555],{"class":684},[449,29521,29522],{"class":451,"line":480},[449,29523,4599],{"class":684},[449,29525,29526],{"class":451,"line":486},[449,29527,4113],{"class":684},[21,29529,29530],{},"Apps can override these values safely.",[73,29532],{},[32,29534,29536],{"id":29535},"dependencies-in-layers","Dependencies in Layers",[21,29538,29539],{},"Layers may include their own dependencies:",[48,29541,29543],{"className":839,"code":29542,"language":841,"meta":56,"style":56},"cd layers\u002Fbase-ui\nnpm install tailwindcss\n",[40,29544,29545,29551],{"__ignoreMap":56},[449,29546,29547,29549],{"class":451,"line":452},[449,29548,4490],{"class":714},[449,29550,28803],{"class":691},[449,29552,29553,29555,29557],{"class":451,"line":114},[449,29554,3703],{"class":721},[449,29556,3706],{"class":691},[449,29558,29559],{"class":691}," tailwindcss\n",[21,29561,29562],{},"Dependencies are merged into the final build.",[28704,29564,29565],{},[21,29566,29567],{},"Be mindful—shared dependencies affect every extending app.",[73,29569],{},[32,29571,29573],{"id":29572},"recommended-monorepo-structure","Recommended Monorepo Structure",[48,29575,29577],{"className":443,"code":29576,"language":445,"meta":56,"style":56},".\n├─ apps\u002F\n│  ├─ web\n│  └─ admin\n├─ layers\u002F\n│  ├─ base-ui\n│  ├─ auth\n│  ├─ billing\n│  └─ dashboard\n",[40,29578,29579,29584,29589,29594,29599,29604,29609,29614,29619],{"__ignoreMap":56},[449,29580,29581],{"class":451,"line":452},[449,29582,29583],{},".\n",[449,29585,29586],{"class":451,"line":114},[449,29587,29588],{},"├─ apps\u002F\n",[449,29590,29591],{"class":451,"line":111},[449,29592,29593],{},"│  ├─ web\n",[449,29595,29596],{"class":451,"line":468},[449,29597,29598],{},"│  └─ admin\n",[449,29600,29601],{"class":451,"line":474},[449,29602,29603],{},"├─ layers\u002F\n",[449,29605,29606],{"class":451,"line":480},[449,29607,29608],{},"│  ├─ base-ui\n",[449,29610,29611],{"class":451,"line":486},[449,29612,29613],{},"│  ├─ auth\n",[449,29615,29616],{"class":451,"line":492},[449,29617,29618],{},"│  ├─ billing\n",[449,29620,29621],{"class":451,"line":498},[449,29622,29623],{},"│  └─ dashboard\n",[21,29625,29626],{},"This structure works well with pnpm workspaces, Turborepo, or Nx.",[73,29628],{},[32,29630,29632],{"id":29631},"real-world-example-saas-starter","Real-World Example: SaaS Starter",[21,29634,29635],{},"A common production setup:",[383,29637,29638,29644,29649,29655],{},[386,29639,29640,29643],{},[40,29641,29642],{},"base-ui"," – Design system",[386,29645,29646,29648],{},[40,29647,28591],{}," – Authentication and guards",[386,29650,29651,29654],{},[40,29652,29653],{},"billing"," – Stripe and subscriptions",[386,29656,29657,29660],{},[40,29658,29659],{},"dashboard"," – Core product UI",[48,29662,29664],{"className":670,"code":29663,"language":673,"meta":56,"style":56},"extends: [\n  '..\u002Flayers\u002Fbase-ui',\n  '..\u002Flayers\u002Fauth',\n  '..\u002Flayers\u002Fbilling',\n  '..\u002Flayers\u002Fdashboard'\n]\n",[40,29665,29666,29674,29681,29688,29695,29700],{"__ignoreMap":56},[449,29667,29668,29671],{"class":451,"line":452},[449,29669,29670],{"class":721},"extends",[449,29672,29673],{"class":684},": [\n",[449,29675,29676,29679],{"class":451,"line":114},[449,29677,29678],{"class":691},"  '..\u002Flayers\u002Fbase-ui'",[449,29680,779],{"class":684},[449,29682,29683,29686],{"class":451,"line":111},[449,29684,29685],{"class":691},"  '..\u002Flayers\u002Fauth'",[449,29687,779],{"class":684},[449,29689,29690,29693],{"class":451,"line":468},[449,29691,29692],{"class":691},"  '..\u002Flayers\u002Fbilling'",[449,29694,779],{"class":684},[449,29696,29697],{"class":451,"line":474},[449,29698,29699],{"class":691},"  '..\u002Flayers\u002Fdashboard'\n",[449,29701,29702],{"class":451,"line":480},[449,29703,15050],{"class":684},[21,29705,29706],{},"New apps can be launched in days instead of weeks.",[73,29708],{},[32,29710,29712],{"id":29711},"overriding-behavior-safely","Overriding Behavior Safely",[21,29714,29715],{},"You can override:",[383,29717,29718,29720,29722,29724],{},[386,29719,28687],{},[386,29721,28684],{},[386,29723,28690],{},[386,29725,28693],{},[21,29727,29728,29729,165],{},"Nuxt resolves conflicts using ",[151,29730,29731],{},"closest-first resolution",[73,29733],{},[32,29735,29737],{"id":29736},"common-pitfalls","Common Pitfalls",[383,29739,29740,29743,29746,29749],{},[386,29741,29742],{},"Putting app-specific logic in base layers",[386,29744,29745],{},"Creating tight coupling between layers",[386,29747,29748],{},"Ignoring layer order",[386,29750,29751],{},"Treating layers like plugins",[21,29753,29754,29755,29758],{},"Design layers as ",[151,29756,29757],{},"products",", not shortcuts.",[73,29760],{},[32,29762,29764],{"id":29763},"layers-vs-nuxt-modules","Layers vs Nuxt Modules",[547,29766,29767,29780],{},[550,29768,29769],{},[553,29770,29771,29774,29777],{},[556,29772,29773],{},"Feature",[556,29775,29776],{},"Layers",[556,29778,29779],{},"Modules",[569,29781,29782,29793,29802,29811],{},[553,29783,29784,29787,29790],{},[574,29785,29786],{},"Pages & layouts",[574,29788,29789],{},"Yes",[574,29791,29792],{},"No",[553,29794,29795,29798,29800],{},[574,29796,29797],{},"File-system reuse",[574,29799,29789],{},[574,29801,29792],{},[553,29803,29804,29807,29809],{},[574,29805,29806],{},"App architecture",[574,29808,29789],{},[574,29810,29792],{},[553,29812,29813,29816,29819],{},[574,29814,29815],{},"Runtime hooks",[574,29817,29818],{},"Limited",[574,29820,29789],{},[21,29822,29823],{},"They solve different problems and often work best together.",[73,29825],{},[32,29827,29829],{"id":29828},"when-you-should-and-shouldnt-use-layers","When You Should (and Shouldn’t) Use Layers",[21,29831,29832],{},[151,29833,29834],{},"Use layers when:",[383,29836,29837,29840,29843],{},[386,29838,29839],{},"You manage multiple Nuxt apps",[386,29841,29842],{},"You want shared features or UI",[386,29844,29845],{},"You’re building a SaaS platform",[21,29847,29848],{},[151,29849,29850],{},"Avoid layers when:",[383,29852,29853,29856],{},[386,29854,29855],{},"You have a single small app",[386,29857,29858],{},"There is little to no shared logic",[73,29860],{},[32,29862,3681],{"id":3680},[21,29864,29865],{},"Nuxt Layers unlock a new level of scalability and maintainability for Nuxt 3 applications. When designed intentionally, they allow teams to move faster, share more, and maintain cleaner architectures over time.",[21,29867,29868],{},"If you’re serious about building scalable Nuxt applications, mastering layers is no longer optional—it’s essential.",[21,29870,29871],{},"Happy building 🚀",[3728,29873,29874],{},"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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":56,"searchDepth":111,"depth":111,"links":29876},[29877,29878,29879,29882,29883,29887,29890,29891,29892,29893,29894,29895,29896,29897,29898,29899,29900,29901,29902],{"id":332,"depth":114,"text":333},{"id":28651,"depth":114,"text":28652},{"id":28713,"depth":114,"text":28714,"children":29880},[29881],{"id":28724,"depth":111,"text":28725},{"id":28753,"depth":114,"text":28754},{"id":28782,"depth":114,"text":28783,"children":29884},[29885,29886],{"id":28786,"depth":111,"text":28787},{"id":28861,"depth":111,"text":28862},{"id":29066,"depth":114,"text":29067,"children":29888},[29889],{"id":29070,"depth":111,"text":29071},{"id":29187,"depth":114,"text":29188},{"id":29251,"depth":114,"text":29252},{"id":29287,"depth":114,"text":29288},{"id":29365,"depth":114,"text":29366},{"id":29480,"depth":114,"text":29481},{"id":29535,"depth":114,"text":29536},{"id":29572,"depth":114,"text":29573},{"id":29631,"depth":114,"text":29632},{"id":29711,"depth":114,"text":29712},{"id":29736,"depth":114,"text":29737},{"id":29763,"depth":114,"text":29764},{"id":29828,"depth":114,"text":29829},{"id":3680,"depth":114,"text":3681},"2026-01-06","A comprehensive, tutorial-style deep dive into Nuxt Layers—learn how to design, build, and scale reusable Nuxt 3 applications using layers.",{"type":13,"value":29906},[29907,29909,29911,29917,29919,29927,29929],[16,29908,28609],{"id":28608},[32,29910,333],{"id":332},[21,29912,28614,29913,28618,29915,28622],{},[151,29914,28617],{},[151,29916,28621],{},[21,29918,28625],{},[383,29920,29921,29923,29925],{},[386,29922,28630],{},[386,29924,28633],{},[386,29926,28636],{},[21,29928,28639],{},[21,29930,28642,29931,28646],{},[151,29932,28645],{},"https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1517433456452-f9633a875f6f",[28590,29935,29936,23640,29937,29938],"nuxt layers","nuxt 3","architecture","monorepo",{},"\u002Fblog\u002Fnuxt-layer",{"title":28602,"description":29904},"blog\u002Fnuxt-layer","9KWnMT6x33L-CQzC53dSqzURulcxL3MpwoC-Y4Vn_yY",{"id":29945,"title":29946,"author":29947,"body":29948,"canonical":116,"categories":116,"date":30027,"description":30028,"excerpt":30029,"extension":119,"img":120,"keywords":30035,"language":128,"meta":30036,"navigation":130,"ogImage":116,"path":30037,"rating":468,"readTime":133,"seo":30038,"stem":30039,"tag":126,"__hash__":30040},"blog\u002Fblog\u002Fnuxt3-lets-talk.md","Nuxt3-RC 10 Lets Talk",{"name":9,"website":10,"img":11},{"type":13,"value":29949,"toc":30018},[29950,29954,29957,29961,29967,29971,29978,29982,29989,29992,30001,30005,30008,30012,30015],[16,29951,29953],{"id":29952},"nuxt3-rc","Nuxt3 RC",[21,29955,29956],{},"Since its release last year October, Nuxt 3 Beta to RC10; Nuxt developers and maintainers have place a lot of\nemphasis on the performance first to be blazing fast with impressive features suchas auto imports (the best feature to date)\nand not coming to nearly a year the stable version should see the long await Hybrid Generation which will allow developers to\ndevelop web application to exisit on both a static and server side rendered environment. So lets look at RC10 and see if we can make heads or tails of this release.",[16,29958,29960],{"id":29959},"features-at-a-glance","Features at a Glance",[48,29962,29965],{"className":29963,"code":29964,"language":53},[51],"Nuxt 3 Release Candidate 10 is out ✨\n\n🚀 Full Static Generation\n⚡️ Link Prefetching\n💨 Inlined Critical Styles\n0️⃣ Experimental Zero-Client-JS Mode\n🪡 55+ enhancements and bug fixes\n",[40,29966,29964],{"__ignoreMap":56},[32,29968,29970],{"id":29969},"full-static-generation","Full Static Generation",[21,29972,29973,29974,29977],{},"Static rendering is basically files being pre-rendered using ",[40,29975,29976],{},"nuxt generate"," which allows for Nuxt to be deployed to any static\nhosting without a server API, but Nuxt3 takes it to whole other level based on the update changes in RC10 - however it does not give\nus the long await hybrid or incremental generated pages so its a start (hopefully RC12).",[32,29979,29981],{"id":29980},"link-prefetching","Link Prefetching",[21,29983,29984,29985],{},"This feature ported from Nuxt 2, is automatically prefetching the next pages when a ",[29986,29987,29988],"nuxt-link",{}," is in the viewport.",[21,29990,29991],{},"This feature is integrated with vue-router to prefetch components of the next route and also payload extraction to prefetch the payload of the next pages ahead of time! You can also hook into link:prefetch to do more prefetches.",[2808,29993,29995,29996],{"id":29994},"so-much-more-have-read-here","so much more have ",[3861,29997,30000],{"href":29998,"rel":29999},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fframework\u002Fdiscussions\u002F7513",[3865],"read here",[32,30002,30004],{"id":30003},"whats-next","What's Next?",[21,30006,30007],{},"Your guess is as good as mine the stable version was promised from Summer and summer is well summer. It seems the best bet\nwould be for Nuxt 3 to come out in October the same time the beta was announced but I am just guessing.",[32,30009,30011],{"id":30010},"how-to-get-started","How to get started",[21,30013,30014],{},"Well the Nuxt3 RC documentation is your best bet for all your nuxt goodness. I will be starting a deep in Nuxt soon be update.",[21,30016,30017],{},"Peace......",{"title":56,"searchDepth":111,"depth":111,"links":30019},[30020,30021,30025,30026],{"id":29969,"depth":114,"text":29970},{"id":29980,"depth":114,"text":29981,"children":30022},[30023],{"id":29994,"depth":111,"text":30024},"so much more have read here",{"id":30003,"depth":114,"text":30004},{"id":30010,"depth":114,"text":30011},"2022-08-10","Nuxt3 RC is now 10 releases old but how does it stack up with past releases.",{"type":13,"value":30030},[30031,30033],[16,30032,29953],{"id":29952},[21,30034,29956],{},[122,123,124,125,126,127],{},"\u002Fblog\u002Fnuxt3-lets-talk",{"title":29946,"description":30028},"blog\u002Fnuxt3-lets-talk","hmAlO8zxdUYcQ-mukkrvk4i94POdgHtqaGp1nuEMpDQ",{"id":30042,"title":30043,"author":30044,"body":30045,"canonical":116,"categories":116,"date":30766,"description":30767,"excerpt":30768,"extension":119,"img":29933,"keywords":30784,"language":3788,"meta":30787,"navigation":130,"ogImage":116,"path":30788,"rating":474,"readTime":133,"seo":30789,"stem":30790,"tag":3793,"__hash__":30791},"blog\u002Fblog\u002Freact-native-vs-dious.md","Dioxus vs React Native: A Modern Cross-Platform UI Comparison",{"name":9,"img":11,"website":327},{"type":13,"value":30046,"toc":30732},[30047,30049,30058,30067,30069,30073,30152,30154,30158,30160,30166,30180,30206,30209,30211,30218,30229,30250,30253,30259,30261,30265,30269,30272,30286,30289,30300,30304,30307,30315,30318,30326,30329,30331,30335,30339,30342,30375,30378,30389,30392,30397,30401,30404,30413,30415,30423,30425,30433,30435,30437,30440,30445,30453,30456,30467,30470,30473,30481,30484,30492,30494,30498,30502,30516,30529,30535,30539,30553,30556,30567,30571,30574,30576,30578,30581,30584,30598,30604,30607,30610,30621,30624,30626,30630,30633,30644,30647,30658,30661,30663,30667,30673,30687,30691,30695,30709,30711,30713,30720,30723,30726,30729],[16,30048,333],{"id":332},[21,30050,30051,30052,30054,30055,30057],{},"Cross-platform development has become the default approach for building modern applications. Among the many frameworks available, ",[151,30053,8883],{}," has long been the industry standard. However, newer frameworks like ",[151,30056,9898],{}," are challenging that position by offering a Rust-first, high-performance alternative.",[21,30059,30060,30061,30063,30064,30066],{},"In this post, we’ll compare ",[151,30062,9927],{}," and ",[151,30065,8883],{}," across architecture, performance, developer experience, tooling, and ecosystem to help you decide which framework fits your next project.",[16,30068,4373],{"id":4372},[32,30070,30072],{"id":30071},"high-level-overview","High-Level Overview",[547,30074,30075,30085],{},[550,30076,30077],{},[553,30078,30079,30081,30083],{},[556,30080,29773],{},[556,30082,9898],{},[556,30084,8883],{},[569,30086,30087,30097,30108,30119,30130,30141],{},[553,30088,30089,30092,30094],{},[574,30090,30091],{},"Language",[574,30093,9902],{},[574,30095,30096],{},"JavaScript \u002F TypeScript",[553,30098,30099,30102,30105],{},[574,30100,30101],{},"UI Model",[574,30103,30104],{},"Declarative (RSX)",[574,30106,30107],{},"Declarative (JSX)",[553,30109,30110,30113,30116],{},[574,30111,30112],{},"Platforms",[574,30114,30115],{},"Web, Desktop, Mobile",[574,30117,30118],{},"Mobile (iOS, Android)",[553,30120,30121,30124,30127],{},[574,30122,30123],{},"Rendering",[574,30125,30126],{},"Native + WASM",[574,30128,30129],{},"Native via bridge",[553,30131,30132,30135,30138],{},[574,30133,30134],{},"Backend Integration",[574,30136,30137],{},"Native Rust fullstack",[574,30139,30140],{},"Separate backend",[553,30142,30143,30146,30149],{},[574,30144,30145],{},"Performance Focus",[574,30147,30148],{},"Compile-time + memory safety",[574,30150,30151],{},"Runtime optimizations",[73,30153],{},[32,30155,30157],{"id":30156},"language-type-safety","Language & Type Safety",[2808,30159,9927],{"id":10435},[21,30161,30162,30163,30165],{},"Dioxus is built entirely in ",[151,30164,9902],{},", giving developers:",[383,30167,30168,30171,30174,30177],{},[386,30169,30170],{},"Strong compile-time guarantees",[386,30172,30173],{},"Memory safety without garbage collection",[386,30175,30176],{},"Fearless refactoring",[386,30178,30179],{},"Fewer runtime crashes",[48,30181,30183],{"className":10027,"code":30182,"language":10029,"meta":56,"style":56},"fn App() -> Element {\n    rsx! {\n        div { \"Hello from Rust!\" }\n    }\n}\n",[40,30184,30185,30189,30193,30198,30202],{"__ignoreMap":56},[449,30186,30187],{"class":451,"line":452},[449,30188,10036],{},[449,30190,30191],{"class":451,"line":114},[449,30192,10041],{},[449,30194,30195],{"class":451,"line":111},[449,30196,30197],{},"        div { \"Hello from Rust!\" }\n",[449,30199,30200],{"class":451,"line":468},[449,30201,7555],{},[449,30203,30204],{"class":451,"line":474},[449,30205,4604],{},[21,30207,30208],{},"Rust errors are caught at compile time, reducing runtime failures common in large JavaScript codebases.\n::",[2808,30210,8883],{"id":8304},[21,30212,30213,30214,30217],{},"React Native relies on ",[151,30215,30216],{},"JavaScript or TypeScript",", which offers:",[383,30219,30220,30223,30226],{},[386,30221,30222],{},"Faster onboarding",[386,30224,30225],{},"Massive community knowledge",[386,30227,30228],{},"Runtime flexibility",[48,30230,30234],{"className":30231,"code":30232,"language":30233,"meta":56,"style":56},"language-tsx shiki shiki-themes github-light github-dark","export default function App() {\n  return \u003CText>Hello from React Native\u003C\u002FText>;\n}\n","tsx",[40,30235,30236,30241,30246],{"__ignoreMap":56},[449,30237,30238],{"class":451,"line":452},[449,30239,30240],{},"export default function App() {\n",[449,30242,30243],{"class":451,"line":114},[449,30244,30245],{},"  return \u003CText>Hello from React Native\u003C\u002FText>;\n",[449,30247,30248],{"class":451,"line":111},[449,30249,4604],{},[21,30251,30252],{},"Despite TypeScript, many errors in React Native still surface at runtime, especially around async state and native bridges.\n::",[21,30254,30255,30258],{},[151,30256,30257],{},"Verdict:","\nDioxus wins on safety and correctness. React Native wins on approachability and familiarity.",[73,30260],{},[32,30262,30264],{"id":30263},"performance-architecture","Performance & Architecture",[2808,30266,30268],{"id":30267},"dioxus-performance-model","Dioxus Performance Model",[21,30270,30271],{},"Dioxus benefits from:",[383,30273,30274,30277,30280,30283],{},[386,30275,30276],{},"Zero-cost abstractions",[386,30278,30279],{},"No JS bridge",[386,30281,30282],{},"Direct native execution",[386,30284,30285],{},"WASM for the web",[21,30287,30288],{},"This leads to:",[383,30290,30291,30294,30297],{},[386,30292,30293],{},"Faster startup times",[386,30295,30296],{},"Lower memory usage",[386,30298,30299],{},"Predictable performance",[2808,30301,30303],{"id":30302},"react-native-performance-model","React Native Performance Model",[21,30305,30306],{},"React Native uses:",[383,30308,30309,30312],{},[386,30310,30311],{},"A JS runtime",[386,30313,30314],{},"A native bridge (even with the new architecture)",[21,30316,30317],{},"While performance has improved significantly, complex UIs can still suffer from:",[383,30319,30320,30323],{},[386,30321,30322],{},"Bridge overhead",[386,30324,30325],{},"Thread synchronization issues",[21,30327,30328],{},"If your app is performance-critical (IoT dashboards, embedded systems, real-time UIs), Dioxus has a structural advantage.\n::",[73,30330],{},[32,30332,30334],{"id":30333},"ui-development-experience","UI Development Experience",[2808,30336,30338],{"id":30337},"dioxus-rsx","Dioxus (RSX)",[21,30340,30341],{},"RSX feels similar to JSX but enforces correctness:",[48,30343,30345],{"className":10027,"code":30344,"language":10029,"meta":56,"style":56},"rsx! {\n    button {\n        onclick: move |_| count += 1,\n        \"Increment\"\n    }\n}\n",[40,30346,30347,30352,30357,30362,30367,30371],{"__ignoreMap":56},[449,30348,30349],{"class":451,"line":452},[449,30350,30351],{},"rsx! {\n",[449,30353,30354],{"class":451,"line":114},[449,30355,30356],{},"    button {\n",[449,30358,30359],{"class":451,"line":111},[449,30360,30361],{},"        onclick: move |_| count += 1,\n",[449,30363,30364],{"class":451,"line":468},[449,30365,30366],{},"        \"Increment\"\n",[449,30368,30369],{"class":451,"line":474},[449,30370,7555],{},[449,30372,30373],{"class":451,"line":480},[449,30374,4604],{},[21,30376,30377],{},"Pros:",[383,30379,30380,30383,30386],{},[386,30381,30382],{},"Compile-time UI validation",[386,30384,30385],{},"No undefined props",[386,30387,30388],{},"Cleaner refactors",[21,30390,30391],{},"Cons:",[383,30393,30394],{},[386,30395,30396],{},"Rust macros have a learning curve",[2808,30398,30400],{"id":30399},"react-native-jsx","React Native (JSX)",[21,30402,30403],{},"JSX is flexible and expressive:",[48,30405,30407],{"className":30231,"code":30406,"language":30233,"meta":56,"style":56},"\u003CButton title=\"Increment\" onPress={() => setCount(c => c + 1)} \u002F>\n",[40,30408,30409],{"__ignoreMap":56},[449,30410,30411],{"class":451,"line":452},[449,30412,30406],{},[21,30414,30377],{},[383,30416,30417,30420],{},[386,30418,30419],{},"Huge component ecosystem",[386,30421,30422],{},"Familiar React mental model",[21,30424,30391],{},[383,30426,30427,30430],{},[386,30428,30429],{},"Runtime UI errors",[386,30431,30432],{},"Props misuse is easy",[73,30434],{},[32,30436,10076],{"id":10075},[2808,30438,9927],{"id":30439},"dioxus-1",[21,30441,10014,30442,30444],{},[151,30443,10082],{}," and hooks that integrate deeply with Rust’s ownership model:",[48,30446,30447],{"className":10027,"code":10090,"language":10029,"meta":56,"style":56},[40,30448,30449],{"__ignoreMap":56},[449,30450,30451],{"class":451,"line":452},[449,30452,10090],{},[21,30454,30455],{},"Benefits:",[383,30457,30458,30461,30464],{},[386,30459,30460],{},"No stale closures",[386,30462,30463],{},"Predictable updates",[386,30465,30466],{},"Compile-time guarantees",[2808,30468,8883],{"id":30469},"react-native-1",[21,30471,30472],{},"React Native relies on:",[383,30474,30475,30478],{},[386,30476,30477],{},"React hooks",[386,30479,30480],{},"External libraries (Redux, Zustand, Recoil)",[21,30482,30483],{},"This flexibility is powerful—but can lead to:",[383,30485,30486,30489],{},[386,30487,30488],{},"State sprawl",[386,30490,30491],{},"Hard-to-debug race conditions",[73,30493],{},[32,30495,30497],{"id":30496},"tooling-developer-experience","Tooling & Developer Experience",[2808,30499,30501],{"id":30500},"dioxus-tooling","Dioxus Tooling",[383,30503,30504,30507,30510,30513],{},[386,30505,30506],{},"Built-in CLI",[386,30508,30509],{},"Hot reload",[386,30511,30512],{},"Unified build pipeline",[386,30514,30515],{},"Rust analyzer integration",[48,30517,30519],{"className":839,"code":30518,"language":841,"meta":56,"style":56},"dx serve\n",[40,30520,30521],{"__ignoreMap":56},[449,30522,30523,30526],{"class":451,"line":452},[449,30524,30525],{"class":721},"dx",[449,30527,30528],{"class":691}," serve\n",[21,30530,30531,30532,165],{},"Everything—from UI to backend—lives in ",[151,30533,30534],{},"one ecosystem",[2808,30536,30538],{"id":30537},"react-native-tooling","React Native Tooling",[383,30540,30541,30544,30547,30550],{},[386,30542,30543],{},"Metro bundler",[386,30545,30546],{},"Native SDKs",[386,30548,30549],{},"Xcode & Android Studio",[386,30551,30552],{},"Many third-party tools",[21,30554,30555],{},"Powerful, but:",[383,30557,30558,30561,30564],{},[386,30559,30560],{},"Complex setup",[386,30562,30563],{},"Platform-specific bugs",[386,30565,30566],{},"Steeper maintenance at scale",[2808,30568,30570],{"id":30569},"tooling-summary","Tooling Summary",[21,30572,30573],{},"Dioxus prioritizes simplicity and cohesion.\nReact Native prioritizes flexibility and scale.\n::",[73,30575],{},[32,30577,10320],{"id":10319},[2808,30579,9927],{"id":30580},"dioxus-2",[21,30582,30583],{},"Dioxus shines here.",[383,30585,30586,30589,30592,30595],{},[386,30587,30588],{},"Frontend and backend both in Rust",[386,30590,30591],{},"Shared types",[386,30593,30594],{},"Server functions",[386,30596,30597],{},"SSR support",[21,30599,30600,30601,165],{},"This makes it ideal for ",[151,30602,30603],{},"end-to-end Rust teams",[2808,30605,8883],{"id":30606},"react-native-2",[21,30608,30609],{},"React Native typically requires:",[383,30611,30612,30615,30618],{},[386,30613,30614],{},"Separate backend stack",[386,30616,30617],{},"API contracts",[386,30619,30620],{},"Data validation duplication",[21,30622,30623],{},"Still powerful—but fragmented.",[73,30625],{},[32,30627,30629],{"id":30628},"ecosystem-community","Ecosystem & Community",[2808,30631,8883],{"id":30632},"react-native-3",[383,30634,30635,30638,30641],{},[386,30636,30637],{},"Massive ecosystem",[386,30639,30640],{},"Thousands of libraries",[386,30642,30643],{},"Long-term industry support",[2808,30645,9927],{"id":30646},"dioxus-3",[383,30648,30649,30652,30655],{},[386,30650,30651],{},"Smaller but growing ecosystem",[386,30653,30654],{},"Strong Rust community backing",[386,30656,30657],{},"Rapid iteration",[21,30659,30660],{},"Dioxus is younger, but its pace of development is significantly faster than React Native’s early years.\n::",[73,30662],{},[32,30664,30666],{"id":30665},"when-to-choose-dioxus","When to Choose Dioxus",[21,30668,30669,30670,30672],{},"Choose ",[151,30671,9927],{}," if:",[383,30674,30675,30678,30681,30684],{},[386,30676,30677],{},"You value performance and safety",[386,30679,30680],{},"Your team knows Rust",[386,30682,30683],{},"You want a unified fullstack solution",[386,30685,30686],{},"You target web + desktop + mobile",[32,30688,30690],{"id":30689},"when-to-choose-react-native","When to Choose React Native",[21,30692,30669,30693,30672],{},[151,30694,8883],{},[383,30696,30697,30700,30703,30706],{},[386,30698,30699],{},"You need mature mobile tooling",[386,30701,30702],{},"You rely on third-party native modules",[386,30704,30705],{},"Your team is React-centric",[386,30707,30708],{},"Time-to-market is critical",[73,30710],{},[16,30712,3681],{"id":3680},[21,30714,30715,30716,30719],{},"React Native remains a dominant force in cross-platform mobile development, backed by a massive ecosystem and years of production usage. However, ",[151,30717,30718],{},"Dioxus 0.7 introduces a compelling alternative","—one built on performance, safety, and architectural simplicity.",[21,30721,30722],{},"For teams willing to invest in Rust, Dioxus offers a future-proof approach to building fast, reliable, cross-platform applications with fewer runtime surprises.",[21,30724,30725],{},"The choice isn’t about which framework is “better”—it’s about which philosophy aligns with your product and team.",[21,30727,30728],{},"And in 2026, Dioxus is no longer experimental—it’s a serious contender. 🚀",[3728,30730,30731],{},"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 .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}",{"title":56,"searchDepth":111,"depth":111,"links":30733},[30734,30735,30739,30743,30747,30751,30756,30760,30764,30765],{"id":30071,"depth":114,"text":30072},{"id":30156,"depth":114,"text":30157,"children":30736},[30737,30738],{"id":10435,"depth":111,"text":9927},{"id":8304,"depth":111,"text":8883},{"id":30263,"depth":114,"text":30264,"children":30740},[30741,30742],{"id":30267,"depth":111,"text":30268},{"id":30302,"depth":111,"text":30303},{"id":30333,"depth":114,"text":30334,"children":30744},[30745,30746],{"id":30337,"depth":111,"text":30338},{"id":30399,"depth":111,"text":30400},{"id":10075,"depth":114,"text":10076,"children":30748},[30749,30750],{"id":30439,"depth":111,"text":9927},{"id":30469,"depth":111,"text":8883},{"id":30496,"depth":114,"text":30497,"children":30752},[30753,30754,30755],{"id":30500,"depth":111,"text":30501},{"id":30537,"depth":111,"text":30538},{"id":30569,"depth":111,"text":30570},{"id":10319,"depth":114,"text":10320,"children":30757},[30758,30759],{"id":30580,"depth":111,"text":9927},{"id":30606,"depth":111,"text":8883},{"id":30628,"depth":114,"text":30629,"children":30761},[30762,30763],{"id":30632,"depth":111,"text":8883},{"id":30646,"depth":111,"text":9927},{"id":30665,"depth":114,"text":30666},{"id":30689,"depth":114,"text":30690},"2026-01-11","A deep comparison of Dioxus 0.7 and React Native, exploring performance, developer experience, tooling, and when to choose each framework.",{"type":13,"value":30769},[30770,30772,30778],[16,30771,333],{"id":332},[21,30773,30051,30774,30054,30776,30057],{},[151,30775,8883],{},[151,30777,9898],{},[21,30779,30060,30780,30063,30782,30066],{},[151,30781,9927],{},[151,30783,8883],{},[10435,30785,10029,10436,30786],"react native","mobile development",{},"\u002Fblog\u002Freact-native-vs-dious",{"title":30043,"description":30767},"blog\u002Freact-native-vs-dious","VlkcpvVRs-Q5BDQX5w19bfZQiDlIXmJbA3bgXLsQqsc",{"id":30793,"title":30794,"author":30795,"body":30796,"canonical":116,"categories":116,"date":30992,"description":30993,"excerpt":30994,"extension":119,"img":31006,"keywords":31007,"language":3788,"meta":31012,"navigation":130,"ogImage":116,"path":31013,"rating":474,"readTime":486,"seo":31014,"stem":31015,"tag":3793,"__hash__":31016},"blog\u002Fblog\u002Fsolo-level.md","Level Up in 2026: Why The Solo Level Is a Game-Changer for Developers",{"name":9,"img":11,"website":327},{"type":13,"value":30797,"toc":30983},[30798,30800,30813,30815,30819,30828,30835,30839,30842,30868,30891,30894,30898,30907,30913,30920,30924,30931,30936,30940,30951,30953,30955,30972,30980],[16,30799,333],{"id":332},[21,30801,30802,30803,30806,30807,30810,30811,165],{},"What if leveling up your ",[151,30804,30805],{},"coding skills"," felt more like playing your favorite RPG than checking off tasks? That’s exactly the experience The Solo Level delivers — a gamified, AI-enhanced platform that makes coding ",[151,30808,30809],{},"fun, competitive, and deeply motivating",". If you’re a developer looking to stay engaged, track progress, and connect with others on the same journey, this is one tool you’ll want in your toolkit in ",[151,30812,21028],{},[16,30814,4373],{"id":4372},[32,30816,30818],{"id":30817},"what-is-the-solo-level","What Is The Solo Level?",[21,30820,30821,30822,30825,30826],{},"The Solo Level is an innovative coding platform that transforms everyday programming work into a ",[151,30823,30824],{},"role-playing game experience",". Every commit becomes XP, streaks power up your multiplier, and even bugs can earn you rewards — turning the grind of coding into an adventure. ",[9909,30827,9912],{"index":9911},[25,30829,30830],{"type":363},[21,30831,30832,30833],{},"With seamless VS Code integration and real-time visual stats, you don’t just write code — you level up your developer avatar as you improve. ",[9909,30834,9938],{"index":4799},[32,30836,30838],{"id":30837},"how-it-works-your-coding-quest-begins","How It Works — Your Coding Quest Begins",[21,30840,30841],{},"Getting started is simple:",[816,30843,30844,30852,30860],{},[386,30845,30846,30849,30850],{},[151,30847,30848],{},"Install the Solo Level VS Code extension"," — your editor becomes your dashboard as you code. ",[9909,30851,9950],{"index":4827},[386,30853,30854,30857,30858],{},[151,30855,30856],{},"Write & Commit Code"," — every commit earns you XP and tracks your progress. ",[9909,30859,9997],{"index":4850},[386,30861,30862,30865,30866],{},[151,30863,30864],{},"Earn Badges & Climb the Leaderboards"," — unlock achievements, compete globally, and celebrate your wins with the community. ",[9909,30867,10005],{"index":4874},[2805,30869,30870,30874],{},[2808,30871,30873],{"id":30872},"core-gameplay-features","Core Gameplay Features",[383,30875,30876,30879,30882,30885,30888],{},[386,30877,30878],{},"✔ XP for commits and daily streaks",[386,30880,30881],{},"✔ Visual progress dashboards",[386,30883,30884],{},"✔ Global and guild leaderboards",[386,30886,30887],{},"✔ Bounty system for challenges",[386,30889,30890],{},"✔ Team management and competitions",[21,30892,30893],{},"Whether you’re coding solo or rallying a guild of teammates, Solo Level turns your codebase into a world of quests and rewards.",[32,30895,30897],{"id":30896},"supercharged-by-ai","Supercharged by AI",[21,30899,30900,30901,30904,30905],{},"One of the most exciting aspects of The Solo Level is how it uses ",[151,30902,30903],{},"AI to boost learning and challenge creation",". Personalized AI-generated coding challenges match your skill level and learning goals, offering custom tasks that help you grow faster. ",[9909,30906,10020],{"index":4898},[427,30908,30910],{"icon":21130,"title":30909},"AI That Learns With You",[21,30911,30912],{},"AI isn’t just a buzzword here — it creates tailored challenges and insightful feedback that adapt to your progress.",[21,30914,30915,30916],{},"AI also powers smart analytics, giving actionable insights into your coding habits so you can identify strengths and areas to improve. ",[9909,30917,30919],{"index":30918},"6","oaicite:6",[32,30921,30923],{"id":30922},"go-beyond-solo-build-a-community","Go Beyond Solo — Build a Community",[21,30925,30926,30927],{},"Coding doesn’t have to be a solitary pursuit. With guilds and team challenges, The Solo Level encourages collaboration, friendly competition, and shared accomplishment. Assemble a team, set goals, track progress together, and climb the leaderboards as a squad. ",[9909,30928,30930],{"index":30929},"7","oaicite:7",[25,30932,30933],{"type":27},[21,30934,30935],{},"Friendly competition isn’t just fun — it motivates consistent progress and boosts productivity.",[32,30937,30939],{"id":30938},"why-developers-love-it","Why Developers Love It",[21,30941,30942,30943,30946,30947],{},"Solo Level isn’t just another tool — it’s a ",[151,30944,30945],{},"motivation engine",". Whether you’re a beginner wanting to build habits or a seasoned pro striving for mastery, gamification helps you stay engaged and productive. From day one you’re rewarded, tracked, and connected. ",[9909,30948,30950],{"index":30949},"8","oaicite:8",[73,30952],{},[16,30954,3681],{"id":3680},[21,30956,30957,30958,30961,30962,349,30965,1542,30968,30971],{},"The Solo Level turns coding into something you ",[156,30959,30960],{},"want"," to come back to every day. It blends ",[151,30963,30964],{},"game mechanics",[151,30966,30967],{},"AI-powered challenges",[151,30969,30970],{},"community engagement"," to make improving your craft both fun and measurable. If you’re ready to level up your skills and join a growing guild of developers, there’s never been a better time to start your journey. 🎮🚀",[21,30973,30974,30975,30979],{},"👉 Check it out at ",[3861,30976,30977],{"href":30977,"rel":30978},"https:\u002F\u002Fwww.thesololevel.com\u002F",[3865]," and begin your own coding adventure.",[9909,30981,30982],{},"oaicite:9",{"title":56,"searchDepth":111,"depth":111,"links":30984},[30985,30986,30989,30990,30991],{"id":30817,"depth":114,"text":30818},{"id":30837,"depth":114,"text":30838,"children":30987},[30988],{"id":30872,"depth":111,"text":30873},{"id":30896,"depth":114,"text":30897},{"id":30922,"depth":114,"text":30923},{"id":30938,"depth":114,"text":30939},"2026-01-05","Explore how The Solo Level turns coding into an RPG adventure with AI-powered challenges, XP systems, and community leaderboards.",{"type":13,"value":30995},[30996,30998],[16,30997,333],{"id":332},[21,30999,30802,31000,30806,31002,30810,31004,165],{},[151,31001,30805],{},[151,31003,30809],{},[151,31005,21028],{},"https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1767533332\u002Fsolo_level_pqfwko.jpg",[31008,31009,3782,31010,31011],"solo level","coding gamification","developers","productivity",{},"\u002Fblog\u002Fsolo-level",{"title":30794,"description":30993},"blog\u002Fsolo-level","mLN2pXFUaKU9_0bbZ48fq4EhyfBty7OHC2KRS2cxt9Y",{"id":31018,"title":31019,"author":31020,"body":31021,"canonical":116,"categories":116,"date":32376,"description":32377,"excerpt":32378,"extension":119,"img":7074,"keywords":32384,"language":3788,"meta":32389,"navigation":130,"ogImage":116,"path":32390,"rating":474,"readTime":492,"seo":32391,"stem":32392,"tag":3793,"__hash__":32393},"blog\u002Fblog\u002Ftanstack-query.md","Mastering Data Fetching with TanStack Query: A Modern Approach",{"name":9,"img":11,"website":327},{"type":13,"value":31022,"toc":32360},[31023,31025,31028,31032,31035,31038,31042,31045,31083,31088,31092,31095,31099,31127,31131,31134,31191,31195,31198,31343,31349,31353,31357,31360,31547,31551,31554,31673,31682,31686,31689,31827,31829,31834,31838,31891,31895,31898,32062,32066,32069,32116,32122,32126,32130,32133,32202,32205,32208,32341,32343,32345,32348,32351,32354,32357],[16,31024,333],{"id":332},[21,31026,31027],{},"Data fetching is one of the most common challenges developers face when building modern web applications. While it might seem straightforward at first, managing server state quickly becomes complex when you need to handle caching, background updates, pagination, and error states. This is where TanStack Query (formerly React Query) comes in—a powerful library that transforms how we think about server state management.",[16,31029,31031],{"id":31030},"what-is-tanstack-query","What is TanStack Query?",[21,31033,31034],{},"TanStack Query is a data-fetching and state management library that provides powerful tools for fetching, caching, synchronizing, and updating server state in your applications. Unlike traditional state management solutions, TanStack Query treats server data as a first-class citizen, recognizing that it has fundamentally different characteristics than client state.",[21,31036,31037],{},"The library works seamlessly with React, Vue, Svelte, and Solid, making it a versatile choice regardless of your framework preference. It eliminates the need for writing complex boilerplate code and helps you build more maintainable applications.",[32,31039,31041],{"id":31040},"why-use-tanstack-query","Why Use TanStack Query?",[21,31043,31044],{},"Traditional data fetching approaches often require you to manage loading states, error handling, caching logic, and refetching strategies manually. TanStack Query handles all of this out of the box, providing:",[383,31046,31047,31053,31059,31065,31071,31077],{},[386,31048,31049,31052],{},[151,31050,31051],{},"Automatic caching"," with intelligent cache invalidation",[386,31054,31055,31058],{},[151,31056,31057],{},"Background refetching"," to keep data fresh",[386,31060,31061,31064],{},[151,31062,31063],{},"Optimistic updates"," for better UX",[386,31066,31067,31070],{},[151,31068,31069],{},"Request deduplication"," to prevent unnecessary network calls",[386,31072,31073,31076],{},[151,31074,31075],{},"Pagination and infinite scroll"," support",[386,31078,31079,31082],{},[151,31080,31081],{},"Prefetching"," for improved perceived performance",[25,31084,31085],{"type":363},[21,31086,31087],{},"TanStack Query doesn't replace fetch or axios—it enhances them by providing a smart layer of abstraction for managing server state.",[16,31089,31091],{"id":31090},"getting-started","Getting Started",[21,31093,31094],{},"Let's dive into how you can start using TanStack Query in your React application.",[32,31096,31098],{"id":31097},"installation","Installation",[48,31100,31102],{"className":839,"code":31101,"filename":4384,"language":841,"meta":56,"style":56},"npm install @tanstack\u002Freact-query\n# or\nyarn add @tanstack\u002Freact-query\n",[40,31103,31104,31113,31118],{"__ignoreMap":56},[449,31105,31106,31108,31110],{"class":451,"line":452},[449,31107,3703],{"class":721},[449,31109,3706],{"class":691},[449,31111,31112],{"class":691}," @tanstack\u002Freact-query\n",[449,31114,31115],{"class":451,"line":114},[449,31116,31117],{"class":705},"# or\n",[449,31119,31120,31123,31125],{"class":451,"line":111},[449,31121,31122],{"class":721},"yarn",[449,31124,2478],{"class":691},[449,31126,31112],{"class":691},[32,31128,31130],{"id":31129},"basic-setup","Basic Setup",[21,31132,31133],{},"First, wrap your application with the QueryClientProvider:",[48,31135,31138],{"className":30231,"code":31136,"filename":31137,"language":30233,"meta":56,"style":56},"import { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query';\n\nconst queryClient = new QueryClient();\n\nfunction App() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      \u003CYourApp \u002F>\n    \u003C\u002FQueryClientProvider>\n  );\n}\n","App.tsx",[40,31139,31140,31145,31149,31154,31158,31163,31168,31173,31178,31183,31187],{"__ignoreMap":56},[449,31141,31142],{"class":451,"line":452},[449,31143,31144],{},"import { QueryClient, QueryClientProvider } from '@tanstack\u002Freact-query';\n",[449,31146,31147],{"class":451,"line":114},[449,31148,700],{"emptyLinePlaceholder":130},[449,31150,31151],{"class":451,"line":111},[449,31152,31153],{},"const queryClient = new QueryClient();\n",[449,31155,31156],{"class":451,"line":468},[449,31157,700],{"emptyLinePlaceholder":130},[449,31159,31160],{"class":451,"line":474},[449,31161,31162],{},"function App() {\n",[449,31164,31165],{"class":451,"line":480},[449,31166,31167],{},"  return (\n",[449,31169,31170],{"class":451,"line":486},[449,31171,31172],{},"    \u003CQueryClientProvider client={queryClient}>\n",[449,31174,31175],{"class":451,"line":492},[449,31176,31177],{},"      \u003CYourApp \u002F>\n",[449,31179,31180],{"class":451,"line":498},[449,31181,31182],{},"    \u003C\u002FQueryClientProvider>\n",[449,31184,31185],{"class":451,"line":133},[449,31186,6436],{},[449,31188,31189],{"class":451,"line":509},[449,31190,4604],{},[32,31192,31194],{"id":31193},"your-first-query","Your First Query",[21,31196,31197],{},"Here's a simple example of fetching user data:",[48,31199,31202],{"className":30231,"code":31200,"filename":31201,"language":30233,"meta":56,"style":56},"import { useQuery } from '@tanstack\u002Freact-query';\n\ninterface User {\n  id: number;\n  name: string;\n  email: string;\n}\n\nasync function fetchUser(userId: string): Promise\u003CUser> {\n  const response = await fetch(`\u002Fapi\u002Fusers\u002F${userId}`);\n  if (!response.ok) throw new Error('Failed to fetch user');\n  return response.json();\n}\n\nfunction UserProfile({ userId }: { userId: string }) {\n  const { data, isLoading, error } = useQuery({\n    queryKey: ['user', userId],\n    queryFn: () => fetchUser(userId),\n  });\n\n  if (isLoading) return \u003Cdiv>Loading...\u003C\u002Fdiv>;\n  if (error) return \u003Cdiv>Error: {error.message}\u003C\u002Fdiv>;\n\n  return (\n    \u003Cdiv>\n      \u003Ch1>{data.name}\u003C\u002Fh1>\n      \u003Cp>{data.email}\u003C\u002Fp>\n    \u003C\u002Fdiv>\n  );\n}\n","UserProfile.tsx",[40,31203,31204,31209,31213,31218,31223,31228,31233,31237,31241,31246,31251,31256,31261,31265,31269,31274,31279,31284,31289,31293,31297,31302,31307,31311,31315,31320,31325,31330,31335,31339],{"__ignoreMap":56},[449,31205,31206],{"class":451,"line":452},[449,31207,31208],{},"import { useQuery } from '@tanstack\u002Freact-query';\n",[449,31210,31211],{"class":451,"line":114},[449,31212,700],{"emptyLinePlaceholder":130},[449,31214,31215],{"class":451,"line":111},[449,31216,31217],{},"interface User {\n",[449,31219,31220],{"class":451,"line":468},[449,31221,31222],{},"  id: number;\n",[449,31224,31225],{"class":451,"line":474},[449,31226,31227],{},"  name: string;\n",[449,31229,31230],{"class":451,"line":480},[449,31231,31232],{},"  email: string;\n",[449,31234,31235],{"class":451,"line":486},[449,31236,4604],{},[449,31238,31239],{"class":451,"line":492},[449,31240,700],{"emptyLinePlaceholder":130},[449,31242,31243],{"class":451,"line":498},[449,31244,31245],{},"async function fetchUser(userId: string): Promise\u003CUser> {\n",[449,31247,31248],{"class":451,"line":133},[449,31249,31250],{},"  const response = await fetch(`\u002Fapi\u002Fusers\u002F${userId}`);\n",[449,31252,31253],{"class":451,"line":509},[449,31254,31255],{},"  if (!response.ok) throw new Error('Failed to fetch user');\n",[449,31257,31258],{"class":451,"line":318},[449,31259,31260],{},"  return response.json();\n",[449,31262,31263],{"class":451,"line":520},[449,31264,4604],{},[449,31266,31267],{"class":451,"line":526},[449,31268,700],{"emptyLinePlaceholder":130},[449,31270,31271],{"class":451,"line":532},[449,31272,31273],{},"function UserProfile({ userId }: { userId: string }) {\n",[449,31275,31276],{"class":451,"line":2790},[449,31277,31278],{},"  const { data, isLoading, error } = useQuery({\n",[449,31280,31281],{"class":451,"line":3511},[449,31282,31283],{},"    queryKey: ['user', userId],\n",[449,31285,31286],{"class":451,"line":3526},[449,31287,31288],{},"    queryFn: () => fetchUser(userId),\n",[449,31290,31291],{"class":451,"line":3531},[449,31292,13106],{},[449,31294,31295],{"class":451,"line":3536},[449,31296,700],{"emptyLinePlaceholder":130},[449,31298,31299],{"class":451,"line":4009},[449,31300,31301],{},"  if (isLoading) return \u003Cdiv>Loading...\u003C\u002Fdiv>;\n",[449,31303,31304],{"class":451,"line":4015},[449,31305,31306],{},"  if (error) return \u003Cdiv>Error: {error.message}\u003C\u002Fdiv>;\n",[449,31308,31309],{"class":451,"line":4021},[449,31310,700],{"emptyLinePlaceholder":130},[449,31312,31313],{"class":451,"line":4027},[449,31314,31167],{},[449,31316,31317],{"class":451,"line":4032},[449,31318,31319],{},"    \u003Cdiv>\n",[449,31321,31322],{"class":451,"line":4038},[449,31323,31324],{},"      \u003Ch1>{data.name}\u003C\u002Fh1>\n",[449,31326,31327],{"class":451,"line":4044},[449,31328,31329],{},"      \u003Cp>{data.email}\u003C\u002Fp>\n",[449,31331,31332],{"class":451,"line":4049},[449,31333,31334],{},"    \u003C\u002Fdiv>\n",[449,31336,31337],{"class":451,"line":4054},[449,31338,6436],{},[449,31340,31341],{"class":451,"line":4059},[449,31342,4604],{},[427,31344,31346],{"icon":11395,"title":31345},"Query Keys",[21,31347,31348],{},"Query keys are crucial in TanStack Query. They uniquely identify your queries and are used for caching, refetching, and cache invalidation. Use arrays with descriptive values for better organization.",[16,31350,31352],{"id":31351},"advanced-features","Advanced Features",[32,31354,31356],{"id":31355},"mutations","Mutations",[21,31358,31359],{},"Mutations handle data updates, creations, and deletions. Here's how to create a mutation for updating user data:",[48,31361,31364],{"className":30231,"code":31362,"filename":31363,"language":30233,"meta":56,"style":56},"import { useMutation, useQueryClient } from '@tanstack\u002Freact-query';\n\ninterface UpdateUserData {\n  name: string;\n  email: string;\n}\n\nfunction UpdateUserForm({ userId }: { userId: string }) {\n  const queryClient = useQueryClient();\n\n  const mutation = useMutation({\n    mutationFn: (userData: UpdateUserData) =>\n      fetch(`\u002Fapi\u002Fusers\u002F${userId}`, {\n        method: 'PUT',\n        body: JSON.stringify(userData),\n        headers: { 'Content-Type': 'application\u002Fjson' },\n      }),\n    onSuccess: () => {\n      \u002F\u002F Invalidate and refetch user data\n      queryClient.invalidateQueries({ queryKey: ['user', userId] });\n    },\n  });\n\n  const handleSubmit = (data: UpdateUserData) => {\n    mutation.mutate(data);\n  };\n\n  return (\n    \u003Cform onSubmit={(e) => {\n      e.preventDefault();\n      \u002F\u002F Form handling logic\n    }}>\n      {mutation.isPending && \u003Cp>Updating...\u003C\u002Fp>}\n      {mutation.isError && \u003Cp>Error updating user\u003C\u002Fp>}\n      {mutation.isSuccess && \u003Cp>User updated successfully!\u003C\u002Fp>}\n      {\u002F* Form fields *\u002F}\n    \u003C\u002Fform>\n  );\n}\n","UpdateUser.tsx",[40,31365,31366,31371,31375,31380,31384,31388,31392,31396,31401,31406,31410,31415,31420,31425,31430,31435,31440,31445,31450,31455,31460,31464,31468,31472,31477,31482,31486,31490,31494,31499,31504,31509,31514,31519,31524,31529,31534,31539,31543],{"__ignoreMap":56},[449,31367,31368],{"class":451,"line":452},[449,31369,31370],{},"import { useMutation, useQueryClient } from '@tanstack\u002Freact-query';\n",[449,31372,31373],{"class":451,"line":114},[449,31374,700],{"emptyLinePlaceholder":130},[449,31376,31377],{"class":451,"line":111},[449,31378,31379],{},"interface UpdateUserData {\n",[449,31381,31382],{"class":451,"line":468},[449,31383,31227],{},[449,31385,31386],{"class":451,"line":474},[449,31387,31232],{},[449,31389,31390],{"class":451,"line":480},[449,31391,4604],{},[449,31393,31394],{"class":451,"line":486},[449,31395,700],{"emptyLinePlaceholder":130},[449,31397,31398],{"class":451,"line":492},[449,31399,31400],{},"function UpdateUserForm({ userId }: { userId: string }) {\n",[449,31402,31403],{"class":451,"line":498},[449,31404,31405],{},"  const queryClient = useQueryClient();\n",[449,31407,31408],{"class":451,"line":133},[449,31409,700],{"emptyLinePlaceholder":130},[449,31411,31412],{"class":451,"line":509},[449,31413,31414],{},"  const mutation = useMutation({\n",[449,31416,31417],{"class":451,"line":318},[449,31418,31419],{},"    mutationFn: (userData: UpdateUserData) =>\n",[449,31421,31422],{"class":451,"line":520},[449,31423,31424],{},"      fetch(`\u002Fapi\u002Fusers\u002F${userId}`, {\n",[449,31426,31427],{"class":451,"line":526},[449,31428,31429],{},"        method: 'PUT',\n",[449,31431,31432],{"class":451,"line":532},[449,31433,31434],{},"        body: JSON.stringify(userData),\n",[449,31436,31437],{"class":451,"line":2790},[449,31438,31439],{},"        headers: { 'Content-Type': 'application\u002Fjson' },\n",[449,31441,31442],{"class":451,"line":3511},[449,31443,31444],{},"      }),\n",[449,31446,31447],{"class":451,"line":3526},[449,31448,31449],{},"    onSuccess: () => {\n",[449,31451,31452],{"class":451,"line":3531},[449,31453,31454],{},"      \u002F\u002F Invalidate and refetch user data\n",[449,31456,31457],{"class":451,"line":3536},[449,31458,31459],{},"      queryClient.invalidateQueries({ queryKey: ['user', userId] });\n",[449,31461,31462],{"class":451,"line":4009},[449,31463,1458],{},[449,31465,31466],{"class":451,"line":4015},[449,31467,13106],{},[449,31469,31470],{"class":451,"line":4021},[449,31471,700],{"emptyLinePlaceholder":130},[449,31473,31474],{"class":451,"line":4027},[449,31475,31476],{},"  const handleSubmit = (data: UpdateUserData) => {\n",[449,31478,31479],{"class":451,"line":4032},[449,31480,31481],{},"    mutation.mutate(data);\n",[449,31483,31484],{"class":451,"line":4038},[449,31485,4764],{},[449,31487,31488],{"class":451,"line":4044},[449,31489,700],{"emptyLinePlaceholder":130},[449,31491,31492],{"class":451,"line":4049},[449,31493,31167],{},[449,31495,31496],{"class":451,"line":4054},[449,31497,31498],{},"    \u003Cform onSubmit={(e) => {\n",[449,31500,31501],{"class":451,"line":4059},[449,31502,31503],{},"      e.preventDefault();\n",[449,31505,31506],{"class":451,"line":4065},[449,31507,31508],{},"      \u002F\u002F Form handling logic\n",[449,31510,31511],{"class":451,"line":4070},[449,31512,31513],{},"    }}>\n",[449,31515,31516],{"class":451,"line":4075},[449,31517,31518],{},"      {mutation.isPending && \u003Cp>Updating...\u003C\u002Fp>}\n",[449,31520,31521],{"class":451,"line":4081},[449,31522,31523],{},"      {mutation.isError && \u003Cp>Error updating user\u003C\u002Fp>}\n",[449,31525,31526],{"class":451,"line":4086},[449,31527,31528],{},"      {mutation.isSuccess && \u003Cp>User updated successfully!\u003C\u002Fp>}\n",[449,31530,31531],{"class":451,"line":4092},[449,31532,31533],{},"      {\u002F* Form fields *\u002F}\n",[449,31535,31536],{"class":451,"line":4098},[449,31537,31538],{},"    \u003C\u002Fform>\n",[449,31540,31541],{"class":451,"line":4104},[449,31542,6436],{},[449,31544,31545],{"class":451,"line":4110},[449,31546,4604],{},[32,31548,31550],{"id":31549},"optimistic-updates","Optimistic Updates",[21,31552,31553],{},"Optimistic updates provide instant feedback by updating the UI before the server responds:",[48,31555,31558],{"className":30231,"code":31556,"filename":31557,"language":30233,"meta":56,"style":56},"const mutation = useMutation({\n  mutationFn: updateTodo,\n  onMutate: async (newTodo) => {\n    \u002F\u002F Cancel outgoing refetches\n    await queryClient.cancelQueries({ queryKey: ['todos'] });\n\n    \u002F\u002F Snapshot the previous value\n    const previousTodos = queryClient.getQueryData(['todos']);\n\n    \u002F\u002F Optimistically update\n    queryClient.setQueryData(['todos'], (old: Todo[]) => [...old, newTodo]);\n\n    \u002F\u002F Return context with the snapshot\n    return { previousTodos };\n  },\n  onError: (err, newTodo, context) => {\n    \u002F\u002F Rollback on error\n    queryClient.setQueryData(['todos'], context?.previousTodos);\n  },\n  onSettled: () => {\n    \u002F\u002F Refetch after error or success\n    queryClient.invalidateQueries({ queryKey: ['todos'] });\n  },\n});\n","OptimisticUpdate.tsx",[40,31559,31560,31565,31570,31575,31580,31585,31589,31594,31599,31603,31608,31613,31617,31622,31627,31631,31636,31641,31646,31650,31655,31660,31665,31669],{"__ignoreMap":56},[449,31561,31562],{"class":451,"line":452},[449,31563,31564],{},"const mutation = useMutation({\n",[449,31566,31567],{"class":451,"line":114},[449,31568,31569],{},"  mutationFn: updateTodo,\n",[449,31571,31572],{"class":451,"line":111},[449,31573,31574],{},"  onMutate: async (newTodo) => {\n",[449,31576,31577],{"class":451,"line":468},[449,31578,31579],{},"    \u002F\u002F Cancel outgoing refetches\n",[449,31581,31582],{"class":451,"line":474},[449,31583,31584],{},"    await queryClient.cancelQueries({ queryKey: ['todos'] });\n",[449,31586,31587],{"class":451,"line":480},[449,31588,700],{"emptyLinePlaceholder":130},[449,31590,31591],{"class":451,"line":486},[449,31592,31593],{},"    \u002F\u002F Snapshot the previous value\n",[449,31595,31596],{"class":451,"line":492},[449,31597,31598],{},"    const previousTodos = queryClient.getQueryData(['todos']);\n",[449,31600,31601],{"class":451,"line":498},[449,31602,700],{"emptyLinePlaceholder":130},[449,31604,31605],{"class":451,"line":133},[449,31606,31607],{},"    \u002F\u002F Optimistically update\n",[449,31609,31610],{"class":451,"line":509},[449,31611,31612],{},"    queryClient.setQueryData(['todos'], (old: Todo[]) => [...old, newTodo]);\n",[449,31614,31615],{"class":451,"line":318},[449,31616,700],{"emptyLinePlaceholder":130},[449,31618,31619],{"class":451,"line":520},[449,31620,31621],{},"    \u002F\u002F Return context with the snapshot\n",[449,31623,31624],{"class":451,"line":526},[449,31625,31626],{},"    return { previousTodos };\n",[449,31628,31629],{"class":451,"line":532},[449,31630,1474],{},[449,31632,31633],{"class":451,"line":2790},[449,31634,31635],{},"  onError: (err, newTodo, context) => {\n",[449,31637,31638],{"class":451,"line":3511},[449,31639,31640],{},"    \u002F\u002F Rollback on error\n",[449,31642,31643],{"class":451,"line":3526},[449,31644,31645],{},"    queryClient.setQueryData(['todos'], context?.previousTodos);\n",[449,31647,31648],{"class":451,"line":3531},[449,31649,1474],{},[449,31651,31652],{"class":451,"line":3536},[449,31653,31654],{},"  onSettled: () => {\n",[449,31656,31657],{"class":451,"line":4009},[449,31658,31659],{},"    \u002F\u002F Refetch after error or success\n",[449,31661,31662],{"class":451,"line":4015},[449,31663,31664],{},"    queryClient.invalidateQueries({ queryKey: ['todos'] });\n",[449,31666,31667],{"class":451,"line":4021},[449,31668,1474],{},[449,31670,31671],{"class":451,"line":4027},[449,31672,784],{},[2805,31674,31675,31679],{},[2808,31676,31678],{"id":31677},"performance-benefits","Performance Benefits",[21,31680,31681],{},"Optimistic updates can make your application feel up to 10x faster by eliminating the perceived latency of server responses.",[32,31683,31685],{"id":31684},"pagination","Pagination",[21,31687,31688],{},"TanStack Query makes pagination simple with built-in support:",[48,31690,31693],{"className":30231,"code":31691,"filename":31692,"language":30233,"meta":56,"style":56},"function PaginatedPosts() {\n  const [page, setPage] = useState(1);\n\n  const { data, isLoading, isPlaceholderData } = useQuery({\n    queryKey: ['posts', page],\n    queryFn: () => fetchPosts(page),\n    placeholderData: (previousData) => previousData,\n  });\n\n  return (\n    \u003Cdiv>\n      {data?.posts.map(post => \u003CPostCard key={post.id} post={post} \u002F>)}\n      \n      \u003Cbutton\n        onClick={() => setPage(old => Math.max(old - 1, 1))}\n        disabled={page === 1}\n      >\n        Previous\n      \u003C\u002Fbutton>\n      \n      \u003Cbutton\n        onClick={() => setPage(old => old + 1)}\n        disabled={isPlaceholderData || !data?.hasMore}\n      >\n        Next\n      \u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  );\n}\n","PaginatedList.tsx",[40,31694,31695,31700,31705,31709,31714,31719,31724,31729,31733,31737,31741,31745,31750,31754,31759,31764,31769,31774,31779,31784,31788,31792,31797,31802,31806,31811,31815,31819,31823],{"__ignoreMap":56},[449,31696,31697],{"class":451,"line":452},[449,31698,31699],{},"function PaginatedPosts() {\n",[449,31701,31702],{"class":451,"line":114},[449,31703,31704],{},"  const [page, setPage] = useState(1);\n",[449,31706,31707],{"class":451,"line":111},[449,31708,700],{"emptyLinePlaceholder":130},[449,31710,31711],{"class":451,"line":468},[449,31712,31713],{},"  const { data, isLoading, isPlaceholderData } = useQuery({\n",[449,31715,31716],{"class":451,"line":474},[449,31717,31718],{},"    queryKey: ['posts', page],\n",[449,31720,31721],{"class":451,"line":480},[449,31722,31723],{},"    queryFn: () => fetchPosts(page),\n",[449,31725,31726],{"class":451,"line":486},[449,31727,31728],{},"    placeholderData: (previousData) => previousData,\n",[449,31730,31731],{"class":451,"line":492},[449,31732,13106],{},[449,31734,31735],{"class":451,"line":498},[449,31736,700],{"emptyLinePlaceholder":130},[449,31738,31739],{"class":451,"line":133},[449,31740,31167],{},[449,31742,31743],{"class":451,"line":509},[449,31744,31319],{},[449,31746,31747],{"class":451,"line":318},[449,31748,31749],{},"      {data?.posts.map(post => \u003CPostCard key={post.id} post={post} \u002F>)}\n",[449,31751,31752],{"class":451,"line":520},[449,31753,26947],{},[449,31755,31756],{"class":451,"line":526},[449,31757,31758],{},"      \u003Cbutton\n",[449,31760,31761],{"class":451,"line":532},[449,31762,31763],{},"        onClick={() => setPage(old => Math.max(old - 1, 1))}\n",[449,31765,31766],{"class":451,"line":2790},[449,31767,31768],{},"        disabled={page === 1}\n",[449,31770,31771],{"class":451,"line":3511},[449,31772,31773],{},"      >\n",[449,31775,31776],{"class":451,"line":3526},[449,31777,31778],{},"        Previous\n",[449,31780,31781],{"class":451,"line":3531},[449,31782,31783],{},"      \u003C\u002Fbutton>\n",[449,31785,31786],{"class":451,"line":3536},[449,31787,26947],{},[449,31789,31790],{"class":451,"line":4009},[449,31791,31758],{},[449,31793,31794],{"class":451,"line":4015},[449,31795,31796],{},"        onClick={() => setPage(old => old + 1)}\n",[449,31798,31799],{"class":451,"line":4021},[449,31800,31801],{},"        disabled={isPlaceholderData || !data?.hasMore}\n",[449,31803,31804],{"class":451,"line":4027},[449,31805,31773],{},[449,31807,31808],{"class":451,"line":4032},[449,31809,31810],{},"        Next\n",[449,31812,31813],{"class":451,"line":4038},[449,31814,31783],{},[449,31816,31817],{"class":451,"line":4044},[449,31818,31334],{},[449,31820,31821],{"class":451,"line":4049},[449,31822,6436],{},[449,31824,31825],{"class":451,"line":4054},[449,31826,4604],{},[16,31828,20154],{"id":22820},[25,31830,31831],{"type":27},[21,31832,31833],{},"Always provide appropriate staleTime and gcTime values based on your data's characteristics. Not all data needs real-time updates!",[32,31835,31837],{"id":31836},"configure-cache-times-appropriately","Configure Cache Times Appropriately",[48,31839,31842],{"className":30231,"code":31840,"filename":31841,"language":30233,"meta":56,"style":56},"const queryClient = new QueryClient({\n  defaultOptions: {\n    queries: {\n      staleTime: 1000 * 60 * 5, \u002F\u002F 5 minutes\n      gcTime: 1000 * 60 * 10, \u002F\u002F 10 minutes (formerly cacheTime)\n      retry: 3,\n      refetchOnWindowFocus: true,\n    },\n  },\n});\n","queryClient.ts",[40,31843,31844,31849,31854,31859,31864,31869,31874,31879,31883,31887],{"__ignoreMap":56},[449,31845,31846],{"class":451,"line":452},[449,31847,31848],{},"const queryClient = new QueryClient({\n",[449,31850,31851],{"class":451,"line":114},[449,31852,31853],{},"  defaultOptions: {\n",[449,31855,31856],{"class":451,"line":111},[449,31857,31858],{},"    queries: {\n",[449,31860,31861],{"class":451,"line":468},[449,31862,31863],{},"      staleTime: 1000 * 60 * 5, \u002F\u002F 5 minutes\n",[449,31865,31866],{"class":451,"line":474},[449,31867,31868],{},"      gcTime: 1000 * 60 * 10, \u002F\u002F 10 minutes (formerly cacheTime)\n",[449,31870,31871],{"class":451,"line":480},[449,31872,31873],{},"      retry: 3,\n",[449,31875,31876],{"class":451,"line":486},[449,31877,31878],{},"      refetchOnWindowFocus: true,\n",[449,31880,31881],{"class":451,"line":492},[449,31882,1458],{},[449,31884,31885],{"class":451,"line":498},[449,31886,1474],{},[449,31888,31889],{"class":451,"line":133},[449,31890,784],{},[32,31892,31894],{"id":31893},"use-query-keys-consistently","Use Query Keys Consistently",[21,31896,31897],{},"Establish a naming convention for your query keys:",[48,31899,31902],{"className":670,"code":31900,"filename":31901,"language":673,"meta":56,"style":56},"export const queryKeys = {\n  users: {\n    all: ['users'] as const,\n    detail: (id: string) => ['users', id] as const,\n    posts: (id: string) => ['users', id, 'posts'] as const,\n  },\n  posts: {\n    all: ['posts'] as const,\n    detail: (id: string) => ['posts', id] as const,\n  },\n};\n","queryKeys.ts",[40,31903,31904,31917,31922,31938,31968,32003,32007,32012,32026,32054,32058],{"__ignoreMap":56},[449,31905,31906,31908,31910,31913,31915],{"class":451,"line":452},[449,31907,4629],{"class":680},[449,31909,10801],{"class":680},[449,31911,31912],{"class":714}," queryKeys",[449,31914,718],{"class":680},[449,31916,1501],{"class":684},[449,31918,31919],{"class":451,"line":114},[449,31920,31921],{"class":684},"  users: {\n",[449,31923,31924,31927,31929,31932,31934,31936],{"class":451,"line":111},[449,31925,31926],{"class":684},"    all: [",[449,31928,23048],{"class":691},[449,31930,31931],{"class":684},"] ",[449,31933,5065],{"class":680},[449,31935,10801],{"class":680},[449,31937,779],{"class":684},[449,31939,31940,31943,31945,31947,31949,31951,31953,31955,31957,31959,31962,31964,31966],{"class":451,"line":468},[449,31941,31942],{"class":721},"    detail",[449,31944,1752],{"class":684},[449,31946,16081],{"class":1491},[449,31948,3070],{"class":680},[449,31950,6083],{"class":714},[449,31952,1758],{"class":684},[449,31954,1498],{"class":680},[449,31956,13480],{"class":684},[449,31958,23048],{"class":691},[449,31960,31961],{"class":684},", id] ",[449,31963,5065],{"class":680},[449,31965,10801],{"class":680},[449,31967,779],{"class":684},[449,31969,31970,31973,31975,31977,31979,31981,31983,31985,31987,31989,31992,31995,31997,31999,32001],{"class":451,"line":474},[449,31971,31972],{"class":721},"    posts",[449,31974,1752],{"class":684},[449,31976,16081],{"class":1491},[449,31978,3070],{"class":680},[449,31980,6083],{"class":714},[449,31982,1758],{"class":684},[449,31984,1498],{"class":680},[449,31986,13480],{"class":684},[449,31988,23048],{"class":691},[449,31990,31991],{"class":684},", id, ",[449,31993,31994],{"class":691},"'posts'",[449,31996,31931],{"class":684},[449,31998,5065],{"class":680},[449,32000,10801],{"class":680},[449,32002,779],{"class":684},[449,32004,32005],{"class":451,"line":480},[449,32006,1474],{"class":684},[449,32008,32009],{"class":451,"line":486},[449,32010,32011],{"class":684},"  posts: {\n",[449,32013,32014,32016,32018,32020,32022,32024],{"class":451,"line":492},[449,32015,31926],{"class":684},[449,32017,31994],{"class":691},[449,32019,31931],{"class":684},[449,32021,5065],{"class":680},[449,32023,10801],{"class":680},[449,32025,779],{"class":684},[449,32027,32028,32030,32032,32034,32036,32038,32040,32042,32044,32046,32048,32050,32052],{"class":451,"line":498},[449,32029,31942],{"class":721},[449,32031,1752],{"class":684},[449,32033,16081],{"class":1491},[449,32035,3070],{"class":680},[449,32037,6083],{"class":714},[449,32039,1758],{"class":684},[449,32041,1498],{"class":680},[449,32043,13480],{"class":684},[449,32045,31994],{"class":691},[449,32047,31961],{"class":684},[449,32049,5065],{"class":680},[449,32051,10801],{"class":680},[449,32053,779],{"class":684},[449,32055,32056],{"class":451,"line":133},[449,32057,1474],{"class":684},[449,32059,32060],{"class":451,"line":509},[449,32061,20848],{"class":684},[32,32063,32065],{"id":32064},"enable-devtools","Enable DevTools",[21,32067,32068],{},"TanStack Query comes with excellent developer tools:",[48,32070,32072],{"className":30231,"code":32071,"filename":31137,"language":30233,"meta":56,"style":56},"import { ReactQueryDevtools } from '@tanstack\u002Freact-query-devtools';\n\nfunction App() {\n  return (\n    \u003CQueryClientProvider client={queryClient}>\n      \u003CYourApp \u002F>\n      \u003CReactQueryDevtools initialIsOpen={false} \u002F>\n    \u003C\u002FQueryClientProvider>\n  );\n}\n",[40,32073,32074,32079,32083,32087,32091,32095,32099,32104,32108,32112],{"__ignoreMap":56},[449,32075,32076],{"class":451,"line":452},[449,32077,32078],{},"import { ReactQueryDevtools } from '@tanstack\u002Freact-query-devtools';\n",[449,32080,32081],{"class":451,"line":114},[449,32082,700],{"emptyLinePlaceholder":130},[449,32084,32085],{"class":451,"line":111},[449,32086,31162],{},[449,32088,32089],{"class":451,"line":468},[449,32090,31167],{},[449,32092,32093],{"class":451,"line":474},[449,32094,31172],{},[449,32096,32097],{"class":451,"line":480},[449,32098,31177],{},[449,32100,32101],{"class":451,"line":486},[449,32102,32103],{},"      \u003CReactQueryDevtools initialIsOpen={false} \u002F>\n",[449,32105,32106],{"class":451,"line":492},[449,32107,31182],{},[449,32109,32110],{"class":451,"line":498},[449,32111,6436],{},[449,32113,32114],{"class":451,"line":133},[449,32115,4604],{},[427,32117,32119],{"icon":14202,"title":32118},"DevTools Pro Tip",[21,32120,32121],{},"The DevTools panel provides real-time insights into your queries, their states, and cached data. It's invaluable for debugging and understanding how TanStack Query manages your data.",[16,32123,32125],{"id":32124},"common-patterns","Common Patterns",[32,32127,32129],{"id":32128},"dependent-queries","Dependent Queries",[21,32131,32132],{},"Sometimes you need to fetch data based on previous query results:",[48,32134,32137],{"className":30231,"code":32135,"filename":32136,"language":30233,"meta":56,"style":56},"function UserPosts({ userId }: { userId: string }) {\n  const { data: user } = useQuery({\n    queryKey: ['user', userId],\n    queryFn: () => fetchUser(userId),\n  });\n\n  const { data: posts } = useQuery({\n    queryKey: ['posts', user?.id],\n    queryFn: () => fetchUserPosts(user!.id),\n    enabled: !!user, \u002F\u002F Only run when user is available\n  });\n\n  return \u003CPostsList posts={posts} \u002F>;\n}\n","DependentQueries.tsx",[40,32138,32139,32144,32149,32153,32157,32161,32165,32170,32175,32180,32185,32189,32193,32198],{"__ignoreMap":56},[449,32140,32141],{"class":451,"line":452},[449,32142,32143],{},"function UserPosts({ userId }: { userId: string }) {\n",[449,32145,32146],{"class":451,"line":114},[449,32147,32148],{},"  const { data: user } = useQuery({\n",[449,32150,32151],{"class":451,"line":111},[449,32152,31283],{},[449,32154,32155],{"class":451,"line":468},[449,32156,31288],{},[449,32158,32159],{"class":451,"line":474},[449,32160,13106],{},[449,32162,32163],{"class":451,"line":480},[449,32164,700],{"emptyLinePlaceholder":130},[449,32166,32167],{"class":451,"line":486},[449,32168,32169],{},"  const { data: posts } = useQuery({\n",[449,32171,32172],{"class":451,"line":492},[449,32173,32174],{},"    queryKey: ['posts', user?.id],\n",[449,32176,32177],{"class":451,"line":498},[449,32178,32179],{},"    queryFn: () => fetchUserPosts(user!.id),\n",[449,32181,32182],{"class":451,"line":133},[449,32183,32184],{},"    enabled: !!user, \u002F\u002F Only run when user is available\n",[449,32186,32187],{"class":451,"line":509},[449,32188,13106],{},[449,32190,32191],{"class":451,"line":318},[449,32192,700],{"emptyLinePlaceholder":130},[449,32194,32195],{"class":451,"line":520},[449,32196,32197],{},"  return \u003CPostsList posts={posts} \u002F>;\n",[449,32199,32200],{"class":451,"line":526},[449,32201,4604],{},[32,32203,31081],{"id":32204},"prefetching",[21,32206,32207],{},"Improve perceived performance by prefetching data:",[48,32209,32212],{"className":30231,"code":32210,"filename":32211,"language":30233,"meta":56,"style":56},"function PostList() {\n  const queryClient = useQueryClient();\n\n  const { data: posts } = useQuery({\n    queryKey: ['posts'],\n    queryFn: fetchPosts,\n  });\n\n  const handleMouseEnter = (postId: string) => {\n    queryClient.prefetchQuery({\n      queryKey: ['post', postId],\n      queryFn: () => fetchPost(postId),\n    });\n  };\n\n  return (\n    \u003Cdiv>\n      {posts?.map(post => (\n        \u003Cdiv \n          key={post.id}\n          onMouseEnter={() => handleMouseEnter(post.id)}\n        >\n          \u003CLink to={`\u002Fposts\u002F${post.id}`}>{post.title}\u003C\u002FLink>\n        \u003C\u002Fdiv>\n      ))}\n    \u003C\u002Fdiv>\n  );\n}\n","Prefetching.tsx",[40,32213,32214,32219,32223,32227,32231,32236,32241,32245,32249,32254,32259,32264,32269,32273,32277,32281,32285,32289,32294,32299,32304,32309,32314,32319,32324,32329,32333,32337],{"__ignoreMap":56},[449,32215,32216],{"class":451,"line":452},[449,32217,32218],{},"function PostList() {\n",[449,32220,32221],{"class":451,"line":114},[449,32222,31405],{},[449,32224,32225],{"class":451,"line":111},[449,32226,700],{"emptyLinePlaceholder":130},[449,32228,32229],{"class":451,"line":468},[449,32230,32169],{},[449,32232,32233],{"class":451,"line":474},[449,32234,32235],{},"    queryKey: ['posts'],\n",[449,32237,32238],{"class":451,"line":480},[449,32239,32240],{},"    queryFn: fetchPosts,\n",[449,32242,32243],{"class":451,"line":486},[449,32244,13106],{},[449,32246,32247],{"class":451,"line":492},[449,32248,700],{"emptyLinePlaceholder":130},[449,32250,32251],{"class":451,"line":498},[449,32252,32253],{},"  const handleMouseEnter = (postId: string) => {\n",[449,32255,32256],{"class":451,"line":133},[449,32257,32258],{},"    queryClient.prefetchQuery({\n",[449,32260,32261],{"class":451,"line":509},[449,32262,32263],{},"      queryKey: ['post', postId],\n",[449,32265,32266],{"class":451,"line":318},[449,32267,32268],{},"      queryFn: () => fetchPost(postId),\n",[449,32270,32271],{"class":451,"line":520},[449,32272,3484],{},[449,32274,32275],{"class":451,"line":526},[449,32276,4764],{},[449,32278,32279],{"class":451,"line":532},[449,32280,700],{"emptyLinePlaceholder":130},[449,32282,32283],{"class":451,"line":2790},[449,32284,31167],{},[449,32286,32287],{"class":451,"line":3511},[449,32288,31319],{},[449,32290,32291],{"class":451,"line":3526},[449,32292,32293],{},"      {posts?.map(post => (\n",[449,32295,32296],{"class":451,"line":3531},[449,32297,32298],{},"        \u003Cdiv \n",[449,32300,32301],{"class":451,"line":3536},[449,32302,32303],{},"          key={post.id}\n",[449,32305,32306],{"class":451,"line":4009},[449,32307,32308],{},"          onMouseEnter={() => handleMouseEnter(post.id)}\n",[449,32310,32311],{"class":451,"line":4015},[449,32312,32313],{},"        >\n",[449,32315,32316],{"class":451,"line":4021},[449,32317,32318],{},"          \u003CLink to={`\u002Fposts\u002F${post.id}`}>{post.title}\u003C\u002FLink>\n",[449,32320,32321],{"class":451,"line":4027},[449,32322,32323],{},"        \u003C\u002Fdiv>\n",[449,32325,32326],{"class":451,"line":4032},[449,32327,32328],{},"      ))}\n",[449,32330,32331],{"class":451,"line":4038},[449,32332,31334],{},[449,32334,32335],{"class":451,"line":4044},[449,32336,6436],{},[449,32338,32339],{"class":451,"line":4049},[449,32340,4604],{},[73,32342],{},[16,32344,3681],{"id":3680},[21,32346,32347],{},"TanStack Query has fundamentally changed how developers approach data fetching and state management in modern applications. By providing intelligent caching, automatic background updates, and a simple yet powerful API, it eliminates much of the boilerplate code traditionally associated with server state management.",[21,32349,32350],{},"Whether you're building a small application or a large-scale enterprise system, TanStack Query's flexibility and performance optimizations make it an excellent choice. The library's active community, comprehensive documentation, and framework-agnostic approach ensure it will remain a valuable tool in your development arsenal.",[21,32352,32353],{},"Start small with basic queries and gradually adopt advanced features like optimistic updates and prefetching as your application grows. Your users will appreciate the snappier experience, and your team will benefit from cleaner, more maintainable code.",[21,32355,32356],{},"Happy querying!",[3728,32358,32359],{},"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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":32361},[32362,32363,32364,32365,32366,32367,32370,32371,32372,32373,32374,32375],{"id":31040,"depth":114,"text":31041},{"id":31097,"depth":114,"text":31098},{"id":31129,"depth":114,"text":31130},{"id":31193,"depth":114,"text":31194},{"id":31355,"depth":114,"text":31356},{"id":31549,"depth":114,"text":31550,"children":32368},[32369],{"id":31677,"depth":111,"text":31678},{"id":31684,"depth":114,"text":31685},{"id":31836,"depth":114,"text":31837},{"id":31893,"depth":114,"text":31894},{"id":32064,"depth":114,"text":32065},{"id":32128,"depth":114,"text":32129},{"id":32204,"depth":114,"text":31081},"2026-02-05","Discover how TanStack Query revolutionizes data fetching in React applications with powerful caching, synchronization, and state management capabilities.",{"type":13,"value":32379},[32380,32382],[16,32381,333],{"id":332},[21,32383,31027],{},[32385,32386,32387,20689,32388,3784],"tanstack query","react query","data fetching","react",{},"\u002Fblog\u002Ftanstack-query",{"title":31019,"description":32377},"blog\u002Ftanstack-query","otrAfik85HDuJo-p8O4tK0JRdsM8c8jGeuWVLKo6pxg",{"id":32395,"title":32396,"author":32397,"body":32398,"canonical":116,"categories":116,"date":22854,"description":33177,"excerpt":33178,"extension":119,"img":3779,"keywords":33198,"language":3788,"meta":33200,"navigation":130,"ogImage":116,"path":33201,"rating":474,"readTime":133,"seo":33202,"stem":33203,"tag":3793,"__hash__":33204},"blog\u002Fblog\u002Ftanstack-router.md","Mastering TanStack Router: Type-Safe Routing for Modern Web Apps",{"name":9,"img":11,"website":327},{"type":13,"value":32399,"toc":33160},[32400,32402,32405,32411,32422,32429,32431,32435,32438,32452,32455,32464,32471,32473,32477,32480,32497,32500,32514,32517,32519,32521,32524,32606,32609,32613,32616,32702,32705,32709,32712,32805,32809,32825,32828,32832,32835,32977,32979,32990,32994,32997,33001,33004,33038,33041,33052,33055,33059,33062,33068,33071,33082,33085,33089,33092,33105,33108,33111,33115,33118,33132,33135,33146,33148,33151,33154,33157],[16,32401,333],{"id":332},[21,32403,32404],{},"Routing has always been one of those “it works… until it doesn’t” parts of web development. As applications grow, routes become harder to reason about, parameters drift out of sync, and refactors turn into a game of whack-a-mole.",[21,32406,32407,32410],{},[151,32408,32409],{},"TanStack Router"," was created to solve exactly that problem.",[21,32412,32413,32414,32417,32418,32421],{},"Built with ",[151,32415,32416],{},"type safety first",", TanStack Router flips the traditional routing model on its head. Instead of hoping your routes, params, loaders, and links line up, the router ",[156,32419,32420],{},"guarantees"," they do—at compile time.",[21,32423,32424,32425,32428],{},"In this post, we’ll explore ",[151,32426,32427],{},"what makes TanStack Router different",", why it matters, and how to use it effectively in real-world applications.",[16,32430,4373],{"id":4372},[32,32432,32434],{"id":32433},"why-routing-needed-a-rethink","Why Routing Needed a Rethink",[21,32436,32437],{},"Most routers share a few common pain points:",[383,32439,32440,32443,32446,32449],{},[386,32441,32442],{},"Route params are strings (until runtime)",[386,32444,32445],{},"Loaders and components drift apart",[386,32447,32448],{},"Refactors break links silently",[386,32450,32451],{},"Nested routing becomes fragile",[21,32453,32454],{},"These issues don’t show up on day one—but they absolutely show up at scale.",[25,32456,32457],{"type":363},[21,32458,32459,32460,32463],{},"TanStack Router treats routing as a ",[156,32461,32462],{},"data problem",", not just a URL-matching problem.",[21,32465,32466,32467,32470],{},"Instead of defining routes and hoping everything connects correctly, TanStack Router builds a ",[151,32468,32469],{},"strongly-typed route tree"," that TypeScript understands end to end.",[73,32472],{},[32,32474,32476],{"id":32475},"core-philosophy-of-tanstack-router","Core Philosophy of TanStack Router",[21,32478,32479],{},"At its core, TanStack Router is built around three ideas:",[816,32481,32482,32487,32492],{},[386,32483,32484],{},[151,32485,32486],{},"Type safety everywhere",[386,32488,32489],{},[151,32490,32491],{},"Colocation of route logic",[386,32493,32494],{},[151,32495,32496],{},"Data-first routing",[21,32498,32499],{},"This means:",[383,32501,32502,32505,32508,32511],{},[386,32503,32504],{},"Routes know their params",[386,32506,32507],{},"Loaders know their return types",[386,32509,32510],{},"Links know where they’re going",[386,32512,32513],{},"Components know what data they receive",[21,32515,32516],{},"No guessing. No runtime surprises.",[73,32518],{},[32,32520,31130],{"id":31129},[21,32522,32523],{},"Here’s a minimal example of defining a route.",[48,32525,32528],{"className":670,"code":32526,"filename":32527,"language":673,"meta":56,"style":56},"import { createRouter, createRootRoute } from '@tanstack\u002Freact-router'\n\nconst rootRoute = createRootRoute({\n  component: () => \u003COutlet \u002F>\n})\n\nexport const router = createRouter({\n  routeTree: rootRoute,\n})\n","router.ts",[40,32529,32530,32542,32546,32560,32577,32581,32585,32597,32602],{"__ignoreMap":56},[449,32531,32532,32534,32537,32539],{"class":451,"line":452},[449,32533,681],{"class":680},[449,32535,32536],{"class":684}," { createRouter, createRootRoute } ",[449,32538,688],{"class":680},[449,32540,32541],{"class":691}," '@tanstack\u002Freact-router'\n",[449,32543,32544],{"class":451,"line":114},[449,32545,700],{"emptyLinePlaceholder":130},[449,32547,32548,32550,32553,32555,32558],{"class":451,"line":111},[449,32549,711],{"class":680},[449,32551,32552],{"class":714}," rootRoute",[449,32554,718],{"class":680},[449,32556,32557],{"class":721}," createRootRoute",[449,32559,1391],{"class":684},[449,32561,32562,32565,32568,32570,32572,32575],{"class":451,"line":468},[449,32563,32564],{"class":721},"  component",[449,32566,32567],{"class":684},": () ",[449,32569,1498],{"class":680},[449,32571,2481],{"class":684},[449,32573,32574],{"class":721},"Outlet",[449,32576,26933],{"class":684},[449,32578,32579],{"class":451,"line":474},[449,32580,4113],{"class":684},[449,32582,32583],{"class":451,"line":480},[449,32584,700],{"emptyLinePlaceholder":130},[449,32586,32587,32590,32592,32595],{"class":451,"line":486},[449,32588,32589],{"class":684},"export const router ",[449,32591,5015],{"class":680},[449,32593,32594],{"class":721}," createRouter",[449,32596,1391],{"class":684},[449,32598,32599],{"class":451,"line":492},[449,32600,32601],{"class":684},"  routeTree: rootRoute,\n",[449,32603,32604],{"class":451,"line":498},[449,32605,4113],{"class":684},[21,32607,32608],{},"Already, TypeScript understands your route tree—even before adding child routes.",[32,32610,32612],{"id":32611},"defining-routes-with-type-safety","Defining Routes with Type Safety",[21,32614,32615],{},"Now let’s define a real route.",[48,32617,32619],{"className":670,"code":32618,"language":673,"meta":56,"style":56},"import { createFileRoute } from '@tanstack\u002Freact-router'\n\nexport const Route = createFileRoute('\u002F')({\n  component: HomePage,\n})\n\nfunction HomePage() {\n  return \u003Ch1>Welcome Home\u003C\u002Fh1>\n}\n",[40,32620,32621,32632,32636,32658,32663,32667,32671,32681,32698],{"__ignoreMap":56},[449,32622,32623,32625,32628,32630],{"class":451,"line":452},[449,32624,681],{"class":680},[449,32626,32627],{"class":684}," { createFileRoute } ",[449,32629,688],{"class":680},[449,32631,32541],{"class":691},[449,32633,32634],{"class":451,"line":114},[449,32635,700],{"emptyLinePlaceholder":130},[449,32637,32638,32640,32642,32645,32647,32650,32652,32655],{"class":451,"line":111},[449,32639,4629],{"class":680},[449,32641,10801],{"class":680},[449,32643,32644],{"class":714}," Route",[449,32646,718],{"class":680},[449,32648,32649],{"class":721}," createFileRoute",[449,32651,725],{"class":684},[449,32653,32654],{"class":691},"'\u002F'",[449,32656,32657],{"class":684},")({\n",[449,32659,32660],{"class":451,"line":468},[449,32661,32662],{"class":684},"  component: HomePage,\n",[449,32664,32665],{"class":451,"line":474},[449,32666,4113],{"class":684},[449,32668,32669],{"class":451,"line":480},[449,32670,700],{"emptyLinePlaceholder":130},[449,32672,32673,32676,32679],{"class":451,"line":486},[449,32674,32675],{"class":680},"function",[449,32677,32678],{"class":721}," HomePage",[449,32680,5438],{"class":684},[449,32682,32683,32685,32687,32689,32692,32694,32696],{"class":451,"line":492},[449,32684,4727],{"class":680},[449,32686,2481],{"class":684},[449,32688,16],{"class":721},[449,32690,32691],{"class":684},">Welcome Home",[449,32693,23103],{"class":680},[449,32695,16],{"class":684},[449,32697,23006],{"class":680},[449,32699,32700],{"class":451,"line":498},[449,32701,4604],{"class":684},[21,32703,32704],{},"No string-based route definitions scattered across your app. The route is the source of truth.",[32,32706,32708],{"id":32707},"route-params-that-dont-lie","Route Params That Don’t Lie",[21,32710,32711],{},"Here’s where TanStack Router really shines.",[48,32713,32715],{"className":670,"code":32714,"language":673,"meta":56,"style":56},"export const Route = createFileRoute('\u002Fusers\u002F$userId')({\n  component: UserPage,\n})\n\nfunction UserPage() {\n  const { userId } = Route.useParams()\n  return \u003Cdiv>User ID: {userId}\u003C\u002Fdiv>\n}\n",[40,32716,32717,32736,32741,32745,32749,32758,32778,32801],{"__ignoreMap":56},[449,32718,32719,32721,32723,32725,32727,32729,32731,32734],{"class":451,"line":452},[449,32720,4629],{"class":680},[449,32722,10801],{"class":680},[449,32724,32644],{"class":714},[449,32726,718],{"class":680},[449,32728,32649],{"class":721},[449,32730,725],{"class":684},[449,32732,32733],{"class":691},"'\u002Fusers\u002F$userId'",[449,32735,32657],{"class":684},[449,32737,32738],{"class":451,"line":114},[449,32739,32740],{"class":684},"  component: UserPage,\n",[449,32742,32743],{"class":451,"line":111},[449,32744,4113],{"class":684},[449,32746,32747],{"class":451,"line":468},[449,32748,700],{"emptyLinePlaceholder":130},[449,32750,32751,32753,32756],{"class":451,"line":474},[449,32752,32675],{"class":680},[449,32754,32755],{"class":721}," UserPage",[449,32757,5438],{"class":684},[449,32759,32760,32762,32764,32766,32768,32770,32773,32776],{"class":451,"line":480},[449,32761,5049],{"class":680},[449,32763,14124],{"class":684},[449,32765,24290],{"class":714},[449,32767,21541],{"class":684},[449,32769,5015],{"class":680},[449,32771,32772],{"class":684}," Route.",[449,32774,32775],{"class":721},"useParams",[449,32777,11220],{"class":684},[449,32779,32780,32782,32784,32786,32789,32792,32795,32797,32799],{"class":451,"line":486},[449,32781,4727],{"class":680},[449,32783,2481],{"class":684},[449,32785,26832],{"class":721},[449,32787,32788],{"class":684},">User ",[449,32790,32791],{"class":714},"ID",[449,32793,32794],{"class":684},": {userId}",[449,32796,23103],{"class":680},[449,32798,26832],{"class":684},[449,32800,23006],{"class":680},[449,32802,32803],{"class":451,"line":492},[449,32804,4604],{"class":684},[2808,32806,32808],{"id":32807},"whats-different-here","What’s Different Here?",[383,32810,32811,32816,32819,32822],{},[386,32812,32813,32815],{},[40,32814,24290],{}," is typed",[386,32817,32818],{},"You can’t forget it",[386,32820,32821],{},"You can’t mistype it",[386,32823,32824],{},"You can’t pass the wrong param shape",[21,32826,32827],{},"TypeScript will stop you before the app runs.",[32,32829,32831],{"id":32830},"loaders-as-first-class-citizens","Loaders as First-Class Citizens",[21,32833,32834],{},"TanStack Router treats data loading as part of routing—not an afterthought.",[48,32836,32838],{"className":670,"code":32837,"language":673,"meta":56,"style":56},"export const Route = createFileRoute('\u002Fusers\u002F$userId')({\n  loader: async ({ params }) => {\n    return fetch(`\u002Fapi\u002Fusers\u002F${params.userId}`).then(res => res.json())\n  },\n  component: UserPage,\n})\n\nfunction UserPage() {\n  const user = Route.useLoaderData()\n  return \u003Cdiv>{user.name}\u003C\u002Fdiv>\n}\n",[40,32839,32840,32858,32878,32917,32921,32925,32929,32933,32941,32956,32973],{"__ignoreMap":56},[449,32841,32842,32844,32846,32848,32850,32852,32854,32856],{"class":451,"line":452},[449,32843,4629],{"class":680},[449,32845,10801],{"class":680},[449,32847,32644],{"class":714},[449,32849,718],{"class":680},[449,32851,32649],{"class":721},[449,32853,725],{"class":684},[449,32855,32733],{"class":691},[449,32857,32657],{"class":684},[449,32859,32860,32863,32865,32867,32869,32872,32874,32876],{"class":451,"line":114},[449,32861,32862],{"class":721},"  loader",[449,32864,1482],{"class":684},[449,32866,1485],{"class":680},[449,32868,1488],{"class":684},[449,32870,32871],{"class":1491},"params",[449,32873,1495],{"class":684},[449,32875,1498],{"class":680},[449,32877,1501],{"class":684},[449,32879,32880,32882,32884,32886,32889,32891,32893,32895,32897,32899,32901,32903,32906,32909,32912,32914],{"class":451,"line":111},[449,32881,1506],{"class":680},[449,32883,5660],{"class":721},[449,32885,725],{"class":684},[449,32887,32888],{"class":691},"`\u002Fapi\u002Fusers\u002F${",[449,32890,32871],{"class":684},[449,32892,165],{"class":691},[449,32894,24290],{"class":684},[449,32896,1517],{"class":691},[449,32898,374],{"class":684},[449,32900,6414],{"class":721},[449,32902,725],{"class":684},[449,32904,32905],{"class":1491},"res",[449,32907,32908],{"class":680}," =>",[449,32910,32911],{"class":684}," res.",[449,32913,4521],{"class":721},[449,32915,32916],{"class":684},"())\n",[449,32918,32919],{"class":451,"line":468},[449,32920,1474],{"class":684},[449,32922,32923],{"class":451,"line":474},[449,32924,32740],{"class":684},[449,32926,32927],{"class":451,"line":480},[449,32928,4113],{"class":684},[449,32930,32931],{"class":451,"line":486},[449,32932,700],{"emptyLinePlaceholder":130},[449,32934,32935,32937,32939],{"class":451,"line":492},[449,32936,32675],{"class":680},[449,32938,32755],{"class":721},[449,32940,5438],{"class":684},[449,32942,32943,32945,32947,32949,32951,32954],{"class":451,"line":498},[449,32944,5049],{"class":680},[449,32946,24831],{"class":714},[449,32948,718],{"class":680},[449,32950,32772],{"class":684},[449,32952,32953],{"class":721},"useLoaderData",[449,32955,11220],{"class":684},[449,32957,32958,32960,32962,32964,32967,32969,32971],{"class":451,"line":133},[449,32959,4727],{"class":680},[449,32961,2481],{"class":684},[449,32963,26832],{"class":721},[449,32965,32966],{"class":684},">{user.name}",[449,32968,23103],{"class":680},[449,32970,26832],{"class":684},[449,32972,23006],{"class":680},[449,32974,32975],{"class":451,"line":509},[449,32976,4604],{"class":684},[2808,32978,22798],{"id":22797},[383,32980,32981,32984,32987],{},[386,32982,32983],{},"Loader data is typed",[386,32985,32986],{},"Components can’t access data that doesn’t exist",[386,32988,32989],{},"Refactors stay safe",[32,32991,32993],{"id":32992},"tanstack-router-tanstack-query","TanStack Router + TanStack Query",[21,32995,32996],{},"When paired with TanStack Query, loaders become even more powerful—combining caching, background refetching, and route awareness.",[32,32998,33000],{"id":32999},"navigation-without-fear","Navigation Without Fear",[21,33002,33003],{},"Links are another classic source of bugs. TanStack Router eliminates them.",[48,33005,33007],{"className":30231,"code":33006,"language":30233,"meta":56,"style":56},"\u003CLink\n  to=\"\u002Fusers\u002F$userId\"\n  params={{ userId: '42' }}\n>\n  View User\n\u003C\u002FLink>\n",[40,33008,33009,33014,33019,33024,33028,33033],{"__ignoreMap":56},[449,33010,33011],{"class":451,"line":452},[449,33012,33013],{},"\u003CLink\n",[449,33015,33016],{"class":451,"line":114},[449,33017,33018],{},"  to=\"\u002Fusers\u002F$userId\"\n",[449,33020,33021],{"class":451,"line":111},[449,33022,33023],{},"  params={{ userId: '42' }}\n",[449,33025,33026],{"class":451,"line":468},[449,33027,23006],{},[449,33029,33030],{"class":451,"line":474},[449,33031,33032],{},"  View User\n",[449,33034,33035],{"class":451,"line":480},[449,33036,33037],{},"\u003C\u002FLink>\n",[21,33039,33040],{},"If you:",[383,33042,33043,33046,33049],{},[386,33044,33045],{},"Forget a param ❌",[386,33047,33048],{},"Misspell a route ❌",[386,33050,33051],{},"Pass the wrong param type ❌",[21,33053,33054],{},"TypeScript catches it instantly.",[32,33056,33058],{"id":33057},"nested-routes-done-right","Nested Routes Done Right",[21,33060,33061],{},"Nested routing is simple, explicit, and predictable.",[48,33063,33066],{"className":33064,"code":33065,"language":53},[51],"\u002Fusers\n\u002Fusers\u002F$userId\n\u002Fusers\u002F$userId\u002Fsettings\n",[40,33067,33065],{"__ignoreMap":56},[21,33069,33070],{},"Each route:",[383,33072,33073,33076,33079],{},[386,33074,33075],{},"Knows its parent",[386,33077,33078],{},"Inherits context safely",[386,33080,33081],{},"Composes layouts naturally",[21,33083,33084],{},"No magic. No hidden behavior.",[32,33086,33088],{"id":33087},"how-tanstack-router-fits-modern-stacks","How TanStack Router Fits Modern Stacks",[21,33090,33091],{},"TanStack Router pairs especially well with:",[383,33093,33094,33097,33099,33102],{},[386,33095,33096],{},"TanStack Query",[386,33098,21032],{},[386,33100,33101],{},"Server-first architectures",[386,33103,33104],{},"Typed backends",[21,33106,33107],{},"If you care about refactoring confidence and long-term maintainability, TanStack Router pays for itself very quickly.",[21,33109,33110],{},"It’s not about being flashy—it’s about eliminating entire classes of bugs.",[32,33112,33114],{"id":33113},"when-should-you-use-tanstack-router","When Should You Use TanStack Router?",[21,33116,33117],{},"TanStack Router is ideal if:",[383,33119,33120,33123,33126,33129],{},[386,33121,33122],{},"You use TypeScript seriously",[386,33124,33125],{},"Your app has complex routing or data needs",[386,33127,33128],{},"You value refactor safety",[386,33130,33131],{},"You’re building long-lived applications",[21,33133,33134],{},"It might be overkill for:",[383,33136,33137,33140,33143],{},[386,33138,33139],{},"Tiny apps",[386,33141,33142],{},"One-page demos",[386,33144,33145],{},"Projects without TypeScript",[16,33147,3681],{"id":3680},[21,33149,33150],{},"TanStack Router isn’t just another router—it’s a rethinking of what routing should be in modern web applications. By embracing type safety, colocation, and data-first design, it gives developers something rare: confidence.",[21,33152,33153],{},"Confidence to refactor.\nConfidence to scale.\nConfidence that your routes won’t betray you at runtime.",[21,33155,33156],{},"If you’ve ever been burned by routing bugs, TanStack Router is absolutely worth your time.",[3728,33158,33159],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":33161},[33162,33163,33164,33165,33166,33169,33172,33173,33174,33175,33176],{"id":32433,"depth":114,"text":32434},{"id":32475,"depth":114,"text":32476},{"id":31129,"depth":114,"text":31130},{"id":32611,"depth":114,"text":32612},{"id":32707,"depth":114,"text":32708,"children":33167},[33168],{"id":32807,"depth":111,"text":32808},{"id":32830,"depth":114,"text":32831,"children":33170},[33171],{"id":22797,"depth":111,"text":22798},{"id":32992,"depth":114,"text":32993},{"id":32999,"depth":114,"text":33000},{"id":33057,"depth":114,"text":33058},{"id":33087,"depth":114,"text":33088},{"id":33113,"depth":114,"text":33114},"A deep dive into TanStack Router, exploring why type-safe routing matters, how it compares to traditional routers, and how to build scalable applications with confidence.",{"type":13,"value":33179},[33180,33182,33184,33188,33194],[16,33181,333],{"id":332},[21,33183,32404],{},[21,33185,33186,32410],{},[151,33187,32409],{},[21,33189,32413,33190,32417,33192,32421],{},[151,33191,32416],{},[156,33193,32420],{},[21,33195,32424,33196,32428],{},[151,33197,32427],{},[21234,33199,3784,32388,20888],"tanstack router",{},"\u002Fblog\u002Ftanstack-router",{"title":32396,"description":33177},"blog\u002Ftanstack-router","PWnsv5PO_qkfN_djpIbv3v8k1z6BPhhRM9l-lLwmDVI",{"id":33206,"title":33207,"author":33208,"body":33209,"canonical":116,"categories":116,"date":34318,"description":34319,"excerpt":34320,"extension":119,"img":21929,"keywords":34336,"language":3788,"meta":34338,"navigation":130,"ogImage":116,"path":34339,"rating":474,"readTime":133,"seo":34340,"stem":34341,"tag":3793,"__hash__":34342},"blog\u002Fblog\u002Funjs-packages.md","Using Core UnJS Packages to Build a Modern JavaScript Application",{"name":9,"img":11,"website":327},{"type":13,"value":33210,"toc":34298},[33211,33213,33225,33232,33234,33241,33243,33247,33250,33256,33265,33268,33357,33366,33368,33375,33385,33442,33448,33450,33457,33466,33518,33524,33526,33533,33542,33545,33653,33659,33661,33668,33677,33747,33753,33755,33762,33765,33815,33821,33823,33833,33854,33931,33937,33939,33943,33946,34001,34004,34006,34010,34013,34260,34270,34272,34274,34288,34295],[16,33212,333],{"id":332},[21,33214,1556,33215,33218,33219,33222,33223],{},[151,33216,33217],{},"UnJS ecosystem"," is a large collection of focused, composable JavaScript utilities and tools created to empower developers with performant, agnostic building blocks. Instead of monolithic frameworks with opinionated APIs, UnJS packages are ",[151,33220,33221],{},"small, well-scoped, and interoperable"," — letting you pick exactly what you need for your app’s architecture and tooling. ",[9909,33224,9912],{"index":9911},[21,33226,33227,33228,33231],{},"In this post, we’ll focus on the ",[151,33229,33230],{},"main UnJS packages"," you’d use to build a real-world application and how they integrate together.",[16,33233,4373],{"id":4372},[25,33235,33236],{"type":363},[21,33237,33238,33239],{},"UnJS packages follow a Unix philosophy — each one does one thing well and can be composed with others to form a complete solution. ",[9909,33240,9938],{"index":4799},[73,33242],{},[32,33244,33246],{"id":33245},"key-unjs-packages-for-your-application","Key UnJS Packages for Your Application",[21,33248,33249],{},"Here’s a curated list of the most impactful UnJS packages you’ll likely use in a modern JavaScript\u002FTypeScript application:",[2808,33251,33253,33255],{"id":33252},"nitro-a-universal-server-engine",[151,33254,4189],{}," — A Universal Server Engine",[21,33257,33258,33260,33261,33264],{},[40,33259,7076],{}," lets you build ",[151,33262,33263],{},"fast, portable servers"," that run on Node.js, Deno, Bun, Cloudflare Workers, and more.",[21,33266,33267],{},"Use Nitro to create API routes and server logic without worrying about runtime differences:",[48,33269,33273],{"className":33270,"code":33271,"language":33272,"meta":56,"style":56},"language-js shiki shiki-themes github-light github-dark","\u002F\u002F server\u002Findex.ts\nimport { createNitro } from \"nitro\";\n\nexport default createNitro({\n  handlers: [\n    {\n      route: \"\u002Fapi\u002Fhello\",\n      handler: () => ({ message: \"Hello from Nitro!\" })\n    }\n  ]\n});\n","js",[40,33274,33275,33280,33294,33298,33309,33314,33319,33329,33345,33349,33353],{"__ignoreMap":56},[449,33276,33277],{"class":451,"line":452},[449,33278,33279],{"class":705},"\u002F\u002F server\u002Findex.ts\n",[449,33281,33282,33284,33287,33289,33292],{"class":451,"line":114},[449,33283,681],{"class":680},[449,33285,33286],{"class":684}," { createNitro } ",[449,33288,688],{"class":680},[449,33290,33291],{"class":691}," \"nitro\"",[449,33293,695],{"class":684},[449,33295,33296],{"class":451,"line":111},[449,33297,700],{"emptyLinePlaceholder":130},[449,33299,33300,33302,33304,33307],{"class":451,"line":468},[449,33301,4629],{"class":680},[449,33303,4632],{"class":680},[449,33305,33306],{"class":721}," createNitro",[449,33308,1391],{"class":684},[449,33310,33311],{"class":451,"line":474},[449,33312,33313],{"class":684},"  handlers: [\n",[449,33315,33316],{"class":451,"line":480},[449,33317,33318],{"class":684},"    {\n",[449,33320,33321,33324,33327],{"class":451,"line":486},[449,33322,33323],{"class":684},"      route: ",[449,33325,33326],{"class":691},"\"\u002Fapi\u002Fhello\"",[449,33328,779],{"class":684},[449,33330,33331,33334,33336,33338,33341,33343],{"class":451,"line":492},[449,33332,33333],{"class":721},"      handler",[449,33335,32567],{"class":684},[449,33337,1498],{"class":680},[449,33339,33340],{"class":684}," ({ message: ",[449,33342,21464],{"class":691},[449,33344,10958],{"class":684},[449,33346,33347],{"class":451,"line":498},[449,33348,7555],{"class":684},[449,33350,33351],{"class":451,"line":133},[449,33352,29239],{"class":684},[449,33354,33355],{"class":451,"line":509},[449,33356,784],{"class":684},[21,33358,33359,33360,827],{},"Nitro becomes the backbone of your backend layer, powering APIs consistently across environments. (",[3861,33361,33365],{"href":33362,"rel":33363,"title":33364},"https:\u002F\u002Funjs.io\u002Fpackages\u002F",[3865],"Packages · UnJS","UnJS",[73,33367],{},[2808,33369,33371,33374],{"id":33370},"c12-smart-configuration-loader",[151,33372,33373],{},"c12"," — Smart Configuration Loader",[21,33376,33377,33379,33380,827],{},[40,33378,33373],{}," simplifies loading configuration from multiple sources (JSON, JSONC, YAML, TOML, .env, RC files) and merges them intelligently. This is useful for environment configs, feature flags, and CLI apps. (",[3861,33381,33365],{"href":33382,"rel":33383,"title":33384},"https:\u002F\u002Funjs.io\u002Fpackages\u002Fc12",[3865],"c12 · Packages · UnJS",[48,33386,33388],{"className":670,"code":33387,"language":673,"meta":56,"style":56},"import { loadConfig } from \"c12\";\n\nconst { config } = await loadConfig({});\nconsole.log(\"App Config:\", config);\n",[40,33389,33390,33404,33408,33428],{"__ignoreMap":56},[449,33391,33392,33394,33397,33399,33402],{"class":451,"line":452},[449,33393,681],{"class":680},[449,33395,33396],{"class":684}," { loadConfig } ",[449,33398,688],{"class":680},[449,33400,33401],{"class":691}," \"c12\"",[449,33403,695],{"class":684},[449,33405,33406],{"class":451,"line":114},[449,33407,700],{"emptyLinePlaceholder":130},[449,33409,33410,33412,33414,33416,33418,33420,33422,33425],{"class":451,"line":111},[449,33411,711],{"class":680},[449,33413,14124],{"class":684},[449,33415,3885],{"class":714},[449,33417,21541],{"class":684},[449,33419,5015],{"class":680},[449,33421,1654],{"class":680},[449,33423,33424],{"class":721}," loadConfig",[449,33426,33427],{"class":684},"({});\n",[449,33429,33430,33432,33434,33436,33439],{"class":451,"line":468},[449,33431,1672],{"class":684},[449,33433,1675],{"class":721},[449,33435,725],{"class":684},[449,33437,33438],{"class":691},"\"App Config:\"",[449,33440,33441],{"class":684},", config);\n",[21,33443,33444,33445,827],{},"It supports layering configs, environment overrides, and HMR during development — making config management a breeze. (",[3861,33446,33365],{"href":33382,"rel":33447,"title":33384},[3865],[73,33449],{},[2808,33451,33453,33456],{"id":33452},"ofetch-a-universal-fetch-api",[151,33454,33455],{},"ofetch"," — A Universal Fetch API",[21,33458,33459,33461,33462,33465],{},[40,33460,33455],{}," gives you a robust, cross-environment ",[40,33463,33464],{},"fetch"," API compatible with Node, browsers, and workers:",[48,33467,33469],{"className":670,"code":33468,"language":673,"meta":56,"style":56},"import { ofetch } from \"ofetch\";\n\nconst user = await ofetch(\"\u002Fapi\u002Fuser\");\nconsole.log(user);\n",[40,33470,33471,33485,33489,33509],{"__ignoreMap":56},[449,33472,33473,33475,33478,33480,33483],{"class":451,"line":452},[449,33474,681],{"class":680},[449,33476,33477],{"class":684}," { ofetch } ",[449,33479,688],{"class":680},[449,33481,33482],{"class":691}," \"ofetch\"",[449,33484,695],{"class":684},[449,33486,33487],{"class":451,"line":114},[449,33488,700],{"emptyLinePlaceholder":130},[449,33490,33491,33493,33495,33497,33499,33502,33504,33507],{"class":451,"line":111},[449,33492,711],{"class":680},[449,33494,24831],{"class":714},[449,33496,718],{"class":680},[449,33498,1654],{"class":680},[449,33500,33501],{"class":721}," ofetch",[449,33503,725],{"class":684},[449,33505,33506],{"class":691},"\"\u002Fapi\u002Fuser\"",[449,33508,740],{"class":684},[449,33510,33511,33513,33515],{"class":451,"line":468},[449,33512,1672],{"class":684},[449,33514,1675],{"class":721},[449,33516,33517],{"class":684},"(user);\n",[21,33519,33520,33521,827],{},"Use it in your client and server code without worrying about environment differences. (",[3861,33522,33365],{"href":33362,"rel":33523,"title":33364},[3865],[73,33525],{},[2808,33527,33529,33532],{"id":33528},"unplugin-plugin-system-across-bundlers",[151,33530,33531],{},"unplugin"," — Plugin System Across Bundlers",[21,33534,33535,33537,33538,33541],{},[40,33536,33531],{}," lets you write ",[151,33539,33540],{},"one plugin"," that works with Vite, Rollup, webpack, and esbuild — ideal for extending builds consistently.",[21,33543,33544],{},"For example, creating a custom import transform:",[48,33546,33548],{"className":33270,"code":33547,"language":33272,"meta":56,"style":56},"import Unplugin from \"unplugin\";\n\nexport default Unplugin(() => ({\n  name: \"example-plugin\",\n  transform(code) {\n    return code.replace(\u002FENV_VAR\u002Fg, JSON.stringify(process.env.NODE_ENV));\n  }\n}));\n",[40,33549,33550,33564,33568,33584,33593,33604,33644,33648],{"__ignoreMap":56},[449,33551,33552,33554,33557,33559,33562],{"class":451,"line":452},[449,33553,681],{"class":680},[449,33555,33556],{"class":684}," Unplugin ",[449,33558,688],{"class":680},[449,33560,33561],{"class":691}," \"unplugin\"",[449,33563,695],{"class":684},[449,33565,33566],{"class":451,"line":114},[449,33567,700],{"emptyLinePlaceholder":130},[449,33569,33570,33572,33574,33577,33579,33581],{"class":451,"line":111},[449,33571,4629],{"class":680},[449,33573,4632],{"class":680},[449,33575,33576],{"class":721}," Unplugin",[449,33578,4718],{"class":684},[449,33580,1498],{"class":680},[449,33582,33583],{"class":684}," ({\n",[449,33585,33586,33588,33591],{"class":451,"line":468},[449,33587,1396],{"class":684},[449,33589,33590],{"class":691},"\"example-plugin\"",[449,33592,779],{"class":684},[449,33594,33595,33598,33600,33602],{"class":451,"line":474},[449,33596,33597],{"class":721},"  transform",[449,33599,725],{"class":684},[449,33601,40],{"class":1491},[449,33603,3417],{"class":684},[449,33605,33606,33608,33611,33614,33616,33618,33622,33624,33627,33629,33632,33634,33637,33640,33642],{"class":451,"line":480},[449,33607,1506],{"class":680},[449,33609,33610],{"class":684}," code.",[449,33612,33613],{"class":721},"replace",[449,33615,725],{"class":684},[449,33617,5874],{"class":691},[449,33619,33621],{"class":33620},"sA_wV","ENV_VAR",[449,33623,5874],{"class":691},[449,33625,33626],{"class":680},"g",[449,33628,349],{"class":684},[449,33630,33631],{"class":714},"JSON",[449,33633,165],{"class":684},[449,33635,33636],{"class":721},"stringify",[449,33638,33639],{"class":684},"(process.env.",[449,33641,25410],{"class":714},[449,33643,5182],{"class":684},[449,33645,33646],{"class":451,"line":486},[449,33647,4599],{"class":684},[449,33649,33650],{"class":451,"line":492},[449,33651,33652],{"class":684},"}));\n",[21,33654,33655,33656,827],{},"It’s data-agnostic and integrates with major build systems seamlessly. (",[3861,33657,33365],{"href":33362,"rel":33658,"title":33364},[3865],[73,33660],{},[2808,33662,33664,33667],{"id":33663},"jiti-runtime-typescript-and-esm-support",[151,33665,33666],{},"jiti"," — Runtime TypeScript and ESM Support",[21,33669,33670,33672,33673,33676],{},[40,33671,33666],{}," makes it easy to load ",[40,33674,33675],{},".ts"," modules at runtime without manual compilation:",[48,33678,33680],{"className":33270,"code":33679,"language":33272,"meta":56,"style":56},"import { createRequire } from \"module\";\nimport jiti from \"jiti\";\n\nconst requireTs = jiti(__filename);\nconst config = requireTs(\".\u002Fconfig.ts\").default;\n",[40,33681,33682,33696,33710,33714,33729],{"__ignoreMap":56},[449,33683,33684,33686,33689,33691,33694],{"class":451,"line":452},[449,33685,681],{"class":680},[449,33687,33688],{"class":684}," { createRequire } ",[449,33690,688],{"class":680},[449,33692,33693],{"class":691}," \"module\"",[449,33695,695],{"class":684},[449,33697,33698,33700,33703,33705,33708],{"class":451,"line":114},[449,33699,681],{"class":680},[449,33701,33702],{"class":684}," jiti ",[449,33704,688],{"class":680},[449,33706,33707],{"class":691}," \"jiti\"",[449,33709,695],{"class":684},[449,33711,33712],{"class":451,"line":111},[449,33713,700],{"emptyLinePlaceholder":130},[449,33715,33716,33718,33721,33723,33726],{"class":451,"line":468},[449,33717,711],{"class":680},[449,33719,33720],{"class":714}," requireTs",[449,33722,718],{"class":680},[449,33724,33725],{"class":721}," jiti",[449,33727,33728],{"class":684},"(__filename);\n",[449,33730,33731,33733,33735,33737,33739,33741,33744],{"class":451,"line":474},[449,33732,711],{"class":680},[449,33734,15338],{"class":714},[449,33736,718],{"class":680},[449,33738,33720],{"class":721},[449,33740,725],{"class":684},[449,33742,33743],{"class":691},"\".\u002Fconfig.ts\"",[449,33745,33746],{"class":684},").default;\n",[21,33748,33749,33750,827],{},"This is invaluable for quick prototyping, custom servers, and tooling that needs TypeScript support without build steps. (",[3861,33751,33365],{"href":33362,"rel":33752,"title":33364},[3865],[73,33754],{},[2808,33756,33758,33761],{"id":33757},"consola-elegant-console-logging",[151,33759,33760],{},"consola"," — Elegant Console Logging",[21,33763,33764],{},"A lightweight yet powerful wrapper around console logs with levels, formatting, and grouping:",[48,33766,33768],{"className":670,"code":33767,"language":673,"meta":56,"style":56},"import { consola } from \"consola\";\n\nconsola.info(\"Server started\");\nconsola.error(\"Unexpected error\");\n",[40,33769,33770,33784,33788,33802],{"__ignoreMap":56},[449,33771,33772,33774,33777,33779,33782],{"class":451,"line":452},[449,33773,681],{"class":680},[449,33775,33776],{"class":684}," { consola } ",[449,33778,688],{"class":680},[449,33780,33781],{"class":691}," \"consola\"",[449,33783,695],{"class":684},[449,33785,33786],{"class":451,"line":114},[449,33787,700],{"emptyLinePlaceholder":130},[449,33789,33790,33793,33795,33797,33800],{"class":451,"line":111},[449,33791,33792],{"class":684},"consola.",[449,33794,363],{"class":721},[449,33796,725],{"class":684},[449,33798,33799],{"class":691},"\"Server started\"",[449,33801,740],{"class":684},[449,33803,33804,33806,33808,33810,33813],{"class":451,"line":468},[449,33805,33792],{"class":684},[449,33807,17384],{"class":721},[449,33809,725],{"class":684},[449,33811,33812],{"class":691},"\"Unexpected error\"",[449,33814,740],{"class":684},[21,33816,33817,33818,827],{},"It improves readability of logs in production and development environments. (",[3861,33819,33365],{"href":33362,"rel":33820,"title":33364},[3865],[73,33822],{},[2808,33824,33826,30063,33829,33832],{"id":33825},"defu-and-destr-defaults-safe-parsing",[151,33827,33828],{},"defu",[151,33830,33831],{},"destr"," — Defaults & Safe Parsing",[383,33834,33835,33843],{},[386,33836,33837,33839,33840,827],{},[40,33838,33828],{}," lets you merge objects with recursive defaults — perfect for configs. (",[3861,33841,33365],{"href":33362,"rel":33842,"title":33364},[3865],[386,33844,33845,33847,33848,10255,33851,827],{},[40,33846,33831],{}," safely parses JSON with better performance and security than ",[40,33849,33850],{},"JSON.parse",[3861,33852,33365],{"href":33362,"rel":33853,"title":33364},[3865],[48,33855,33857],{"className":670,"code":33856,"language":673,"meta":56,"style":56},"import { defu } from \"defu\";\nimport { destr } from \"destr\";\n\nconst defaults = { port: 3000 };\nconst config = defu(destr(process.env.APP_CONFIG), defaults);\n",[40,33858,33859,33873,33887,33891,33908],{"__ignoreMap":56},[449,33860,33861,33863,33866,33868,33871],{"class":451,"line":452},[449,33862,681],{"class":680},[449,33864,33865],{"class":684}," { defu } ",[449,33867,688],{"class":680},[449,33869,33870],{"class":691}," \"defu\"",[449,33872,695],{"class":684},[449,33874,33875,33877,33880,33882,33885],{"class":451,"line":114},[449,33876,681],{"class":680},[449,33878,33879],{"class":684}," { destr } ",[449,33881,688],{"class":680},[449,33883,33884],{"class":691}," \"destr\"",[449,33886,695],{"class":684},[449,33888,33889],{"class":451,"line":111},[449,33890,700],{"emptyLinePlaceholder":130},[449,33892,33893,33895,33898,33900,33903,33906],{"class":451,"line":468},[449,33894,711],{"class":680},[449,33896,33897],{"class":714}," defaults",[449,33899,718],{"class":680},[449,33901,33902],{"class":684}," { port: ",[449,33904,33905],{"class":714},"3000",[449,33907,1520],{"class":684},[449,33909,33910,33912,33914,33916,33919,33921,33923,33925,33928],{"class":451,"line":474},[449,33911,711],{"class":680},[449,33913,15338],{"class":714},[449,33915,718],{"class":680},[449,33917,33918],{"class":721}," defu",[449,33920,725],{"class":684},[449,33922,33831],{"class":721},[449,33924,33639],{"class":684},[449,33926,33927],{"class":714},"APP_CONFIG",[449,33929,33930],{"class":684},"), defaults);\n",[21,33932,33933,33934,33936],{},"These utilities pair well with ",[40,33935,33373],{}," for robust config and data handling.",[73,33938],{},[32,33940,33942],{"id":33941},"how-these-fit-together-in-a-real-app","How These Fit Together in a Real App",[21,33944,33945],{},"Let’s imagine building a minimal app with UnJS tools:",[816,33947,33948,33956,33964,33972,33981,33989],{},[386,33949,33950,33952,33953,33955],{},[151,33951,28699],{}," – Use ",[40,33954,33373],{}," to load app configs, merging defaults and environment settings.",[386,33957,33958,33952,33961,33963],{},[151,33959,33960],{},"Server Setup",[40,33962,7076],{}," to define API endpoints and middleware.",[386,33965,33966,33952,33969,33971],{},[151,33967,33968],{},"Data Fetching",[40,33970,33455],{}," on both client-side and server-side for HTTP requests.",[386,33973,33974,33977,33978,33980],{},[151,33975,33976],{},"Build\u002FPlugin Support"," – Enhance the build pipeline with ",[40,33979,33531],{}," for consistent tooling across environments.",[386,33982,33983,33952,33986,33988],{},[151,33984,33985],{},"Runtime TypeScript",[40,33987,33666],{}," in scripts or custom tooling parts of the app.",[386,33990,33991,33952,33994,349,33996,1542,33998,34000],{},[151,33992,33993],{},"Logging & Utilities",[40,33995,33760],{},[40,33997,33828],{},[40,33999,33831],{}," for reliable logging, defaults handling, and parsing.",[21,34002,34003],{},"Each UnJS package is independent and tree-shakeable. Only install what you need to keep your bundles lean.\n(::)",[73,34005],{},[32,34007,34009],{"id":34008},"code-example-putting-it-all-together","Code Example: Putting It All Together",[21,34011,34012],{},"Below is a simple example of a Nitro server with config loading and fetch utility:",[48,34014,34016],{"className":670,"code":34015,"filename":672,"language":673,"meta":56,"style":56},"import { createNitro } from \"nitro\";\nimport { loadConfig } from \"c12\";\nimport { ofetch } from \"ofetch\";\nimport { consola } from \"consola\";\n\nconst { config } = await loadConfig({});\n\nexport default createNitro({\n  async handlers() {\n    return [\n      {\n        route: \"\u002Fapi\u002Fhello\",\n        handler: () => ({\n          message: \"Hello from UnJS App!\",\n          env: config.NODE_ENV\n        })\n      },\n      {\n        route: \"\u002Fapi\u002Fuser\",\n        handler: async () => {\n          const user = await ofetch(\"https:\u002F\u002Fjsonplaceholder.typicode.com\u002Fusers\u002F1\");\n          consola.info(\"Fetched user:\", user);\n          return user;\n        }\n      }\n    ];\n  }\n});\n",[40,34017,34018,34030,34042,34054,34066,34070,34088,34092,34102,34112,34118,34123,34132,34143,34153,34161,34166,34170,34174,34182,34196,34216,34231,34239,34243,34247,34252,34256],{"__ignoreMap":56},[449,34019,34020,34022,34024,34026,34028],{"class":451,"line":452},[449,34021,681],{"class":680},[449,34023,33286],{"class":684},[449,34025,688],{"class":680},[449,34027,33291],{"class":691},[449,34029,695],{"class":684},[449,34031,34032,34034,34036,34038,34040],{"class":451,"line":114},[449,34033,681],{"class":680},[449,34035,33396],{"class":684},[449,34037,688],{"class":680},[449,34039,33401],{"class":691},[449,34041,695],{"class":684},[449,34043,34044,34046,34048,34050,34052],{"class":451,"line":111},[449,34045,681],{"class":680},[449,34047,33477],{"class":684},[449,34049,688],{"class":680},[449,34051,33482],{"class":691},[449,34053,695],{"class":684},[449,34055,34056,34058,34060,34062,34064],{"class":451,"line":468},[449,34057,681],{"class":680},[449,34059,33776],{"class":684},[449,34061,688],{"class":680},[449,34063,33781],{"class":691},[449,34065,695],{"class":684},[449,34067,34068],{"class":451,"line":474},[449,34069,700],{"emptyLinePlaceholder":130},[449,34071,34072,34074,34076,34078,34080,34082,34084,34086],{"class":451,"line":480},[449,34073,711],{"class":680},[449,34075,14124],{"class":684},[449,34077,3885],{"class":714},[449,34079,21541],{"class":684},[449,34081,5015],{"class":680},[449,34083,1654],{"class":680},[449,34085,33424],{"class":721},[449,34087,33427],{"class":684},[449,34089,34090],{"class":451,"line":486},[449,34091,700],{"emptyLinePlaceholder":130},[449,34093,34094,34096,34098,34100],{"class":451,"line":492},[449,34095,4629],{"class":680},[449,34097,4632],{"class":680},[449,34099,33306],{"class":721},[449,34101,1391],{"class":684},[449,34103,34104,34107,34110],{"class":451,"line":498},[449,34105,34106],{"class":680},"  async",[449,34108,34109],{"class":721}," handlers",[449,34111,5438],{"class":684},[449,34113,34114,34116],{"class":451,"line":133},[449,34115,1506],{"class":680},[449,34117,4791],{"class":684},[449,34119,34120],{"class":451,"line":509},[449,34121,34122],{"class":684},"      {\n",[449,34124,34125,34128,34130],{"class":451,"line":318},[449,34126,34127],{"class":684},"        route: ",[449,34129,33326],{"class":691},[449,34131,779],{"class":684},[449,34133,34134,34137,34139,34141],{"class":451,"line":520},[449,34135,34136],{"class":721},"        handler",[449,34138,32567],{"class":684},[449,34140,1498],{"class":680},[449,34142,33583],{"class":684},[449,34144,34145,34148,34151],{"class":451,"line":526},[449,34146,34147],{"class":684},"          message: ",[449,34149,34150],{"class":691},"\"Hello from UnJS App!\"",[449,34152,779],{"class":684},[449,34154,34155,34158],{"class":451,"line":532},[449,34156,34157],{"class":684},"          env: config.",[449,34159,34160],{"class":714},"NODE_ENV\n",[449,34162,34163],{"class":451,"line":2790},[449,34164,34165],{"class":684},"        })\n",[449,34167,34168],{"class":451,"line":3511},[449,34169,4095],{"class":684},[449,34171,34172],{"class":451,"line":3526},[449,34173,34122],{"class":684},[449,34175,34176,34178,34180],{"class":451,"line":3531},[449,34177,34127],{"class":684},[449,34179,33506],{"class":691},[449,34181,779],{"class":684},[449,34183,34184,34186,34188,34190,34192,34194],{"class":451,"line":3536},[449,34185,34136],{"class":721},[449,34187,1482],{"class":684},[449,34189,1485],{"class":680},[449,34191,2090],{"class":684},[449,34193,1498],{"class":680},[449,34195,1501],{"class":684},[449,34197,34198,34201,34203,34205,34207,34209,34211,34214],{"class":451,"line":4009},[449,34199,34200],{"class":680},"          const",[449,34202,24831],{"class":714},[449,34204,718],{"class":680},[449,34206,1654],{"class":680},[449,34208,33501],{"class":721},[449,34210,725],{"class":684},[449,34212,34213],{"class":691},"\"https:\u002F\u002Fjsonplaceholder.typicode.com\u002Fusers\u002F1\"",[449,34215,740],{"class":684},[449,34217,34218,34221,34223,34225,34228],{"class":451,"line":4015},[449,34219,34220],{"class":684},"          consola.",[449,34222,363],{"class":721},[449,34224,725],{"class":684},[449,34226,34227],{"class":691},"\"Fetched user:\"",[449,34229,34230],{"class":684},", user);\n",[449,34232,34233,34236],{"class":451,"line":4021},[449,34234,34235],{"class":680},"          return",[449,34237,34238],{"class":684}," user;\n",[449,34240,34241],{"class":451,"line":4027},[449,34242,9219],{"class":684},[449,34244,34245],{"class":451,"line":4032},[449,34246,8797],{"class":684},[449,34248,34249],{"class":451,"line":4038},[449,34250,34251],{"class":684},"    ];\n",[449,34253,34254],{"class":451,"line":4044},[449,34255,4599],{"class":684},[449,34257,34258],{"class":451,"line":4049},[449,34259,784],{"class":684},[21,34261,34262,34263,34266,34267,827],{},"This sample shows how ",[151,34264,34265],{},"Nitro, c12, ofetch, and consola"," can power a flexible, full-featured backend. (",[3861,34268,33365],{"href":33362,"rel":34269,"title":33364},[3865],[73,34271],{},[16,34273,3681],{"id":3680},[21,34275,1556,34276,34278,34279,34282,34283,827],{},[151,34277,33217],{}," offers a rich — yet modular — toolkit for modern JavaScript applications. By adopting packages like ",[151,34280,34281],{},"Nitro, c12, ofetch, unplugin, jiti, consola, defu, and destr",", you can assemble a highly productive development environment while keeping your dependencies lean. (",[3861,34284,33365],{"href":34285,"rel":34286,"title":34287},"https:\u002F\u002Funjs.io\u002F",[3865],"UnJS: Unleash JavaScript's Potential",[21,34289,34290,34291,34294],{},"Whether you're building servers, tooling, or full-stack apps, UnJS packages empower you to choose ",[156,34292,34293],{},"exactly the components you need"," — embracing composability and simplicity in equal measure. 🚀",[3728,34296,34297],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":56,"searchDepth":111,"depth":111,"links":34299},[34300,34316,34317],{"id":33245,"depth":114,"text":33246,"children":34301},[34302,34304,34306,34308,34310,34312,34314],{"id":33252,"depth":111,"text":34303},"Nitro — A Universal Server Engine",{"id":33370,"depth":111,"text":34305},"c12 — Smart Configuration Loader",{"id":33452,"depth":111,"text":34307},"ofetch — A Universal Fetch API",{"id":33528,"depth":111,"text":34309},"unplugin — Plugin System Across Bundlers",{"id":33663,"depth":111,"text":34311},"jiti — Runtime TypeScript and ESM Support",{"id":33757,"depth":111,"text":34313},"consola — Elegant Console Logging",{"id":33825,"depth":111,"text":34315},"defu and destr — Defaults & Safe Parsing",{"id":33941,"depth":114,"text":33942},{"id":34008,"depth":114,"text":34009},"2026-01-12","Explore the main UnJS packages and learn how to use them together in a real-world application — from config loading and HTTP servers to fetch utilities and building tools.",{"type":13,"value":34321},[34322,34324,34332],[16,34323,333],{"id":332},[21,34325,1556,34326,33218,34328,33222,34330],{},[151,34327,33217],{},[151,34329,33221],{},[9909,34331,9912],{"index":9911},[21,34333,33227,34334,33231],{},[151,34335,33230],{},[17134,7076,33455,33531,34337,33373],"unstorage",{},"\u002Fblog\u002Funjs-packages",{"title":33207,"description":34319},"blog\u002Funjs-packages","vdCQko37SZVB8LKaQh1u-JLdHqeOhRnmKgH-aZdC6mA",{"id":34344,"title":34345,"author":34346,"body":34347,"canonical":116,"categories":116,"date":37991,"description":37992,"excerpt":37993,"extension":119,"img":38011,"keywords":38012,"language":3788,"meta":38018,"navigation":130,"ogImage":116,"path":38019,"rating":474,"readTime":133,"seo":38020,"stem":38021,"tag":3793,"__hash__":38022},"blog\u002Fblog\u002Fviteplus-teacher-toolkit-package.md","Building a Teacher's Grading Toolkit with Vite+",{"name":9,"img":11,"website":327},{"type":13,"value":34348,"toc":37983},[34349,34351,34354,34361,34376,34385,34389,34392,34424,34427,34431,34434,34455,34466,34469,34504,34508,34513,34713,34717,34721,35135,35139,35369,35372,35460,35464,35764,35768,36231,36235,36312,36316,36321,37201,37204,37216,37231,37235,37240,37328,37331,37345,37349,37361,37364,37394,37407,37411,37416,37590,37593,37605,37609,37612,37625,37838,37842,37909,37914,37926,37930,37962,37964,37974,37977,37980],[16,34350,333],{"id":332},[21,34352,34353],{},"Teachers spend hours every week calculating grades, curving scores, and converting percentages to letter grades. Most of this work happens in spreadsheets with fragile formulas that break the moment a student is added out of order.",[21,34355,34356,34357,34360],{},"What if we packaged those utilities as a reusable TypeScript library — something any teacher or school app developer could ",[40,34358,34359],{},"npm install"," and use?",[21,34362,34363,34364,34367,34368,34371,34372,34375],{},"In this post, we'll build ",[151,34365,34366],{},"teacher-toolkit"," — an npm package for grade calculations — using ",[151,34369,34370],{},"Vite+"," and its ",[40,34373,34374],{},"vp pack"," command. No Webpack config. No tsconfig gymnastics. Just code, test, and ship.",[25,34377,34378],{"type":363},[21,34379,34380,34381,34384],{},"This tutorial assumes you have Vite+ installed. If not, run ",[40,34382,34383],{},"curl -fsSL https:\u002F\u002Fvite.plus | bash"," and open a new terminal.",[16,34386,34388],{"id":34387},"what-were-building","What We're Building",[21,34390,34391],{},"A lightweight TypeScript library that provides:",[383,34393,34394,34400,34406,34412,34418],{},[386,34395,34396,34399],{},[151,34397,34398],{},"Letter grade conversion"," — percentage to A\u002FB\u002FC\u002FD\u002FF",[386,34401,34402,34405],{},[151,34403,34404],{},"Weighted grade calculation"," — combine homework, exams, participation",[386,34407,34408,34411],{},[151,34409,34410],{},"Score curving"," — boost grades based on class highest score",[386,34413,34414,34417],{},[151,34415,34416],{},"GPA conversion"," — letter grades to 4.0 scale",[386,34419,34420,34423],{},[151,34421,34422],{},"Class statistics"," — mean, median, standard deviation",[21,34425,34426],{},"All with full type safety and zero dependencies.",[16,34428,34430],{"id":34429},"step-1-scaffold-the-library","Step 1: Scaffold the Library",[21,34432,34433],{},"Vite+ has a built-in library template. Run:",[48,34435,34437],{"className":839,"code":34436,"language":841,"meta":56,"style":56},"vp create vite:library -- teacher-toolkit\n",[40,34438,34439],{"__ignoreMap":56},[449,34440,34441,34443,34446,34449,34452],{"class":451,"line":452},[449,34442,3637],{"class":721},[449,34444,34445],{"class":691}," create",[449,34447,34448],{"class":691}," vite:library",[449,34450,34451],{"class":714}," --",[449,34453,34454],{"class":691}," teacher-toolkit\n",[48,34456,34458],{"className":839,"code":34457,"language":841,"meta":56,"style":56},"vp install\n",[40,34459,34460],{"__ignoreMap":56},[449,34461,34462,34464],{"class":451,"line":452},[449,34463,3637],{"class":721},[449,34465,4512],{"class":691},[21,34467,34468],{},"This gives us a project structure like:",[48,34470,34472],{"className":443,"code":34471,"language":445,"meta":56,"style":56},"teacher-toolkit\u002F\n  src\u002F\n    index.ts        # Entry point\n  vite.config.ts\n  package.json\n  tsconfig.json\n",[40,34473,34474,34479,34484,34489,34494,34499],{"__ignoreMap":56},[449,34475,34476],{"class":451,"line":452},[449,34477,34478],{},"teacher-toolkit\u002F\n",[449,34480,34481],{"class":451,"line":114},[449,34482,34483],{},"  src\u002F\n",[449,34485,34486],{"class":451,"line":111},[449,34487,34488],{},"    index.ts        # Entry point\n",[449,34490,34491],{"class":451,"line":468},[449,34492,34493],{},"  vite.config.ts\n",[449,34495,34496],{"class":451,"line":474},[449,34497,34498],{},"  package.json\n",[449,34500,34501],{"class":451,"line":480},[449,34502,34503],{},"  tsconfig.json\n",[16,34505,34507],{"id":34506},"step-2-define-the-types","Step 2: Define the Types",[21,34509,4698,34510,3070],{},[40,34511,34512],{},"src\u002Ftypes.ts",[48,34514,34516],{"className":670,"code":34515,"filename":34512,"language":673,"meta":56,"style":56},"export interface GradeScale {\n  letter: string;\n  min: number;\n  max: number;\n  gpa: number;\n}\n\nexport interface WeightedComponent {\n  name: string;\n  score: number;\n  weight: number;\n}\n\nexport interface ClassStats {\n  mean: number;\n  median: number;\n  stdDev: number;\n  highest: number;\n  lowest: number;\n  count: number;\n}\n",[40,34517,34518,34529,34540,34551,34562,34573,34577,34581,34592,34602,34613,34624,34628,34632,34643,34654,34665,34676,34687,34698,34709],{"__ignoreMap":56},[449,34519,34520,34522,34524,34527],{"class":451,"line":452},[449,34521,4629],{"class":680},[449,34523,14887],{"class":680},[449,34525,34526],{"class":721}," GradeScale",[449,34528,1501],{"class":684},[449,34530,34531,34534,34536,34538],{"class":451,"line":114},[449,34532,34533],{"class":1491},"  letter",[449,34535,3070],{"class":680},[449,34537,6083],{"class":714},[449,34539,695],{"class":684},[449,34541,34542,34545,34547,34549],{"class":451,"line":111},[449,34543,34544],{"class":1491},"  min",[449,34546,3070],{"class":680},[449,34548,4976],{"class":714},[449,34550,695],{"class":684},[449,34552,34553,34556,34558,34560],{"class":451,"line":468},[449,34554,34555],{"class":1491},"  max",[449,34557,3070],{"class":680},[449,34559,4976],{"class":714},[449,34561,695],{"class":684},[449,34563,34564,34567,34569,34571],{"class":451,"line":474},[449,34565,34566],{"class":1491},"  gpa",[449,34568,3070],{"class":680},[449,34570,4976],{"class":714},[449,34572,695],{"class":684},[449,34574,34575],{"class":451,"line":480},[449,34576,4604],{"class":684},[449,34578,34579],{"class":451,"line":486},[449,34580,700],{"emptyLinePlaceholder":130},[449,34582,34583,34585,34587,34590],{"class":451,"line":492},[449,34584,4629],{"class":680},[449,34586,14887],{"class":680},[449,34588,34589],{"class":721}," WeightedComponent",[449,34591,1501],{"class":684},[449,34593,34594,34596,34598,34600],{"class":451,"line":498},[449,34595,6078],{"class":1491},[449,34597,3070],{"class":680},[449,34599,6083],{"class":714},[449,34601,695],{"class":684},[449,34603,34604,34607,34609,34611],{"class":451,"line":133},[449,34605,34606],{"class":1491},"  score",[449,34608,3070],{"class":680},[449,34610,4976],{"class":714},[449,34612,695],{"class":684},[449,34614,34615,34618,34620,34622],{"class":451,"line":509},[449,34616,34617],{"class":1491},"  weight",[449,34619,3070],{"class":680},[449,34621,4976],{"class":714},[449,34623,695],{"class":684},[449,34625,34626],{"class":451,"line":318},[449,34627,4604],{"class":684},[449,34629,34630],{"class":451,"line":520},[449,34631,700],{"emptyLinePlaceholder":130},[449,34633,34634,34636,34638,34641],{"class":451,"line":526},[449,34635,4629],{"class":680},[449,34637,14887],{"class":680},[449,34639,34640],{"class":721}," ClassStats",[449,34642,1501],{"class":684},[449,34644,34645,34648,34650,34652],{"class":451,"line":532},[449,34646,34647],{"class":1491},"  mean",[449,34649,3070],{"class":680},[449,34651,4976],{"class":714},[449,34653,695],{"class":684},[449,34655,34656,34659,34661,34663],{"class":451,"line":2790},[449,34657,34658],{"class":1491},"  median",[449,34660,3070],{"class":680},[449,34662,4976],{"class":714},[449,34664,695],{"class":684},[449,34666,34667,34670,34672,34674],{"class":451,"line":3511},[449,34668,34669],{"class":1491},"  stdDev",[449,34671,3070],{"class":680},[449,34673,4976],{"class":714},[449,34675,695],{"class":684},[449,34677,34678,34681,34683,34685],{"class":451,"line":3526},[449,34679,34680],{"class":1491},"  highest",[449,34682,3070],{"class":680},[449,34684,4976],{"class":714},[449,34686,695],{"class":684},[449,34688,34689,34692,34694,34696],{"class":451,"line":3531},[449,34690,34691],{"class":1491},"  lowest",[449,34693,3070],{"class":680},[449,34695,4976],{"class":714},[449,34697,695],{"class":684},[449,34699,34700,34703,34705,34707],{"class":451,"line":3536},[449,34701,34702],{"class":1491},"  count",[449,34704,3070],{"class":680},[449,34706,4976],{"class":714},[449,34708,695],{"class":684},[449,34710,34711],{"class":451,"line":4009},[449,34712,4604],{"class":684},[16,34714,34716],{"id":34715},"step-3-build-the-core-utilities","Step 3: Build the Core Utilities",[32,34718,34720],{"id":34719},"letter-grade-conversion","Letter Grade Conversion",[48,34722,34725],{"className":670,"code":34723,"filename":34724,"language":673,"meta":56,"style":56},"import type { GradeScale } from '.\u002Ftypes';\n\nconst DEFAULT_SCALE: GradeScale[] = [\n  { letter: 'A', min: 90, max: 100, gpa: 4.0 },\n  { letter: 'B', min: 80, max: 89, gpa: 3.0 },\n  { letter: 'C', min: 70, max: 79, gpa: 2.0 },\n  { letter: 'D', min: 60, max: 69, gpa: 1.0 },\n  { letter: 'F', min: 0, max: 59, gpa: 0.0 },\n];\n\nexport function toLetterGrade(\n  percentage: number,\n  scale: GradeScale[] = DEFAULT_SCALE\n): string {\n  const clamped = Math.max(0, Math.min(100, percentage));\n  const match = scale.find(s => clamped >= s.min && clamped \u003C= s.max);\n  return match?.letter ?? 'F';\n}\n\nexport function toGPA(\n  percentage: number,\n  scale: GradeScale[] = DEFAULT_SCALE\n): number {\n  const letter = toLetterGrade(percentage, scale);\n  const match = scale.find(s => s.letter === letter);\n  return match?.gpa ?? 0;\n}\n","src\u002Fgrades.ts",[40,34726,34727,34743,34747,34764,34790,34813,34837,34860,34883,34887,34891,34902,34913,34929,34939,34970,35010,35024,35028,35032,35043,35053,35067,35077,35091,35118,35131],{"__ignoreMap":56},[449,34728,34729,34731,34733,34736,34738,34741],{"class":451,"line":452},[449,34730,681],{"class":680},[449,34732,11333],{"class":680},[449,34734,34735],{"class":684}," { GradeScale } ",[449,34737,688],{"class":680},[449,34739,34740],{"class":691}," '.\u002Ftypes'",[449,34742,695],{"class":684},[449,34744,34745],{"class":451,"line":114},[449,34746,700],{"emptyLinePlaceholder":130},[449,34748,34749,34751,34754,34756,34758,34760,34762],{"class":451,"line":111},[449,34750,711],{"class":680},[449,34752,34753],{"class":714}," DEFAULT_SCALE",[449,34755,3070],{"class":680},[449,34757,34526],{"class":721},[449,34759,5012],{"class":684},[449,34761,5015],{"class":680},[449,34763,4791],{"class":684},[449,34765,34766,34769,34772,34775,34778,34781,34783,34786,34788],{"class":451,"line":468},[449,34767,34768],{"class":684},"  { letter: ",[449,34770,34771],{"class":691},"'A'",[449,34773,34774],{"class":684},", min: ",[449,34776,34777],{"class":714},"90",[449,34779,34780],{"class":684},", max: ",[449,34782,5179],{"class":714},[449,34784,34785],{"class":684},", gpa: ",[449,34787,4884],{"class":714},[449,34789,4820],{"class":684},[449,34791,34792,34794,34797,34799,34802,34804,34807,34809,34811],{"class":451,"line":474},[449,34793,34768],{"class":684},[449,34795,34796],{"class":691},"'B'",[449,34798,34774],{"class":684},[449,34800,34801],{"class":714},"80",[449,34803,34780],{"class":684},[449,34805,34806],{"class":714},"89",[449,34808,34785],{"class":684},[449,34810,4837],{"class":714},[449,34812,4820],{"class":684},[449,34814,34815,34817,34820,34822,34825,34827,34830,34832,34835],{"class":451,"line":480},[449,34816,34768],{"class":684},[449,34818,34819],{"class":691},"'C'",[449,34821,34774],{"class":684},[449,34823,34824],{"class":714},"70",[449,34826,34780],{"class":684},[449,34828,34829],{"class":714},"79",[449,34831,34785],{"class":684},[449,34833,34834],{"class":714},"2.0",[449,34836,4820],{"class":684},[449,34838,34839,34841,34844,34846,34848,34850,34853,34855,34858],{"class":451,"line":486},[449,34840,34768],{"class":684},[449,34842,34843],{"class":691},"'D'",[449,34845,34774],{"class":684},[449,34847,25435],{"class":714},[449,34849,34780],{"class":684},[449,34851,34852],{"class":714},"69",[449,34854,34785],{"class":684},[449,34856,34857],{"class":714},"1.0",[449,34859,4820],{"class":684},[449,34861,34862,34864,34867,34869,34871,34873,34876,34878,34881],{"class":451,"line":492},[449,34863,34768],{"class":684},[449,34865,34866],{"class":691},"'F'",[449,34868,34774],{"class":684},[449,34870,9911],{"class":714},[449,34872,34780],{"class":684},[449,34874,34875],{"class":714},"59",[449,34877,34785],{"class":684},[449,34879,34880],{"class":714},"0.0",[449,34882,4820],{"class":684},[449,34884,34885],{"class":451,"line":498},[449,34886,4918],{"class":684},[449,34888,34889],{"class":451,"line":133},[449,34890,700],{"emptyLinePlaceholder":130},[449,34892,34893,34895,34897,34900],{"class":451,"line":509},[449,34894,4629],{"class":680},[449,34896,5432],{"class":680},[449,34898,34899],{"class":721}," toLetterGrade",[449,34901,5566],{"class":684},[449,34903,34904,34907,34909,34911],{"class":451,"line":318},[449,34905,34906],{"class":1491},"  percentage",[449,34908,3070],{"class":680},[449,34910,4976],{"class":714},[449,34912,779],{"class":684},[449,34914,34915,34918,34920,34922,34924,34926],{"class":451,"line":520},[449,34916,34917],{"class":1491},"  scale",[449,34919,3070],{"class":680},[449,34921,34526],{"class":721},[449,34923,5012],{"class":684},[449,34925,5015],{"class":680},[449,34927,34928],{"class":714}," DEFAULT_SCALE\n",[449,34930,34931,34933,34935,34937],{"class":451,"line":526},[449,34932,827],{"class":684},[449,34934,3070],{"class":680},[449,34936,6083],{"class":714},[449,34938,1501],{"class":684},[449,34940,34941,34943,34946,34948,34950,34953,34955,34957,34960,34963,34965,34967],{"class":451,"line":532},[449,34942,5049],{"class":680},[449,34944,34945],{"class":714}," clamped",[449,34947,718],{"class":680},[449,34949,5858],{"class":684},[449,34951,34952],{"class":721},"max",[449,34954,725],{"class":684},[449,34956,9911],{"class":714},[449,34958,34959],{"class":684},", Math.",[449,34961,34962],{"class":721},"min",[449,34964,725],{"class":684},[449,34966,5179],{"class":714},[449,34968,34969],{"class":684},", percentage));\n",[449,34971,34972,34974,34977,34979,34982,34985,34987,34990,34992,34995,34997,35000,35002,35004,35007],{"class":451,"line":2790},[449,34973,5049],{"class":680},[449,34975,34976],{"class":714}," match",[449,34978,718],{"class":680},[449,34980,34981],{"class":684}," scale.",[449,34983,34984],{"class":721},"find",[449,34986,725],{"class":684},[449,34988,34989],{"class":1491},"s",[449,34991,32908],{"class":680},[449,34993,34994],{"class":684}," clamped ",[449,34996,5613],{"class":680},[449,34998,34999],{"class":684}," s.min ",[449,35001,16831],{"class":680},[449,35003,34994],{"class":684},[449,35005,35006],{"class":680},"\u003C=",[449,35008,35009],{"class":684}," s.max);\n",[449,35011,35012,35014,35017,35019,35022],{"class":451,"line":3511},[449,35013,4727],{"class":680},[449,35015,35016],{"class":684}," match?.letter ",[449,35018,734],{"class":680},[449,35020,35021],{"class":691}," 'F'",[449,35023,695],{"class":684},[449,35025,35026],{"class":451,"line":3526},[449,35027,4604],{"class":684},[449,35029,35030],{"class":451,"line":3531},[449,35031,700],{"emptyLinePlaceholder":130},[449,35033,35034,35036,35038,35041],{"class":451,"line":3536},[449,35035,4629],{"class":680},[449,35037,5432],{"class":680},[449,35039,35040],{"class":721}," toGPA",[449,35042,5566],{"class":684},[449,35044,35045,35047,35049,35051],{"class":451,"line":4009},[449,35046,34906],{"class":1491},[449,35048,3070],{"class":680},[449,35050,4976],{"class":714},[449,35052,779],{"class":684},[449,35054,35055,35057,35059,35061,35063,35065],{"class":451,"line":4015},[449,35056,34917],{"class":1491},[449,35058,3070],{"class":680},[449,35060,34526],{"class":721},[449,35062,5012],{"class":684},[449,35064,5015],{"class":680},[449,35066,34928],{"class":714},[449,35068,35069,35071,35073,35075],{"class":451,"line":4021},[449,35070,827],{"class":684},[449,35072,3070],{"class":680},[449,35074,4976],{"class":714},[449,35076,1501],{"class":684},[449,35078,35079,35081,35084,35086,35088],{"class":451,"line":4027},[449,35080,5049],{"class":680},[449,35082,35083],{"class":714}," letter",[449,35085,718],{"class":680},[449,35087,34899],{"class":721},[449,35089,35090],{"class":684},"(percentage, scale);\n",[449,35092,35093,35095,35097,35099,35101,35103,35105,35107,35109,35112,35115],{"class":451,"line":4032},[449,35094,5049],{"class":680},[449,35096,34976],{"class":714},[449,35098,718],{"class":680},[449,35100,34981],{"class":684},[449,35102,34984],{"class":721},[449,35104,725],{"class":684},[449,35106,34989],{"class":1491},[449,35108,32908],{"class":680},[449,35110,35111],{"class":684}," s.letter ",[449,35113,35114],{"class":680},"===",[449,35116,35117],{"class":684}," letter);\n",[449,35119,35120,35122,35125,35127,35129],{"class":451,"line":4038},[449,35121,4727],{"class":680},[449,35123,35124],{"class":684}," match?.gpa ",[449,35126,734],{"class":680},[449,35128,5102],{"class":714},[449,35130,695],{"class":684},[449,35132,35133],{"class":451,"line":4044},[449,35134,4604],{"class":684},[32,35136,35138],{"id":35137},"weighted-grade-calculation","Weighted Grade Calculation",[48,35140,35143],{"className":670,"code":35141,"filename":35142,"language":673,"meta":56,"style":56},"import type { WeightedComponent } from '.\u002Ftypes';\n\nexport function weightedAverage(components: WeightedComponent[]): number {\n  if (components.length === 0) return 0;\n\n  const totalWeight = components.reduce((sum, c) => sum + c.weight, 0);\n  if (totalWeight === 0) return 0;\n\n  const weighted = components.reduce(\n    (sum, c) => sum + c.score * c.weight,\n    0\n  );\n\n  return Math.round((weighted \u002F totalWeight) * 100) \u002F 100;\n}\n","src\u002Fweights.ts",[40,35144,35145,35160,35164,35191,35213,35217,35259,35278,35282,35297,35324,35329,35333,35337,35365],{"__ignoreMap":56},[449,35146,35147,35149,35151,35154,35156,35158],{"class":451,"line":452},[449,35148,681],{"class":680},[449,35150,11333],{"class":680},[449,35152,35153],{"class":684}," { WeightedComponent } ",[449,35155,688],{"class":680},[449,35157,34740],{"class":691},[449,35159,695],{"class":684},[449,35161,35162],{"class":451,"line":114},[449,35163,700],{"emptyLinePlaceholder":130},[449,35165,35166,35168,35170,35173,35175,35178,35180,35182,35185,35187,35189],{"class":451,"line":111},[449,35167,4629],{"class":680},[449,35169,5432],{"class":680},[449,35171,35172],{"class":721}," weightedAverage",[449,35174,725],{"class":684},[449,35176,35177],{"class":1491},"components",[449,35179,3070],{"class":680},[449,35181,34589],{"class":721},[449,35183,35184],{"class":684},"[])",[449,35186,3070],{"class":680},[449,35188,4976],{"class":714},[449,35190,1501],{"class":684},[449,35192,35193,35195,35198,35200,35202,35204,35206,35209,35211],{"class":451,"line":468},[449,35194,5079],{"class":680},[449,35196,35197],{"class":684}," (components.",[449,35199,5096],{"class":714},[449,35201,5099],{"class":680},[449,35203,5102],{"class":714},[449,35205,1758],{"class":684},[449,35207,35208],{"class":680},"return",[449,35210,5102],{"class":714},[449,35212,695],{"class":684},[449,35214,35215],{"class":451,"line":474},[449,35216,700],{"emptyLinePlaceholder":130},[449,35218,35219,35221,35224,35226,35229,35232,35234,35237,35239,35242,35244,35246,35249,35252,35255,35257],{"class":451,"line":480},[449,35220,5049],{"class":680},[449,35222,35223],{"class":714}," totalWeight",[449,35225,718],{"class":680},[449,35227,35228],{"class":684}," components.",[449,35230,35231],{"class":721},"reduce",[449,35233,5163],{"class":684},[449,35235,35236],{"class":1491},"sum",[449,35238,349],{"class":684},[449,35240,35241],{"class":1491},"c",[449,35243,1758],{"class":684},[449,35245,1498],{"class":680},[449,35247,35248],{"class":684}," sum ",[449,35250,35251],{"class":680},"+",[449,35253,35254],{"class":684}," c.weight, ",[449,35256,9911],{"class":714},[449,35258,740],{"class":684},[449,35260,35261,35263,35266,35268,35270,35272,35274,35276],{"class":451,"line":486},[449,35262,5079],{"class":680},[449,35264,35265],{"class":684}," (totalWeight ",[449,35267,35114],{"class":680},[449,35269,5102],{"class":714},[449,35271,1758],{"class":684},[449,35273,35208],{"class":680},[449,35275,5102],{"class":714},[449,35277,695],{"class":684},[449,35279,35280],{"class":451,"line":492},[449,35281,700],{"emptyLinePlaceholder":130},[449,35283,35284,35286,35289,35291,35293,35295],{"class":451,"line":498},[449,35285,5049],{"class":680},[449,35287,35288],{"class":714}," weighted",[449,35290,718],{"class":680},[449,35292,35228],{"class":684},[449,35294,35231],{"class":721},[449,35296,5566],{"class":684},[449,35298,35299,35302,35304,35306,35308,35310,35312,35314,35316,35319,35321],{"class":451,"line":133},[449,35300,35301],{"class":684},"    (",[449,35303,35236],{"class":1491},[449,35305,349],{"class":684},[449,35307,35241],{"class":1491},[449,35309,1758],{"class":684},[449,35311,1498],{"class":680},[449,35313,35248],{"class":684},[449,35315,35251],{"class":680},[449,35317,35318],{"class":684}," c.score ",[449,35320,12951],{"class":680},[449,35322,35323],{"class":684}," c.weight,\n",[449,35325,35326],{"class":451,"line":509},[449,35327,35328],{"class":714},"    0\n",[449,35330,35331],{"class":451,"line":318},[449,35332,6436],{"class":684},[449,35334,35335],{"class":451,"line":520},[449,35336,700],{"emptyLinePlaceholder":130},[449,35338,35339,35341,35343,35345,35348,35350,35353,35355,35357,35359,35361,35363],{"class":451,"line":526},[449,35340,4727],{"class":680},[449,35342,5858],{"class":684},[449,35344,5861],{"class":721},[449,35346,35347],{"class":684},"((weighted ",[449,35349,5874],{"class":680},[449,35351,35352],{"class":684}," totalWeight) ",[449,35354,12951],{"class":680},[449,35356,5419],{"class":714},[449,35358,1758],{"class":684},[449,35360,5874],{"class":680},[449,35362,5419],{"class":714},[449,35364,695],{"class":684},[449,35366,35367],{"class":451,"line":532},[449,35368,4604],{"class":684},[21,35370,35371],{},"A teacher can use this like:",[48,35373,35375],{"className":670,"code":35374,"language":673,"meta":56,"style":56},"const finalGrade = weightedAverage([\n  { name: 'Homework', score: 88, weight: 0.3 },\n  { name: 'Midterm', score: 76, weight: 0.3 },\n  { name: 'Final Exam', score: 82, weight: 0.4 },\n]);\n\u002F\u002F => 81.8\n",[40,35376,35377,35391,35413,35431,35450,35455],{"__ignoreMap":56},[449,35378,35379,35381,35384,35386,35388],{"class":451,"line":452},[449,35380,711],{"class":680},[449,35382,35383],{"class":714}," finalGrade",[449,35385,718],{"class":680},[449,35387,35172],{"class":721},[449,35389,35390],{"class":684},"([\n",[449,35392,35393,35396,35399,35402,35405,35408,35411],{"class":451,"line":114},[449,35394,35395],{"class":684},"  { name: ",[449,35397,35398],{"class":691},"'Homework'",[449,35400,35401],{"class":684},", score: ",[449,35403,35404],{"class":714},"88",[449,35406,35407],{"class":684},", weight: ",[449,35409,35410],{"class":714},"0.3",[449,35412,4820],{"class":684},[449,35414,35415,35417,35420,35422,35425,35427,35429],{"class":451,"line":111},[449,35416,35395],{"class":684},[449,35418,35419],{"class":691},"'Midterm'",[449,35421,35401],{"class":684},[449,35423,35424],{"class":714},"76",[449,35426,35407],{"class":684},[449,35428,35410],{"class":714},[449,35430,4820],{"class":684},[449,35432,35433,35435,35438,35440,35443,35445,35448],{"class":451,"line":468},[449,35434,35395],{"class":684},[449,35436,35437],{"class":691},"'Final Exam'",[449,35439,35401],{"class":684},[449,35441,35442],{"class":714},"82",[449,35444,35407],{"class":684},[449,35446,35447],{"class":714},"0.4",[449,35449,4820],{"class":684},[449,35451,35452],{"class":451,"line":474},[449,35453,35454],{"class":684},"]);\n",[449,35456,35457],{"class":451,"line":480},[449,35458,35459],{"class":705},"\u002F\u002F => 81.8\n",[32,35461,35463],{"id":35462},"score-curving","Score Curving",[48,35465,35468],{"className":670,"code":35466,"filename":35467,"language":673,"meta":56,"style":56},"export function curveByHighest(\n  scores: number[],\n  targetHigh: number = 100\n): number[] {\n  const highest = Math.max(...scores);\n  if (highest >= targetHigh) return scores;\n\n  const boost = targetHigh - highest;\n  return scores.map(s => Math.min(100, s + boost));\n}\n\nexport function curveByMean(\n  scores: number[],\n  targetMean: number = 75\n): number[] {\n  const mean = scores.reduce((a, b) => a + b, 0) \u002F scores.length;\n  const shift = targetMean - mean;\n  return scores.map(s => Math.max(0, Math.min(100, s + shift)));\n}\n","src\u002Fcurve.ts",[40,35469,35470,35481,35493,35507,35518,35538,35555,35559,35576,35607,35611,35615,35626,35636,35650,35660,35706,35723,35760],{"__ignoreMap":56},[449,35471,35472,35474,35476,35479],{"class":451,"line":452},[449,35473,4629],{"class":680},[449,35475,5432],{"class":680},[449,35477,35478],{"class":721}," curveByHighest",[449,35480,5566],{"class":684},[449,35482,35483,35486,35488,35490],{"class":451,"line":114},[449,35484,35485],{"class":1491},"  scores",[449,35487,3070],{"class":680},[449,35489,4976],{"class":714},[449,35491,35492],{"class":684},"[],\n",[449,35494,35495,35498,35500,35502,35504],{"class":451,"line":111},[449,35496,35497],{"class":1491},"  targetHigh",[449,35499,3070],{"class":680},[449,35501,4976],{"class":714},[449,35503,718],{"class":680},[449,35505,35506],{"class":714}," 100\n",[449,35508,35509,35511,35513,35515],{"class":451,"line":468},[449,35510,827],{"class":684},[449,35512,3070],{"class":680},[449,35514,4976],{"class":714},[449,35516,35517],{"class":684},"[] {\n",[449,35519,35520,35522,35525,35527,35529,35531,35533,35535],{"class":451,"line":474},[449,35521,5049],{"class":680},[449,35523,35524],{"class":714}," highest",[449,35526,718],{"class":680},[449,35528,5858],{"class":684},[449,35530,34952],{"class":721},[449,35532,725],{"class":684},[449,35534,14127],{"class":680},[449,35536,35537],{"class":684},"scores);\n",[449,35539,35540,35542,35545,35547,35550,35552],{"class":451,"line":480},[449,35541,5079],{"class":680},[449,35543,35544],{"class":684}," (highest ",[449,35546,5613],{"class":680},[449,35548,35549],{"class":684}," targetHigh) ",[449,35551,35208],{"class":680},[449,35553,35554],{"class":684}," scores;\n",[449,35556,35557],{"class":451,"line":486},[449,35558,700],{"emptyLinePlaceholder":130},[449,35560,35561,35563,35566,35568,35571,35573],{"class":451,"line":492},[449,35562,5049],{"class":680},[449,35564,35565],{"class":714}," boost",[449,35567,718],{"class":680},[449,35569,35570],{"class":684}," targetHigh ",[449,35572,5842],{"class":680},[449,35574,35575],{"class":684}," highest;\n",[449,35577,35578,35580,35583,35585,35587,35589,35591,35593,35595,35597,35599,35602,35604],{"class":451,"line":498},[449,35579,4727],{"class":680},[449,35581,35582],{"class":684}," scores.",[449,35584,6397],{"class":721},[449,35586,725],{"class":684},[449,35588,34989],{"class":1491},[449,35590,32908],{"class":680},[449,35592,5858],{"class":684},[449,35594,34962],{"class":721},[449,35596,725],{"class":684},[449,35598,5179],{"class":714},[449,35600,35601],{"class":684},", s ",[449,35603,35251],{"class":680},[449,35605,35606],{"class":684}," boost));\n",[449,35608,35609],{"class":451,"line":133},[449,35610,4604],{"class":684},[449,35612,35613],{"class":451,"line":509},[449,35614,700],{"emptyLinePlaceholder":130},[449,35616,35617,35619,35621,35624],{"class":451,"line":318},[449,35618,4629],{"class":680},[449,35620,5432],{"class":680},[449,35622,35623],{"class":721}," curveByMean",[449,35625,5566],{"class":684},[449,35627,35628,35630,35632,35634],{"class":451,"line":520},[449,35629,35485],{"class":1491},[449,35631,3070],{"class":680},[449,35633,4976],{"class":714},[449,35635,35492],{"class":684},[449,35637,35638,35641,35643,35645,35647],{"class":451,"line":526},[449,35639,35640],{"class":1491},"  targetMean",[449,35642,3070],{"class":680},[449,35644,4976],{"class":714},[449,35646,718],{"class":680},[449,35648,35649],{"class":714}," 75\n",[449,35651,35652,35654,35656,35658],{"class":451,"line":532},[449,35653,827],{"class":684},[449,35655,3070],{"class":680},[449,35657,4976],{"class":714},[449,35659,35517],{"class":684},[449,35661,35662,35664,35667,35669,35671,35673,35675,35677,35679,35682,35684,35686,35689,35691,35694,35696,35698,35700,35702,35704],{"class":451,"line":2790},[449,35663,5049],{"class":680},[449,35665,35666],{"class":714}," mean",[449,35668,718],{"class":680},[449,35670,35582],{"class":684},[449,35672,35231],{"class":721},[449,35674,5163],{"class":684},[449,35676,3861],{"class":1491},[449,35678,349],{"class":684},[449,35680,35681],{"class":1491},"b",[449,35683,1758],{"class":684},[449,35685,1498],{"class":680},[449,35687,35688],{"class":684}," a ",[449,35690,35251],{"class":680},[449,35692,35693],{"class":684}," b, ",[449,35695,9911],{"class":714},[449,35697,1758],{"class":684},[449,35699,5874],{"class":680},[449,35701,35582],{"class":684},[449,35703,5096],{"class":714},[449,35705,695],{"class":684},[449,35707,35708,35710,35713,35715,35718,35720],{"class":451,"line":3511},[449,35709,5049],{"class":680},[449,35711,35712],{"class":714}," shift",[449,35714,718],{"class":680},[449,35716,35717],{"class":684}," targetMean ",[449,35719,5842],{"class":680},[449,35721,35722],{"class":684}," mean;\n",[449,35724,35725,35727,35729,35731,35733,35735,35737,35739,35741,35743,35745,35747,35749,35751,35753,35755,35757],{"class":451,"line":3526},[449,35726,4727],{"class":680},[449,35728,35582],{"class":684},[449,35730,6397],{"class":721},[449,35732,725],{"class":684},[449,35734,34989],{"class":1491},[449,35736,32908],{"class":680},[449,35738,5858],{"class":684},[449,35740,34952],{"class":721},[449,35742,725],{"class":684},[449,35744,9911],{"class":714},[449,35746,34959],{"class":684},[449,35748,34962],{"class":721},[449,35750,725],{"class":684},[449,35752,5179],{"class":714},[449,35754,35601],{"class":684},[449,35756,35251],{"class":680},[449,35758,35759],{"class":684}," shift)));\n",[449,35761,35762],{"class":451,"line":3531},[449,35763,4604],{"class":684},[32,35765,35767],{"id":35766},"class-statistics","Class Statistics",[48,35769,35772],{"className":670,"code":35770,"filename":35771,"language":673,"meta":56,"style":56},"import type { ClassStats } from '.\u002Ftypes';\n\nexport function classStats(scores: number[]): ClassStats {\n  if (scores.length === 0) {\n    return { mean: 0, median: 0, stdDev: 0, highest: 0, lowest: 0, count: 0 };\n  }\n\n  const sorted = [...scores].sort((a, b) => a - b);\n  const count = sorted.length;\n  const sum = sorted.reduce((a, b) => a + b, 0);\n  const mean = sum \u002F count;\n\n  const mid = Math.floor(count \u002F 2);\n  const median = count % 2 === 0\n    ? (sorted[mid - 1] + sorted[mid]) \u002F 2\n    : sorted[mid];\n\n  const variance = sorted.reduce((acc, s) => acc + (s - mean) ** 2, 0) \u002F count;\n  const stdDev = Math.round(Math.sqrt(variance) * 100) \u002F 100;\n\n  return {\n    mean: Math.round(mean * 100) \u002F 100,\n    median,\n    stdDev,\n    highest: sorted[count - 1],\n    lowest: sorted[0],\n    count,\n  };\n}\n","src\u002Fstats.ts\"",[40,35773,35774,35789,35793,35819,35834,35870,35874,35878,35916,35932,35967,35982,35986,36010,36032,36056,36064,36068,36122,36156,36160,36166,36188,36193,36198,36209,36218,36223,36227],{"__ignoreMap":56},[449,35775,35776,35778,35780,35783,35785,35787],{"class":451,"line":452},[449,35777,681],{"class":680},[449,35779,11333],{"class":680},[449,35781,35782],{"class":684}," { ClassStats } ",[449,35784,688],{"class":680},[449,35786,34740],{"class":691},[449,35788,695],{"class":684},[449,35790,35791],{"class":451,"line":114},[449,35792,700],{"emptyLinePlaceholder":130},[449,35794,35795,35797,35799,35802,35804,35807,35809,35811,35813,35815,35817],{"class":451,"line":111},[449,35796,4629],{"class":680},[449,35798,5432],{"class":680},[449,35800,35801],{"class":721}," classStats",[449,35803,725],{"class":684},[449,35805,35806],{"class":1491},"scores",[449,35808,3070],{"class":680},[449,35810,4976],{"class":714},[449,35812,35184],{"class":684},[449,35814,3070],{"class":680},[449,35816,34640],{"class":721},[449,35818,1501],{"class":684},[449,35820,35821,35823,35826,35828,35830,35832],{"class":451,"line":468},[449,35822,5079],{"class":680},[449,35824,35825],{"class":684}," (scores.",[449,35827,5096],{"class":714},[449,35829,5099],{"class":680},[449,35831,5102],{"class":714},[449,35833,3417],{"class":684},[449,35835,35836,35838,35841,35843,35846,35848,35851,35853,35856,35858,35861,35863,35866,35868],{"class":451,"line":474},[449,35837,1506],{"class":680},[449,35839,35840],{"class":684}," { mean: ",[449,35842,9911],{"class":714},[449,35844,35845],{"class":684},", median: ",[449,35847,9911],{"class":714},[449,35849,35850],{"class":684},", stdDev: ",[449,35852,9911],{"class":714},[449,35854,35855],{"class":684},", highest: ",[449,35857,9911],{"class":714},[449,35859,35860],{"class":684},", lowest: ",[449,35862,9911],{"class":714},[449,35864,35865],{"class":684},", count: ",[449,35867,9911],{"class":714},[449,35869,1520],{"class":684},[449,35871,35872],{"class":451,"line":480},[449,35873,4599],{"class":684},[449,35875,35876],{"class":451,"line":486},[449,35877,700],{"emptyLinePlaceholder":130},[449,35879,35880,35882,35885,35887,35889,35891,35894,35897,35899,35901,35903,35905,35907,35909,35911,35913],{"class":451,"line":492},[449,35881,5049],{"class":680},[449,35883,35884],{"class":714}," sorted",[449,35886,718],{"class":680},[449,35888,13480],{"class":684},[449,35890,14127],{"class":680},[449,35892,35893],{"class":684},"scores].",[449,35895,35896],{"class":721},"sort",[449,35898,5163],{"class":684},[449,35900,3861],{"class":1491},[449,35902,349],{"class":684},[449,35904,35681],{"class":1491},[449,35906,1758],{"class":684},[449,35908,1498],{"class":680},[449,35910,35688],{"class":684},[449,35912,5842],{"class":680},[449,35914,35915],{"class":684}," b);\n",[449,35917,35918,35920,35923,35925,35928,35930],{"class":451,"line":498},[449,35919,5049],{"class":680},[449,35921,35922],{"class":714}," count",[449,35924,718],{"class":680},[449,35926,35927],{"class":684}," sorted.",[449,35929,5096],{"class":714},[449,35931,695],{"class":684},[449,35933,35934,35936,35939,35941,35943,35945,35947,35949,35951,35953,35955,35957,35959,35961,35963,35965],{"class":451,"line":133},[449,35935,5049],{"class":680},[449,35937,35938],{"class":714}," sum",[449,35940,718],{"class":680},[449,35942,35927],{"class":684},[449,35944,35231],{"class":721},[449,35946,5163],{"class":684},[449,35948,3861],{"class":1491},[449,35950,349],{"class":684},[449,35952,35681],{"class":1491},[449,35954,1758],{"class":684},[449,35956,1498],{"class":680},[449,35958,35688],{"class":684},[449,35960,35251],{"class":680},[449,35962,35693],{"class":684},[449,35964,9911],{"class":714},[449,35966,740],{"class":684},[449,35968,35969,35971,35973,35975,35977,35979],{"class":451,"line":509},[449,35970,5049],{"class":680},[449,35972,35666],{"class":714},[449,35974,718],{"class":680},[449,35976,35248],{"class":684},[449,35978,5874],{"class":680},[449,35980,35981],{"class":684}," count;\n",[449,35983,35984],{"class":451,"line":318},[449,35985,700],{"emptyLinePlaceholder":130},[449,35987,35988,35990,35993,35995,35997,36000,36003,36005,36008],{"class":451,"line":520},[449,35989,5049],{"class":680},[449,35991,35992],{"class":714}," mid",[449,35994,718],{"class":680},[449,35996,5858],{"class":684},[449,35998,35999],{"class":721},"floor",[449,36001,36002],{"class":684},"(count ",[449,36004,5874],{"class":680},[449,36006,36007],{"class":714}," 2",[449,36009,740],{"class":684},[449,36011,36012,36014,36017,36019,36022,36025,36027,36029],{"class":451,"line":526},[449,36013,5049],{"class":680},[449,36015,36016],{"class":714}," median",[449,36018,718],{"class":680},[449,36020,36021],{"class":684}," count ",[449,36023,36024],{"class":680},"%",[449,36026,36007],{"class":714},[449,36028,5099],{"class":680},[449,36030,36031],{"class":714}," 0\n",[449,36033,36034,36037,36040,36042,36044,36046,36048,36051,36053],{"class":451,"line":532},[449,36035,36036],{"class":680},"    ?",[449,36038,36039],{"class":684}," (sorted[mid ",[449,36041,5842],{"class":680},[449,36043,19911],{"class":714},[449,36045,31931],{"class":684},[449,36047,35251],{"class":680},[449,36049,36050],{"class":684}," sorted[mid]) ",[449,36052,5874],{"class":680},[449,36054,36055],{"class":714}," 2\n",[449,36057,36058,36061],{"class":451,"line":2790},[449,36059,36060],{"class":680},"    :",[449,36062,36063],{"class":684}," sorted[mid];\n",[449,36065,36066],{"class":451,"line":3511},[449,36067,700],{"emptyLinePlaceholder":130},[449,36069,36070,36072,36075,36077,36079,36081,36083,36086,36088,36090,36092,36094,36097,36099,36102,36104,36107,36110,36112,36114,36116,36118,36120],{"class":451,"line":3526},[449,36071,5049],{"class":680},[449,36073,36074],{"class":714}," variance",[449,36076,718],{"class":680},[449,36078,35927],{"class":684},[449,36080,35231],{"class":721},[449,36082,5163],{"class":684},[449,36084,36085],{"class":1491},"acc",[449,36087,349],{"class":684},[449,36089,34989],{"class":1491},[449,36091,1758],{"class":684},[449,36093,1498],{"class":680},[449,36095,36096],{"class":684}," acc ",[449,36098,35251],{"class":680},[449,36100,36101],{"class":684}," (s ",[449,36103,5842],{"class":680},[449,36105,36106],{"class":684}," mean) ",[449,36108,36109],{"class":680},"**",[449,36111,36007],{"class":714},[449,36113,349],{"class":684},[449,36115,9911],{"class":714},[449,36117,1758],{"class":684},[449,36119,5874],{"class":680},[449,36121,35981],{"class":684},[449,36123,36124,36126,36129,36131,36133,36135,36138,36141,36144,36146,36148,36150,36152,36154],{"class":451,"line":3531},[449,36125,5049],{"class":680},[449,36127,36128],{"class":714}," stdDev",[449,36130,718],{"class":680},[449,36132,5858],{"class":684},[449,36134,5861],{"class":721},[449,36136,36137],{"class":684},"(Math.",[449,36139,36140],{"class":721},"sqrt",[449,36142,36143],{"class":684},"(variance) ",[449,36145,12951],{"class":680},[449,36147,5419],{"class":714},[449,36149,1758],{"class":684},[449,36151,5874],{"class":680},[449,36153,5419],{"class":714},[449,36155,695],{"class":684},[449,36157,36158],{"class":451,"line":3536},[449,36159,700],{"emptyLinePlaceholder":130},[449,36161,36162,36164],{"class":451,"line":4009},[449,36163,4727],{"class":680},[449,36165,1501],{"class":684},[449,36167,36168,36171,36173,36176,36178,36180,36182,36184,36186],{"class":451,"line":4015},[449,36169,36170],{"class":684},"    mean: Math.",[449,36172,5861],{"class":721},[449,36174,36175],{"class":684},"(mean ",[449,36177,12951],{"class":680},[449,36179,5419],{"class":714},[449,36181,1758],{"class":684},[449,36183,5874],{"class":680},[449,36185,5419],{"class":714},[449,36187,779],{"class":684},[449,36189,36190],{"class":451,"line":4021},[449,36191,36192],{"class":684},"    median,\n",[449,36194,36195],{"class":451,"line":4027},[449,36196,36197],{"class":684},"    stdDev,\n",[449,36199,36200,36203,36205,36207],{"class":451,"line":4032},[449,36201,36202],{"class":684},"    highest: sorted[count ",[449,36204,5842],{"class":680},[449,36206,19911],{"class":714},[449,36208,1469],{"class":684},[449,36210,36211,36214,36216],{"class":451,"line":4038},[449,36212,36213],{"class":684},"    lowest: sorted[",[449,36215,9911],{"class":714},[449,36217,1469],{"class":684},[449,36219,36220],{"class":451,"line":4044},[449,36221,36222],{"class":684},"    count,\n",[449,36224,36225],{"class":451,"line":4049},[449,36226,4764],{"class":684},[449,36228,36229],{"class":451,"line":4054},[449,36230,4604],{"class":684},[16,36232,36234],{"id":36233},"step-4-wire-up-the-entry-point","Step 4: Wire Up the Entry Point",[48,36236,36239],{"className":670,"code":36237,"filename":36238,"language":673,"meta":56,"style":56},"export { toLetterGrade, toGPA } from '.\u002Fgrades';\nexport { weightedAverage } from '.\u002Fweights';\nexport { curveByHighest, curveByMean } from '.\u002Fcurve';\nexport { classStats } from '.\u002Fstats';\nexport type { GradeScale, WeightedComponent, ClassStats } from '.\u002Ftypes';\n","src\u002Findex.ts",[40,36240,36241,36255,36269,36283,36297],{"__ignoreMap":56},[449,36242,36243,36245,36248,36250,36253],{"class":451,"line":452},[449,36244,4629],{"class":680},[449,36246,36247],{"class":684}," { toLetterGrade, toGPA } ",[449,36249,688],{"class":680},[449,36251,36252],{"class":691}," '.\u002Fgrades'",[449,36254,695],{"class":684},[449,36256,36257,36259,36262,36264,36267],{"class":451,"line":114},[449,36258,4629],{"class":680},[449,36260,36261],{"class":684}," { weightedAverage } ",[449,36263,688],{"class":680},[449,36265,36266],{"class":691}," '.\u002Fweights'",[449,36268,695],{"class":684},[449,36270,36271,36273,36276,36278,36281],{"class":451,"line":111},[449,36272,4629],{"class":680},[449,36274,36275],{"class":684}," { curveByHighest, curveByMean } ",[449,36277,688],{"class":680},[449,36279,36280],{"class":691}," '.\u002Fcurve'",[449,36282,695],{"class":684},[449,36284,36285,36287,36290,36292,36295],{"class":451,"line":468},[449,36286,4629],{"class":680},[449,36288,36289],{"class":684}," { classStats } ",[449,36291,688],{"class":680},[449,36293,36294],{"class":691}," '.\u002Fstats'",[449,36296,695],{"class":684},[449,36298,36299,36301,36303,36306,36308,36310],{"class":451,"line":474},[449,36300,4629],{"class":680},[449,36302,11333],{"class":680},[449,36304,36305],{"class":684}," { GradeScale, WeightedComponent, ClassStats } ",[449,36307,688],{"class":680},[449,36309,34740],{"class":691},[449,36311,695],{"class":684},[16,36313,36315],{"id":36314},"step-5-write-tests","Step 5: Write Tests",[21,36317,4698,36318,3070],{},[40,36319,36320],{},"src\u002Fgrades.test.ts",[48,36322,36324],{"className":670,"code":36323,"filename":36320,"language":673,"meta":56,"style":56},"import { describe, it, expect } from 'vitest';\nimport { toLetterGrade, toGPA } from '.\u002Fgrades';\nimport { weightedAverage } from '.\u002Fweights';\nimport { curveByHighest, curveByMean } from '.\u002Fcurve';\nimport { classStats } from '.\u002Fstats';\n\ndescribe('toLetterGrade', () => {\n  it('converts percentages to letter grades', () => {\n    expect(toLetterGrade(95)).toBe('A');\n    expect(toLetterGrade(83)).toBe('B');\n    expect(toLetterGrade(72)).toBe('C');\n    expect(toLetterGrade(65)).toBe('D');\n    expect(toLetterGrade(40)).toBe('F');\n  });\n\n  it('clamps out-of-range values', () => {\n    expect(toLetterGrade(110)).toBe('A');\n    expect(toLetterGrade(-5)).toBe('F');\n  });\n});\n\ndescribe('toGPA', () => {\n  it('converts to 4.0 scale', () => {\n    expect(toGPA(92)).toBe(4.0);\n    expect(toGPA(85)).toBe(3.0);\n    expect(toGPA(55)).toBe(0.0);\n  });\n});\n\ndescribe('weightedAverage', () => {\n  it('calculates weighted grade', () => {\n    const result = weightedAverage([\n      { name: 'Homework', score: 90, weight: 0.4 },\n      { name: 'Exam', score: 80, weight: 0.6 },\n    ]);\n    expect(result).toBe(84);\n  });\n\n  it('returns 0 for empty input', () => {\n    expect(weightedAverage([])).toBe(0);\n  });\n});\n\ndescribe('curveByHighest', () => {\n  it('curves scores up to target', () => {\n    const curved = curveByHighest([70, 80, 90], 100);\n    expect(curved).toEqual([80, 90, 100]);\n  });\n\n  it('does not curve if highest already meets target', () => {\n    const scores = [70, 80, 95];\n    expect(curveByHighest(scores, 100)).toEqual(scores);\n  });\n});\n\ndescribe('classStats', () => {\n  it('calculates stats correctly', () => {\n    const stats = classStats([90, 80, 70, 60, 50]);\n    expect(stats.mean).toBe(70);\n    expect(stats.median).toBe(70);\n    expect(stats.highest).toBe(90);\n    expect(stats.lowest).toBe(50);\n    expect(stats.count).toBe(5);\n  });\n});\n",[40,36325,36326,36340,36352,36364,36376,36388,36392,36408,36424,36451,36474,36497,36520,36543,36547,36551,36566,36589,36613,36617,36621,36625,36640,36655,36679,36702,36725,36729,36733,36737,36752,36767,36779,36796,36814,36819,36835,36839,36843,36858,36878,36882,36886,36890,36905,36920,36951,36975,36979,36983,36998,37021,37042,37046,37050,37054,37069,37084,37118,37133,37148,37163,37178,37193,37197],{"__ignoreMap":56},[449,36327,36328,36330,36333,36335,36338],{"class":451,"line":452},[449,36329,681],{"class":680},[449,36331,36332],{"class":684}," { describe, it, expect } ",[449,36334,688],{"class":680},[449,36336,36337],{"class":691}," 'vitest'",[449,36339,695],{"class":684},[449,36341,36342,36344,36346,36348,36350],{"class":451,"line":114},[449,36343,681],{"class":680},[449,36345,36247],{"class":684},[449,36347,688],{"class":680},[449,36349,36252],{"class":691},[449,36351,695],{"class":684},[449,36353,36354,36356,36358,36360,36362],{"class":451,"line":111},[449,36355,681],{"class":680},[449,36357,36261],{"class":684},[449,36359,688],{"class":680},[449,36361,36266],{"class":691},[449,36363,695],{"class":684},[449,36365,36366,36368,36370,36372,36374],{"class":451,"line":468},[449,36367,681],{"class":680},[449,36369,36275],{"class":684},[449,36371,688],{"class":680},[449,36373,36280],{"class":691},[449,36375,695],{"class":684},[449,36377,36378,36380,36382,36384,36386],{"class":451,"line":474},[449,36379,681],{"class":680},[449,36381,36289],{"class":684},[449,36383,688],{"class":680},[449,36385,36294],{"class":691},[449,36387,695],{"class":684},[449,36389,36390],{"class":451,"line":480},[449,36391,700],{"emptyLinePlaceholder":130},[449,36393,36394,36397,36399,36402,36404,36406],{"class":451,"line":486},[449,36395,36396],{"class":721},"describe",[449,36398,725],{"class":684},[449,36400,36401],{"class":691},"'toLetterGrade'",[449,36403,23051],{"class":684},[449,36405,1498],{"class":680},[449,36407,1501],{"class":684},[449,36409,36410,36413,36415,36418,36420,36422],{"class":451,"line":492},[449,36411,36412],{"class":721},"  it",[449,36414,725],{"class":684},[449,36416,36417],{"class":691},"'converts percentages to letter grades'",[449,36419,23051],{"class":684},[449,36421,1498],{"class":680},[449,36423,1501],{"class":684},[449,36425,36426,36429,36431,36434,36436,36439,36442,36445,36447,36449],{"class":451,"line":498},[449,36427,36428],{"class":721},"    expect",[449,36430,725],{"class":684},[449,36432,36433],{"class":721},"toLetterGrade",[449,36435,725],{"class":684},[449,36437,36438],{"class":714},"95",[449,36440,36441],{"class":684},")).",[449,36443,36444],{"class":721},"toBe",[449,36446,725],{"class":684},[449,36448,34771],{"class":691},[449,36450,740],{"class":684},[449,36452,36453,36455,36457,36459,36461,36464,36466,36468,36470,36472],{"class":451,"line":133},[449,36454,36428],{"class":721},[449,36456,725],{"class":684},[449,36458,36433],{"class":721},[449,36460,725],{"class":684},[449,36462,36463],{"class":714},"83",[449,36465,36441],{"class":684},[449,36467,36444],{"class":721},[449,36469,725],{"class":684},[449,36471,34796],{"class":691},[449,36473,740],{"class":684},[449,36475,36476,36478,36480,36482,36484,36487,36489,36491,36493,36495],{"class":451,"line":509},[449,36477,36428],{"class":721},[449,36479,725],{"class":684},[449,36481,36433],{"class":721},[449,36483,725],{"class":684},[449,36485,36486],{"class":714},"72",[449,36488,36441],{"class":684},[449,36490,36444],{"class":721},[449,36492,725],{"class":684},[449,36494,34819],{"class":691},[449,36496,740],{"class":684},[449,36498,36499,36501,36503,36505,36507,36510,36512,36514,36516,36518],{"class":451,"line":318},[449,36500,36428],{"class":721},[449,36502,725],{"class":684},[449,36504,36433],{"class":721},[449,36506,725],{"class":684},[449,36508,36509],{"class":714},"65",[449,36511,36441],{"class":684},[449,36513,36444],{"class":721},[449,36515,725],{"class":684},[449,36517,34843],{"class":691},[449,36519,740],{"class":684},[449,36521,36522,36524,36526,36528,36530,36533,36535,36537,36539,36541],{"class":451,"line":520},[449,36523,36428],{"class":721},[449,36525,725],{"class":684},[449,36527,36433],{"class":721},[449,36529,725],{"class":684},[449,36531,36532],{"class":714},"40",[449,36534,36441],{"class":684},[449,36536,36444],{"class":721},[449,36538,725],{"class":684},[449,36540,34866],{"class":691},[449,36542,740],{"class":684},[449,36544,36545],{"class":451,"line":526},[449,36546,13106],{"class":684},[449,36548,36549],{"class":451,"line":532},[449,36550,700],{"emptyLinePlaceholder":130},[449,36552,36553,36555,36557,36560,36562,36564],{"class":451,"line":2790},[449,36554,36412],{"class":721},[449,36556,725],{"class":684},[449,36558,36559],{"class":691},"'clamps out-of-range values'",[449,36561,23051],{"class":684},[449,36563,1498],{"class":680},[449,36565,1501],{"class":684},[449,36567,36568,36570,36572,36574,36576,36579,36581,36583,36585,36587],{"class":451,"line":3511},[449,36569,36428],{"class":721},[449,36571,725],{"class":684},[449,36573,36433],{"class":721},[449,36575,725],{"class":684},[449,36577,36578],{"class":714},"110",[449,36580,36441],{"class":684},[449,36582,36444],{"class":721},[449,36584,725],{"class":684},[449,36586,34771],{"class":691},[449,36588,740],{"class":684},[449,36590,36591,36593,36595,36597,36599,36601,36603,36605,36607,36609,36611],{"class":451,"line":3526},[449,36592,36428],{"class":721},[449,36594,725],{"class":684},[449,36596,36433],{"class":721},[449,36598,725],{"class":684},[449,36600,5842],{"class":680},[449,36602,4898],{"class":714},[449,36604,36441],{"class":684},[449,36606,36444],{"class":721},[449,36608,725],{"class":684},[449,36610,34866],{"class":691},[449,36612,740],{"class":684},[449,36614,36615],{"class":451,"line":3531},[449,36616,13106],{"class":684},[449,36618,36619],{"class":451,"line":3536},[449,36620,784],{"class":684},[449,36622,36623],{"class":451,"line":4009},[449,36624,700],{"emptyLinePlaceholder":130},[449,36626,36627,36629,36631,36634,36636,36638],{"class":451,"line":4015},[449,36628,36396],{"class":721},[449,36630,725],{"class":684},[449,36632,36633],{"class":691},"'toGPA'",[449,36635,23051],{"class":684},[449,36637,1498],{"class":680},[449,36639,1501],{"class":684},[449,36641,36642,36644,36646,36649,36651,36653],{"class":451,"line":4021},[449,36643,36412],{"class":721},[449,36645,725],{"class":684},[449,36647,36648],{"class":691},"'converts to 4.0 scale'",[449,36650,23051],{"class":684},[449,36652,1498],{"class":680},[449,36654,1501],{"class":684},[449,36656,36657,36659,36661,36664,36666,36669,36671,36673,36675,36677],{"class":451,"line":4027},[449,36658,36428],{"class":721},[449,36660,725],{"class":684},[449,36662,36663],{"class":721},"toGPA",[449,36665,725],{"class":684},[449,36667,36668],{"class":714},"92",[449,36670,36441],{"class":684},[449,36672,36444],{"class":721},[449,36674,725],{"class":684},[449,36676,4884],{"class":714},[449,36678,740],{"class":684},[449,36680,36681,36683,36685,36687,36689,36692,36694,36696,36698,36700],{"class":451,"line":4032},[449,36682,36428],{"class":721},[449,36684,725],{"class":684},[449,36686,36663],{"class":721},[449,36688,725],{"class":684},[449,36690,36691],{"class":714},"85",[449,36693,36441],{"class":684},[449,36695,36444],{"class":721},[449,36697,725],{"class":684},[449,36699,4837],{"class":714},[449,36701,740],{"class":684},[449,36703,36704,36706,36708,36710,36712,36715,36717,36719,36721,36723],{"class":451,"line":4038},[449,36705,36428],{"class":721},[449,36707,725],{"class":684},[449,36709,36663],{"class":721},[449,36711,725],{"class":684},[449,36713,36714],{"class":714},"55",[449,36716,36441],{"class":684},[449,36718,36444],{"class":721},[449,36720,725],{"class":684},[449,36722,34880],{"class":714},[449,36724,740],{"class":684},[449,36726,36727],{"class":451,"line":4044},[449,36728,13106],{"class":684},[449,36730,36731],{"class":451,"line":4049},[449,36732,784],{"class":684},[449,36734,36735],{"class":451,"line":4054},[449,36736,700],{"emptyLinePlaceholder":130},[449,36738,36739,36741,36743,36746,36748,36750],{"class":451,"line":4059},[449,36740,36396],{"class":721},[449,36742,725],{"class":684},[449,36744,36745],{"class":691},"'weightedAverage'",[449,36747,23051],{"class":684},[449,36749,1498],{"class":680},[449,36751,1501],{"class":684},[449,36753,36754,36756,36758,36761,36763,36765],{"class":451,"line":4065},[449,36755,36412],{"class":721},[449,36757,725],{"class":684},[449,36759,36760],{"class":691},"'calculates weighted grade'",[449,36762,23051],{"class":684},[449,36764,1498],{"class":680},[449,36766,1501],{"class":684},[449,36768,36769,36771,36773,36775,36777],{"class":451,"line":4070},[449,36770,12761],{"class":680},[449,36772,1649],{"class":714},[449,36774,718],{"class":680},[449,36776,35172],{"class":721},[449,36778,35390],{"class":684},[449,36780,36781,36784,36786,36788,36790,36792,36794],{"class":451,"line":4075},[449,36782,36783],{"class":684},"      { name: ",[449,36785,35398],{"class":691},[449,36787,35401],{"class":684},[449,36789,34777],{"class":714},[449,36791,35407],{"class":684},[449,36793,35447],{"class":714},[449,36795,4820],{"class":684},[449,36797,36798,36800,36803,36805,36807,36809,36812],{"class":451,"line":4081},[449,36799,36783],{"class":684},[449,36801,36802],{"class":691},"'Exam'",[449,36804,35401],{"class":684},[449,36806,34801],{"class":714},[449,36808,35407],{"class":684},[449,36810,36811],{"class":714},"0.6",[449,36813,4820],{"class":684},[449,36815,36816],{"class":451,"line":4086},[449,36817,36818],{"class":684},"    ]);\n",[449,36820,36821,36823,36826,36828,36830,36833],{"class":451,"line":4092},[449,36822,36428],{"class":721},[449,36824,36825],{"class":684},"(result).",[449,36827,36444],{"class":721},[449,36829,725],{"class":684},[449,36831,36832],{"class":714},"84",[449,36834,740],{"class":684},[449,36836,36837],{"class":451,"line":4098},[449,36838,13106],{"class":684},[449,36840,36841],{"class":451,"line":4104},[449,36842,700],{"emptyLinePlaceholder":130},[449,36844,36845,36847,36849,36852,36854,36856],{"class":451,"line":4110},[449,36846,36412],{"class":721},[449,36848,725],{"class":684},[449,36850,36851],{"class":691},"'returns 0 for empty input'",[449,36853,23051],{"class":684},[449,36855,1498],{"class":680},[449,36857,1501],{"class":684},[449,36859,36860,36862,36864,36867,36870,36872,36874,36876],{"class":451,"line":5784},[449,36861,36428],{"class":721},[449,36863,725],{"class":684},[449,36865,36866],{"class":721},"weightedAverage",[449,36868,36869],{"class":684},"([])).",[449,36871,36444],{"class":721},[449,36873,725],{"class":684},[449,36875,9911],{"class":714},[449,36877,740],{"class":684},[449,36879,36880],{"class":451,"line":5790},[449,36881,13106],{"class":684},[449,36883,36884],{"class":451,"line":5796},[449,36885,784],{"class":684},[449,36887,36888],{"class":451,"line":5801},[449,36889,700],{"emptyLinePlaceholder":130},[449,36891,36892,36894,36896,36899,36901,36903],{"class":451,"line":5806},[449,36893,36396],{"class":721},[449,36895,725],{"class":684},[449,36897,36898],{"class":691},"'curveByHighest'",[449,36900,23051],{"class":684},[449,36902,1498],{"class":680},[449,36904,1501],{"class":684},[449,36906,36907,36909,36911,36914,36916,36918],{"class":451,"line":5821},[449,36908,36412],{"class":721},[449,36910,725],{"class":684},[449,36912,36913],{"class":691},"'curves scores up to target'",[449,36915,23051],{"class":684},[449,36917,1498],{"class":680},[449,36919,1501],{"class":684},[449,36921,36922,36924,36927,36929,36931,36934,36936,36938,36940,36942,36944,36947,36949],{"class":451,"line":5826},[449,36923,12761],{"class":680},[449,36925,36926],{"class":714}," curved",[449,36928,718],{"class":680},[449,36930,35478],{"class":721},[449,36932,36933],{"class":684},"([",[449,36935,34824],{"class":714},[449,36937,349],{"class":684},[449,36939,34801],{"class":714},[449,36941,349],{"class":684},[449,36943,34777],{"class":714},[449,36945,36946],{"class":684},"], ",[449,36948,5179],{"class":714},[449,36950,740],{"class":684},[449,36952,36953,36955,36958,36961,36963,36965,36967,36969,36971,36973],{"class":451,"line":5848},[449,36954,36428],{"class":721},[449,36956,36957],{"class":684},"(curved).",[449,36959,36960],{"class":721},"toEqual",[449,36962,36933],{"class":684},[449,36964,34801],{"class":714},[449,36966,349],{"class":684},[449,36968,34777],{"class":714},[449,36970,349],{"class":684},[449,36972,5179],{"class":714},[449,36974,35454],{"class":684},[449,36976,36977],{"class":451,"line":5882},[449,36978,13106],{"class":684},[449,36980,36981],{"class":451,"line":5887},[449,36982,700],{"emptyLinePlaceholder":130},[449,36984,36985,36987,36989,36992,36994,36996],{"class":451,"line":5901},[449,36986,36412],{"class":721},[449,36988,725],{"class":684},[449,36990,36991],{"class":691},"'does not curve if highest already meets target'",[449,36993,23051],{"class":684},[449,36995,1498],{"class":680},[449,36997,1501],{"class":684},[449,36999,37000,37002,37005,37007,37009,37011,37013,37015,37017,37019],{"class":451,"line":5921},[449,37001,12761],{"class":680},[449,37003,37004],{"class":714}," scores",[449,37006,718],{"class":680},[449,37008,13480],{"class":684},[449,37010,34824],{"class":714},[449,37012,349],{"class":684},[449,37014,34801],{"class":714},[449,37016,349],{"class":684},[449,37018,36438],{"class":714},[449,37020,4918],{"class":684},[449,37022,37023,37025,37027,37030,37033,37035,37037,37039],{"class":451,"line":5940},[449,37024,36428],{"class":721},[449,37026,725],{"class":684},[449,37028,37029],{"class":721},"curveByHighest",[449,37031,37032],{"class":684},"(scores, ",[449,37034,5179],{"class":714},[449,37036,36441],{"class":684},[449,37038,36960],{"class":721},[449,37040,37041],{"class":684},"(scores);\n",[449,37043,37044],{"class":451,"line":5975},[449,37045,13106],{"class":684},[449,37047,37048],{"class":451,"line":5980},[449,37049,784],{"class":684},[449,37051,37052],{"class":451,"line":5985},[449,37053,700],{"emptyLinePlaceholder":130},[449,37055,37056,37058,37060,37063,37065,37067],{"class":451,"line":13024},[449,37057,36396],{"class":721},[449,37059,725],{"class":684},[449,37061,37062],{"class":691},"'classStats'",[449,37064,23051],{"class":684},[449,37066,1498],{"class":680},[449,37068,1501],{"class":684},[449,37070,37071,37073,37075,37078,37080,37082],{"class":451,"line":13029},[449,37072,36412],{"class":721},[449,37074,725],{"class":684},[449,37076,37077],{"class":691},"'calculates stats correctly'",[449,37079,23051],{"class":684},[449,37081,1498],{"class":680},[449,37083,1501],{"class":684},[449,37085,37086,37088,37091,37093,37095,37097,37099,37101,37103,37105,37107,37109,37111,37113,37116],{"class":451,"line":13045},[449,37087,12761],{"class":680},[449,37089,37090],{"class":714}," stats",[449,37092,718],{"class":680},[449,37094,35801],{"class":721},[449,37096,36933],{"class":684},[449,37098,34777],{"class":714},[449,37100,349],{"class":684},[449,37102,34801],{"class":714},[449,37104,349],{"class":684},[449,37106,34824],{"class":714},[449,37108,349],{"class":684},[449,37110,25435],{"class":714},[449,37112,349],{"class":684},[449,37114,37115],{"class":714},"50",[449,37117,35454],{"class":684},[449,37119,37120,37122,37125,37127,37129,37131],{"class":451,"line":13051},[449,37121,36428],{"class":721},[449,37123,37124],{"class":684},"(stats.mean).",[449,37126,36444],{"class":721},[449,37128,725],{"class":684},[449,37130,34824],{"class":714},[449,37132,740],{"class":684},[449,37134,37135,37137,37140,37142,37144,37146],{"class":451,"line":13064},[449,37136,36428],{"class":721},[449,37138,37139],{"class":684},"(stats.median).",[449,37141,36444],{"class":721},[449,37143,725],{"class":684},[449,37145,34824],{"class":714},[449,37147,740],{"class":684},[449,37149,37150,37152,37155,37157,37159,37161],{"class":451,"line":13073},[449,37151,36428],{"class":721},[449,37153,37154],{"class":684},"(stats.highest).",[449,37156,36444],{"class":721},[449,37158,725],{"class":684},[449,37160,34777],{"class":714},[449,37162,740],{"class":684},[449,37164,37165,37167,37170,37172,37174,37176],{"class":451,"line":13079},[449,37166,36428],{"class":721},[449,37168,37169],{"class":684},"(stats.lowest).",[449,37171,36444],{"class":721},[449,37173,725],{"class":684},[449,37175,37115],{"class":714},[449,37177,740],{"class":684},[449,37179,37180,37182,37185,37187,37189,37191],{"class":451,"line":13091},[449,37181,36428],{"class":721},[449,37183,37184],{"class":684},"(stats.count).",[449,37186,36444],{"class":721},[449,37188,725],{"class":684},[449,37190,4898],{"class":714},[449,37192,740],{"class":684},[449,37194,37195],{"class":451,"line":13103},[449,37196,13106],{"class":684},[449,37198,37199],{"class":451,"line":13109},[449,37200,784],{"class":684},[21,37202,37203],{},"Run the tests:",[48,37205,37207],{"className":839,"code":37206,"language":841,"meta":56,"style":56},"vp test\n",[40,37208,37209],{"__ignoreMap":56},[449,37210,37211,37213],{"class":451,"line":452},[449,37212,3637],{"class":721},[449,37214,37215],{"class":691}," test\n",[427,37217,37220],{"icon":37218,"title":37219},"✅","Vitest Is Built In",[21,37221,37222,37223,37226,37227,37230],{},"No extra install or config. ",[40,37224,37225],{},"vp test"," picks up ",[40,37228,37229],{},".test.ts"," files automatically using the same transform pipeline as your source code.",[16,37232,37234],{"id":37233},"step-6-configure-packaging","Step 6: Configure Packaging",[21,37236,4616,37237,3070],{},[40,37238,37239],{},"vite.config.ts",[48,37241,37243],{"className":670,"code":37242,"filename":37239,"language":673,"meta":56,"style":56},"import { defineConfig } from 'vite-plus';\n\nexport default defineConfig({\n  pack: {\n    entry: ['src\u002Findex.ts'],\n    dts: true,\n    format: ['esm', 'cjs'],\n    sourcemap: true,\n  },\n});\n",[40,37244,37245,37258,37262,37272,37277,37287,37296,37311,37320,37324],{"__ignoreMap":56},[449,37246,37247,37249,37251,37253,37256],{"class":451,"line":452},[449,37248,681],{"class":680},[449,37250,10643],{"class":684},[449,37252,688],{"class":680},[449,37254,37255],{"class":691}," 'vite-plus'",[449,37257,695],{"class":684},[449,37259,37260],{"class":451,"line":114},[449,37261,700],{"emptyLinePlaceholder":130},[449,37263,37264,37266,37268,37270],{"class":451,"line":111},[449,37265,4629],{"class":680},[449,37267,4632],{"class":680},[449,37269,10663],{"class":721},[449,37271,1391],{"class":684},[449,37273,37274],{"class":451,"line":468},[449,37275,37276],{"class":684},"  pack: {\n",[449,37278,37279,37282,37285],{"class":451,"line":474},[449,37280,37281],{"class":684},"    entry: [",[449,37283,37284],{"class":691},"'src\u002Findex.ts'",[449,37286,1469],{"class":684},[449,37288,37289,37292,37294],{"class":451,"line":480},[449,37290,37291],{"class":684},"    dts: ",[449,37293,2169],{"class":714},[449,37295,779],{"class":684},[449,37297,37298,37301,37304,37306,37309],{"class":451,"line":486},[449,37299,37300],{"class":684},"    format: [",[449,37302,37303],{"class":691},"'esm'",[449,37305,349],{"class":684},[449,37307,37308],{"class":691},"'cjs'",[449,37310,1469],{"class":684},[449,37312,37313,37316,37318],{"class":451,"line":492},[449,37314,37315],{"class":684},"    sourcemap: ",[449,37317,2169],{"class":714},[449,37319,779],{"class":684},[449,37321,37322],{"class":451,"line":498},[449,37323,1474],{"class":684},[449,37325,37326],{"class":451,"line":133},[449,37327,784],{"class":684},[21,37329,37330],{},"This tells Vite+ to:",[383,37332,37333,37339,37342],{},[386,37334,37335,37336,827],{},"Generate TypeScript declaration files (",[40,37337,37338],{},".d.ts",[386,37340,37341],{},"Output both ESM and CJS formats",[386,37343,37344],{},"Include source maps for debugging",[16,37346,37348],{"id":37347},"step-7-build-the-package","Step 7: Build the Package",[48,37350,37352],{"className":839,"code":37351,"language":841,"meta":56,"style":56},"vp pack\n",[40,37353,37354],{"__ignoreMap":56},[449,37355,37356,37358],{"class":451,"line":452},[449,37357,3637],{"class":721},[449,37359,37360],{"class":691}," pack\n",[21,37362,37363],{},"You'll see output like:",[48,37365,37367],{"className":443,"code":37366,"language":445,"meta":56,"style":56},"CLI Building entry: src\u002Findex.ts\nESM dist\u002Findex.js     2.1 kB\nCJS dist\u002Findex.cjs    2.2 kB\nDTS dist\u002Findex.d.ts   1.4 kB\n✓ Pack completed in 94ms\n",[40,37368,37369,37374,37379,37384,37389],{"__ignoreMap":56},[449,37370,37371],{"class":451,"line":452},[449,37372,37373],{},"CLI Building entry: src\u002Findex.ts\n",[449,37375,37376],{"class":451,"line":114},[449,37377,37378],{},"ESM dist\u002Findex.js     2.1 kB\n",[449,37380,37381],{"class":451,"line":111},[449,37382,37383],{},"CJS dist\u002Findex.cjs    2.2 kB\n",[449,37385,37386],{"class":451,"line":468},[449,37387,37388],{},"DTS dist\u002Findex.d.ts   1.4 kB\n",[449,37390,37391],{"class":451,"line":474},[449,37392,37393],{},"✓ Pack completed in 94ms\n",[25,37395,37396],{"type":363},[21,37397,37398,37400,37401,37406],{},[40,37399,34374],{}," uses ",[3861,37402,37405],{"href":37403,"rel":37404},"https:\u002F\u002Ftsdown.dev\u002F",[3865],"tsdown"," under the hood — powered by Rolldown, which itself is written in Rust. That's why it's fast.",[16,37408,37410],{"id":37409},"step-8-publish-to-npm","Step 8: Publish to npm",[21,37412,4616,37413,37415],{},[40,37414,4520],{}," with the essentials:",[48,37417,37419],{"className":4518,"code":37418,"filename":4520,"language":4521,"meta":56,"style":56},"{\n  \"name\": \"teacher-toolkit\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Grade calculation utilities for teachers\",\n  \"type\": \"module\",\n  \"main\": \"dist\u002Findex.cjs\",\n  \"module\": \"dist\u002Findex.js\",\n  \"types\": \"dist\u002Findex.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"import\": \".\u002Fdist\u002Findex.js\",\n      \"require\": \".\u002Fdist\u002Findex.cjs\",\n      \"types\": \".\u002Fdist\u002Findex.d.ts\"\n    }\n  },\n  \"files\": [\"dist\"],\n  \"license\": \"MIT\"\n}\n",[40,37420,37421,37425,37437,37448,37460,37472,37484,37496,37508,37515,37522,37534,37546,37556,37560,37564,37576,37586],{"__ignoreMap":56},[449,37422,37423],{"class":451,"line":452},[449,37424,4528],{"class":684},[449,37426,37427,37430,37432,37435],{"class":451,"line":114},[449,37428,37429],{"class":714},"  \"name\"",[449,37431,1482],{"class":684},[449,37433,37434],{"class":691},"\"teacher-toolkit\"",[449,37436,779],{"class":684},[449,37438,37439,37442,37444,37446],{"class":451,"line":111},[449,37440,37441],{"class":714},"  \"version\"",[449,37443,1482],{"class":684},[449,37445,3403],{"class":691},[449,37447,779],{"class":684},[449,37449,37450,37453,37455,37458],{"class":451,"line":468},[449,37451,37452],{"class":714},"  \"description\"",[449,37454,1482],{"class":684},[449,37456,37457],{"class":691},"\"Grade calculation utilities for teachers\"",[449,37459,779],{"class":684},[449,37461,37462,37465,37467,37470],{"class":451,"line":474},[449,37463,37464],{"class":714},"  \"type\"",[449,37466,1482],{"class":684},[449,37468,37469],{"class":691},"\"module\"",[449,37471,779],{"class":684},[449,37473,37474,37477,37479,37482],{"class":451,"line":480},[449,37475,37476],{"class":714},"  \"main\"",[449,37478,1482],{"class":684},[449,37480,37481],{"class":691},"\"dist\u002Findex.cjs\"",[449,37483,779],{"class":684},[449,37485,37486,37489,37491,37494],{"class":451,"line":486},[449,37487,37488],{"class":714},"  \"module\"",[449,37490,1482],{"class":684},[449,37492,37493],{"class":691},"\"dist\u002Findex.js\"",[449,37495,779],{"class":684},[449,37497,37498,37501,37503,37506],{"class":451,"line":492},[449,37499,37500],{"class":714},"  \"types\"",[449,37502,1482],{"class":684},[449,37504,37505],{"class":691},"\"dist\u002Findex.d.ts\"",[449,37507,779],{"class":684},[449,37509,37510,37513],{"class":451,"line":498},[449,37511,37512],{"class":714},"  \"exports\"",[449,37514,4548],{"class":684},[449,37516,37517,37520],{"class":451,"line":133},[449,37518,37519],{"class":714},"    \".\"",[449,37521,4548],{"class":684},[449,37523,37524,37527,37529,37532],{"class":451,"line":509},[449,37525,37526],{"class":714},"      \"import\"",[449,37528,1482],{"class":684},[449,37530,37531],{"class":691},"\".\u002Fdist\u002Findex.js\"",[449,37533,779],{"class":684},[449,37535,37536,37539,37541,37544],{"class":451,"line":318},[449,37537,37538],{"class":714},"      \"require\"",[449,37540,1482],{"class":684},[449,37542,37543],{"class":691},"\".\u002Fdist\u002Findex.cjs\"",[449,37545,779],{"class":684},[449,37547,37548,37551,37553],{"class":451,"line":520},[449,37549,37550],{"class":714},"      \"types\"",[449,37552,1482],{"class":684},[449,37554,37555],{"class":691},"\".\u002Fdist\u002Findex.d.ts\"\n",[449,37557,37558],{"class":451,"line":526},[449,37559,7555],{"class":684},[449,37561,37562],{"class":451,"line":532},[449,37563,1474],{"class":684},[449,37565,37566,37569,37571,37574],{"class":451,"line":2790},[449,37567,37568],{"class":714},"  \"files\"",[449,37570,17279],{"class":684},[449,37572,37573],{"class":691},"\"dist\"",[449,37575,1469],{"class":684},[449,37577,37578,37581,37583],{"class":451,"line":3511},[449,37579,37580],{"class":714},"  \"license\"",[449,37582,1482],{"class":684},[449,37584,37585],{"class":691},"\"MIT\"\n",[449,37587,37588],{"class":451,"line":3526},[449,37589,4604],{"class":684},[21,37591,37592],{},"Then publish:",[48,37594,37596],{"className":839,"code":37595,"language":841,"meta":56,"style":56},"npm publish\n",[40,37597,37598],{"__ignoreMap":56},[449,37599,37600,37602],{"class":451,"line":452},[449,37601,3703],{"class":721},[449,37603,37604],{"class":691}," publish\n",[16,37606,37608],{"id":37607},"using-the-package","Using the Package",[21,37610,37611],{},"Once published, any teacher or developer can install it:",[48,37613,37615],{"className":839,"code":37614,"language":841,"meta":56,"style":56},"npm install teacher-toolkit\n",[40,37616,37617],{"__ignoreMap":56},[449,37618,37619,37621,37623],{"class":451,"line":452},[449,37620,3703],{"class":721},[449,37622,3706],{"class":691},[449,37624,34454],{"class":691},[48,37626,37628],{"className":670,"code":37627,"language":673,"meta":56,"style":56},"import { toLetterGrade, weightedAverage, curveByHighest, classStats } from 'teacher-toolkit';\n\n\u002F\u002F Convert a score\ntoLetterGrade(87);              \u002F\u002F => \"B\"\n\n\u002F\u002F Calculate a final grade\nconst final = weightedAverage([\n  { name: 'Quiz', score: 92, weight: 0.2 },\n  { name: 'Project', score: 78, weight: 0.3 },\n  { name: 'Final', score: 85, weight: 0.5 },\n]);                             \u002F\u002F => 84.2\n\n\u002F\u002F Curve a set of exam scores\nconst curved = curveByHighest([62, 74, 81, 90]);  \u002F\u002F => [72, 84, 91, 100]\n\n\u002F\u002F Get class overview\nclassStats([90, 82, 75, 68, 55]);\n\u002F\u002F => { mean: 74, median: 75, stdDev: 12.49, highest: 90, lowest: 55, count: 5 }\n",[40,37629,37630,37644,37648,37653,37668,37672,37677,37690,37708,37726,37743,37751,37755,37760,37795,37799,37804,37833],{"__ignoreMap":56},[449,37631,37632,37634,37637,37639,37642],{"class":451,"line":452},[449,37633,681],{"class":680},[449,37635,37636],{"class":684}," { toLetterGrade, weightedAverage, curveByHighest, classStats } ",[449,37638,688],{"class":680},[449,37640,37641],{"class":691}," 'teacher-toolkit'",[449,37643,695],{"class":684},[449,37645,37646],{"class":451,"line":114},[449,37647,700],{"emptyLinePlaceholder":130},[449,37649,37650],{"class":451,"line":111},[449,37651,37652],{"class":705},"\u002F\u002F Convert a score\n",[449,37654,37655,37657,37659,37662,37665],{"class":451,"line":468},[449,37656,36433],{"class":721},[449,37658,725],{"class":684},[449,37660,37661],{"class":714},"87",[449,37663,37664],{"class":684},");              ",[449,37666,37667],{"class":705},"\u002F\u002F => \"B\"\n",[449,37669,37670],{"class":451,"line":474},[449,37671,700],{"emptyLinePlaceholder":130},[449,37673,37674],{"class":451,"line":480},[449,37675,37676],{"class":705},"\u002F\u002F Calculate a final grade\n",[449,37678,37679,37681,37684,37686,37688],{"class":451,"line":486},[449,37680,711],{"class":680},[449,37682,37683],{"class":714}," final",[449,37685,718],{"class":680},[449,37687,35172],{"class":721},[449,37689,35390],{"class":684},[449,37691,37692,37694,37697,37699,37701,37703,37706],{"class":451,"line":492},[449,37693,35395],{"class":684},[449,37695,37696],{"class":691},"'Quiz'",[449,37698,35401],{"class":684},[449,37700,36668],{"class":714},[449,37702,35407],{"class":684},[449,37704,37705],{"class":714},"0.2",[449,37707,4820],{"class":684},[449,37709,37710,37712,37715,37717,37720,37722,37724],{"class":451,"line":498},[449,37711,35395],{"class":684},[449,37713,37714],{"class":691},"'Project'",[449,37716,35401],{"class":684},[449,37718,37719],{"class":714},"78",[449,37721,35407],{"class":684},[449,37723,35410],{"class":714},[449,37725,4820],{"class":684},[449,37727,37728,37730,37733,37735,37737,37739,37741],{"class":451,"line":133},[449,37729,35395],{"class":684},[449,37731,37732],{"class":691},"'Final'",[449,37734,35401],{"class":684},[449,37736,36691],{"class":714},[449,37738,35407],{"class":684},[449,37740,29021],{"class":714},[449,37742,4820],{"class":684},[449,37744,37745,37748],{"class":451,"line":509},[449,37746,37747],{"class":684},"]);                             ",[449,37749,37750],{"class":705},"\u002F\u002F => 84.2\n",[449,37752,37753],{"class":451,"line":318},[449,37754,700],{"emptyLinePlaceholder":130},[449,37756,37757],{"class":451,"line":520},[449,37758,37759],{"class":705},"\u002F\u002F Curve a set of exam scores\n",[449,37761,37762,37764,37766,37768,37770,37772,37775,37777,37780,37782,37785,37787,37789,37792],{"class":451,"line":526},[449,37763,711],{"class":680},[449,37765,36926],{"class":714},[449,37767,718],{"class":680},[449,37769,35478],{"class":721},[449,37771,36933],{"class":684},[449,37773,37774],{"class":714},"62",[449,37776,349],{"class":684},[449,37778,37779],{"class":714},"74",[449,37781,349],{"class":684},[449,37783,37784],{"class":714},"81",[449,37786,349],{"class":684},[449,37788,34777],{"class":714},[449,37790,37791],{"class":684},"]);  ",[449,37793,37794],{"class":705},"\u002F\u002F => [72, 84, 91, 100]\n",[449,37796,37797],{"class":451,"line":532},[449,37798,700],{"emptyLinePlaceholder":130},[449,37800,37801],{"class":451,"line":2790},[449,37802,37803],{"class":705},"\u002F\u002F Get class overview\n",[449,37805,37806,37809,37811,37813,37815,37817,37819,37822,37824,37827,37829,37831],{"class":451,"line":3511},[449,37807,37808],{"class":721},"classStats",[449,37810,36933],{"class":684},[449,37812,34777],{"class":714},[449,37814,349],{"class":684},[449,37816,35442],{"class":714},[449,37818,349],{"class":684},[449,37820,37821],{"class":714},"75",[449,37823,349],{"class":684},[449,37825,37826],{"class":714},"68",[449,37828,349],{"class":684},[449,37830,36714],{"class":714},[449,37832,35454],{"class":684},[449,37834,37835],{"class":451,"line":3526},[449,37836,37837],{"class":705},"\u002F\u002F => { mean: 74, median: 75, stdDev: 12.49, highest: 90, lowest: 55, count: 5 }\n",[16,37839,37841],{"id":37840},"full-workflow-recap","Full Workflow Recap",[48,37843,37845],{"className":839,"code":37844,"language":841,"meta":56,"style":56},"vp create vite:library -- teacher-toolkit   # Scaffold\nvp install                                   # Install deps\nvp check                                     # Format + lint + type-check\nvp test                                      # Run tests\nvp pack                                      # Build for npm\nnpm publish                                  # Ship it\n",[40,37846,37847,37863,37872,37881,37890,37899],{"__ignoreMap":56},[449,37848,37849,37851,37853,37855,37857,37860],{"class":451,"line":452},[449,37850,3637],{"class":721},[449,37852,34445],{"class":691},[449,37854,34448],{"class":691},[449,37856,34451],{"class":714},[449,37858,37859],{"class":691}," teacher-toolkit",[449,37861,37862],{"class":705},"   # Scaffold\n",[449,37864,37865,37867,37869],{"class":451,"line":114},[449,37866,3637],{"class":721},[449,37868,3706],{"class":691},[449,37870,37871],{"class":705},"                                   # Install deps\n",[449,37873,37874,37876,37878],{"class":451,"line":111},[449,37875,3637],{"class":721},[449,37877,3640],{"class":691},[449,37879,37880],{"class":705},"                                     # Format + lint + type-check\n",[449,37882,37883,37885,37887],{"class":451,"line":468},[449,37884,3637],{"class":721},[449,37886,3650],{"class":691},[449,37888,37889],{"class":705},"                                      # Run tests\n",[449,37891,37892,37894,37896],{"class":451,"line":474},[449,37893,3637],{"class":721},[449,37895,3660],{"class":691},[449,37897,37898],{"class":705},"                                      # Build for npm\n",[449,37900,37901,37903,37906],{"class":451,"line":480},[449,37902,3703],{"class":721},[449,37904,37905],{"class":691}," publish",[449,37907,37908],{"class":705},"                                  # Ship it\n",[21,37910,37911,37912,165],{},"Six commands. Zero config files beyond ",[40,37913,37239],{},[2805,37915,37916,37920],{},[2808,37917,37919],{"id":37918},"why-vite-for-libraries","Why Vite+ for Libraries?",[21,37921,37922,37923,37925],{},"With ",[40,37924,34374],{},", you get DTS generation, dual ESM\u002FCJS output, and source maps without touching Rollup, tsup, or unbuild configs. The Vitest and Oxc integrations mean testing and linting are already part of the workflow.",[16,37927,37929],{"id":37928},"ideas-to-extend-this-package","Ideas to Extend This Package",[383,37931,37932,37938,37944,37950,37956],{},[386,37933,37934,37937],{},[151,37935,37936],{},"Letter grade with +\u002F-"," — A+, A, A-, B+, etc.",[386,37939,37940,37943],{},[151,37941,37942],{},"Rubric scoring"," — multi-criteria evaluation helpers",[386,37945,37946,37949],{},[151,37947,37948],{},"Attendance percentage"," — days present vs. total",[386,37951,37952,37955],{},[151,37953,37954],{},"Assignment deadline tracker"," — date math utilities",[386,37957,37958,37961],{},[151,37959,37960],{},"Export to CSV"," — grade reports for school systems",[16,37963,3681],{"id":3680},[21,37965,37966,37967,37970,37971,37973],{},"Vite+ makes it trivial to go from idea to published package. The ",[40,37968,37969],{},"vite:library"," template, built-in Vitest, and ",[40,37972,34374],{}," remove the usual friction of library authoring — no build config rabbit holes, no DTS plugin debugging.",[21,37975,37976],{},"For teachers and education developers, this means you can spend your time solving real problems (grade calculations, student analytics) instead of fighting tooling.",[21,37978,37979],{},"Ship the package. Help a teacher.",[3728,37981,37982],{},"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":56,"searchDepth":111,"depth":111,"links":37984},[37985,37986,37987,37988],{"id":34719,"depth":114,"text":34720},{"id":35137,"depth":114,"text":35138},{"id":35462,"depth":114,"text":35463},{"id":35766,"depth":114,"text":35767,"children":37989},[37990],{"id":37918,"depth":111,"text":37919},"2026-03-30","A hands-on guide to packaging a reusable npm library for educators using Vite+ — covering grade calculations, score curving, and GPA utilities.",{"type":13,"value":37994},[37995,37997,37999,38003],[16,37996,333],{"id":332},[21,37998,34353],{},[21,38000,34356,38001,34360],{},[40,38002,34359],{},[21,38004,34363,38005,34367,38007,34371,38009,34375],{},[151,38006,34366],{},[151,38008,34370],{},[40,38010,34374],{},"https:\u002F\u002Fres.cloudinary.com\u002Fddszyeplg\u002Fimage\u002Fupload\u002Fv1775009589\u002Fviteplus_et4prw.png",[38013,127,38014,3703,38015,38016,38017,3784],"vite+","library","education","teachers","grading",{},"\u002Fblog\u002Fviteplus-teacher-toolkit-package",{"title":34345,"description":37992},"blog\u002Fviteplus-teacher-toolkit-package","xWzhCaOm5SYTix9V__dTbVoVpqXgQRSVWUv-Tvrd76w",{"id":38024,"title":38025,"author":38026,"body":38027,"canonical":116,"categories":116,"date":37991,"description":38587,"excerpt":38588,"extension":119,"img":38011,"keywords":38603,"language":3788,"meta":38608,"navigation":130,"ogImage":116,"path":38609,"rating":474,"readTime":492,"seo":38610,"stem":38611,"tag":3793,"__hash__":38612},"blog\u002Fblog\u002Fviteplus-unified-toolchain.md","Vite+: The Unified Toolchain That Replaces Your Entire Frontend Setup",{"name":9,"img":17146,"website":327},{"type":13,"value":38028,"toc":38581},[38029,38031,38034,38047,38059,38063,38066,38116,38134,38136,38138,38141,38159,38162,38180,38183,38195,38199,38213,38216,38220,38223,38284,38290,38315,38322,38325,38348,38374,38391,38397,38400,38414,38421,38427,38454,38457,38463,38469,38479,38482,38486,38489,38501,38510,38513,38521,38531,38535,38567,38569,38572,38578],[16,38030,333],{"id":332},[21,38032,38033],{},"If you've been juggling Node version managers, package managers, linters, formatters, test runners, and build tools — you're not alone. The JavaScript tooling landscape has gotten out of hand.",[21,38035,38036,38038,38039,38044,38045,165],{},[151,38037,34370],{}," aims to fix that. Created by ",[3861,38040,38043],{"href":38041,"rel":38042},"https:\u002F\u002Fvoidzero.dev\u002F",[3865],"VoidZero"," — the team behind Vite, Vitest, Oxc, and Rolldown — it unifies your entire development workflow into a single CLI tool called ",[40,38046,3637],{},[25,38048,38049],{"type":363},[21,38050,38051,38052,38054,38055,38058],{},"Vite+ ships in two parts: ",[40,38053,3637],{},", the global command-line tool, and ",[40,38056,38057],{},"vite-plus",", the local package installed in each project.",[16,38060,38062],{"id":38061},"what-is-vite","What Is Vite+?",[21,38064,38065],{},"Vite+ is the unified toolchain and entry point for web development. It manages your runtime, package manager, and frontend stack by combining:",[383,38067,38068,38076,38084,38092,38099,38107,38113],{},[386,38069,38070,38075],{},[3861,38071,38074],{"href":38072,"rel":38073},"https:\u002F\u002Fvite.dev\u002F",[3865],"Vite"," — dev server and builds",[386,38077,38078,38083],{},[3861,38079,38082],{"href":38080,"rel":38081},"https:\u002F\u002Fvitest.dev\u002F",[3865],"Vitest"," — testing",[386,38085,38086,38091],{},[3861,38087,38090],{"href":38088,"rel":38089},"https:\u002F\u002Foxc.rs\u002F",[3865],"Oxlint"," — linting",[386,38093,38094,38098],{},[3861,38095,38097],{"href":38088,"rel":38096},[3865],"Oxfmt"," — formatting",[386,38100,38101,38106],{},[3861,38102,38105],{"href":38103,"rel":38104},"https:\u002F\u002Frolldown.rs\u002F",[3865],"Rolldown"," — bundling",[386,38108,38109,38112],{},[3861,38110,37405],{"href":37403,"rel":38111},[3865]," — library packaging",[386,38114,38115],{},"Vite Task — monorepo task orchestration",[427,38117,38119],{"icon":5302,"title":38118},"Rust-Powered Performance",[21,38120,38121,38122,38125,38126,38129,38130,38133],{},"With low-level components written in Rust, Vite+ delivers up to ",[151,38123,38124],{},"40x faster builds"," than Webpack, ",[151,38127,38128],{},"~50–100x faster linting"," than ESLint, and ",[151,38131,38132],{},"~30x faster formatting"," than Prettier.",[16,38135,31091],{"id":31090},[32,38137,31098],{"id":31097},[21,38139,38140],{},"macOS \u002F Linux:",[48,38142,38144],{"className":839,"code":38143,"language":841,"meta":56,"style":56},"curl -fsSL https:\u002F\u002Fvite.plus | bash\n",[40,38145,38146],{"__ignoreMap":56},[449,38147,38148,38150,38152,38155,38157],{"class":451,"line":452},[449,38149,4396],{"class":721},[449,38151,4399],{"class":714},[449,38153,38154],{"class":691}," https:\u002F\u002Fvite.plus",[449,38156,4405],{"class":680},[449,38158,4408],{"class":721},[21,38160,38161],{},"Windows (PowerShell):",[48,38163,38165],{"className":839,"code":38164,"language":841,"meta":56,"style":56},"irm https:\u002F\u002Fvite.plus\u002Fps1 | iex\n",[40,38166,38167],{"__ignoreMap":56},[449,38168,38169,38172,38175,38177],{"class":451,"line":452},[449,38170,38171],{"class":721},"irm",[449,38173,38174],{"class":691}," https:\u002F\u002Fvite.plus\u002Fps1",[449,38176,4405],{"class":680},[449,38178,38179],{"class":721}," iex\n",[21,38181,38182],{},"Open a new terminal session and verify:",[48,38184,38186],{"className":839,"code":38185,"language":841,"meta":56,"style":56},"vp help\n",[40,38187,38188],{"__ignoreMap":56},[449,38189,38190,38192],{"class":451,"line":452},[449,38191,3637],{"class":721},[449,38193,38194],{"class":691}," help\n",[32,38196,38198],{"id":38197},"create-a-new-project","Create a New Project",[48,38200,38202],{"className":839,"code":38201,"language":841,"meta":56,"style":56},"vp create my-app\n",[40,38203,38204],{"__ignoreMap":56},[449,38205,38206,38208,38210],{"class":451,"line":452},[449,38207,3637],{"class":721},[449,38209,34445],{"class":691},[449,38211,38212],{"class":691}," my-app\n",[21,38214,38215],{},"Vite+ supports every framework built on Vite — React, Vue, Svelte, Solid, and 20+ more.",[16,38217,38219],{"id":38218},"core-commands","Core Commands",[21,38221,38222],{},"Vite+ handles the entire local frontend development cycle. Here's the workflow:",[48,38224,38226],{"className":839,"code":38225,"language":841,"meta":56,"style":56},"vp create        # Scaffold a new project\nvp install       # Install dependencies (auto-detects pnpm\u002Fnpm\u002Fyarn)\nvp dev           # Start the dev server\nvp check         # Format, lint, and type-check in one pass\nvp test          # Run tests with Vitest\nvp build         # Build for production\n",[40,38227,38228,38237,38246,38256,38265,38274],{"__ignoreMap":56},[449,38229,38230,38232,38234],{"class":451,"line":452},[449,38231,3637],{"class":721},[449,38233,34445],{"class":691},[449,38235,38236],{"class":705},"        # Scaffold a new project\n",[449,38238,38239,38241,38243],{"class":451,"line":114},[449,38240,3637],{"class":721},[449,38242,3706],{"class":691},[449,38244,38245],{"class":705},"       # Install dependencies (auto-detects pnpm\u002Fnpm\u002Fyarn)\n",[449,38247,38248,38250,38253],{"class":451,"line":111},[449,38249,3637],{"class":721},[449,38251,38252],{"class":691}," dev",[449,38254,38255],{"class":705},"           # Start the dev server\n",[449,38257,38258,38260,38262],{"class":451,"line":468},[449,38259,3637],{"class":721},[449,38261,3640],{"class":691},[449,38263,38264],{"class":705},"         # Format, lint, and type-check in one pass\n",[449,38266,38267,38269,38271],{"class":451,"line":474},[449,38268,3637],{"class":721},[449,38270,3650],{"class":691},[449,38272,38273],{"class":705},"          # Run tests with Vitest\n",[449,38275,38276,38278,38281],{"class":451,"line":480},[449,38277,3637],{"class":721},[449,38279,38280],{"class":691}," build",[449,38282,38283],{"class":705},"         # Build for production\n",[21,38285,38286,38287,38289],{},"You can also run ",[40,38288,3637],{}," on its own for an interactive command picker.",[2805,38291,38292,38294],{},[2808,38293,22798],{"id":22797},[21,38295,38296,38297,349,38300,349,38302,349,38305,349,38308,38311,38312,38314],{},"Instead of configuring ",[40,38298,38299],{},"nvm",[40,38301,3703],{},[40,38303,38304],{},"vitest",[40,38306,38307],{},"eslint",[40,38309,38310],{},"prettier",", and build scripts separately, ",[40,38313,3637],{}," handles all of it with a consistent interface across every project.",[16,38316,38318,38319],{"id":38317},"format-lint-and-type-check-with-vp-check","Format, Lint, and Type-Check with ",[40,38320,38321],{},"vp check",[21,38323,38324],{},"One command runs all three — powered by Oxc under the hood:",[383,38326,38327,38333,38339],{},[386,38328,38329,38332],{},[151,38330,38331],{},"Prettier-compatible"," formatting via Oxfmt",[386,38334,38335,38338],{},[151,38336,38337],{},"600+ ESLint-compatible"," rules via Oxlint",[386,38340,38341,38344,38345],{},[151,38342,38343],{},"Type-aware linting"," and fast type checks via ",[40,38346,38347],{},"tsgo",[48,38349,38351],{"className":839,"code":38350,"language":841,"meta":56,"style":56},"vp check         # Run all checks\nvp check --fix   # Auto-fix where possible\n",[40,38352,38353,38362],{"__ignoreMap":56},[449,38354,38355,38357,38359],{"class":451,"line":452},[449,38356,3637],{"class":721},[449,38358,3640],{"class":691},[449,38360,38361],{"class":705},"         # Run all checks\n",[449,38363,38364,38366,38368,38371],{"class":451,"line":114},[449,38365,3637],{"class":721},[449,38367,3640],{"class":691},[449,38369,38370],{"class":714}," --fix",[449,38372,38373],{"class":705},"   # Auto-fix where possible\n",[25,38375,38376],{"type":27},[21,38377,38378,38380,38381,349,38384,38387,38388,38390],{},[40,38379,38321],{}," runs format, lint, and type checks together in a single pass. To run them individually, use ",[40,38382,38383],{},"vp fmt",[40,38385,38386],{},"vp lint",", or invoke ",[40,38389,38347],{}," directly.",[16,38392,38394,38395],{"id":38393},"testing-with-vp-test","Testing with ",[40,38396,37225],{},[21,38398,38399],{},"Vitest is built in — no separate install or config needed. It reuses the same resolve and transform config from your application.",[383,38401,38402,38405,38408,38411],{},[386,38403,38404],{},"Jest-compatible API",[386,38406,38407],{},"Test isolation by default",[386,38409,38410],{},"Browser Mode: run unit tests in actual browsers",[386,38412,38413],{},"Coverage, snapshots, type tests, visual regression",[16,38415,38417,38418],{"id":38416},"monorepo-task-caching-with-vp-run","Monorepo Task Caching with ",[40,38419,38420],{},"vp run",[21,38422,38423,38424,38426],{},"For monorepos, ",[40,38425,38420],{}," provides automated caching and dependency-aware execution:",[48,38428,38430],{"className":839,"code":38429,"language":841,"meta":56,"style":56},"vp run build     # Run build across workspaces with caching\nvp run test      # Run tests with input tracking\n",[40,38431,38432,38443],{"__ignoreMap":56},[449,38433,38434,38436,38438,38440],{"class":451,"line":452},[449,38435,3637],{"class":721},[449,38437,5296],{"class":691},[449,38439,38280],{"class":691},[449,38441,38442],{"class":705},"     # Run build across workspaces with caching\n",[449,38444,38445,38447,38449,38451],{"class":451,"line":114},[449,38446,3637],{"class":721},[449,38448,5296],{"class":691},[449,38450,3650],{"class":691},[449,38452,38453],{"class":705},"      # Run tests with input tracking\n",[21,38455,38456],{},"Tasks are cached automatically based on input files — no Turborepo config needed.",[16,38458,38460,38461],{"id":38459},"library-packaging-with-vp-pack","Library Packaging with ",[40,38462,34374],{},[21,38464,38465,38466,38468],{},"Publishing a library? ",[40,38467,34374],{}," handles DTS generation, bundling, and automatic package exports:",[48,38470,38471],{"className":839,"code":37351,"language":841,"meta":56,"style":56},[40,38472,38473],{"__ignoreMap":56},[449,38474,38475,38477],{"class":451,"line":452},[449,38476,3637],{"class":721},[449,38478,37360],{"class":691},[21,38480,38481],{},"Powered by Rolldown \u002F tsdown, it outputs ESM, CJS, and declaration files.",[16,38483,38485],{"id":38484},"migrating-an-existing-vite-project","Migrating an Existing Vite Project",[21,38487,38488],{},"Already have a Vite project? Migrate it with:",[48,38490,38492],{"className":839,"code":38491,"language":841,"meta":56,"style":56},"vp migrate\n",[40,38493,38494],{"__ignoreMap":56},[449,38495,38496,38498],{"class":451,"line":452},[449,38497,3637],{"class":721},[449,38499,38500],{"class":691}," migrate\n",[21,38502,38503,38504,38509],{},"Or paste the ",[3861,38505,38508],{"href":38506,"rel":38507},"https:\u002F\u002Fviteplus.dev\u002Fguide\u002Fmigrate#migration-prompt",[3865],"migration prompt"," into your coding agent.",[16,38511,38512],{"id":8308},"Deployment",[21,38514,38515,38516,38520],{},"Vite+ pairs with ",[3861,38517,4189],{"href":38518,"rel":38519},"https:\u002F\u002Fnitro.unjs.io\u002F",[3865]," for platform-agnostic deployment. First-class support for Vercel, Netlify, Cloudflare Workers, and Render.",[427,38522,38524],{"icon":429,"title":38523},"Platform Agnostic",[21,38525,38526,38527,38530],{},"Build once with ",[40,38528,38529],{},"vp build"," and deploy anywhere — Nitro handles the server runtime transformation.",[16,38532,38534],{"id":38533},"why-vite","Why Vite+?",[383,38536,38537,38543,38549,38555,38561],{},[386,38538,38539,38542],{},[151,38540,38541],{},"Stop wasting time on tooling maintenance"," — one CLI, one config, one workflow",[386,38544,38545,38548],{},[151,38546,38547],{},"Improve cross-team mobility"," — consistent DX across every project",[386,38550,38551,38554],{},[151,38552,38553],{},"Stay fast at scale"," — Rust-powered internals, enterprise-grade performance",[386,38556,38557,38560],{},[151,38558,38559],{},"Supply chain security"," — rigorous dependency vetting across the unified toolchain",[386,38562,38563,38566],{},[151,38564,38565],{},"Free and open source"," — MIT license, maintained by VoidZero and community contributors",[73,38568],{},[21,38570,38571],{},"Vite+ isn't reinventing the wheel — it's putting high-performance tires on it. By unifying your runtime, package manager, linter, formatter, test runner, and bundler into a single CLI, it removes the tooling tax that slows down every new project.",[21,38573,38574,38575,165],{},"If Vite was the upgrade from Webpack, Vite+ is the upgrade from ",[156,38576,38577],{},"tool fatigue",[3728,38579,38580],{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":56,"searchDepth":111,"depth":111,"links":38582},[38583,38584],{"id":31097,"depth":114,"text":31098},{"id":38197,"depth":114,"text":38198,"children":38585},[38586],{"id":22797,"depth":111,"text":22798},"Vite+ combines Vite, Vitest, Oxlint, Oxfmt, and Rolldown into a single CLI — managing your runtime, package manager, and toolchain in one place.",{"type":13,"value":38589},[38590,38592,38594],[16,38591,333],{"id":332},[21,38593,38033],{},[21,38595,38596,38038,38598,38044,38601,165],{},[151,38597,34370],{},[3861,38599,38043],{"href":38041,"rel":38600},[3865],[40,38602,3637],{},[38013,127,38604,38605,7080,3784,38606,38304,38607],"frontend","toolchain","oxc","rolldown",{},"\u002Fblog\u002Fviteplus-unified-toolchain",{"title":38025,"description":38587},"blog\u002Fviteplus-unified-toolchain","Sg_ro7CXnz7y9Ugam_dig_kH9YpHYV1P7cVDEIlkyKE",{"id":38614,"title":38615,"author":38616,"body":38617,"canonical":116,"categories":116,"date":21230,"description":38627,"excerpt":116,"extension":119,"img":120,"keywords":116,"language":116,"meta":38628,"navigation":130,"ogImage":116,"path":38629,"rating":116,"readTime":116,"seo":38630,"stem":38631,"tag":38632,"__hash__":38633},"blog\u002Fblog\u002Fwork-in-progress.md","Work in Progress",{"name":9,"img":11},{"type":13,"value":38618,"toc":38625},[38619,38622],[16,38620,38615],{"id":38621},"work-in-progress",[21,38623,38624],{},"This post is currently under construction. Check back soon!",{"title":56,"searchDepth":111,"depth":111,"links":38626},[],"A placeholder for an upcoming post.",{},"\u002Fblog\u002Fwork-in-progress",{"title":38615,"description":38627},"blog\u002Fwork-in-progress","General","D2XqR3BH1UxjrAkn93_s4Kxi2i8goOhVluLzMwmSYLA",{"id":38635,"title":38636,"author":116,"body":38637,"description":38935,"difficulty":116,"estMinutes":116,"extension":119,"feature":38936,"img":116,"meta":38937,"navigation":130,"nuxtVersion":116,"order":468,"path":38938,"releaseDate":116,"seo":38939,"series":38940,"sourcePRs":38941,"sourceUrl":116,"stem":38943,"__hash__":38944},"tutorials\u002Flearn\u002Fnuxt-4-3\u002F04-route-groups-in-page-meta.md","Route Groups in Page Meta",{"type":13,"value":38638,"toc":38926},[38639,38643,38657,38661,38668,38672,38675,38717,38720,38724,38727,38770,38843,38847,38885,38889,38914,38918,38923],[32,38640,38642],{"id":38641},"whats-new","What's new",[21,38644,38645,38646,38649,38650,38653,38654,165],{},"Route groups — folders wrapped in parentheses like ",[40,38647,38648],{},"(protected)\u002F"," that organize files without affecting the URL — are now exposed in ",[151,38651,38652],{},"page meta"," as ",[40,38655,38656],{},"meta.groups",[32,38658,38660],{"id":38659},"why-youd-care","Why you'd care",[21,38662,38663,38664,38667],{},"Route groups were purely organizational. Now they carry meaning you can read at runtime. That means convention-based authorization: put protected pages in a ",[40,38665,38666],{},"(protected)"," folder and let one middleware handle them all — no per-page flags.",[32,38669,38671],{"id":38670},"before","Before",[21,38673,38674],{},"To protect pages, you added a marker to each one:",[48,38676,38679],{"className":26812,"code":38677,"filename":38678,"language":23640,"meta":56,"style":56},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({ requiresAuth: true })\n\u003C\u002Fscript>\n","pages\u002Fdashboard.vue",[40,38680,38681,38697,38709],{"__ignoreMap":56},[449,38682,38683,38685,38687,38689,38691,38693,38695],{"class":451,"line":452},[449,38684,5546],{"class":684},[449,38686,23106],{"class":17260},[449,38688,27178],{"class":721},[449,38690,27181],{"class":721},[449,38692,5015],{"class":684},[449,38694,23003],{"class":691},[449,38696,23006],{"class":684},[449,38698,38699,38702,38705,38707],{"class":451,"line":114},[449,38700,38701],{"class":721},"definePageMeta",[449,38703,38704],{"class":684},"({ requiresAuth: ",[449,38706,2169],{"class":714},[449,38708,10958],{"class":684},[449,38710,38711,38713,38715],{"class":451,"line":111},[449,38712,23103],{"class":684},[449,38714,23106],{"class":17260},[449,38716,23006],{"class":684},[21,38718,38719],{},"Every protected page needed that line, and forgetting it silently left a page open.",[32,38721,38723],{"id":38722},"after","After",[21,38725,38726],{},"Group the pages by folder, and read the group in middleware:",[48,38728,38731],{"className":26812,"code":38729,"filename":38730,"language":23640,"meta":56,"style":56},"\u003Cscript setup lang=\"ts\">\n\u002F\u002F meta now includes: { groups: ['protected'] }\nuseRoute().meta.groups\n\u003C\u002Fscript>\n","pages\u002F(protected)\u002Fdashboard.vue",[40,38732,38733,38749,38754,38762],{"__ignoreMap":56},[449,38734,38735,38737,38739,38741,38743,38745,38747],{"class":451,"line":452},[449,38736,5546],{"class":684},[449,38738,23106],{"class":17260},[449,38740,27178],{"class":721},[449,38742,27181],{"class":721},[449,38744,5015],{"class":684},[449,38746,23003],{"class":691},[449,38748,23006],{"class":684},[449,38750,38751],{"class":451,"line":114},[449,38752,38753],{"class":705},"\u002F\u002F meta now includes: { groups: ['protected'] }\n",[449,38755,38756,38759],{"class":451,"line":111},[449,38757,38758],{"class":721},"useRoute",[449,38760,38761],{"class":684},"().meta.groups\n",[449,38763,38764,38766,38768],{"class":451,"line":468},[449,38765,23103],{"class":684},[449,38767,23106],{"class":17260},[449,38769,23006],{"class":684},[48,38771,38774],{"className":670,"code":38772,"filename":38773,"language":673,"meta":56,"style":56},"export default defineNuxtRouteMiddleware((to) => {\n  if (to.meta.groups?.includes('protected') && !isAuthenticated()) {\n    return navigateTo('\u002Flogin')\n  }\n})\n","middleware\u002Fauth.ts",[40,38775,38776,38796,38822,38835,38839],{"__ignoreMap":56},[449,38777,38778,38780,38782,38785,38787,38790,38792,38794],{"class":451,"line":452},[449,38779,4629],{"class":680},[449,38781,4632],{"class":680},[449,38783,38784],{"class":721}," defineNuxtRouteMiddleware",[449,38786,5163],{"class":684},[449,38788,38789],{"class":1491},"to",[449,38791,1758],{"class":684},[449,38793,1498],{"class":680},[449,38795,1501],{"class":684},[449,38797,38798,38800,38803,38805,38807,38810,38812,38814,38816,38819],{"class":451,"line":114},[449,38799,5079],{"class":680},[449,38801,38802],{"class":684}," (to.meta.groups?.",[449,38804,13632],{"class":721},[449,38806,725],{"class":684},[449,38808,38809],{"class":691},"'protected'",[449,38811,1758],{"class":684},[449,38813,16831],{"class":680},[449,38815,12644],{"class":680},[449,38817,38818],{"class":721},"isAuthenticated",[449,38820,38821],{"class":684},"()) {\n",[449,38823,38824,38826,38828,38830,38833],{"class":451,"line":111},[449,38825,1506],{"class":680},[449,38827,27439],{"class":721},[449,38829,725],{"class":684},[449,38831,38832],{"class":691},"'\u002Flogin'",[449,38834,11211],{"class":684},[449,38836,38837],{"class":451,"line":468},[449,38838,4599],{"class":684},[449,38840,38841],{"class":451,"line":474},[449,38842,4113],{"class":684},[32,38844,38846],{"id":38845},"do-it-yourself","Do it yourself",[816,38848,38849,38860,38867,38873,38880],{},[386,38850,38851,38852,38855,38856,38859],{},"Create a folder ",[40,38853,38854],{},"pages\u002F(protected)\u002F"," and move a page like ",[40,38857,38858],{},"dashboard.vue"," into it.",[386,38861,38862,38863,38866],{},"Confirm the URL is still ",[40,38864,38865],{},"\u002Fdashboard"," — the group name never appears in the path.",[386,38868,38869,38870,165],{},"Add a global middleware at ",[40,38871,38872],{},"middleware\u002Fauth.global.ts",[386,38874,38875,38876,38879],{},"In it, check ",[40,38877,38878],{},"to.meta.groups?.includes('protected')"," and redirect unauthenticated users.",[386,38881,7226,38882,38884],{},[40,38883,38865],{}," signed out and confirm the redirect fires.",[32,38886,38888],{"id":38887},"gotchas","Gotchas",[383,38890,38891,38902],{},[386,38892,38893,38894,38897,38898,2429,38900,165],{},"The group name is the folder name ",[151,38895,38896],{},"without"," the parentheses: ",[40,38899,38666],{},[40,38901,38809],{},[386,38903,38904,38905,38907,38908,38911,38912,165],{},"A route can belong to nested groups; ",[40,38906,38656],{}," is an array, so always use ",[40,38909,38910],{},".includes()"," rather than ",[40,38913,35114],{},[32,38915,38917],{"id":38916},"recap","Recap",[21,38919,38920,38922],{},[40,38921,38656],{}," turns route-group folders into runtime-readable labels. It's the cleanest way to do route-level authorization without touching every page.",[3728,38924,38925],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":38927},[38928,38929,38930,38931,38932,38933,38934],{"id":38641,"depth":114,"text":38642},{"id":38659,"depth":114,"text":38660},{"id":38670,"depth":114,"text":38671},{"id":38722,"depth":114,"text":38723},{"id":38845,"depth":114,"text":38846},{"id":38887,"depth":114,"text":38888},{"id":38916,"depth":114,"text":38917},"Parenthesized route-group folders now show up in page meta, so middleware can make decisions based on which group a route belongs to.","route groups exposed via meta.groups",{},"\u002Flearn\u002Fnuxt-4-3\u002F04-route-groups-in-page-meta",{"title":38636,"description":38935},"nuxt-4-3",[38942],"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fpull\u002F33460","learn\u002Fnuxt-4-3\u002F04-route-groups-in-page-meta","gYp66lSSE308hjAOZLNkZqxcF2bFjtzf4ghuK4Ccqw8",[38946,39240,39468,39646,39881,40161],{"id":38947,"title":38948,"author":116,"body":38949,"description":39231,"difficulty":116,"estMinutes":116,"extension":119,"feature":39232,"img":116,"meta":39233,"navigation":130,"nuxtVersion":116,"order":452,"path":39234,"releaseDate":116,"seo":39235,"series":38940,"sourcePRs":39236,"sourceUrl":116,"stem":39238,"__hash__":39239},"tutorials\u002Flearn\u002Fnuxt-4-3\u002F01-route-rule-layouts.md","Route Rule Layouts",{"type":13,"value":38950,"toc":39222},[38951,38953,38964,38966,38980,38982,38985,39027,39040,39042,39047,39111,39121,39123,39173,39182,39184,39209,39211,39219],[32,38952,38642],{"id":38641},[21,38954,38955,38956,38959,38960,38963],{},"Nuxt 4.3 lets you set a page's layout ",[151,38957,38958],{},"from route rules"," using the new ",[40,38961,38962],{},"appLayout"," property. No more opening every page just to tell it which layout to use.",[32,38965,38660],{"id":38659},[21,38967,38968,38969,38972,38973,38976,38977,38979],{},"Picture an admin panel with twenty pages, all needing the ",[40,38970,38971],{},"admin"," layout. Until now, that meant twenty copies of the same ",[40,38974,38975],{},"definePageMeta({ layout: 'admin' })",". Miss one and you get a broken-looking page. ",[40,38978,38962],{}," moves that decision to a single, declarative place.",[32,38981,38671],{"id":38670},[21,38983,38984],{},"Every page carried its own layout declaration:",[48,38986,38989],{"className":26812,"code":38987,"filename":38988,"language":23640,"meta":56,"style":56},"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({ layout: 'admin' })\n\u003C\u002Fscript>\n","pages\u002Fadmin\u002Fusers.vue",[40,38990,38991,39007,39019],{"__ignoreMap":56},[449,38992,38993,38995,38997,38999,39001,39003,39005],{"class":451,"line":452},[449,38994,5546],{"class":684},[449,38996,23106],{"class":17260},[449,38998,27178],{"class":721},[449,39000,27181],{"class":721},[449,39002,5015],{"class":684},[449,39004,23003],{"class":691},[449,39006,23006],{"class":684},[449,39008,39009,39011,39014,39017],{"class":451,"line":114},[449,39010,38701],{"class":721},[449,39012,39013],{"class":684},"({ layout: ",[449,39015,39016],{"class":691},"'admin'",[449,39018,10958],{"class":684},[449,39020,39021,39023,39025],{"class":451,"line":111},[449,39022,23103],{"class":684},[449,39024,23106],{"class":17260},[449,39026,23006],{"class":684},[21,39028,39029,39030,349,39033,349,39036,39039],{},"Repeat that in ",[40,39031,39032],{},"admin\u002Fsettings.vue",[40,39034,39035],{},"admin\u002Fbilling.vue",[40,39037,39038],{},"admin\u002Flogs.vue","… you get the idea.",[32,39041,38723],{"id":38722},[21,39043,39044,39045,3070],{},"Declare it once, for the whole tree, in ",[40,39046,4619],{},[48,39048,39050],{"className":670,"code":39049,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  routeRules: {\n    '\u002Fadmin\u002F**': { appLayout: 'admin' },\n    '\u002Fdashboard\u002F**': { appLayout: 'dashboard' },\n    '\u002Fauth\u002F**': { appLayout: 'minimal' }\n  }\n})\n",[40,39051,39052,39062,39067,39079,39091,39103,39107],{"__ignoreMap":56},[449,39053,39054,39056,39058,39060],{"class":451,"line":452},[449,39055,4629],{"class":680},[449,39057,4632],{"class":680},[449,39059,4635],{"class":721},[449,39061,1391],{"class":684},[449,39063,39064],{"class":451,"line":114},[449,39065,39066],{"class":684},"  routeRules: {\n",[449,39068,39069,39072,39075,39077],{"class":451,"line":111},[449,39070,39071],{"class":691},"    '\u002Fadmin\u002F**'",[449,39073,39074],{"class":684},": { appLayout: ",[449,39076,39016],{"class":691},[449,39078,4820],{"class":684},[449,39080,39081,39084,39086,39089],{"class":451,"line":468},[449,39082,39083],{"class":691},"    '\u002Fdashboard\u002F**'",[449,39085,39074],{"class":684},[449,39087,39088],{"class":691},"'dashboard'",[449,39090,4820],{"class":684},[449,39092,39093,39096,39098,39101],{"class":451,"line":474},[449,39094,39095],{"class":691},"    '\u002Fauth\u002F**'",[449,39097,39074],{"class":684},[449,39099,39100],{"class":691},"'minimal'",[449,39102,15045],{"class":684},[449,39104,39105],{"class":451,"line":480},[449,39106,4599],{"class":684},[449,39108,39109],{"class":451,"line":486},[449,39110,4113],{"class":684},[21,39112,39113,39114,39117,39118,39120],{},"Every page under ",[40,39115,39116],{},"\u002Fadmin"," now uses the ",[40,39119,38971],{}," layout — and you never touched the page files.",[32,39122,38846],{"id":38845},[816,39124,39125,39131,39148,39161,39167],{},[386,39126,39127,39128,165],{},"Make sure you have a layout at ",[40,39129,39130],{},"layouts\u002Fadmin.vue",[386,39132,39133,39134,39136,39137,39140,39141,39144,39145,165],{},"Open ",[40,39135,4619],{}," and add a ",[40,39138,39139],{},"routeRules"," entry mapping ",[40,39142,39143],{},"'\u002Fadmin\u002F**'"," to ",[40,39146,39147],{},"{ appLayout: 'admin' }",[386,39149,39150,39151,21148,39154,39157,39158,39160],{},"Create a page at ",[40,39152,39153],{},"pages\u002Fadmin\u002Findex.vue",[151,39155,39156],{},"no"," ",[40,39159,38701],{}," layout call.",[386,39162,39163,39164,39166],{},"Run the dev server and visit ",[40,39165,39116],{},". Confirm the admin layout renders.",[386,39168,39169,39170,39172],{},"Add a second page under ",[40,39171,39116],{}," and confirm it inherits the layout too.",[25,39174,39175],{"type":363},[21,39176,39177,39178,39181],{},"A page-level ",[40,39179,39180],{},"definePageMeta({ layout })"," still wins if you need to override a single route inside the tree.",[32,39183,38888],{"id":38887},[383,39185,39186,39203],{},[386,39187,39188,39189,39192,39193,39196,39197,39199,39200,39202],{},"Route rules match by path pattern — double-check your glob (",[40,39190,39191],{},"\u002Fadmin\u002F**"," vs ",[40,39194,39195],{},"\u002Fadmin\u002F*","). ",[40,39198,36109],{}," matches nested segments; ",[40,39201,12951],{}," matches only one.",[386,39204,39205,39206,39208],{},"This is server-driven config. After editing ",[40,39207,4619],{},", restart the dev server if the change doesn't pick up.",[32,39210,38917],{"id":38916},[21,39212,39213,39214,21805,39216,39218],{},"Layouts are now a route-tree concern you can declare centrally with ",[40,39215,38962],{},[40,39217,39139],{},". Reach for it whenever a whole section shares a layout.",[3728,39220,39221],{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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 .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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":56,"searchDepth":111,"depth":111,"links":39223},[39224,39225,39226,39227,39228,39229,39230],{"id":38641,"depth":114,"text":38642},{"id":38659,"depth":114,"text":38660},{"id":38670,"depth":114,"text":38671},{"id":38722,"depth":114,"text":38723},{"id":38845,"depth":114,"text":38846},{"id":38887,"depth":114,"text":38888},{"id":38916,"depth":114,"text":38917},"Assign layouts to whole route trees from one place, without scattering definePageMeta across every page.","appLayout route rule",{},"\u002Flearn\u002Fnuxt-4-3\u002F01-route-rule-layouts",{"title":38948,"description":39231},[39237],"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fpull\u002F31092","learn\u002Fnuxt-4-3\u002F01-route-rule-layouts","-NNNgTbfFue2HfZpQ04PveGjpm7Bs5gHAJTyw3sJQLs",{"id":39241,"title":39242,"author":116,"body":39243,"description":39459,"difficulty":116,"estMinutes":116,"extension":119,"feature":39460,"img":116,"meta":39461,"navigation":130,"nuxtVersion":116,"order":114,"path":39462,"releaseDate":116,"seo":39463,"series":38940,"sourcePRs":39464,"sourceUrl":116,"stem":39466,"__hash__":39467},"tutorials\u002Flearn\u002Fnuxt-4-3\u002F02-isr-swr-payload-extraction.md","ISR \u002F SWR Payload Extraction",{"type":13,"value":39244,"toc":39450},[39245,39247,39266,39268,39271,39273,39276,39316,39319,39321,39326,39373,39380,39382,39420,39422,39434,39436,39447],[32,39246,38642],{"id":38641},[21,39248,39249,39250,39253,39254,39257,39258,39261,39262,39265],{},"Payload extraction now works with ",[151,39251,39252],{},"ISR"," (incremental static regeneration), ",[151,39255,39256],{},"SWR"," (stale-while-revalidate), and ",[40,39259,39260],{},"cache"," route rules. Previously only fully pre-rendered pages produced ",[40,39263,39264],{},"_payload.json"," files.",[32,39267,38660],{"id":38659},[21,39269,39270],{},"A payload file is the page's data, serialized. When it exists, client-side navigation to that page reuses the cached data instead of hitting your API again. Before 4.3, ISR\u002FSWR pages couldn't do this — every navigation re-fetched. Now they can, and CDNs (Vercel, Netlify, Cloudflare) can cache the payload right next to the HTML.",[32,39272,38671],{"id":38670},[21,39274,39275],{},"An ISR product page revalidated its HTML on a schedule, but client navigation still fired fresh API calls for the data:",[48,39277,39279],{"className":670,"code":39278,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  routeRules: {\n    '\u002Fproducts\u002F**': { isr: 3600 }\n  }\n})\n",[40,39280,39281,39291,39295,39308,39312],{"__ignoreMap":56},[449,39282,39283,39285,39287,39289],{"class":451,"line":452},[449,39284,4629],{"class":680},[449,39286,4632],{"class":680},[449,39288,4635],{"class":721},[449,39290,1391],{"class":684},[449,39292,39293],{"class":451,"line":114},[449,39294,39066],{"class":684},[449,39296,39297,39300,39303,39306],{"class":451,"line":111},[449,39298,39299],{"class":691},"    '\u002Fproducts\u002F**'",[449,39301,39302],{"class":684},": { isr: ",[449,39304,39305],{"class":714},"3600",[449,39307,15045],{"class":684},[449,39309,39310],{"class":451,"line":468},[449,39311,4599],{"class":684},[449,39313,39314],{"class":451,"line":474},[449,39315,4113],{"class":684},[21,39317,39318],{},"HTML: cached. Data on client nav: re-fetched every time.",[32,39320,38723],{"id":38722},[21,39322,39323,39324,3070],{},"Same config — but now Nuxt also emits a cached ",[40,39325,39264],{},[48,39327,39329],{"className":670,"code":39328,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  routeRules: {\n    '\u002Fproducts\u002F**': {\n      isr: 3600 \u002F\u002F revalidate hourly; payload cached alongside\n    }\n  }\n})\n",[40,39330,39331,39341,39345,39351,39361,39365,39369],{"__ignoreMap":56},[449,39332,39333,39335,39337,39339],{"class":451,"line":452},[449,39334,4629],{"class":680},[449,39336,4632],{"class":680},[449,39338,4635],{"class":721},[449,39340,1391],{"class":684},[449,39342,39343],{"class":451,"line":114},[449,39344,39066],{"class":684},[449,39346,39347,39349],{"class":451,"line":111},[449,39348,39299],{"class":691},[449,39350,4548],{"class":684},[449,39352,39353,39356,39358],{"class":451,"line":468},[449,39354,39355],{"class":684},"      isr: ",[449,39357,39305],{"class":714},[449,39359,39360],{"class":705}," \u002F\u002F revalidate hourly; payload cached alongside\n",[449,39362,39363],{"class":451,"line":474},[449,39364,7555],{"class":684},[449,39366,39367],{"class":451,"line":480},[449,39368,4599],{"class":684},[449,39370,39371],{"class":451,"line":486},[449,39372,4113],{"class":684},[21,39374,39375,39376,39379],{},"Client navigation to ",[40,39377,39378],{},"\u002Fproducts\u002F123"," can now serve data straight from the cached payload — fewer API calls, faster transitions.",[32,39381,38846],{"id":38845},[816,39383,39384,39394,39404,39414,39417],{},[386,39385,39386,39387,39390,39391,165],{},"Add an ",[40,39388,39389],{},"isr"," route rule for a data-driven section, e.g. ",[40,39392,39393],{},"'\u002Fproducts\u002F**': { isr: 3600 }",[386,39395,39396,39397,1124,39400,39403],{},"In that page, load data with ",[40,39398,39399],{},"useAsyncData",[40,39401,39402],{},"useFetch"," as normal.",[386,39405,39406,39407,39410,39411,374],{},"Build and preview the app (",[40,39408,39409],{},"nuxt build"," then ",[40,39412,39413],{},"nuxt preview",[386,39415,39416],{},"Navigate to a product page, then use client-side links to move between products.",[386,39418,39419],{},"Watch the network tab — data should come from the cached payload, not repeated API calls.",[32,39421,38888],{"id":38887},[383,39423,39424,39431],{},[386,39425,39426,39427,39430],{},"This is a production\u002Fcaching behavior. You won't see the full effect with a plain ",[40,39428,39429],{},"nuxt dev"," server unless payload extraction in dev is enabled (that's the next step).",[386,39432,39433],{},"Payloads are only as fresh as your ISR\u002FSWR window — pick a revalidation interval that matches how often the data actually changes.",[32,39435,38917],{"id":38916},[21,39437,39438,39439,349,39441,39444,39445,165],{},"Caching route rules now extract payloads too, so cached pages stop re-fetching their data on client navigation. Turn it on wherever you already use ",[40,39440,39389],{},[40,39442,39443],{},"swr",", or ",[40,39446,39260],{},[3728,39448,39449],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":56,"searchDepth":111,"depth":111,"links":39451},[39452,39453,39454,39455,39456,39457,39458],{"id":38641,"depth":114,"text":38642},{"id":38659,"depth":114,"text":38660},{"id":38670,"depth":114,"text":38671},{"id":38722,"depth":114,"text":38723},{"id":38845,"depth":114,"text":38846},{"id":38887,"depth":114,"text":38888},{"id":38916,"depth":114,"text":38917},"Cached pages can now generate _payload.json files, so client navigation and CDNs reuse data instead of re-fetching it.","Payload extraction for ISR\u002FSWR\u002Fcache route rules",{},"\u002Flearn\u002Fnuxt-4-3\u002F02-isr-swr-payload-extraction",{"title":39242,"description":39459},[39465],"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fpull\u002F33467","learn\u002Fnuxt-4-3\u002F02-isr-swr-payload-extraction","jJmVrUIxvabtsaG9qEuhN8LNOU50VFD5SK_QuBPoDAk",{"id":39469,"title":39470,"author":116,"body":39471,"description":39637,"difficulty":116,"estMinutes":116,"extension":119,"feature":39638,"img":116,"meta":39639,"navigation":130,"nuxtVersion":116,"order":111,"path":39640,"releaseDate":116,"seo":39641,"series":38940,"sourcePRs":39642,"sourceUrl":116,"stem":39644,"__hash__":39645},"tutorials\u002Flearn\u002Fnuxt-4-3\u002F03-server-alias.md","The #server Alias",{"type":13,"value":39472,"toc":39628},[39473,39475,39490,39492,39502,39504,39507,39525,39528,39530,39533,39549,39551,39585,39596,39598,39615,39617,39625],[32,39474,38642],{"id":38641},[21,39476,39477,39478,39481,39482,39485,39486,39489],{},"Nuxt 4.3 adds a ",[40,39479,39480],{},"#server"," alias for imports ",[151,39483,39484],{},"within your server directory",", mirroring how ",[40,39487,39488],{},"#shared"," already works.",[32,39491,38660],{"id":38659},[21,39493,39494,39495,39498,39499,39501],{},"Deeply nested server routes turn imports into a counting exercise: was it three ",[40,39496,39497],{},"..\u002F"," or four? Get it wrong and the build breaks. ",[40,39500,39480],{}," gives every server file the same stable, absolute-feeling import path.",[32,39503,38671],{"id":38670},[21,39505,39506],{},"From a nested API route, reaching a utility meant walking back up the tree:",[48,39508,39511],{"className":670,"code":39509,"filename":39510,"language":673,"meta":56,"style":56},"import { helper } from '..\u002F..\u002F..\u002F..\u002Futils\u002Fhelper'\n","server\u002Fapi\u002Fusers\u002F[id]\u002Fprofile.ts",[40,39512,39513],{"__ignoreMap":56},[449,39514,39515,39517,39520,39522],{"class":451,"line":452},[449,39516,681],{"class":680},[449,39518,39519],{"class":684}," { helper } ",[449,39521,688],{"class":680},[449,39523,39524],{"class":691}," '..\u002F..\u002F..\u002F..\u002Futils\u002Fhelper'\n",[21,39526,39527],{},"Move the file and every one of those paths breaks.",[32,39529,38723],{"id":38722},[21,39531,39532],{},"Import from the alias — same path no matter where the file lives:",[48,39534,39536],{"className":670,"code":39535,"filename":39510,"language":673,"meta":56,"style":56},"import { helper } from '#server\u002Futils\u002Fhelper'\n",[40,39537,39538],{"__ignoreMap":56},[449,39539,39540,39542,39544,39546],{"class":451,"line":452},[449,39541,681],{"class":680},[449,39543,39519],{"class":684},[449,39545,688],{"class":680},[449,39547,39548],{"class":691}," '#server\u002Futils\u002Fhelper'\n",[32,39550,38846],{"id":38845},[816,39552,39553,39560,39565,39571,39574],{},[386,39554,39555,39556,39559],{},"Create a utility at ",[40,39557,39558],{},"server\u002Futils\u002Fhelper.ts"," that exports a simple function.",[386,39561,39562,39563,165],{},"Create a nested route, e.g. ",[40,39564,39510],{},[386,39566,39567,39568,165],{},"Import the helper using ",[40,39569,39570],{},"import { helper } from '#server\u002Futils\u002Fhelper'",[386,39572,39573],{},"Hit the endpoint and confirm it works.",[386,39575,39576,39577,39580,39581,39584],{},"Try importing ",[40,39578,39579],{},"#server\u002F..."," from a component in ",[40,39582,39583],{},"app\u002F"," — note that it's blocked.",[25,39586,39587],{"type":27},[21,39588,39589,39591,39592,39595],{},[40,39590,39480],{}," includes import protection: you ",[151,39593,39594],{},"can't"," import server code from client or shared contexts. That's a feature — it keeps server-only code (and secrets) out of your client bundle.",[32,39597,38888],{"id":38887},[383,39599,39600,39609],{},[386,39601,39602,39603,39606,39607,165],{},"The alias resolves relative to the ",[40,39604,39605],{},"server\u002F"," directory root, not the file you're in. Write the full path from ",[40,39608,39605],{},[386,39610,39611,39612,39614],{},"If your editor doesn't resolve ",[40,39613,39480],{}," at first, restart the TS server so it picks up the regenerated Nuxt types.",[32,39616,38917],{"id":38916},[21,39618,17670,39619,39621,39622,39624],{},[40,39620,39579],{}," for imports inside ",[40,39623,39605],{},". Cleaner paths, safe refactors, and a hard boundary against leaking server code to the client.",[3728,39626,39627],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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);}",{"title":56,"searchDepth":111,"depth":111,"links":39629},[39630,39631,39632,39633,39634,39635,39636],{"id":38641,"depth":114,"text":38642},{"id":38659,"depth":114,"text":38660},{"id":38670,"depth":114,"text":38671},{"id":38722,"depth":114,"text":38723},{"id":38845,"depth":114,"text":38846},{"id":38887,"depth":114,"text":38888},{"id":38916,"depth":114,"text":38917},"Clean, predictable imports inside your server directory — no more ..\u002F..\u002F..\u002F relative-path hell.","#server import alias",{},"\u002Flearn\u002Fnuxt-4-3\u002F03-server-alias",{"title":39470,"description":39637},[39643],"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fpull\u002F33870","learn\u002Fnuxt-4-3\u002F03-server-alias","yKxBhnbczDuOUfbSD4W3Fmj-EU9ne5Eoph674uYUnUE",{"id":38635,"title":38636,"author":116,"body":39647,"description":38935,"difficulty":116,"estMinutes":116,"extension":119,"feature":38936,"img":116,"meta":39878,"navigation":130,"nuxtVersion":116,"order":468,"path":38938,"releaseDate":116,"seo":39879,"series":38940,"sourcePRs":39880,"sourceUrl":116,"stem":38943,"__hash__":38944},{"type":13,"value":39648,"toc":39869},[39649,39651,39659,39661,39665,39667,39669,39707,39709,39711,39713,39751,39815,39817,39841,39843,39861,39863,39867],[32,39650,38642],{"id":38641},[21,39652,38645,39653,38649,39655,38653,39657,165],{},[40,39654,38648],{},[151,39656,38652],{},[40,39658,38656],{},[32,39660,38660],{"id":38659},[21,39662,38663,39663,38667],{},[40,39664,38666],{},[32,39666,38671],{"id":38670},[21,39668,38674],{},[48,39670,39671],{"className":26812,"code":38677,"filename":38678,"language":23640,"meta":56,"style":56},[40,39672,39673,39689,39699],{"__ignoreMap":56},[449,39674,39675,39677,39679,39681,39683,39685,39687],{"class":451,"line":452},[449,39676,5546],{"class":684},[449,39678,23106],{"class":17260},[449,39680,27178],{"class":721},[449,39682,27181],{"class":721},[449,39684,5015],{"class":684},[449,39686,23003],{"class":691},[449,39688,23006],{"class":684},[449,39690,39691,39693,39695,39697],{"class":451,"line":114},[449,39692,38701],{"class":721},[449,39694,38704],{"class":684},[449,39696,2169],{"class":714},[449,39698,10958],{"class":684},[449,39700,39701,39703,39705],{"class":451,"line":111},[449,39702,23103],{"class":684},[449,39704,23106],{"class":17260},[449,39706,23006],{"class":684},[21,39708,38719],{},[32,39710,38723],{"id":38722},[21,39712,38726],{},[48,39714,39715],{"className":26812,"code":38729,"filename":38730,"language":23640,"meta":56,"style":56},[40,39716,39717,39733,39737,39743],{"__ignoreMap":56},[449,39718,39719,39721,39723,39725,39727,39729,39731],{"class":451,"line":452},[449,39720,5546],{"class":684},[449,39722,23106],{"class":17260},[449,39724,27178],{"class":721},[449,39726,27181],{"class":721},[449,39728,5015],{"class":684},[449,39730,23003],{"class":691},[449,39732,23006],{"class":684},[449,39734,39735],{"class":451,"line":114},[449,39736,38753],{"class":705},[449,39738,39739,39741],{"class":451,"line":111},[449,39740,38758],{"class":721},[449,39742,38761],{"class":684},[449,39744,39745,39747,39749],{"class":451,"line":468},[449,39746,23103],{"class":684},[449,39748,23106],{"class":17260},[449,39750,23006],{"class":684},[48,39752,39753],{"className":670,"code":38772,"filename":38773,"language":673,"meta":56,"style":56},[40,39754,39755,39773,39795,39807,39811],{"__ignoreMap":56},[449,39756,39757,39759,39761,39763,39765,39767,39769,39771],{"class":451,"line":452},[449,39758,4629],{"class":680},[449,39760,4632],{"class":680},[449,39762,38784],{"class":721},[449,39764,5163],{"class":684},[449,39766,38789],{"class":1491},[449,39768,1758],{"class":684},[449,39770,1498],{"class":680},[449,39772,1501],{"class":684},[449,39774,39775,39777,39779,39781,39783,39785,39787,39789,39791,39793],{"class":451,"line":114},[449,39776,5079],{"class":680},[449,39778,38802],{"class":684},[449,39780,13632],{"class":721},[449,39782,725],{"class":684},[449,39784,38809],{"class":691},[449,39786,1758],{"class":684},[449,39788,16831],{"class":680},[449,39790,12644],{"class":680},[449,39792,38818],{"class":721},[449,39794,38821],{"class":684},[449,39796,39797,39799,39801,39803,39805],{"class":451,"line":111},[449,39798,1506],{"class":680},[449,39800,27439],{"class":721},[449,39802,725],{"class":684},[449,39804,38832],{"class":691},[449,39806,11211],{"class":684},[449,39808,39809],{"class":451,"line":468},[449,39810,4599],{"class":684},[449,39812,39813],{"class":451,"line":474},[449,39814,4113],{"class":684},[32,39816,38846],{"id":38845},[816,39818,39819,39825,39829,39833,39837],{},[386,39820,38851,39821,38855,39823,38859],{},[40,39822,38854],{},[40,39824,38858],{},[386,39826,38862,39827,38866],{},[40,39828,38865],{},[386,39830,38869,39831,165],{},[40,39832,38872],{},[386,39834,38875,39835,38879],{},[40,39836,38878],{},[386,39838,7226,39839,38884],{},[40,39840,38865],{},[32,39842,38888],{"id":38887},[383,39844,39845,39853],{},[386,39846,38893,39847,38897,39849,2429,39851,165],{},[151,39848,38896],{},[40,39850,38666],{},[40,39852,38809],{},[386,39854,38904,39855,38907,39857,38911,39859,165],{},[40,39856,38656],{},[40,39858,38910],{},[40,39860,35114],{},[32,39862,38917],{"id":38916},[21,39864,39865,38922],{},[40,39866,38656],{},[3728,39868,38925],{},{"title":56,"searchDepth":111,"depth":111,"links":39870},[39871,39872,39873,39874,39875,39876,39877],{"id":38641,"depth":114,"text":38642},{"id":38659,"depth":114,"text":38660},{"id":38670,"depth":114,"text":38671},{"id":38722,"depth":114,"text":38723},{"id":38845,"depth":114,"text":38846},{"id":38887,"depth":114,"text":38888},{"id":38916,"depth":114,"text":38917},{},{"title":38636,"description":38935},[38942],{"id":39882,"title":39883,"author":116,"body":39884,"description":40152,"difficulty":116,"estMinutes":116,"extension":119,"feature":40153,"img":116,"meta":40154,"navigation":130,"nuxtVersion":116,"order":474,"path":40155,"releaseDate":116,"seo":40156,"series":38940,"sourcePRs":40157,"sourceUrl":116,"stem":40159,"__hash__":40160},"tutorials\u002Flearn\u002Fnuxt-4-3\u002F05-layout-props-setpagelayout.md","Layout Props with setPageLayout",{"type":13,"value":39885,"toc":40143},[39886,39888,39898,39900,39903,39905,39908,39952,39955,39957,39964,40020,40087,40089,40117,40119,40132,40134,40140],[32,39887,38642],{"id":38641},[21,39889,1556,39890,39893,39894,39897],{},[40,39891,39892],{},"setPageLayout"," composable now takes a ",[151,39895,39896],{},"second argument",": props to pass into the layout.",[32,39899,38660],{"id":38659},[21,39901,39902],{},"Layouts used to be all-or-nothing. If you wanted an admin layout with the sidebar collapsed on some routes, you reached for a store or a global flag. Now you can hand the layout its configuration directly at the moment you select it.",[32,39904,38671],{"id":38670},[21,39906,39907],{},"The layout read shared state to decide how to render:",[48,39909,39911],{"className":670,"code":39910,"language":673,"meta":56,"style":56},"\u002F\u002F somewhere global\nconst uiStore = useUiStore()\nuiStore.sidebar = true\nuiStore.theme = 'dark'\n",[40,39912,39913,39918,39932,39942],{"__ignoreMap":56},[449,39914,39915],{"class":451,"line":452},[449,39916,39917],{"class":705},"\u002F\u002F somewhere global\n",[449,39919,39920,39922,39925,39927,39930],{"class":451,"line":114},[449,39921,711],{"class":680},[449,39923,39924],{"class":714}," uiStore",[449,39926,718],{"class":680},[449,39928,39929],{"class":721}," useUiStore",[449,39931,11220],{"class":684},[449,39933,39934,39937,39939],{"class":451,"line":111},[449,39935,39936],{"class":684},"uiStore.sidebar ",[449,39938,5015],{"class":680},[449,39940,39941],{"class":714}," true\n",[449,39943,39944,39947,39949],{"class":451,"line":468},[449,39945,39946],{"class":684},"uiStore.theme ",[449,39948,5015],{"class":680},[449,39950,39951],{"class":691}," 'dark'\n",[21,39953,39954],{},"The layout and the caller were coupled through a store neither of them owned cleanly.",[32,39956,38723],{"id":38722},[21,39958,39959,39960,39963],{},"Select the layout ",[151,39961,39962],{},"and"," pass its props in one call:",[48,39965,39968],{"className":670,"code":39966,"filename":39967,"language":673,"meta":56,"style":56},"export default defineNuxtRouteMiddleware(() => {\n  setPageLayout('admin', {\n    sidebar: true,\n    theme: 'dark'\n  })\n})\n","middleware\u002Fadmin.ts",[40,39969,39970,39984,39995,40004,40012,40016],{"__ignoreMap":56},[449,39971,39972,39974,39976,39978,39980,39982],{"class":451,"line":452},[449,39973,4629],{"class":680},[449,39975,4632],{"class":680},[449,39977,38784],{"class":721},[449,39979,4718],{"class":684},[449,39981,1498],{"class":680},[449,39983,1501],{"class":684},[449,39985,39986,39989,39991,39993],{"class":451,"line":114},[449,39987,39988],{"class":721},"  setPageLayout",[449,39990,725],{"class":684},[449,39992,39016],{"class":691},[449,39994,768],{"class":684},[449,39996,39997,40000,40002],{"class":451,"line":111},[449,39998,39999],{"class":684},"    sidebar: ",[449,40001,2169],{"class":714},[449,40003,779],{"class":684},[449,40005,40006,40009],{"class":451,"line":468},[449,40007,40008],{"class":684},"    theme: ",[449,40010,40011],{"class":691},"'dark'\n",[449,40013,40014],{"class":451,"line":474},[449,40015,16717],{"class":684},[449,40017,40018],{"class":451,"line":480},[449,40019,4113],{"class":684},[48,40021,40023],{"className":26812,"code":40022,"filename":39130,"language":23640,"meta":56,"style":56},"\u003Cscript setup lang=\"ts\">\ndefineProps\u003C{\n  sidebar?: boolean\n  theme?: 'light' | 'dark'\n}>()\n\u003C\u002Fscript>\n",[40,40024,40025,40041,40049,40060,40074,40079],{"__ignoreMap":56},[449,40026,40027,40029,40031,40033,40035,40037,40039],{"class":451,"line":452},[449,40028,5546],{"class":684},[449,40030,23106],{"class":17260},[449,40032,27178],{"class":721},[449,40034,27181],{"class":721},[449,40036,5015],{"class":684},[449,40038,23003],{"class":691},[449,40040,23006],{"class":684},[449,40042,40043,40046],{"class":451,"line":114},[449,40044,40045],{"class":721},"defineProps",[449,40047,40048],{"class":684},"\u003C{\n",[449,40050,40051,40054,40057],{"class":451,"line":111},[449,40052,40053],{"class":1491},"  sidebar",[449,40055,40056],{"class":680},"?:",[449,40058,40059],{"class":714}," boolean\n",[449,40061,40062,40065,40067,40070,40072],{"class":451,"line":468},[449,40063,40064],{"class":1491},"  theme",[449,40066,40056],{"class":680},[449,40068,40069],{"class":691}," 'light'",[449,40071,4405],{"class":680},[449,40073,39951],{"class":691},[449,40075,40076],{"class":451,"line":474},[449,40077,40078],{"class":684},"}>()\n",[449,40080,40081,40083,40085],{"class":451,"line":480},[449,40082,23103],{"class":684},[449,40084,23106],{"class":17260},[449,40086,23006],{"class":684},[32,40088,38846],{"id":38845},[816,40090,40091,40098,40105,40111,40114],{},[386,40092,40093,40094,21148,40096,165],{},"Add typed props to ",[40,40095,39130],{},[40,40097,40045],{},[386,40099,40100,40101,40104],{},"Use those props in the template — e.g. show the sidebar only when ",[40,40102,40103],{},"sidebar"," is true.",[386,40106,40107,40108,165],{},"In a route middleware, call ",[40,40109,40110],{},"setPageLayout('admin', { sidebar: true, theme: 'dark' })",[386,40112,40113],{},"Visit a route that runs the middleware and confirm the layout renders with those props.",[386,40115,40116],{},"Change the props for a different route and confirm the layout responds.",[32,40118,38888],{"id":38887},[383,40120,40121,40129],{},[386,40122,40123,40124,40126,40127,165],{},"Type your ",[40,40125,40045],{}," so you get autocomplete and catch typos in the prop names passed to ",[40,40128,39892],{},[386,40130,40131],{},"Props apply to the layout for that navigation. Set them where the decision belongs (middleware for route-wide rules, the page for page-specific ones).",[32,40133,38917],{"id":38916},[21,40135,40136,40139],{},[40,40137,40138],{},"setPageLayout('admin', props)"," lets you configure a layout at selection time. Pair it with the route rule layouts from step 1 for a fully declarative layout system.",[3728,40141,40142],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":40144},[40145,40146,40147,40148,40149,40150,40151],{"id":38641,"depth":114,"text":38642},{"id":38659,"depth":114,"text":38660},{"id":38670,"depth":114,"text":38671},{"id":38722,"depth":114,"text":38723},{"id":38845,"depth":114,"text":38846},{"id":38887,"depth":114,"text":38888},{"id":38916,"depth":114,"text":38917},"Pass props into your layout at runtime — toggle a sidebar or theme from middleware or a page, no global state required.","setPageLayout second argument",{},"\u002Flearn\u002Fnuxt-4-3\u002F05-layout-props-setpagelayout",{"title":39883,"description":40152},[40158],"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Fpull\u002F33805","learn\u002Fnuxt-4-3\u002F05-layout-props-setpagelayout","DT5h5qtMp_8VshouOKhtb8dcFVcK706URgtXjym1gbg",{"id":40162,"title":40163,"author":116,"body":40164,"description":40449,"difficulty":116,"estMinutes":116,"extension":119,"feature":40450,"img":116,"meta":40451,"navigation":130,"nuxtVersion":116,"order":480,"path":40452,"releaseDate":116,"seo":40453,"series":38940,"sourcePRs":40454,"sourceUrl":116,"stem":40456,"__hash__":40457},"tutorials\u002Flearn\u002Fnuxt-4-3\u002F06-quick-hits.md","Quick Hits & What to Watch",{"type":13,"value":40165,"toc":40441},[40166,40169,40173,40180,40208,40223,40227,40232,40270,40274,40277,40281,40284,40350,40354,40361,40416,40421,40423,40435,40438],[21,40167,40168],{},"Not every feature needs a full tutorial. Here are the rest of Nuxt 4.3's changes worth knowing, in quick-hit form — enough to recognize them and reach for them later.",[32,40170,40172],{"id":40171},"dev-mode-payload-extraction","Dev-mode payload extraction",[21,40174,40175,40176,40179],{},"Payload extraction (step 2) now also works in ",[151,40177,40178],{},"development",", so you can test and debug payload behavior without a production build.",[48,40181,40183],{"className":670,"code":40182,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  nitro: { static: true }\n})\n",[40,40184,40185,40195,40204],{"__ignoreMap":56},[449,40186,40187,40189,40191,40193],{"class":451,"line":452},[449,40188,4629],{"class":680},[449,40190,4632],{"class":680},[449,40192,4635],{"class":721},[449,40194,1391],{"class":684},[449,40196,40197,40200,40202],{"class":451,"line":114},[449,40198,40199],{"class":684},"  nitro: { static: ",[449,40201,2169],{"class":714},[449,40203,15045],{"class":684},[449,40205,40206],{"class":451,"line":111},[449,40207,4113],{"class":684},[21,40209,40210,40211,349,40213,349,40215,39444,40218,40220,40221,165],{},"It also kicks in for individual pages with ",[40,40212,39389],{},[40,40214,39443],{},[40,40216,40217],{},"prerender",[40,40219,39260],{}," route rules. Handy for verifying the caching work from step 2 without ",[40,40222,39409],{},[32,40224,40226],{"id":40225},"disable-modules-from-layers","Disable modules from layers",[21,40228,40229,40230,3070],{},"Extending a layer but don't want one of its modules? Pass ",[40,40231,27238],{},[48,40233,40235],{"className":670,"code":40234,"filename":4619,"language":673,"meta":56,"style":56},"export default defineNuxtConfig({\n  extends: ['..\u002Fshared-layer'],\n  image: false \u002F\u002F drop @nuxt\u002Fimage from the layer\n})\n",[40,40236,40237,40247,40256,40266],{"__ignoreMap":56},[449,40238,40239,40241,40243,40245],{"class":451,"line":452},[449,40240,4629],{"class":680},[449,40242,4632],{"class":680},[449,40244,4635],{"class":721},[449,40246,1391],{"class":684},[449,40248,40249,40251,40254],{"class":451,"line":114},[449,40250,29127],{"class":684},[449,40252,40253],{"class":691},"'..\u002Fshared-layer'",[449,40255,1469],{"class":684},[449,40257,40258,40261,40263],{"class":451,"line":111},[449,40259,40260],{"class":684},"  image: ",[449,40262,27238],{"class":714},[449,40264,40265],{"class":705}," \u002F\u002F drop @nuxt\u002Fimage from the layer\n",[449,40267,40268],{"class":451,"line":468},[449,40269,4113],{"class":684},[32,40271,40273],{"id":40272},"draggable-error-overlay","Draggable error overlay",[21,40275,40276],{},"The dev error overlay is now draggable and minimizable — snap it to any corner, shrink it to a pill while you fix things, and it remembers where you left it across reloads. Nothing to configure; just nicer.",[32,40278,40280],{"id":40279},"async-plugin-constructors","Async plugin constructors",[21,40282,40283],{},"Module authors can now use async functions when adding build plugins, enabling true lazy loading:",[48,40285,40288],{"className":670,"code":40286,"filename":40287,"language":673,"meta":56,"style":56},"export default defineNuxtModule({\n  async setup() {\n    addVitePlugin(() => import('my-cool-plugin').then(r => r.default()))\n  }\n})\n","modules\u002Fmy-module.ts",[40,40289,40290,40301,40309,40342,40346],{"__ignoreMap":56},[449,40291,40292,40294,40296,40299],{"class":451,"line":452},[449,40293,4629],{"class":680},[449,40295,4632],{"class":680},[449,40297,40298],{"class":721}," defineNuxtModule",[449,40300,1391],{"class":684},[449,40302,40303,40305,40307],{"class":451,"line":114},[449,40304,34106],{"class":680},[449,40306,27178],{"class":721},[449,40308,5438],{"class":684},[449,40310,40311,40314,40316,40318,40321,40323,40326,40328,40330,40332,40334,40336,40338,40340],{"class":451,"line":111},[449,40312,40313],{"class":721},"    addVitePlugin",[449,40315,4718],{"class":684},[449,40317,1498],{"class":680},[449,40319,40320],{"class":680}," import",[449,40322,725],{"class":684},[449,40324,40325],{"class":691},"'my-cool-plugin'",[449,40327,374],{"class":684},[449,40329,6414],{"class":721},[449,40331,725],{"class":684},[449,40333,6419],{"class":1491},[449,40335,32908],{"class":680},[449,40337,6426],{"class":684},[449,40339,10932],{"class":721},[449,40341,6431],{"class":684},[449,40343,40344],{"class":451,"line":468},[449,40345,4599],{"class":684},[449,40347,40348],{"class":451,"line":474},[449,40349,4113],{"class":684},[32,40351,40353],{"id":40352},"deprecations-to-get-ahead-of","Deprecations to get ahead of",[21,40355,40356,40357,40360],{},"In preparation for Nitro v3 and H3 v2, ",[40,40358,40359],{},"createError"," is moving to Web API naming:",[48,40362,40364],{"className":670,"code":40363,"language":673,"meta":56,"style":56},"\u002F\u002F old (still works, deprecated)\nthrow createError({ statusCode: 404, statusMessage: 'Not Found' })\n\n\u002F\u002F new\nthrow createError({ status: 404, statusText: 'Not Found' })\n",[40,40365,40366,40371,40389,40393,40398],{"__ignoreMap":56},[449,40367,40368],{"class":451,"line":452},[449,40369,40370],{"class":705},"\u002F\u002F old (still works, deprecated)\n",[449,40372,40373,40376,40378,40380,40382,40384,40387],{"class":451,"line":114},[449,40374,40375],{"class":680},"throw",[449,40377,5112],{"class":721},[449,40379,16526],{"class":684},[449,40381,12452],{"class":714},[449,40383,16531],{"class":684},[449,40385,40386],{"class":691},"'Not Found'",[449,40388,10958],{"class":684},[449,40390,40391],{"class":451,"line":111},[449,40392,700],{"emptyLinePlaceholder":130},[449,40394,40395],{"class":451,"line":468},[449,40396,40397],{"class":705},"\u002F\u002F new\n",[449,40399,40400,40402,40404,40407,40409,40412,40414],{"class":451,"line":474},[449,40401,40375],{"class":680},[449,40403,5112],{"class":721},[449,40405,40406],{"class":684},"({ status: ",[449,40408,12452],{"class":714},[449,40410,40411],{"class":684},", statusText: ",[449,40413,40386],{"class":691},[449,40415,10958],{"class":684},[25,40417,40418],{"type":27},[21,40419,40420],{},"The old properties still work in 4.3, but updating now saves you a migration later when v5 lands.",[32,40422,38917],{"id":38916},[21,40424,40425,40426,38653,40431,40434],{},"That's Nuxt 4.3. The headline features (steps 1–5) change how you structure layouts, caching, and server imports; these quick hits smooth the edges and point toward v5. Keep an eye on the ",[3861,40427,40430],{"href":40428,"rel":40429},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Fupgrade",[3865],"upgrade guide",[40,40432,40433],{},"future.compatibilityVersion: 5"," becomes available.",[21,40436,40437],{},"You made it through the series — nice work. On to the next release.",[3728,40439,40440],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":56,"searchDepth":111,"depth":111,"links":40442},[40443,40444,40445,40446,40447,40448],{"id":40171,"depth":114,"text":40172},{"id":40225,"depth":114,"text":40226},{"id":40272,"depth":114,"text":40273},{"id":40279,"depth":114,"text":40280},{"id":40352,"depth":114,"text":40353},{"id":38916,"depth":114,"text":38917},"The smaller 4.3 wins worth knowing — dev-mode payloads, disabling layer modules, the draggable error overlay — plus the deprecations to get ahead of.","Round-up: DX, layers, deprecations",{},"\u002Flearn\u002Fnuxt-4-3\u002F06-quick-hits",{"title":40163,"description":40449},[40455],"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt\u002Freleases\u002Ftag\u002Fv4.3.0","learn\u002Fnuxt-4-3\u002F06-quick-hits","SVnLzTVh9Q_vm4C7WYMXfLw_RouFD57SNk1yTaOEnc8",{"id":40459,"title":40460,"author":40461,"body":40462,"description":40539,"difficulty":40540,"estMinutes":4086,"extension":119,"feature":116,"img":40541,"meta":40542,"navigation":130,"nuxtVersion":40543,"order":116,"path":40544,"releaseDate":40545,"seo":40546,"series":38940,"sourcePRs":116,"sourceUrl":40481,"stem":40547,"__hash__":40548},"tutorials\u002Flearn\u002Fnuxt-4-3\u002Findex.md","Learning Nuxt 4.3",{"name":9,"img":11,"website":327},{"type":13,"value":40463,"toc":40536},[40464,40467,40474,40489,40493,40496,40533],[21,40465,40466],{},"Here's the honest truth: I use Nuxt every day, but every release ships features I've never actually put my hands on. Nuxt 4.3 is a perfect example — I read the release notes, nodded along, and then kept writing code the old way out of habit.",[21,40468,40469,40470,40473],{},"So this series is me fixing that, and you're coming with me. We're a room full of novices where it counts: we know the framework, we just haven't used ",[156,40471,40472],{},"these"," features yet.",[25,40475,40476],{"type":363},[21,40477,40478,40479,40484,40485,40488],{},"Everything here is based on the official ",[3861,40480,40483],{"href":40481,"rel":40482},"https:\u002F\u002Fnuxt.com\u002Fblog\u002Fv4-3",[3865],"Nuxt 4.3 release notes",". We're not replacing the docs — we're learning to actually ",[156,40486,40487],{},"use"," what they announced.",[32,40490,40492],{"id":40491},"how-this-series-works","How this series works",[21,40494,40495],{},"Each step takes one feature and walks the same path:",[816,40497,40498,40503,40508,40513,40518,40523,40528],{},[386,40499,40500,40502],{},[151,40501,38642],{}," — the feature in one breath",[386,40504,40505,40507],{},[151,40506,38660],{}," — the real problem it solves",[386,40509,40510,40512],{},[151,40511,38671],{}," — how we did it without the feature",[386,40514,40515,40517],{},[151,40516,38723],{}," — how we do it now",[386,40519,40520,40522],{},[151,40521,38846],{}," — hands-on steps you run",[386,40524,40525,40527],{},[151,40526,38888],{}," — what tripped me up",[386,40529,40530,40532],{},[151,40531,38917],{}," — what to remember",[21,40534,40535],{},"Mark each step complete as you go — your progress is saved in your browser. Ready? Start with step one.",{"title":56,"searchDepth":111,"depth":111,"links":40537},[40538],{"id":40491,"depth":114,"text":40492},"Nuxt 4.3 shipped a stack of features I hadn't reached for yet — route rule layouts, ISR payload extraction, the #server alias and more. Let's learn them together, one step at a time.","Intermediate","\u002Fnuxt_learn.png",{},"4.3","\u002Flearn\u002Fnuxt-4-3","2026-01-22",{"title":40460,"description":40539},"learn\u002Fnuxt-4-3\u002Findex","tPT7YuQbW1yNPaJ-gkDeBBwaDV1J6dirygEbU8weDvw",1784737127499]