-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathvite.config.ts
More file actions
166 lines (162 loc) · 5.84 KB
/
Copy pathvite.config.ts
File metadata and controls
166 lines (162 loc) · 5.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/**
* @docs ARCHITECTURE:Core
*
* ### AI Assist Note
* **@module ViteConfig**
* Handles reactive state and high-fidelity user interactions.
*
* ### 🔍 Debugging & Observability
* - **Failure Path**: UI regression, hook desync, or API timeout.
* - **Telemetry Link**: Search `[vite_config]` in observability traces.
*/
/**
* @module ViteConfig
* Build, dev server, and test configuration for Tadpole OS frontend.
* Includes manual vendor chunk splitting for cache efficiency
* and Vitest integration with jsdom + v8 coverage.
*/
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
import path from 'path'
import { fileURLToPath } from 'url'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
// https://vite.dev/config/
export default defineConfig({
plugins: [
react(),
tailwindcss(),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
server: {
host: '0.0.0.0',
port: 5173,
watch: {
ignored: [
'**/coverage/**',
'**/dist/**',
'**/logs/**',
'**/data/**',
'**/reports/**',
'**/scratch/**',
'**/.tmp/**',
'**/.git/**',
'**/server-rs/target/**',
'**/src-tauri/target/**'
]
},
// ─── Security Headers ────────────────────────────────────────────────────
// Injects production-grade security headers on every dev-server response.
// These are also applied via the configureServer plugin below.
// TODO: tighten CSP for production (remove 'unsafe-eval', 'unsafe-inline')
// when HMR / Vite style injection is no longer needed.
headers: {
'Content-Security-Policy': [
"default-src 'self'",
// Vite HMR requires unsafe-eval; ws: for the WebSocket HMR channel
"script-src 'self' 'unsafe-eval' 'unsafe-inline'",
// Vite injects <style> tags at runtime in dev mode
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
// Google Fonts + data URIs for icons
"font-src 'self' https://fonts.gstatic.com data:",
// API, WebSocket to same host; wss: for production TLS upgrade
"connect-src 'self' http://localhost:8000 http://127.0.0.1:8000 https://raw.githubusercontent.com ws: wss:",
// Media capture (getUserMedia for voice)
"media-src 'self' blob:",
// Inline SVGs and canvas used by the visualizer
"img-src 'self' data: blob:",
// Disallow all framing
"frame-src 'none'",
"frame-ancestors 'none'",
// No plugins (Flash etc.)
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
].join('; '),
'X-Frame-Options': 'DENY',
'X-Content-Type-Options': 'nosniff',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'camera=(), microphone=(self), geolocation=()',
},
},
build: {
chunkSizeWarningLimit: 600,
rollupOptions: {
output: {
manualChunks(id: string) {
if (id.includes('node_modules')) {
if (id.includes('react/') || id.includes('react-dom/')) return 'vendor-react';
if (id.includes('react-router-dom/')) return 'vendor-router';
if (id.includes('framer-motion')) return 'vendor-motion';
if (id.includes('reactflow/') || id.includes('dagre/')) return 'vendor-flow';
if (
id.includes('react-force-graph-2d') ||
id.includes('force-graph') ||
id.includes('/d3-') ||
id.includes('/three/')
) {
return 'vendor-graph';
}
if (
id.includes('react-markdown') ||
id.includes('/remark-') ||
id.includes('/rehype-') ||
id.includes('/unified/') ||
id.includes('/micromark') ||
id.includes('/mdast-') ||
id.includes('/hast-')
) {
return 'vendor-markdown';
}
if (id.includes('@google/genai') || id.includes('groq-sdk')) return 'vendor-ai';
if (id.includes('@msgpack/msgpack') || id.includes('/lodash/')) return 'vendor-utils';
if (id.includes('lucide-react')) return 'vendor-ui';
if (id.includes('zustand')) return 'vendor-state';
}
},
},
},
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: './tests/setup.ts',
// ─── Parallel Execution ────────────────────────────────────────────────
// forks pool gives better process isolation than threads for JSDOM-heavy
// suites; prevents shared-memory context leaks across test files.
pool: 'forks',
// Cap workers to avoid CPU starvation on local and CI machines.
// Raise to os.cpus().length - 1 on high-core build agents.
maxWorkers: 4,
alias: {
'react-dom/test-utils': 'react-dom/test-utils',
},
exclude: ['**/node_modules/**', '**/dist/**', '**/.tmp/**', '**/setup.ts', 'tests/e2e/**', '**/external/**'],
deps: {
optimizer: {
web: {
include: ['react-dom', 'react-dom/client']
}
}
},
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
include: ['src/**/*.ts', 'src/**/*.tsx'],
exclude: ['src/main.tsx', 'src/vite-env.d.ts', 'tests/**'],
// GAP-TEST-04: Minimum coverage thresholds to prevent regressions.
// Set at 70% as a starting floor — raise incrementally.
thresholds: {
lines: 70,
functions: 70,
branches: 70,
statements: 70,
},
}
},
})
// Metadata: [vite_config]