--- name: vue-nuxt description: Vue 3 and Nuxt 3 for JARVIS AI Assistant UI development with security-first patterns model: sonnet risk_level: MEDIUM version: 1.0.0 --- # Vue 3 / Nuxt 3 Development Skill > **File Organization**: This skill uses split structure. See `references/` for advanced patterns and security examples. ## 1. Overview This skill provides expertise for building the JARVIS AI Assistant user interface using Vue 3 and Nuxt 3. It focuses on creating responsive, performant 3D HUD interfaces with security-first development practices. **Risk Level**: MEDIUM - Handles user input, renders dynamic content, potential XSS vectors **Primary Use Cases**: - Building reactive 3D HUD components for JARVIS interface - Server-side rendering for initial load performance - Client-side state management integration - Secure handling of user inputs and API responses ## 2. Core Responsibilities ### 2.1 Fundamental Principles 1. **TDD First**: Write tests before implementation - red/green/refactor cycle 2. **Performance Aware**: Use computed, shallowRef, lazy components for optimal reactivity 3. **Composition API First**: Use Vue 3 Composition API with ` ``` ### 4.2 Input Sanitization Pattern ```typescript // composables/useSanitize.ts import DOMPurify from 'isomorphic-dompurify' export function useSanitize() { const sanitizeHTML = (dirty: string): string => { // ✅ Strict sanitization for any HTML content return DOMPurify.sanitize(dirty, { ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'span'], ALLOWED_ATTR: ['class'] }) } const sanitizeText = (input: string): string => { // ✅ Strip all HTML for plain text return DOMPurify.sanitize(input, { ALLOWED_TAGS: [] }) } return { sanitizeHTML, sanitizeText } } ``` ### 4.3 Secure API Route Pattern ```typescript // server/api/jarvis/command.post.ts import { z } from 'zod' // ✅ Define strict schema for command validation const commandSchema = z.object({ action: z.enum(['status', 'control', 'query']), target: z.string().max(100).regex(/^[a-zA-Z0-9-_]+$/), parameters: z.record(z.string()).optional() }) export default defineEventHandler(async (event) => { const body = await readBody(event) // ✅ Validate input against schema const result = commandSchema.safeParse(body) if (!result.success) { throw createError({ statusCode: 400, message: 'Invalid command format' // ✅ Generic error message }) } // ✅ Process validated command const command = result.data // Never log sensitive data console.log(`Processing command: ${command.action}`) return { success: true, commandId: generateSecureId() } }) ``` ### 4.4 Secure Environment Configuration ```typescript // nuxt.config.ts export default defineNuxtConfig({ // ✅ Security headers routeRules: { '/**': { headers: { 'Content-Security-Policy': "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'", 'X-Content-Type-Options': 'nosniff', 'X-Frame-Options': 'DENY', 'X-XSS-Protection': '1; mode=block' } } }, // ✅ Runtime config - secrets stay server-side runtimeConfig: { apiSecret: process.env.API_SECRET, // Server only public: { apiBase: '/api' // Client accessible } }, // ✅ Disable devtools in production devtools: { enabled: process.env.NODE_ENV === 'development' } }) ``` ### 4.5 3D HUD Component Integration ```vue ``` ## 5. Implementation Workflow (TDD) ### 5.1 Step 1: Write Failing Test First Always start by writing tests that define expected behavior: ```typescript // tests/components/VoiceIndicator.test.ts import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import VoiceIndicator from '@/components/VoiceIndicator.vue' describe('VoiceIndicator', () => { it('displays idle state by default', () => { const wrapper = mount(VoiceIndicator) expect(wrapper.find('.indicator').classes()).toContain('idle') expect(wrapper.text()).toContain('Ready') }) it('shows listening state when active', async () => { const wrapper = mount(VoiceIndicator, { props: { isListening: true } }) expect(wrapper.find('.indicator').classes()).toContain('listening') expect(wrapper.find('.pulse-animation').exists()).toBe(true) }) it('emits cancel event on escape key', async () => { const wrapper = mount(VoiceIndicator, { props: { isListening: true } }) await wrapper.trigger('keydown.escape') expect(wrapper.emitted('cancel')).toBeTruthy() }) }) ``` ### 5.2 Step 2: Implement Minimum to Pass Write only enough code to make the tests pass: ```vue ``` ### 5.3 Step 3: Refactor if Needed After tests pass, improve code quality without changing behavior. Re-run tests after each refactor. ### 5.4 Step 4: Run Full Verification ```bash # Run all verification steps before committing npx vitest run # Unit tests npx eslint . --ext .vue,.ts # Linting npx nuxi typecheck # Type checking npm run build # Build verification ``` ## 6. Performance Patterns ### 6.1 Computed Properties for Derived State ```typescript // ❌ BAD - Recalculates in template on every render // ✅ GOOD - Cached until dependencies change const activeCount = computed(() => items.value.filter(i => i.active).length) ``` ### 6.2 shallowRef for Large Objects ```typescript // ❌ BAD - Deep reactivity on large 3D data const meshData = ref({ vertices: new Float32Array(100000), ... }) // ✅ GOOD - Shallow reactivity, manual trigger const meshData = shallowRef({ vertices: new Float32Array(100000), ... }) // Trigger update explicitly meshData.value = { ...newData } triggerRef(meshData) ``` ### 6.3 defineAsyncComponent for Lazy Loading ```typescript // ❌ BAD - All components loaded upfront import HeavyChart from '@/components/HeavyChart.vue' // ✅ GOOD - Load only when needed const HeavyChart = defineAsyncComponent(() => import('@/components/HeavyChart.vue') ) // With loading state const HeavyChart = defineAsyncComponent({ loader: () => import('@/components/HeavyChart.vue'), loadingComponent: LoadingSpinner, delay: 200 }) ``` ### 6.4 v-memo for List Optimization ```vue
``` ### 6.5 Virtual Scrolling for Long Lists ```vue ``` ### 6.6 Debounced Watchers ```typescript // ❌ BAD - Fires on every keystroke watch(searchQuery, async (query) => { results.value = await searchAPI(query) }) // ✅ GOOD - Debounced to reduce API calls import { watchDebounced } from '@vueuse/core' watchDebounced( searchQuery, async (query) => { results.value = await searchAPI(query) }, { debounce: 300 } ) // Alternative with manual debounce watch(searchQuery, useDebounceFn(async (query) => { results.value = await searchAPI(query) }, 300)) ``` ## 7. Security Standards ### 7.1 Known Vulnerabilities (CVE Research) | CVE | Severity | Description | Mitigation | |-----|----------|-------------|------------| | CVE-2024-34344 | HIGH | Nuxt RCE via test component | Update to Nuxt 3.12.4+ | | CVE-2024-23657 | HIGH | Devtools path traversal/RCE | Update devtools to 1.3.9+ | | CVE-2023-3224 | CRITICAL | Dev server code injection | Update to Nuxt 3.4.4+, never expose dev server | **See**: `references/security-examples.md` for detailed mitigation code ### 5.2 OWASP Top 10 Coverage | OWASP Category | Risk | Mitigation Strategy | |----------------|------|---------------------| | A01 Broken Access Control | HIGH | Server-side route guards, middleware auth | | A03 Injection | HIGH | Input validation with Zod, parameterized queries | | A05 Security Misconfiguration | MEDIUM | CSP headers, secure nuxt.config | | A07 XSS | HIGH | v-text directive, DOMPurify sanitization | ### 5.3 Input Validation Framework ```typescript // ❌ DANGEROUS - Direct v-html with user input
// ✅ SECURE - Sanitized HTML or plain text
``` ### 5.4 Authentication Middleware ```typescript // middleware/auth.ts export default defineNuxtRouteMiddleware((to) => { const { authenticated } = useAuthState() if (!authenticated.value && to.meta.requiresAuth) { return navigateTo('/login') } }) ``` ## 6. Testing & Quality ### 6.1 Security Testing ```typescript // tests/security/xss.test.ts import { describe, it, expect } from 'vitest' import { mount } from '@vue/test-utils' import HUDPanel from '@/components/HUDPanel.vue' describe('XSS Prevention', () => { it('should sanitize malicious input', () => { const wrapper = mount(HUDPanel, { props: { title: 'Hello' } }) expect(wrapper.html()).not.toContain('