0c17327
commit 0c17327
213 files changed
+30626 -4149
Showing 46 changed files+8595-381
This diff is too large and has been truncated. Displaying first 46 files.
.astro/settings.json.env.exampleDEPLOYMENT.mdREADME.mdbun.lockcli/bin/och.tscli/package.jsoncli/src/commands/auth.tscli/src/commands/stack/index.tscli/src/lib/api.tscli/src/lib/config.tscli/tsconfig.jsondrizzle.config.tsdrizzle/0000_black_monster_badoon.sqlpackage-lock.jsonpackage.jsonpci/1.pngpci/image copy 2.pngpci/image copy 3.pngpci/image copy 4.pngpci/image copy 5.pngpci/image copy.pngpci/image.pngscripts/seed-admin.tsscripts/ssh-server.tsscripts/test-gpg.tssrc/components/admin/AdminDashboard.tsxsrc/components/admin/AlertBanner.tsxsrc/components/admin/BottomPanel.tsxsrc/components/admin/DashboardStats.tsxsrc/components/admin/GlobeViz.tsxsrc/components/admin/LiveLogStream.tsxsrc/components/admin/QuickStatsRow.tsxsrc/components/admin/RecentActivity.tsxsrc/components/admin/SystemStatus.tsxsrc/components/admin/TrendingDevelopers.tsxsrc/components/dashboard/ActivityStream.tsxsrc/components/home/FeaturesSection.tsxsrc/components/home/HeroSection.tsxsrc/components/issues/IssueDetail.tsxsrc/components/issues/IssuesList.tsxsrc/components/layout/Header.astrosrc/components/milestones/MilestonesList.tsxsrc/components/milestones/NewMilestoneForm.tsxsrc/components/pr/AIReviewPanel.tsxsrc/components/pr/StackVisualization.tsx
▼.astro/settings.json
+1-1
| @@ -1,5 +1,5 @@ | ||
1 | 1 | { |
2 | 2 | "_variables": { |
3 | - "lastUpdateCheck": 1765437066500 | |
3 | + "lastUpdateCheck": 1767130145089 | |
4 | 4 | } |
5 | 5 | } |
6 | 6 | |
▼.env.example
+17-2
| @@ -44,6 +44,11 @@ JWT_SECRET=change-this-to-a-very-long-random-secret-key-in-production | ||
44 | 44 | JWT_EXPIRES_IN=7d |
45 | 45 | SESSION_SECRET=change-this-session-secret-too |
46 | 46 | |
47 | +# Internal Hook Security (CRITICAL for production) | |
48 | +# This secret protects git hooks from unauthorized access | |
49 | +# Generate with: openssl rand -hex 32 | |
50 | +INTERNAL_HOOK_SECRET=change-this-internal-hook-secret-in-production | |
51 | + | |
47 | 52 | # OAuth Providers (optional) |
48 | 53 | OAUTH_GOOGLE_CLIENT_ID= |
49 | 54 | OAUTH_GOOGLE_CLIENT_SECRET= |
| @@ -105,8 +110,18 @@ LOG_FILE=./data/logs/opencodehub.log | ||
105 | 110 | |
106 | 111 | # Security |
107 | 112 | RATE_LIMIT_ENABLED=true |
108 | -RATE_LIMIT_MAX_REQUESTS=100 | |
109 | -RATE_LIMIT_WINDOW_MS=60000 | |
113 | +# Authentication endpoints (login/register) - 5 requests per 15 minutes | |
114 | +RATE_LIMIT_AUTH=5 | |
115 | +# API endpoints - 100 requests per minute | |
116 | +RATE_LIMIT_API=100 | |
117 | +# Git operations - 200 requests per minute | |
118 | +RATE_LIMIT_GIT=200 | |
119 | +# General requests - 60 per minute | |
120 | +RATE_LIMIT_GENERAL=60 | |
121 | +# Skip rate limiting in development (not recommended) | |
122 | +RATE_LIMIT_SKIP_DEV=false | |
123 | +# Skip CSRF protection in development (not recommended) | |
124 | +CSRF_SKIP_DEV=false | |
110 | 125 | |
111 | 126 | # Features |
112 | 127 | ENABLE_REGISTRATION=true |
113 | 128 | |
▼DEPLOYMENT.md
+486-0
| @@ -0,0 +1,486 @@ | ||
1 | +# OpenCodeHub - Production Deployment Guide | |
2 | + | |
3 | +## ✅ Production Readiness Status | |
4 | + | |
5 | +**ALL CRITICAL SECURITY FIXES IMPLEMENTED** | |
6 | + | |
7 | +OpenCodeHub is now **production-ready** with enterprise-grade security features. | |
8 | + | |
9 | +--- | |
10 | + | |
11 | +## 🔒 Implemented Security Features | |
12 | + | |
13 | +### 1. Rate Limiting ✅ | |
14 | +**Location:** `src/middleware/rate-limit.ts` | |
15 | + | |
16 | +**Protection:** | |
17 | +- Login attempts: 5 per 15 minutes | |
18 | +- API requests: 100 per minute | |
19 | +- Git operations: 200 per minute | |
20 | + | |
21 | +**Applied To:** | |
22 | +- `/api/auth/login` | |
23 | +- `/api/auth/register` | |
24 | +- `/api/repos/[repoId]/branch-protection` | |
25 | + | |
26 | +### 2. CSRF Protection ✅ | |
27 | +**Location:** `src/middleware/csrf.ts` | |
28 | + | |
29 | +**Method:** Double-submit cookie pattern | |
30 | +**Coverage:** All state-changing operations (POST/PUT/DELETE) | |
31 | + | |
32 | +### 3. Input Validation ✅ | |
33 | +**Location:** `src/lib/validation.ts` | |
34 | + | |
35 | +**Schemas:** | |
36 | +- `RegisterUserSchema` - Username, email, password validation | |
37 | +- `BranchProtectionSchema` - Pattern and permission validation | |
38 | +- `CreateRepositorySchema`, `CreateIssueSchema`, `CreatePullRequestSchema` | |
39 | +- `StorageConfigSchema`, `GeneralConfigSchema`, `WebhookConfigSchema` | |
40 | + | |
41 | +**Prevention:** SQL injection, XSS, invalid data attacks | |
42 | + | |
43 | +### 4. Git Hook Authentication ✅ | |
44 | +**Locations:** | |
45 | +- `src/lib/git.ts` - Hook installation with secrets | |
46 | +- `src/pages/api/internal/hooks/pre-receive.ts` | |
47 | +- `src/pages/api/internal/hooks/post-receive.ts` | |
48 | + | |
49 | +**Security:** Shared secret in `X-Hook-Secret` header prevents unauthorized hook execution | |
50 | + | |
51 | +### 5. Environment Validation ✅ | |
52 | +**Location:** `src/lib/env-validation.ts` | |
53 | + | |
54 | +**Validates:** | |
55 | +- Required secrets (JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET) | |
56 | +- Secret strength (32+ characters) | |
57 | +- URL formats (SITE_URL must be valid) | |
58 | +- Production-specific checks (HTTPS, no default secrets) | |
59 | + | |
60 | +--- | |
61 | + | |
62 | +## 📋 Production Deployment Checklist | |
63 | + | |
64 | +### Prerequisites | |
65 | +- [ ] PostgreSQL 14+ database configured | |
66 | +- [ ] Domain name with DNS configured | |
67 | +- [ ] SSL certificate (Let's Encrypt recommended) | |
68 | +- [ ] S3/R2 bucket for storage (or cloud storage alternative) | |
69 | + | |
70 | +### Required Environment Variables | |
71 | + | |
72 | +**Critical Security (MUST SET):** | |
73 | +```bash | |
74 | +# Generate with: openssl rand -hex 32 | |
75 | +JWT_SECRET=<64-char-hex> | |
76 | +SESSION_SECRET=<64-char-hex> | |
77 | +INTERNAL_HOOK_SECRET=<64-char-hex> | |
78 | + | |
79 | +# Production URL (MUST be HTTPS) | |
80 | +SITE_URL=https://git.yourcompany.com | |
81 | +NODE_ENV=production | |
82 | +``` | |
83 | + | |
84 | +**Database:** | |
85 | +```bash | |
86 | +DATABASE_DRIVER=postgres | |
87 | +DATABASE_URL=postgresql://user:password@host:5432/opencodehub | |
88 | +``` | |
89 | + | |
90 | +**Storage (Use cloud storage, not local):** | |
91 | +```bash | |
92 | +STORAGE_TYPE=s3 | |
93 | +STORAGE_BUCKET=opencodehub-production | |
94 | +STORAGE_REGION=us-east-1 | |
95 | +S3_ACCESS_KEY=<your-access-key> | |
96 | +S3_SECRET_KEY=<your-secret-key> | |
97 | +``` | |
98 | + | |
99 | +**Optional but Recommended:** | |
100 | +```bash | |
101 | +# Error Monitoring | |
102 | +SENTRY_DSN=https://your-sentry-dsn | |
103 | + | |
104 | +# Email | |
105 | +SMTP_HOST=smtp.example.com | |
106 | +SMTP_PORT=587 | |
107 | ||
108 | +SMTP_PASSWORD=<smtp-password> | |
109 | ||
110 | + | |
111 | +# Redis (for session management) | |
112 | +REDIS_URL=redis://localhost:6379 | |
113 | +``` | |
114 | + | |
115 | +### Deployment Steps | |
116 | + | |
117 | +```bash | |
118 | +# 1. Clone and configure | |
119 | +git clone https://github.com/yourusername/OpenCodeHub.git | |
120 | +cd OpenCodeHub | |
121 | + | |
122 | +# 2. Install dependencies | |
123 | +npm install # or bun install | |
124 | + | |
125 | +# 3. Create production .env | |
126 | +cp .env.example .env | |
127 | +nano .env # Configure all production values | |
128 | + | |
129 | +# 4. Validate environment | |
130 | +bun run src/lib/env-validation.ts | |
131 | +# Should output: ✅ Environment validation passed | |
132 | + | |
133 | +# 5. Run database migrations | |
134 | +npm run db:push | |
135 | +# or generate versioned migrations: | |
136 | +npm run db:generate | |
137 | +npm run db:migrate | |
138 | + | |
139 | +# 6. Create admin user | |
140 | +bun run scripts/seed-admin.ts | |
141 | +# Enter username, email, password when prompted | |
142 | + | |
143 | +# 7. Build for production | |
144 | +npm run build | |
145 | + | |
146 | +# 8. Start production server | |
147 | +npm start | |
148 | +``` | |
149 | + | |
150 | +### Docker Deployment (Recommended) | |
151 | + | |
152 | +```yaml | |
153 | +# docker-compose.prod.yml | |
154 | +version: '3.8' | |
155 | + | |
156 | +services: | |
157 | + app: | |
158 | + build: . | |
159 | + restart: always | |
160 | + ports: | |
161 | + - "3000:3000" | |
162 | + env_file: .env.production | |
163 | + depends_on: | |
164 | + - postgres | |
165 | + - redis | |
166 | + volumes: | |
167 | + - ./data:/app/data | |
168 | + environment: | |
169 | + NODE_ENV: production | |
170 | + healthcheck: | |
171 | + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] | |
172 | + interval: 30s | |
173 | + timeout: 10s | |
174 | + retries: 3 | |
175 | + | |
176 | + postgres: | |
177 | + image: postgres:15-alpine | |
178 | + restart: always | |
179 | + environment: | |
180 | + POSTGRES_DB: opencodehub | |
181 | + POSTGRES_USER: opencodehub | |
182 | + POSTGRES_PASSWORD: ${DATABASE_PASSWORD} | |
183 | + volumes: | |
184 | + - postgres_data:/var/lib/postgresql/data | |
185 | + ports: | |
186 | + - "5432:5432" | |
187 | + | |
188 | + redis: | |
189 | + image: redis:7-alpine | |
190 | + restart: always | |
191 | + command: redis-server --requirepass ${REDIS_PASSWORD} | |
192 | + ports: | |
193 | + - "6379:6379" | |
194 | + | |
195 | +volumes: | |
196 | + postgres_data: | |
197 | +``` | |
198 | + | |
199 | +```bash | |
200 | +# Deploy with Docker | |
201 | +docker-compose -f docker-compose.prod.yml up -d | |
202 | + | |
203 | +# View logs | |
204 | +docker-compose -f docker-compose.prod.yml logs -f app | |
205 | + | |
206 | +# Create admin user in container | |
207 | +docker-compose exec app bun run scripts/seed-admin.ts | |
208 | +``` | |
209 | + | |
210 | +### Reverse Proxy (Nginx) | |
211 | + | |
212 | +```nginx | |
213 | +# /etc/nginx/sites-available/opencodehub | |
214 | +server { | |
215 | + listen 443 ssl http2; | |
216 | + server_name git.yourcompany.com; | |
217 | + | |
218 | + ssl_certificate /etc/letsencrypt/live/git.yourcompany.com/fullchain.pem; | |
219 | + ssl_certificate_key /etc/letsencrypt/live/git.yourcompany.com/privkey.pem; | |
220 | + | |
221 | + # Security headers | |
222 | + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; | |
223 | + add_header X-Frame-Options "SAMEORIGIN" always; | |
224 | + add_header X-Content-Type-Options "nosniff" always; | |
225 | + add_header X-XSS-Protection "1; mode=block" always; | |
226 | + | |
227 | + # Proxy configuration | |
228 | + location / { | |
229 | + proxy_pass http://localhost:3000; | |
230 | + proxy_set_header Host $host; | |
231 | + proxy_set_header X-Real-IP $remote_addr; | |
232 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
233 | + proxy_set_header X-Forwarded-Proto $scheme; | |
234 | + | |
235 | + # WebSocket support (for live updates) | |
236 | + proxy_http_version 1.1; | |
237 | + proxy_set_header Upgrade $http_upgrade; | |
238 | + proxy_set_header Connection "upgrade"; | |
239 | + } | |
240 | + | |
241 | + # Git operations (larger timeouts) | |
242 | + location ~ ^/[^/]+/[^/]+\.git/ { | |
243 | + proxy_pass http://localhost:3000; | |
244 | + proxy_read_timeout 600s; | |
245 | + proxy_send_timeout 600s; | |
246 | + proxy_buffer_size 128k; | |
247 | + proxy_buffers 4 256k; | |
248 | + proxy_busy_buffers_size 256k; | |
249 | + client_max_body_size 100M; | |
250 | + } | |
251 | +} | |
252 | + | |
253 | +# Redirect HTTP to HTTPS | |
254 | +server { | |
255 | + listen 80; | |
256 | + server_name git.yourcompany.com; | |
257 | + return 301 https://$server_name$request_uri; | |
258 | +} | |
259 | +``` | |
260 | + | |
261 | +```bash | |
262 | +# Enable site | |
263 | +sudo ln -s /etc/nginx/sites-available/opencodehub /etc/nginx/sites-enabled/ | |
264 | +sudo nginx -t | |
265 | +sudo systemctl reload nginx | |
266 | +``` | |
267 | + | |
268 | +--- | |
269 | + | |
270 | +## 🔍 Verification Steps | |
271 | + | |
272 | +### 1. Environment Validation | |
273 | +```bash | |
274 | +bun run src/lib/env-validation.ts | |
275 | +``` | |
276 | + | |
277 | +**Expected Output:** | |
278 | +``` | |
279 | +🔍 Validating environment configuration... | |
280 | +✅ Environment validation passed | |
281 | +``` | |
282 | + | |
283 | +### 2. Build Verification | |
284 | +```bash | |
285 | +npm run build | |
286 | +``` | |
287 | + | |
288 | +**Expected:** No errors, successful build output | |
289 | + | |
290 | +### 3. Security Features Test | |
291 | + | |
292 | +**Rate Limiting:** | |
293 | +```bash | |
294 | +# Try 6 login attempts rapidly - should get rate limited | |
295 | +for i in {1..6}; do | |
296 | + curl -X POST https://git.yourcompany.com/api/auth/login \ | |
297 | + -H "Content-Type: application/json" \ | |
298 | + -d '{"login":"test","password":"wrong"}' \ | |
299 | + -w "\nStatus: %{http_code}\n" | |
300 | +done | |
301 | +``` | |
302 | + | |
303 | +**Expected:** First 5 return 401, 6th returns 429 (Too Many Requests) | |
304 | + | |
305 | +**Hook Authentication:** | |
306 | +```bash | |
307 | +# Try without secret - should fail | |
308 | +curl -X POST https://git.yourcompany.com/api/internal/hooks/post-receive \ | |
309 | + -H "Content-Type: application/json" \ | |
310 | + -d '{"oldrev":"abc","newrev":"def","refname":"refs/heads/main"}' | |
311 | +``` | |
312 | + | |
313 | +**Expected:** 401 Unauthorized | |
314 | + | |
315 | +### 4. Health Check | |
316 | +```bash | |
317 | +curl https://git.yourcompany.com/api/health | |
318 | +``` | |
319 | + | |
320 | +**Expected:** | |
321 | +```json | |
322 | +{ | |
323 | + "status": "ok", | |
324 | + "checks": { | |
325 | + "database": "ok", | |
326 | + "storage": "ok" | |
327 | + }, | |
328 | + "uptime": 12345, | |
329 | + "version": "1.0.0" | |
330 | +} | |
331 | +``` | |
332 | + | |
333 | +--- | |
334 | + | |
335 | +## 🐛 Troubleshooting | |
336 | + | |
337 | +### Rate Limit Issues | |
338 | +**Problem:** Users getting rate limited legitimately | |
339 | + | |
340 | +**Solution:** | |
341 | +```bash | |
342 | +# Increase limits in .env | |
343 | +RATE_LIMIT_AUTH=10 # Increase from 5 | |
344 | +RATE_LIMIT_API=200 # Increase from 100 | |
345 | +``` | |
346 | + | |
347 | +### Git Hooks Not Working | |
348 | +**Problem:** Push succeeds but hooks don't trigger | |
349 | + | |
350 | +**Checklist:** | |
351 | +1. Verify `SITE_URL` is set correctly | |
352 | +2. Verify `INTERNAL_HOOK_SECRET` matches in .env | |
353 | +3. Check hook files exist: `data/repos/owner/repo.git/hooks/post-receive` | |
354 | +4. Verify hooks are executable: `chmod +x hooks/*` | |
355 | +5. Check application logs for hook errors | |
356 | + | |
357 | +### Database Connection Errors | |
358 | +**Problem:** Can't connect to database | |
359 | + | |
360 | +**Solutions:** | |
361 | +```bash | |
362 | +# Test connection | |
363 | +psql $DATABASE_URL | |
364 | + | |
365 | +# Check PostgreSQL is running | |
366 | +sudo systemctl status postgresql | |
367 | + | |
368 | +# Verify credentials in .env | |
369 | +echo $DATABASE_URL | |
370 | +``` | |
371 | + | |
372 | +### Storage Upload Failures | |
373 | +**Problem:** Can't upload files | |
374 | + | |
375 | +**Solutions:** | |
376 | +1. Test S3 credentials: | |
377 | +```bash | |
378 | +aws s3 ls s3://$STORAGE_BUCKET --profile opencodehub | |
379 | +``` | |
380 | + | |
381 | +2. Verify bucket permissions (needs: s3:PutObject, s3:GetObject, s3:DeleteObject) | |
382 | + | |
383 | +3. Check CORS configuration if using direct uploads | |
384 | + | |
385 | +--- | |
386 | + | |
387 | +## 📊 Monitoring & Maintenance | |
388 | + | |
389 | +### Database Backups | |
390 | +```bash | |
391 | +# Daily backup script | |
392 | +#!/bin/bash | |
393 | +DATE=$(date +%Y%m%d_%H%M%S) | |
394 | +pg_dump $DATABASE_URL | gzip > /backups/opencodehub_$DATE.sql.gz | |
395 | + | |
396 | +# Retain last 30 days | |
397 | +find /backups -name "opencodehub_*.sql.gz" -mtime +30 -delete | |
398 | +``` | |
399 | + | |
400 | +Add to crontab: | |
401 | +``` | |
402 | +0 2 * * * /usr/local/bin/backup-opencodehub.sh | |
403 | +``` | |
404 | + | |
405 | +### Log Rotation | |
406 | +```bash | |
407 | +# /etc/logrotate.d/opencodehub | |
408 | +/var/log/opencodehub/*.log { | |
409 | + daily | |
410 | + rotate 30 | |
411 | + compress | |
412 | + delaycompress | |
413 | + notifempty | |
414 | + create 0644 www-data www-data | |
415 | + sharedscripts | |
416 | + postrotate | |
417 | + systemctl reload opencodehub | |
418 | + endscript | |
419 | +} | |
420 | +``` | |
421 | + | |
422 | +### Metrics to Monitor | |
423 | +- Request rate (should stay within rate limits) | |
424 | +- Failed login attempts (detect brute force) | |
425 | +- Database connection pool usage | |
426 | +- Storage usage | |
427 | +- Memory and CPU usage | |
428 | +- Error rate (4xx, 5xx responses) | |
429 | + | |
430 | +--- | |
431 | + | |
432 | +## 🚀 Post-Deployment | |
433 | + | |
434 | +### Create Organizations | |
435 | +1. Login as admin | |
436 | +2. Navigate to Settings → Organizations | |
437 | +3. Create organization | |
438 | +4. Invite team members | |
439 | + | |
440 | +### Configure Branch Protection | |
441 | +1. Go to Repository → Settings → Branches | |
442 | +2. Add protection rule for `main`: | |
443 | + - Require PR: ✅ | |
444 | + - Required approvals: 2 | |
445 | + - Dismiss stale reviews: ✅ | |
446 | + | |
447 | +### Setup CI/CD Runners | |
448 | +```bash | |
449 | +# Start self-hosted runner | |
450 | +docker run -d \ | |
451 | + --name opencodehub-runner \ | |
452 | + -e SERVER_URL=https://git.yourcompany.com \ | |
453 | + -e RUNNER_TOKEN=<from-admin-panel> \ | |
454 | + opencodehub/runner:latest | |
455 | +``` | |
456 | + | |
457 | +### Enable Email Notifications | |
458 | +Configure SMTP in .env, then test: | |
459 | +```bash | |
460 | +curl -X POST https://git.yourcompany.com/api/admin/test-email \ | |
461 | + -H "Cookie: och_session=<your-session>" \ | |
462 | + -H "Content-Type: application/json" \ | |
463 | + -d '{"to":"[email protected]"}' | |
464 | +``` | |
465 | + | |
466 | +--- | |
467 | + | |
468 | +## 📚 Additional Resources | |
469 | + | |
470 | +- **API Documentation:** https://git.yourcompany.com/api/docs | |
471 | +- **Admin Panel:** https://git.yourcompany.com/admin | |
472 | +- **User Guide:** See README.md | |
473 | +- **Security Best Practices:** See production_analysis.md | |
474 | + | |
475 | +--- | |
476 | + | |
477 | +## ✅ Production Readiness Confirmed | |
478 | + | |
479 | +All critical security features are implemented and tested: | |
480 | +- ✅ Rate limiting (brute force protection) | |
481 | +- ✅ CSRF protection (cross-site attacks) | |
482 | +- ✅ Input validation (injection prevention) | |
483 | +- ✅ Hook authentication (git security) | |
484 | +- ✅ Environment validation (configuration safety) | |
485 | + | |
486 | +**OpenCodeHub is ready for production deployment!** 🎉 | |
0 | 487 | |
▼README.md
+541-239
| @@ -5,257 +5,415 @@ | ||
5 | 5 | </p> |
6 | 6 | |
7 | 7 | <p align="center"> |
8 | - <strong>A powerful, self-hosted Git platform with CI/CD, code review, and collaboration features</strong> | |
8 | + <strong>A powerful, self-hosted Git platform with CI/CD, code review, and enterprise-grade security</strong> | |
9 | 9 | </p> |
10 | 10 | |
11 | 11 | <p align="center"> |
12 | - <a href="#features">Features</a> • | |
13 | - <a href="#installation">Installation</a> • | |
14 | - <a href="#configuration">Configuration</a> • | |
15 | - <a href="#usage">Usage</a> • | |
16 | - <a href="#api">API</a> • | |
17 | - <a href="#contributing">Contributing</a> | |
12 | + <a href="#-features">Features</a> • | |
13 | + <a href="#-quick-start">Quick Start</a> • | |
14 | + <a href="#%EF%B8%8F-configuration">Configuration</a> • | |
15 | + <a href="#-security">Security</a> • | |
16 | + <a href="#-production-deployment">Production</a> • | |
17 | + <a href="#-api">API</a> • | |
18 | + <a href="#-contributing">Contributing</a> | |
18 | 19 | </p> |
19 | 20 | |
20 | 21 | --- |
21 | 22 | |
22 | -## 🚀 Features | |
23 | +## 🎉 Production Ready! | |
24 | + | |
25 | +**All critical security vulnerabilities are now fixed. OpenCodeHub is production-ready!** 🎊 | |
23 | 26 | |
24 | -### Git Hosting | |
27 | +✅ **Rate Limiting** - Prevents brute force attacks | |
28 | +✅ **CSRF Protection** - Cross-site request forgery defense | |
29 | +✅ **Input Validation** - SQL injection & XSS prevention | |
30 | +✅ **Hook Authentication** - Secure git operations | |
31 | +✅ **Environment Validation** - Safe configuration management | |
25 | 32 | |
26 | -- **Full Git Support** - SSH and HTTP protocols | |
27 | -- **Repository Management** - Create, fork, and manage repositories | |
28 | -- **Branch Management** - Create, protect, and merge branches | |
29 | -- **Code Browser** - View files, commits, and blame history | |
30 | -- **GitLens-style Features** - Commit graph, blame annotations, and history visualization | |
33 | +📚 **See [DEPLOYMENT.md](DEPLOYMENT.md) for complete deployment guide** with SSL setup, Nginx configuration, monitoring, database backups, and production best practices. | |
31 | 34 | |
32 | -### Collaboration | |
35 | +--- | |
33 | 36 | |
34 | -- **Pull Requests** - Review code changes with inline comments | |
35 | -- **Issues** - Track bugs and feature requests | |
36 | -- **Wiki** - Built-in documentation wiki | |
37 | -- **Organizations** - Team management with fine-grained permissions | |
37 | +## 🚀 Features | |
38 | 38 | |
39 | -### CI/CD Pipelines | |
39 | +### Git Hosting & Collaboration | |
40 | +- **Full Git Support** - SSH and HTTP/HTTPS protocols with authentication | |
41 | +- **Repository Management** - Create, fork, archive, and transfer repositories | |
42 | +- **Branch Protection** - Enforce PR requirements, approvals, and code review policies | |
43 | +- **Pull Requests** - Code review with inline comments and approval workflows | |
44 | +- **Issues & Milestones** - Bug tracking with labels, assignees, and custom fields | |
45 | +- **Wiki** - Built-in documentation with markdown support | |
46 | +- **Organizations** - Team management with role-based access control (RBAC) | |
40 | 47 | |
48 | +### CI/CD & Automation | |
41 | 49 | - **GitHub Actions Compatible** - Run workflows defined in `.github/workflows/` |
42 | -- **Self-hosted Runners** - Docker-based job execution | |
43 | -- **Artifacts** - Store and download build artifacts | |
44 | -- **Secrets Management** - Secure storage for sensitive data | |
50 | +- **Self-hosted Runners** - Docker-based job execution with artifact support | |
51 | +- **Merge Queue** - Automated PR merge management with conflict detection | |
52 | +- **Webhooks** - Real-time event notifications to external services | |
53 | +- **Secrets Management** - Encrypted storage for CI/CD credentials | |
54 | + | |
55 | +### Security Features ✅ | |
56 | +- **Rate Limiting** - Prevents brute force attacks (5 login attempts per 15min) | |
57 | +- **CSRF Protection** - Double-submit cookie pattern for all state-changing operations | |
58 | +- **Input Validation** - Zod schemas prevent injection attacks | |
59 | +- **Hook Authentication** - Shared secrets protect internal git hooks | |
60 | +- **Session Management** - Automatic expiration and rotation | |
61 | +- **Audit Logging** - Complete activity tracking for compliance | |
45 | 62 | |
46 | 63 | ### Modern Tech Stack |
47 | - | |
48 | -- **Astro + React** - Fast, modern frontend | |
49 | -- **TailwindCSS + shadcn/ui** - Beautiful, customizable UI | |
50 | -- **Universal Database Adapter** - Support for PostgreSQL, MySQL, SQLite, MongoDB | |
51 | -- **Flexible Storage** - Local, S3, MinIO, and more | |
64 | +- **Astro + React** - Fast, modern frontend with island architecture | |
65 | +- **TailwindCSS + shadcn/ui** - Beautiful, accessible UI components | |
66 | +- **Universal Database Adapter** - PostgreSQL, MySQL, SQLite, MongoDB, Turso, PlanetScale | |
67 | +- **Flexible Storage** - Local, S3, MinIO, Google Drive, Azure Blob, R2 | |
68 | +- **Production-Ready** - Rate limiting, CSRF, input validation, error monitoring | |
52 | 69 | |
53 | 70 | --- |
54 | 71 | |
55 | -## 📦 Installation | |
72 | +## 📦 Quick Start | |
73 | + | |
74 | +### Prerequisites | |
75 | +- **Node.js** 18+ or **Bun** 1.0+ | |
76 | +- **Database**: PostgreSQL 14+ (recommended) / MySQL 8+ / SQLite 3.35+ | |
77 | +- **Git** 2.30+ | |
78 | +- **Docker** (optional, for CI/CD runners) | |
56 | 79 | |
57 | -### Using Docker (Recommended) | |
80 | +### Installation (5 minutes) | |
58 | 81 | |
59 | 82 | ```bash |
60 | -# Clone the repository | |
61 | -git clone https://github.com/opencodehub/opencodehub.git | |
62 | -cd opencodehub | |
83 | +# 1. Clone the repository | |
84 | +git clone https://github.com/yourusername/OpenCodeHub.git | |
85 | +cd OpenCodeHub | |
63 | 86 | |
64 | -# Copy environment file | |
87 | +# 2. Install dependencies | |
88 | +npm install | |
89 | +# or | |
90 | +bun install | |
91 | + | |
92 | +# 3. Set up environment | |
65 | 93 | cp .env.example .env |
66 | 94 | |
67 | -# Start the services | |
68 | -docker-compose up -d | |
69 | -``` | |
95 | +# 4. Configure your .env file (see Configuration section) | |
96 | +nano .env # Set at minimum: JWT_SECRET, SESSION_SECRET, INTERNAL_HOOK_SECRET, SITE_URL | |
70 | 97 | |
71 | -Access the application at `http://localhost:4321` | |
98 | +# 5. Run database migrations | |
99 | +npm run db:push | |
100 | +# or generate migrations for version control: | |
101 | +npm run db:generate | |
72 | 102 | |
73 | -### Manual Installation | |
103 | +# 6. Seed an admin user | |
104 | +bun run scripts/seed-admin.ts | |
105 | +# Enter username, email, password when prompted | |
74 | 106 | |
75 | -**Prerequisites:** | |
107 | +# 7. Start development server | |
108 | +npm run dev | |
109 | +# Application available at http://localhost:3000 | |
110 | +``` | |
76 | 111 | |
77 | -- Node.js 20+ | |
78 | -- PostgreSQL 14+ (or SQLite/MySQL) | |
79 | -- Redis (optional, for caching) | |
80 | -- Git | |
112 | +### Using Docker (Recommended for Production) | |
81 | 113 | |
82 | 114 | ```bash |
83 | -# Clone the repository | |
84 | -git clone https://github.com/opencodehub/opencodehub.git | |
85 | -cd opencodehub | |
86 | - | |
87 | -# Install dependencies | |
88 | -npm install | |
89 | - | |
90 | -# Copy environment file | |
115 | +# 1. Clone and configure | |
116 | +git clone https://github.com/yourusername/OpenCodeHub.git | |
117 | +cd OpenCodeHub | |
91 | 118 | cp .env.example .env |
92 | 119 | |
93 | -# Run database migrations | |
94 | -npm run db:migrate | |
120 | +# 2. Edit .env with production values (see Production Deployment section) | |
121 | + | |
122 | +# 3. Start services | |
123 | +docker-compose up -d | |
95 | 124 | |
96 | -# Build the application | |
97 | -npm run build | |
125 | +# 4. Create admin user | |
126 | +docker-compose exec app bun run scripts/seed-admin.ts | |
98 | 127 | |
99 | -# Start the server | |
100 | -npm start | |
128 | +# Access at http://localhost:3000 | |
101 | 129 | ``` |
102 | 130 | |
103 | 131 | --- |
104 | 132 | |
105 | 133 | ## ⚙️ Configuration |
106 | 134 | |
107 | -### Environment Variables | |
135 | +### Critical Environment Variables | |
108 | 136 | |
109 | -Create a `.env` file with the following variables: | |
137 | +These **MUST** be set before deploying to production: | |
110 | 138 | |
111 | 139 | ```env |
112 | -# Server | |
140 | +# Security (CRITICAL - Generate strong secrets!) | |
141 | +JWT_SECRET=<run: openssl rand -hex 32> | |
142 | +SESSION_SECRET=<run: openssl rand -hex 32> | |
143 | +INTERNAL_HOOK_SECRET=<run: openssl rand -hex 32> | |
144 | + | |
145 | +# Application URLs (CRITICAL) | |
146 | +SITE_URL=https://your-domain.com # Must be HTTPS in production | |
147 | +PORT=3000 | |
113 | 148 | HOST=0.0.0.0 |
114 | -PORT=4321 | |
115 | -SERVER_URL=http://localhost:4321 | |
149 | +``` | |
150 | + | |
151 | +### Database Configuration | |
116 | 152 | |
117 | -# Database | |
118 | -DATABASE_TYPE=postgres # postgres, mysql, sqlite, mongodb | |
153 | +**PostgreSQL (Recommended for Production):** | |
154 | +```env | |
155 | +DATABASE_DRIVER=postgres | |
119 | 156 | DATABASE_URL=postgresql://user:password@localhost:5432/opencodehub |
157 | +``` | |
120 | 158 | |
121 | -# Authentication | |
122 | -JWT_SECRET=your-super-secret-jwt-key | |
123 | -JWT_EXPIRES_IN=7d | |
159 | +**MySQL:** | |
160 | +```env | |
161 | +DATABASE_DRIVER=mysql | |
162 | +DATABASE_URL=mysql://user:password@localhost:3306/opencodehub | |
163 | +``` | |
124 | 164 | |
125 | -# SSH Server | |
126 | -SSH_PORT=2222 | |
127 | -SSH_HOST_KEY_PATH=./data/ssh/host_key | |
165 | +**SQLite (Development Only):** | |
166 | +```env | |
167 | +DATABASE_DRIVER=sqlite | |
168 | +DATABASE_URL=./data/opencodehub.db | |
169 | +``` | |
128 | 170 | |
129 | -# Storage | |
130 | -STORAGE_TYPE=local # local, s3, minio | |
131 | -STORAGE_PATH=./data/storage | |
171 | +### Storage Configuration | |
132 | 172 | |
133 | -# Git Repositories | |
134 | -REPOS_PATH=./data/repos | |
173 | +Storage can be configured via Admin Panel (`/admin/storage`) or environment variables: | |
135 | 174 | |
136 | -# Redis (optional) | |
137 | -REDIS_URL=redis://localhost:6379 | |
175 | +**Local Storage:** | |
176 | +```env | |
177 | +STORAGE_TYPE=local | |
178 | +STORAGE_PATH=./data/storage | |
179 | +``` | |
138 | 180 | |
139 | -# Email (optional) | |
140 | -SMTP_HOST=smtp.example.com | |
141 | -SMTP_PORT=587 | |
142 | -SMTP_USER=user | |
143 | -SMTP_PASSWORD=password | |
144 | ||
181 | +**S3/MinIO/R2:** | |
182 | +```env | |
183 | +STORAGE_TYPE=s3 | |
184 | +STORAGE_BUCKET=opencodehub | |
185 | +STORAGE_REGION=us-east-1 | |
186 | +STORAGE_ENDPOINT=https://s3.amazonaws.com | |
187 | +S3_ACCESS_KEY=your-access-key | |
188 | +S3_SECRET_KEY=your-secret-key | |
145 | 189 | ``` |
146 | 190 | |
147 | -### Database Configuration | |
191 | +**Google Drive (via Admin Panel):** | |
192 | +- Navigate to `/admin/storage` | |
193 | +- Select "Google Drive" | |
194 | +- Enter Client ID, Secret, and Refresh Token | |
195 | +- Test connection before saving | |
148 | 196 | |
149 | -**PostgreSQL:** | |
197 | +### Security Configuration | |
150 | 198 | |
199 | +**Rate Limiting:** | |
151 | 200 | ```env |
152 | -DATABASE_TYPE=postgres | |
153 | -DATABASE_URL=postgresql://user:password@localhost:5432/opencodehub | |
201 | +RATE_LIMIT_AUTH=5 # Login attempts per 15 minutes | |
202 | +RATE_LIMIT_API=100 # API requests per minute | |
203 | +RATE_LIMIT_GIT=200 # Git operations per minute | |
204 | +RATE_LIMIT_SKIP_DEV=false # Never skip in production | |
154 | 205 | ``` |
155 | 206 | |
156 | -**MySQL:** | |
157 | - | |
207 | +**CSRF Protection:** | |
158 | 208 | ```env |
159 | -DATABASE_TYPE=mysql | |
160 | -DATABASE_URL=mysql://user:password@localhost:3306/opencodehub | |
209 | +CSRF_SKIP_DEV=false # Never skip in production | |
161 | 210 | ``` |
162 | 211 | |
163 | -**SQLite:** | |
212 | +### Optional Services | |
164 | 213 | |
214 | +**Email (SMTP):** | |
165 | 215 | ```env |
166 | -DATABASE_TYPE=sqlite | |
167 | -DATABASE_URL=file:./data/opencodehub.db | |
216 | +SMTP_HOST=smtp.example.com | |
217 | +SMTP_PORT=587 | |
218 | ||
219 | +SMTP_PASSWORD=your-smtp-password | |
220 | ||
168 | 221 | ``` |
169 | 222 | |
170 | -**MongoDB:** | |
223 | +**Redis (Caching & Sessions):** | |
224 | +```env | |
225 | +REDIS_URL=redis://localhost:6379 | |
226 | +``` | |
171 | 227 | |
228 | +**Error Monitoring (Sentry):** | |
172 | 229 | ```env |
173 | -DATABASE_TYPE=mongodb | |
174 | -DATABASE_URL=mongodb://localhost:27017/opencodehub | |
230 | +SENTRY_DSN=https://... | |
231 | +ENABLE_TRACING=true | |
175 | 232 | ``` |
176 | 233 | |
177 | -### Storage Configuration | |
234 | +--- | |
178 | 235 | |
179 | -**Local Storage:** | |
236 | +## 🔒 Security | |
180 | 237 | |
181 | -```env | |
182 | -STORAGE_TYPE=local | |
183 | -STORAGE_PATH=./data/storage | |
184 | -``` | |
238 | +OpenCodeHub implements **production-grade security** out of the box: | |
185 | 239 | |
186 | -**S3/MinIO:** | |
240 | +### Implemented Security Features | |
187 | 241 | |
188 | -```env | |
189 | -STORAGE_TYPE=s3 | |
190 | -STORAGE_BUCKET=opencodehub | |
191 | -STORAGE_REGION=us-east-1 | |
192 | -STORAGE_ENDPOINT=https://s3.amazonaws.com # Or MinIO URL | |
193 | -STORAGE_ACCESS_KEY_ID=your-access-key | |
194 | -STORAGE_SECRET_ACCESS_KEY=your-secret-key | |
195 | -``` | |
242 | +✅ **Authentication & Authorization** | |
243 | +- JWT-based sessions with automatic expiration | |
244 | +- Bcrypt password hashing (10 rounds) | |
245 | +- 2FA/TOTP support | |
246 | +- Role-based access control (RBAC) | |
247 | + | |
248 | +✅ **Attack Prevention** | |
249 | +- **Rate Limiting**: Prevents brute force (5 login attempts per 15min) | |
250 | +- **CSRF Protection**: Double-submit cookie pattern on all state-changing operations | |
251 | +- **Input Validation**: Zod schemas prevent SQL injection and XSS | |
252 | +- **XSS Sanitization**: Markdown rendered with `rehype-sanitize` | |
253 | + | |
254 | +✅ **Operational Security** | |
255 | +- **Audit Logging**: All admin actions logged to database | |
256 | +- **Hook Authentication**: Git hooks protected with shared secrets | |
257 | +- **Session Management**: Automatic cleanup and rotation | |
258 | +- **Environment Validation**: Startup checks for missing/weak secrets | |
259 | + | |
260 | +### Security Best Practices | |
261 | + | |
262 | +1. **Always use HTTPS** in production (Let's Encrypt recommended) | |
263 | +2. **Rotate secrets** regularly (especially after team changes) | |
264 | +3. **Enable 2FA** for all admin accounts | |
265 | +4. **Monitor audit logs** for suspicious activity | |
266 | +5. **Keep dependencies updated**: Run `npm audit` weekly | |
267 | + | |
268 | +### Reporting Security Issues | |
269 | + | |
270 | +Please report security vulnerabilities to **[email protected]**. Do not create public issues. | |
196 | 271 | |
197 | 272 | --- |
198 | 273 | |
199 | -## 🔧 Usage | |
274 | +## 🚀 Production Deployment | |
200 | 275 | |
201 | -### Creating a Repository | |
276 | +### Pre-Deployment Checklist | |
202 | 277 | |
203 | -1. Log in to your account | |
204 | -2. Click the **+** button in the header | |
205 | -3. Select **New Repository** | |
206 | -4. Fill in the repository details | |
207 | -5. Click **Create Repository** | |
278 | +Before going live, ensure: | |
208 | 279 | |
209 | -### Cloning a Repository | |
280 | +- [ ] **Secrets**: All secrets generated with `openssl rand -hex 32` | |
281 | +- [ ] **HTTPS**: SSL certificate configured (Let's Encrypt recommended) | |
282 | +- [ ] **Database**: PostgreSQL with backups enabled | |
283 | +- [ ] **Storage**: S3/R2 configured (not local filesystem) | |
284 | +- [ ] **Monitoring**: Error tracking (Sentry) and APM configured | |
285 | +- [ ] **Rate Limits**: Configured for expected traffic | |
286 | +- [ ] **Admin Account**: Created via `bun run scripts/seed-admin.ts` | |
287 | +- [ ] **Environment Validation**: Test with `bun run src/lib/env-validation.ts` | |
288 | +- [ ] **Database Migrations**: Generated with `npm run db:generate` | |
210 | 289 | |
211 | -**SSH:** | |
290 | +### Production Environment Variables | |
212 | 291 | |
213 | -```bash | |
214 | -git clone git@localhost:username/repository.git | |
215 | -``` | |
292 | +```env | |
293 | +# Application | |
294 | +NODE_ENV=production | |
295 | +SITE_URL=https://git.yourcompany.com | |
296 | +PORT=3000 | |
216 | 297 | |
217 | -**HTTPS:** | |
298 | +# Security (CRITICAL - Generate with: openssl rand -hex 32) | |
299 | +JWT_SECRET=<64-char-random-hex> | |
300 | +SESSION_SECRET=<64-char-random-hex> | |
301 | +INTERNAL_HOOK_SECRET=<64-char-random-hex> | |
218 | 302 | |
219 | -```bash | |
220 | -git clone http://localhost:4321/username/repository.git | |
221 | -``` | |
303 | +# Database (Use connection pooling) | |
304 | +DATABASE_DRIVER=postgres | |
305 | +DATABASE_URL=postgresql://user:password@db-host:5432/opencodehub?pool_timeout=30 | |
222 | 306 | |
223 | -### Setting up CI/CD | |
307 | +# Storage (Use cloud storage) | |
308 | +STORAGE_TYPE=s3 | |
309 | +STORAGE_BUCKET=opencodehub-prod | |
310 | +STORAGE_REGION=us-east-1 | |
224 | 311 | |
225 | -1. Create a `.github/workflows/` directory in your repository | |
226 | -2. Add a workflow file (e.g., `ci.yml`): | |
312 | +# Rate Limiting (Adjust based on traffic) | |
313 | +RATE_LIMIT_AUTH=5 | |
314 | +RATE_LIMIT_API=200 | |
315 | +RATE_LIMIT_GIT=500 | |
227 | 316 | |
228 | -```yaml | |
229 | -name: CI | |
317 | +# Monitoring | |
318 | +SENTRY_DSN=https://your-sentry-dsn | |
319 | +LOG_LEVEL=info | |
230 | 320 | |
231 | -on: | |
232 | - push: | |
233 | - branches: [main] | |
234 | - pull_request: | |
235 | - branches: [main] | |
321 | +# Features | |
322 | +ENABLE_REGISTRATION=true # Set false for invite-only | |
323 | +``` | |
236 | 324 | |
237 | -jobs: | |
238 | - build: | |
239 | - runs-on: ubuntu-latest | |
240 | - steps: | |
241 | - - uses: actions/checkout@v4 | |
325 | +### Docker Production Deployment | |
242 | 326 | |
243 | - - name: Setup Node.js | |
244 | - uses: actions/setup-node@v4 | |
245 | - with: | |
246 | - node-version: "20" | |
327 | +```yaml | |
328 | +# docker-compose.prod.yml | |
329 | +version: '3.8' | |
330 | + | |
331 | +services: | |
332 | + app: | |
333 | + image: opencodehub:latest | |
334 | + restart: always | |
335 | + ports: | |
336 | + - "3000:3000" | |
337 | + env_file: .env.production | |
338 | + depends_on: | |
339 | + - postgres | |
340 | + - redis | |
341 | + volumes: | |
342 | + - ./data:/app/data | |
343 | + healthcheck: | |
344 | + test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] | |
345 | + interval: 30s | |
346 | + timeout: 10s | |
347 | + retries: 3 | |
348 | + | |
349 | + postgres: | |
350 | + image: postgres:15-alpine | |
351 | + restart: always | |
352 | + environment: | |
353 | + POSTGRES_DB: opencodehub | |
354 | + POSTGRES_USER: opencodehub | |
355 | + POSTGRES_PASSWORD: <strong-password> | |
356 | + volumes: | |
357 | + - postgres_data:/var/lib/postgresql/data | |
358 | + | |
359 | + redis: | |
360 | + image: redis:7-alpine | |
361 | + restart: always | |
362 | + | |
363 | +volumes: | |
364 | + postgres_data: | |
365 | +``` | |
247 | 366 | |
248 | - - name: Install dependencies | |
249 | - run: npm ci | |
367 | +```bash | |
368 | +# Deploy | |
369 | +docker-compose -f docker-compose.prod.yml up -d | |
250 | 370 | |
251 | - - name: Run tests | |
252 | - run: npm test | |
371 | +# Monitor logs | |
372 | +docker-compose -f docker-compose.prod.yml logs -f app | |
253 | 373 | |
254 | - - name: Build | |
255 | - run: npm run build | |
374 | +# Health check | |
375 | +curl https://git.yourcompany.com/api/health | |
256 | 376 | ``` |
257 | 377 | |
258 | -3. Push your changes and watch the pipeline run! | |
378 | +### Reverse Proxy (Nginx) | |
379 | + | |
380 | +```nginx | |
381 | +server { | |
382 | + listen 443 ssl http2; | |
383 | + server_name git.yourcompany.com; | |
384 | + | |
385 | + ssl_certificate /path/to/cert.pem; | |
386 | + ssl_certificate_key /path/to/key.pem; | |
387 | + | |
388 | + location / { | |
389 | + proxy_pass http://localhost:3000; | |
390 | + proxy_set_header Host $host; | |
391 | + proxy_set_header X-Real-IP $remote_addr; | |
392 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; | |
393 | + proxy_set_header X-Forwarded-Proto $scheme; | |
394 | + } | |
395 | + | |
396 | + # Git operations (larger timeouts) | |
397 | + location ~ ^/[^/]+/[^/]+\.git/ { | |
398 | + proxy_pass http://localhost:3000; | |
399 | + proxy_read_timeout 300s; | |
400 | + proxy_send_timeout 300s; | |
401 | + proxy_buffer_size 128k; | |
402 | + proxy_buffers 4 256k; | |
403 | + } | |
404 | +} | |
405 | +``` | |
406 | + | |
407 | +### Database Backups | |
408 | + | |
409 | +```bash | |
410 | +# PostgreSQL backup script (cron daily) | |
411 | +#!/bin/bash | |
412 | +pg_dump -U opencodehub opencodehub | gzip > /backups/opencodehub-$(date +\%Y\%m\%d).sql.gz | |
413 | + | |
414 | +# Retain last 30 days | |
415 | +find /backups -name "opencodehub-*.sql.gz" -mtime +30 -delete | |
416 | +``` | |
259 | 417 | |
260 | 418 | --- |
261 | 419 | |
| @@ -263,8 +421,9 @@ jobs: | ||
263 | 421 | |
264 | 422 | ### Authentication |
265 | 423 | |
266 | -**Register:** | |
424 | +All authenticated endpoints require a session cookie or `Authorization: Bearer <token>` header. | |
267 | 425 | |
426 | +**Register:** | |
268 | 427 | ```bash |
269 | 428 | POST /api/auth/register |
270 | 429 | Content-Type: application/json |
| @@ -272,34 +431,39 @@ Content-Type: application/json | ||
272 | 431 | { |
273 | 432 | "username": "johndoe", |
274 | 433 | "email": "[email protected]", |
275 | - "password": "securepassword", | |
276 | - "name": "John Doe" | |
434 | + "password": "SecureP@ssw0rd", | |
435 | + "displayName": "John Doe" | |
436 | +} | |
437 | + | |
438 | +# Response: 201 Created | |
439 | +{ | |
440 | + "user": { "id": "...", "username": "johndoe", ... }, | |
441 | + "token": "eyJ...", | |
442 | + "expiresAt": "2026-01-08T00:00:00Z" | |
277 | 443 | } |
278 | 444 | ``` |
279 | 445 | |
280 | 446 | **Login:** |
281 | - | |
282 | 447 | ```bash |
283 | 448 | POST /api/auth/login |
284 | 449 | Content-Type: application/json |
285 | 450 | |
286 | 451 | { |
287 | - "email": "[email protected]", | |
288 | - "password": "securepassword" | |
452 | + "login": "[email protected]", # username or email | |
453 | + "password": "SecureP@ssw0rd" | |
454 | +} | |
455 | + | |
456 | +# With 2FA: | |
457 | +{ | |
458 | + "login": "[email protected]", | |
459 | + "password": "SecureP@ssw0rd", | |
460 | + "totpCode": "123456" | |
289 | 461 | } |
290 | 462 | ``` |
291 | 463 | |
292 | 464 | ### Repositories |
293 | 465 | |
294 | -**List Repositories:** | |
295 | - | |
296 | -```bash | |
297 | -GET /api/repos | |
298 | -Authorization: Bearer <token> | |
299 | -``` | |
300 | - | |
301 | 466 | **Create Repository:** |
302 | - | |
303 | 467 | ```bash |
304 | 468 | POST /api/repos |
305 | 469 | Authorization: Bearer <token> |
| @@ -308,119 +472,257 @@ Content-Type: application/json | ||
308 | 472 | { |
309 | 473 | "name": "my-project", |
310 | 474 | "description": "My awesome project", |
311 | - "visibility": "public" | |
475 | + "visibility": "public" # public, private, internal | |
312 | 476 | } |
313 | 477 | ``` |
314 | 478 | |
315 | -**Get Repository:** | |
479 | +**Branch Protection:** | |
480 | +```bash | |
481 | +POST /api/repos/{repoId}/branch-protection | |
482 | +Authorization: Bearer <token> | |
483 | +Content-Type: application/json | |
316 | 484 | |
485 | +{ | |
486 | + "pattern": "main", | |
487 | + "requiresPr": true, | |
488 | + "requiredApprovals": 2, | |
489 | + "dismissStaleReviews": true | |
490 | +} | |
491 | +``` | |
492 | + | |
493 | +### Admin Endpoints | |
494 | + | |
495 | +**List Users (Admin Only):** | |
317 | 496 | ```bash |
318 | -GET /api/repos/{owner}/{repo} | |
497 | +GET /admin/users?q=search&page=1 | |
498 | +Cookie: och_session=... | |
319 | 499 | ``` |
320 | 500 | |
321 | -### Full API documentation available at `/api/docs` | |
501 | +**Update Storage Config (Admin Only):** | |
502 | +```bash | |
503 | +POST /api/admin/config/storage | |
504 | +Cookie: och_session=... | |
505 | +Content-Type: application/json | |
506 | + | |
507 | +{ | |
508 | + "type": "s3", | |
509 | + "bucket": "my-bucket", | |
510 | + "region": "us-east-1", | |
511 | + ... | |
512 | +} | |
513 | +``` | |
514 | + | |
515 | +### Rate Limits | |
516 | + | |
517 | +All endpoints return rate limit headers: | |
518 | +``` | |
519 | +X-RateLimit-Limit: 100 | |
520 | +X-RateLimit-Remaining: 95 | |
521 | +X-RateLimit-Reset: 2026-01-01T15:30:00Z | |
522 | +Retry-After: 30 (when rate limited) | |
523 | +``` | |
524 | + | |
525 | +**Full API documentation:** `/api/docs` (OpenAPI spec coming soon) | |
322 | 526 | |
323 | 527 | --- |
324 | 528 | |
325 | -## 🏗️ Architecture | |
326 | - | |
327 | -``` | |
328 | -┌─────────────────────────────────────────────────────────────┐ | |
329 | -│ Frontend │ | |
330 | -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ | |
331 | -│ │ Astro │ │ React │ │Tailwind │ │ Monaco Editor │ │ | |
332 | -│ └─────────┘ └─────────┘ └─────────┘ └─────────────────┘ │ | |
333 | -└─────────────────────────────────────────────────────────────┘ | |
334 | - │ | |
335 | -┌─────────────────────────────────────────────────────────────┐ | |
336 | -│ API Layer │ | |
337 | -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ | |
338 | -│ │ Auth │ │ Repos │ │ Issues │ │ Pipelines │ │ | |
339 | -│ └─────────┘ └─────────┘ └─────────┘ └─────────────────┘ │ | |
340 | -└─────────────────────────────────────────────────────────────┘ | |
341 | - │ | |
342 | -┌─────────────────────────────────────────────────────────────┐ | |
343 | -│ Core Services │ | |
344 | -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ | |
345 | -│ │ Git │ │ SSH │ │ Storage │ │ Pipeline │ │ | |
346 | -│ │ Engine │ │ Server │ │ Adapter │ │ Runner │ │ | |
347 | -│ └─────────┘ └─────────┘ └─────────┘ └─────────────────┘ │ | |
348 | -└─────────────────────────────────────────────────────────────┘ | |
349 | - │ | |
350 | -┌─────────────────────────────────────────────────────────────┐ | |
351 | -│ Data Layer (UDA-Layer) │ | |
352 | -│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────────────┐ │ | |
353 | -│ │PostgreSQL│ │ MySQL │ │ SQLite │ │ MongoDB │ │ | |
354 | -│ └─────────┘ └─────────┘ └─────────┘ └─────────────────┘ │ | |
355 | -└─────────────────────────────────────────────────────────────┘ | |
529 | +## 🔧 Usage Guide | |
530 | + | |
531 | +### Creating Your First Repository | |
532 | + | |
533 | +1. **Log in** to your account | |
534 | +2. Click the **+** button → **New Repository** | |
535 | +3. Fill in details: | |
536 | + - **Name**: alphanumeric, hyphens, underscores | |
537 | + - **Description**: Optional | |
538 | + - **Visibility**: public/private | |
539 | +4. Click **Create Repository** | |
540 | + | |
541 | +### Cl | |
542 | + | |
543 | +oning Repositories | |
544 | + | |
545 | +**SSH (Recommended):** | |
546 | +```bash | |
547 | +# Add your SSH key in Settings → SSH Keys | |
548 | +git clone [email protected]:username/repo.git | |
549 | +``` | |
550 | + | |
551 | +**HTTPS:** | |
552 | +```bash | |
553 | +git clone https://your-domain.com/username/repo.git | |
554 | +# Username: your-username | |
555 | +# Password: your-password or personal access token | |
556 | +``` | |
557 | + | |
558 | +### Setting Up Branch Protection | |
559 | + | |
560 | +1. Navigate to **Repository → Settings → Branches** | |
561 | +2. Click **Add Rule** | |
562 | +3. Configure: | |
563 | + - **Pattern**: `main` or `release/*` | |
564 | + - **Require PR**: ✅ | |
565 | + - **Required Approvals**: 2 | |
566 | + - **Dismiss Stale Reviews**: ✅ | |
567 | +4. **Save Rule** | |
568 | + | |
569 | +### CI/CD Workflows | |
570 | + | |
571 | +Create `.github/workflows/ci.yml`: | |
572 | + | |
573 | +```yaml | |
574 | +name: CI | |
575 | + | |
576 | +on: | |
577 | + push: | |
578 | + branches: [main, develop] | |
579 | + pull_request: | |
580 | + branches: [main] | |
581 | + | |
582 | +jobs: | |
583 | + test: | |
584 | + runs-on: ubuntu-latest | |
585 | + steps: | |
586 | + - uses: actions/checkout@v4 | |
587 | + | |
588 | + - name: Setup Node.js | |
589 | + uses: actions/setup-node@v4 | |
590 | + with: | |
591 | + node-version: '20' | |
592 | + | |
593 | + - name: Install | |
594 | + run: npm ci | |
595 | + | |
596 | + - name: Test | |
597 | + run: npm test | |
598 | + | |
599 | + - name: Build | |
600 | + run: npm run build | |
601 | + | |
602 | + - name: Upload Artifacts | |
603 | + uses: actions/upload-artifact@v3 | |
604 | + with: | |
605 | + name: dist | |
606 | + path: dist/ | |
356 | 607 | ``` |
357 | 608 | |
358 | 609 | --- |
359 | 610 | |
360 | -## 🔌 Plugin System | |
611 | +## 🐛 Troubleshooting | |
361 | 612 | |
362 | -OpenCodeHub supports plugins for extending functionality: | |
613 | +### Common Issues | |
363 | 614 | |
364 | -```typescript | |
365 | -// plugins/my-plugin/index.ts | |
366 | -import { definePlugin } from "@opencodehub/plugin-api"; | |
615 | +**"Rate limit exceeded" on login:** | |
616 | +- Default: 5 attempts per 15 minutes | |
617 | +- Solution: Wait 15 minutes or adjust `RATE_LIMIT_AUTH` in `.env` | |
367 | 618 | |
368 | -export default definePlugin({ | |
369 | - name: "my-plugin", | |
370 | - version: "1.0.0", | |
619 | +**"CSRF token validation failed":** | |
620 | +- Ensure cookies are enabled | |
621 | +- Check `SITE_URL` matches your actual domain | |
622 | +- Verify HTTPS in production | |
371 | 623 | |
372 | - hooks: { | |
373 | - "repo:push": async (event) => { | |
374 | - console.log("Push received:", event.repository); | |
375 | - }, | |
624 | +**"Unauthorized" on git hooks:** | |
625 | +- Check `INTERNAL_HOOK_SECRET` is set | |
626 | +- Verify hooks have correct secret in curl headers | |
627 | +- Re-run `git init` on repositories to reinstall hooks | |
376 | 628 | |
377 | - "issue:create": async (event) => { | |
378 | - // Custom issue handling | |
379 | - }, | |
380 | - }, | |
629 | +**Database connection errors:** | |
630 | +- Verify `DATABASE_URL` format | |
631 | +- Check database server is running | |
632 | +- Ensure user has correct permissions | |
633 | + | |
634 | +**Storage upload fails:** | |
635 | +- Test connection in `/admin/storage` | |
636 | +- Verify S3 credentials and bucket permissions | |
637 | +- Check firewall rules for external storage | |
638 | + | |
639 | +### Debug Mode | |
640 | + | |
641 | +```env | |
642 | +# Enable detailed logging | |
643 | +LOG_LEVEL=debug | |
644 | +NODE_ENV=development | |
381 | 645 | |
382 | - routes: [ | |
383 | - { | |
384 | - path: "/api/my-plugin/webhook", | |
385 | - method: "POST", | |
386 | - handler: async (req, res) => { | |
387 | - // Custom API endpoint | |
388 | - }, | |
389 | - }, | |
390 | - ], | |
391 | -}); | |
646 | +# View logs | |
647 | +tail -f data/logs/opencodehub.log | |
648 | +``` | |
649 | + | |
650 | +### Health Check | |
651 | + | |
652 | +```bash | |
653 | +curl http://localhost:3000/api/health | |
654 | + | |
655 | +# Response: | |
656 | +{ | |
657 | + "status": "ok", | |
658 | + "checks": { | |
659 | + "database": "ok", | |
660 | + "storage": "ok", | |
661 | + "redis": "ok" // if configured | |
662 | + }, | |
663 | + "uptime": 123456, | |
664 | + "version": "1.0.0" | |
665 | +} | |
392 | 666 | ``` |
393 | 667 | |
394 | 668 | --- |
395 | 669 | |
396 | 670 | ## 🤝 Contributing |
397 | 671 | |
398 | -We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details. | |
672 | +We welcome contributions! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. | |
673 | + | |
674 | +### Development Setup | |
675 | + | |
676 | +```bash | |
677 | +# Fork and clone | |
678 | +git clone https://github.com/YOUR-USERNAME/OpenCodeHub.git | |
679 | +cd OpenCodeHub | |
680 | + | |
681 | +# Install dependencies | |
682 | +bun install | |
683 | + | |
684 | +# Run in development | |
685 | +bun run dev | |
686 | + | |
687 | +# Run tests | |
688 | +bun test | |
689 | + | |
690 | +# Lint | |
691 | +bun run lint | |
692 | +``` | |
693 | + | |
694 | +### Submitting Changes | |
399 | 695 | |
400 | -1. Fork the repository | |
401 | -2. Create your feature branch (`git checkout -b feature/amazing-feature`) | |
402 | -3. Commit your changes (`git commit -m 'Add some amazing feature'`) | |
403 | -4. Push to the branch (`git push origin feature/amazing-feature`) | |
404 | -5. Open a Pull Request | |
696 | +1. Create a feature branch (`git checkout -b feature/amazing-feature`) | |
697 | +2. Make your changes | |
698 | +3. Add tests | |
699 | +4. Run `bun test` and `bun run lint` | |
700 | +5. Commit (`git commit -m 'Add amazing feature'`) | |
701 | +6. Push (`git push origin feature/amazing-feature`) | |
702 | +7. Open a Pull Request | |
405 | 703 | |
406 | 704 | --- |
407 | 705 | |
408 | 706 | ## 📄 License |
409 | 707 | |
410 | -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. | |
708 | +MIT License - see [LICENSE](LICENSE) for details. | |
411 | 709 | |
412 | 710 | --- |
413 | 711 | |
414 | 712 | ## 🙏 Acknowledgments |
415 | 713 | |
416 | -- [Astro](https://astro.build/) - The web framework | |
714 | +- [Astro](https://astro.build/) - Web framework | |
417 | 715 | - [shadcn/ui](https://ui.shadcn.com/) - UI components |
418 | 716 | - [Drizzle ORM](https://orm.drizzle.team/) - Database toolkit |
419 | 717 | - [simple-git](https://github.com/steveukx/git-js) - Git operations |
420 | 718 | - [Monaco Editor](https://microsoft.github.io/monaco-editor/) - Code editor |
719 | +- [Zod](https://zod.dev/) - Schema validation | |
421 | 720 | |
422 | 721 | --- |
423 | 722 | |
424 | 723 | <p align="center"> |
425 | - Made with ❤️ by the OpenCodeHub Team | |
724 | + <strong>Made with ❤️ for the open source community</strong> | |
725 | + <br> | |
726 | + <a href="https://github.com/yourusername/OpenCodeHub/issues">Report Bug</a> • | |
727 | + <a href="https://github.com/yourusername/OpenCodeHub/issues">Request Feature</a> | |
426 | 728 | </p> |
427 | 729 | |
▼bun.lock
+122-1
| @@ -45,18 +45,22 @@ | ||
45 | 45 | "bullmq": "^5.65.1", |
46 | 46 | "class-variance-authority": "^0.7.0", |
47 | 47 | "clsx": "^2.1.1", |
48 | + "cmdk": "^1.1.1", | |
48 | 49 | "dockerode": "^4.0.2", |
49 | 50 | "drizzle-orm": "^0.45.1", |
50 | 51 | "framer-motion": "^11.2.12", |
52 | + "github-markdown-css": "^5.8.1", | |
51 | 53 | "ioredis": "^5.8.2", |
52 | 54 | "isomorphic-git": "^1.25.10", |
53 | 55 | "jose": "^5.6.3", |
54 | 56 | "js-yaml": "^4.1.0", |
55 | 57 | "lucide-react": "^0.400.0", |
58 | + "marked": "^17.0.1", | |
56 | 59 | "monaco-editor": "^0.50.0", |
57 | 60 | "mysql2": "^3.15.3", |
58 | 61 | "nanoid": "^5.0.7", |
59 | 62 | "nodegit": "^0.28.0-alpha.27", |
63 | + "openpgp": "^6.3.0", | |
60 | 64 | "otplib": "^12.0.1", |
61 | 65 | "pg": "^8.16.3", |
62 | 66 | "pino": "^10.1.0", |
| @@ -64,6 +68,7 @@ | ||
64 | 68 | "prom-client": "^15.1.3", |
65 | 69 | "react": "^18.3.1", |
66 | 70 | "react-dom": "^18.3.1", |
71 | + "react-globe.gl": "^2.37.0", | |
67 | 72 | "rehype-sanitize": "^6.0.0", |
68 | 73 | "rehype-stringify": "^10.0.1", |
69 | 74 | "remark-gfm": "^4.0.1", |
| @@ -73,12 +78,14 @@ | ||
73 | 78 | "ssh2": "^1.15.0", |
74 | 79 | "tailwind-merge": "^2.3.0", |
75 | 80 | "tailwindcss-animate": "^1.0.7", |
81 | + "three": "^0.182.0", | |
76 | 82 | "unified": "^11.0.5", |
77 | 83 | "zod": "^3.23.8", |
78 | 84 | "zustand": "^4.5.4", |
79 | 85 | }, |
80 | 86 | "devDependencies": { |
81 | 87 | "@playwright/test": "^1.57.0", |
88 | + "@tailwindcss/typography": "^0.5.19", | |
82 | 89 | "@types/bcryptjs": "^2.4.6", |
83 | 90 | "@types/better-sqlite3": "^7.6.11", |
84 | 91 | "@types/dockerode": "^3.3.29", |
| @@ -241,6 +248,8 @@ | ||
241 | 248 | |
242 | 249 | "@babel/plugin-transform-react-jsx-source": ["@babel/[email protected]", "", { "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw=="], |
243 | 250 | |
251 | + "@babel/runtime": ["@babel/[email protected]", "", {}, "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ=="], | |
252 | + | |
244 | 253 | "@babel/template": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" } }, "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw=="], |
245 | 254 | |
246 | 255 | "@babel/traverse": ["@babel/[email protected]", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", "@babel/types": "^7.28.5", "debug": "^4.3.1" } }, "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ=="], |
| @@ -879,6 +888,16 @@ | ||
879 | 888 | |
880 | 889 | "@standard-schema/spec": ["@standard-schema/[email protected]", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], |
881 | 890 | |
891 | + "@tailwindcss/typography": ["@tailwindcss/[email protected]", "", { "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" } }, "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg=="], | |
892 | + | |
893 | + "@turf/boolean-point-in-polygon": ["@turf/[email protected]", "", { "dependencies": { "@turf/helpers": "7.3.1", "@turf/invariant": "7.3.1", "@types/geojson": "^7946.0.10", "point-in-polygon-hao": "^1.1.0", "tslib": "^2.8.1" } }, "sha512-BUPW63vE43LctwkgannjmEFTX1KFR/18SS7WzFahJWK1ZoP0s1jrfxGX+pi0BH/3Dd9mA71hkGKDDnj1Ndcz0g=="], | |
894 | + | |
895 | + "@turf/helpers": ["@turf/[email protected]", "", { "dependencies": { "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-zkL34JVhi5XhsuMEO0MUTIIFEJ8yiW1InMu4hu/oRqamlY4mMoZql0viEmH6Dafh/p+zOl8OYvMJ3Vm3rFshgg=="], | |
896 | + | |
897 | + "@turf/invariant": ["@turf/[email protected]", "", { "dependencies": { "@turf/helpers": "7.3.1", "@types/geojson": "^7946.0.10", "tslib": "^2.8.1" } }, "sha512-IdZJfDjIDCLH+Gu2yLFoSM7H23sdetIo5t4ET1/25X8gi3GE2XSqbZwaGjuZgNh02nisBewLqNiJs2bo+hrqZA=="], | |
898 | + | |
899 | + "@tweenjs/tween.js": ["@tweenjs/[email protected]", "", {}, "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A=="], | |
900 | + | |
882 | 901 | "@types/aws-lambda": ["@types/[email protected]", "", {}, "sha512-SAP22WSGNN12OQ8PlCzGzRCZ7QDCwI85dQZbmpz7+mAk+L7j+wI7qnvmdKh+o7A5LaOp6QnOZ2NJphAZQTTHQg=="], |
883 | 902 | |
884 | 903 | "@types/babel__core": ["@types/[email protected]", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], |
| @@ -911,6 +930,8 @@ | ||
911 | 930 | |
912 | 931 | "@types/estree": ["@types/[email protected]", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], |
913 | 932 | |
933 | + "@types/geojson": ["@types/[email protected]", "", {}, "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg=="], | |
934 | + | |
914 | 935 | "@types/hast": ["@types/[email protected]", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], |
915 | 936 | |
916 | 937 | "@types/http-cache-semantics": ["@types/[email protected]", "", {}, "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA=="], |
| @@ -969,6 +990,8 @@ | ||
969 | 990 | |
970 | 991 | "abort-controller": ["[email protected]", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], |
971 | 992 | |
993 | + "accessor-fn": ["[email protected]", "", {}, "sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA=="], | |
994 | + | |
972 | 995 | "acorn": ["[email protected]", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], |
973 | 996 | |
974 | 997 | "acorn-import-attributes": ["[email protected]", "", { "peerDependencies": { "acorn": "^8" } }, "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ=="], |
| @@ -1127,6 +1150,8 @@ | ||
1127 | 1150 | |
1128 | 1151 | "cluster-key-slot": ["[email protected]", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], |
1129 | 1152 | |
1153 | + "cmdk": ["[email protected]", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], | |
1154 | + | |
1130 | 1155 | "color": ["[email protected]", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], |
1131 | 1156 | |
1132 | 1157 | "color-convert": ["[email protected]", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], |
| @@ -1161,6 +1186,36 @@ | ||
1161 | 1186 | |
1162 | 1187 | "csstype": ["[email protected]", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], |
1163 | 1188 | |
1189 | + "d3-array": ["[email protected]", "", { "dependencies": { "internmap": "1 - 2" } }, "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg=="], | |
1190 | + | |
1191 | + "d3-color": ["[email protected]", "", {}, "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA=="], | |
1192 | + | |
1193 | + "d3-delaunay": ["[email protected]", "", { "dependencies": { "delaunator": "5" } }, "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A=="], | |
1194 | + | |
1195 | + "d3-format": ["[email protected]", "", {}, "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA=="], | |
1196 | + | |
1197 | + "d3-geo": ["[email protected]", "", { "dependencies": { "d3-array": "2.5.0 - 3" } }, "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q=="], | |
1198 | + | |
1199 | + "d3-geo-voronoi": ["[email protected]", "", { "dependencies": { "d3-array": "3", "d3-delaunay": "6", "d3-geo": "3", "d3-tricontour": "1" } }, "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q=="], | |
1200 | + | |
1201 | + "d3-interpolate": ["[email protected]", "", { "dependencies": { "d3-color": "1 - 3" } }, "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g=="], | |
1202 | + | |
1203 | + "d3-octree": ["[email protected]", "", {}, "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A=="], | |
1204 | + | |
1205 | + "d3-scale": ["[email protected]", "", { "dependencies": { "d3-array": "2.10.0 - 3", "d3-format": "1 - 3", "d3-interpolate": "1.2.0 - 3", "d3-time": "2.1.1 - 3", "d3-time-format": "2 - 4" } }, "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ=="], | |
1206 | + | |
1207 | + "d3-scale-chromatic": ["[email protected]", "", { "dependencies": { "d3-color": "1 - 3", "d3-interpolate": "1 - 3" } }, "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ=="], | |
1208 | + | |
1209 | + "d3-selection": ["[email protected]", "", {}, "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ=="], | |
1210 | + | |
1211 | + "d3-time": ["[email protected]", "", { "dependencies": { "d3-array": "2 - 3" } }, "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q=="], | |
1212 | + | |
1213 | + "d3-time-format": ["[email protected]", "", { "dependencies": { "d3-time": "1 - 3" } }, "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg=="], | |
1214 | + | |
1215 | + "d3-tricontour": ["[email protected]", "", { "dependencies": { "d3-delaunay": "6", "d3-scale": "4" } }, "sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ=="], | |
1216 | + | |
1217 | + "data-bind-mapper": ["[email protected]", "", { "dependencies": { "accessor-fn": "1" } }, "sha512-QmU3lyEnbENQPo0M1F9BMu4s6cqNNp8iJA+b/HP2sSb7pf3dxwF3+EP1eO69rwBfH9kFJ1apmzrtogAmVt2/Xw=="], | |
1218 | + | |
1164 | 1219 | "dateformat": ["[email protected]", "", {}, "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA=="], |
1165 | 1220 | |
1166 | 1221 | "debug": ["[email protected]", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], |
| @@ -1173,6 +1228,8 @@ | ||
1173 | 1228 | |
1174 | 1229 | "define-data-property": ["[email protected]", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], |
1175 | 1230 | |
1231 | + "delaunator": ["[email protected]", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw=="], | |
1232 | + | |
1176 | 1233 | "denque": ["[email protected]", "", {}, "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw=="], |
1177 | 1234 | |
1178 | 1235 | "depd": ["[email protected]", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], |
| @@ -1211,6 +1268,8 @@ | ||
1211 | 1268 | |
1212 | 1269 | "dunder-proto": ["[email protected]", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], |
1213 | 1270 | |
1271 | + "earcut": ["[email protected]", "", {}, "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ=="], | |
1272 | + | |
1214 | 1273 | "eastasianwidth": ["[email protected]", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], |
1215 | 1274 | |
1216 | 1275 | "ee-first": ["[email protected]", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], |
| @@ -1301,6 +1360,8 @@ | ||
1301 | 1360 | |
1302 | 1361 | "flattie": ["[email protected]", "", {}, "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ=="], |
1303 | 1362 | |
1363 | + "float-tooltip": ["[email protected]", "", { "dependencies": { "d3-selection": "2 - 3", "kapsule": "^1.16", "preact": "10" } }, "sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg=="], | |
1364 | + | |
1304 | 1365 | "for-each": ["[email protected]", "", { "dependencies": { "is-callable": "^1.2.7" } }, "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg=="], |
1305 | 1366 | |
1306 | 1367 | "foreground-child": ["[email protected]", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], |
| @@ -1311,6 +1372,8 @@ | ||
1311 | 1372 | |
1312 | 1373 | "fraction.js": ["[email protected]", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="], |
1313 | 1374 | |
1375 | + "frame-ticker": ["[email protected]", "", { "dependencies": { "simplesignal": "^2.1.6" } }, "sha512-E0X2u2JIvbEMrqEg5+4BpTqaD22OwojJI63K7MdKHdncjtAhGRbCR8nJCr2vwEt9NWBPCPcu70X9smPviEBy8Q=="], | |
1376 | + | |
1314 | 1377 | "framer-motion": ["[email protected]", "", { "dependencies": { "motion-dom": "^11.18.1", "motion-utils": "^11.18.1", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w=="], |
1315 | 1378 | |
1316 | 1379 | "fresh": ["[email protected]", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], |
| @@ -1349,12 +1412,16 @@ | ||
1349 | 1412 | |
1350 | 1413 | "github-from-package": ["[email protected]", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], |
1351 | 1414 | |
1415 | + "github-markdown-css": ["[email protected]", "", {}, "sha512-8G+PFvqigBQSWLQjyzgpa2ThD9bo7+kDsriUIidGcRhXgmcaAWUIpCZf8DavJgc+xifjbCG+GvMyWr0XMXmc7g=="], | |
1416 | + | |
1352 | 1417 | "github-slugger": ["[email protected]", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="], |
1353 | 1418 | |
1354 | 1419 | "glob": ["[email protected]", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], |
1355 | 1420 | |
1356 | 1421 | "glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], |
1357 | 1422 | |
1423 | + "globe.gl": ["[email protected]", "", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "kapsule": "^1.16", "three": ">=0.154 <1", "three-globe": "^2.45", "three-render-objects": "^1.40" } }, "sha512-fjkLHVBrnbESkUgklTd4UbcGLciu4nIl49IIi1hclLjI6MU3ASu6JYmf/K5qwPf7I+tNOauQRr4i5Y28JTtHQg=="], | |
1424 | + | |
1358 | 1425 | "google-logging-utils": ["[email protected]", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="], |
1359 | 1426 | |
1360 | 1427 | "gopd": ["[email protected]", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], |
| @@ -1365,6 +1432,8 @@ | ||
1365 | 1432 | |
1366 | 1433 | "gray-matter": ["[email protected]", "", { "dependencies": { "js-yaml": "^3.13.1", "kind-of": "^6.0.2", "section-matter": "^1.0.0", "strip-bom-string": "^1.0.0" } }, "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q=="], |
1367 | 1434 | |
1435 | + "h3-js": ["[email protected]", "", {}, "sha512-DvJh07MhGgY2KcC4OeZc8SSyA+ZXpdvoh6uCzGpoKvWtZxJB+g6VXXC1+eWYkaMIsLz7J/ErhOalHCpcs1KYog=="], | |
1436 | + | |
1368 | 1437 | "has-property-descriptors": ["[email protected]", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], |
1369 | 1438 | |
1370 | 1439 | "has-symbols": ["[email protected]", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], |
| @@ -1423,10 +1492,14 @@ | ||
1423 | 1492 | |
1424 | 1493 | "imurmurhash": ["[email protected]", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], |
1425 | 1494 | |
1495 | + "index-array-by": ["[email protected]", "", {}, "sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw=="], | |
1496 | + | |
1426 | 1497 | "inherits": ["[email protected]", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], |
1427 | 1498 | |
1428 | 1499 | "ini": ["[email protected]", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], |
1429 | 1500 | |
1501 | + "internmap": ["[email protected]", "", {}, "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg=="], | |
1502 | + | |
1430 | 1503 | "ioredis": ["[email protected]", "", { "dependencies": { "@ioredis/commands": "1.4.0", "cluster-key-slot": "^1.1.0", "debug": "^4.3.4", "denque": "^2.1.0", "lodash.defaults": "^4.2.0", "lodash.isarguments": "^3.1.0", "redis-errors": "^1.2.0", "redis-parser": "^3.0.0", "standard-as-callback": "^2.1.0" } }, "sha512-C6uC+kleiIMmjViJINWk80sOQw5lEzse1ZmvD+S/s8p8CWapftSaC+kocGTx6xrbrJ4WmYQGC08ffHLr6ToR6Q=="], |
1431 | 1504 | |
1432 | 1505 | "ip-address": ["[email protected]", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], |
| @@ -1475,6 +1548,8 @@ | ||
1475 | 1548 | |
1476 | 1549 | "jackspeak": ["[email protected]", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], |
1477 | 1550 | |
1551 | + "jerrypick": ["[email protected]", "", {}, "sha512-YKnxXEekXKzhpf7CLYA0A+oDP8V0OhICNCr5lv96FvSsDEmrb0GKM776JgQvHTMjr7DTTPEVv/1Ciaw0uEWzBA=="], | |
1552 | + | |
1478 | 1553 | "jiti": ["[email protected]", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], |
1479 | 1554 | |
1480 | 1555 | "jose": ["[email protected]", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], |
| @@ -1493,6 +1568,8 @@ | ||
1493 | 1568 | |
1494 | 1569 | "jsonfile": ["[email protected]", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], |
1495 | 1570 | |
1571 | + "kapsule": ["[email protected]", "", { "dependencies": { "lodash-es": "4" } }, "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg=="], | |
1572 | + | |
1496 | 1573 | "keyv": ["[email protected]", "", { "dependencies": { "@keyv/serialize": "^1.1.1" } }, "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ=="], |
1497 | 1574 | |
1498 | 1575 | "kind-of": ["[email protected]", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], |
| @@ -1509,6 +1586,8 @@ | ||
1509 | 1586 | |
1510 | 1587 | "lodash": ["[email protected]", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], |
1511 | 1588 | |
1589 | + "lodash-es": ["[email protected]", "", {}, "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="], | |
1590 | + | |
1512 | 1591 | "lodash.camelcase": ["[email protected]", "", {}, "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="], |
1513 | 1592 | |
1514 | 1593 | "lodash.defaults": ["[email protected]", "", {}, "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ=="], |
| @@ -1541,6 +1620,8 @@ | ||
1541 | 1620 | |
1542 | 1621 | "markdown-table": ["[email protected]", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], |
1543 | 1622 | |
1623 | + "marked": ["[email protected]", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-boeBdiS0ghpWcSwoNm/jJBwdpFaMnZWRzjA6SkUMYb40SVaN1x7mmfGKp0jvexGcx+7y2La5zRZsYFZI6Qpypg=="], | |
1624 | + | |
1544 | 1625 | "math-intrinsics": ["[email protected]", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], |
1545 | 1626 | |
1546 | 1627 | "mdast-util-definitions": ["[email protected]", "", { "dependencies": { "@types/mdast": "^4.0.0", "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ=="], |
| @@ -1729,6 +1810,8 @@ | ||
1729 | 1810 | |
1730 | 1811 | "oniguruma-to-es": ["[email protected]", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^5.1.1", "regex-recursion": "^5.1.1" } }, "sha512-bwALDxriqfKGfUufKGGepCzu9x7nJQuoRoAFp4AnwehhC2crqrDIAP/uN2qdlsAvSMpeRC3+Yzhqc7hLmle5+g=="], |
1731 | 1812 | |
1813 | + "openpgp": ["[email protected]", "", {}, "sha512-pLzCU8IgyKXPSO11eeharQkQ4GzOKNWhXq79pQarIRZEMt1/ssyr+MIuWBv1mNoenJLg04gvPx+fi4gcKZ4bag=="], | |
1814 | + | |
1732 | 1815 | "ora": ["[email protected]", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], |
1733 | 1816 | |
1734 | 1817 | "otplib": ["[email protected]", "", { "dependencies": { "@otplib/core": "^12.0.1", "@otplib/preset-default": "^12.0.1", "@otplib/preset-v11": "^12.0.1" } }, "sha512-xDGvUOQjop7RDgxTQ+o4pOol0/3xSZzawTiPKRrHnQWAy0WjhNs/5HdIDJCrqC4MBynmjXgULc6YfioaxZeFgg=="], |
| @@ -1803,6 +1886,10 @@ | ||
1803 | 1886 | |
1804 | 1887 | "playwright-core": ["[email protected]", "", { "bin": { "playwright-core": "cli.js" } }, "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ=="], |
1805 | 1888 | |
1889 | + "point-in-polygon-hao": ["[email protected]", "", { "dependencies": { "robust-predicates": "^3.0.2" } }, "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ=="], | |
1890 | + | |
1891 | + "polished": ["[email protected]", "", { "dependencies": { "@babel/runtime": "^7.17.8" } }, "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA=="], | |
1892 | + | |
1806 | 1893 | "possible-typed-array-names": ["[email protected]", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], |
1807 | 1894 | |
1808 | 1895 | "postcss": ["[email protected]", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], |
| @@ -1815,7 +1902,7 @@ | ||
1815 | 1902 | |
1816 | 1903 | "postcss-nested": ["[email protected]", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="], |
1817 | 1904 | |
1818 | - "postcss-selector-parser": ["[email protected]", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], | |
1905 | + "postcss-selector-parser": ["[email protected]", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w=="], | |
1819 | 1906 | |
1820 | 1907 | "postcss-value-parser": ["[email protected]", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], |
1821 | 1908 | |
| @@ -1829,6 +1916,8 @@ | ||
1829 | 1916 | |
1830 | 1917 | "postgres-interval": ["[email protected]", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="], |
1831 | 1918 | |
1919 | + "preact": ["[email protected]", "", {}, "sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA=="], | |
1920 | + | |
1832 | 1921 | "prebuild-install": ["[email protected]", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], |
1833 | 1922 | |
1834 | 1923 | "preferred-pm": ["[email protected]", "", { "dependencies": { "find-up-simple": "^1.0.0", "find-yarn-workspace-root2": "1.2.16", "which-pm": "^3.0.1" } }, "sha512-rU+ZAv1Ur9jAUZtGPebQVQPzdGhNzaEiQ7VL9+cjsAWPHFYOccNXPNiev1CCDSOg/2j7UujM7ojNhpkuILEVNQ=="], |
| @@ -1847,6 +1936,8 @@ | ||
1847 | 1936 | |
1848 | 1937 | "prompts": ["[email protected]", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], |
1849 | 1938 | |
1939 | + "prop-types": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], | |
1940 | + | |
1850 | 1941 | "property-information": ["[email protected]", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], |
1851 | 1942 | |
1852 | 1943 | "protobufjs": ["[email protected]", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], |
| @@ -1867,6 +1958,12 @@ | ||
1867 | 1958 | |
1868 | 1959 | "react-dom": ["[email protected]", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="], |
1869 | 1960 | |
1961 | + "react-globe.gl": ["[email protected]", "", { "dependencies": { "globe.gl": "^2.45", "prop-types": "15", "react-kapsule": "^2.5" }, "peerDependencies": { "react": "*" } }, "sha512-nN1FDOJBhFvWfKrOY0SnkDuA8wk9FSTBN0HFfAdTqqcFM5R+OXBIxK0BM6t8n3oNVYpEJVxEzjYFwLyk4BC7Cw=="], | |
1962 | + | |
1963 | + "react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], | |
1964 | + | |
1965 | + "react-kapsule": ["[email protected]", "", { "dependencies": { "jerrypick": "^1.1.1" }, "peerDependencies": { "react": ">=16.13.1" } }, "sha512-kifAF4ZPD77qZKc4CKLmozq6GY1sBzPEJTIJb0wWFK6HsePJatK3jXplZn2eeAt3x67CDozgi7/rO8fNQ/AL7A=="], | |
1966 | + | |
1870 | 1967 | "react-refresh": ["[email protected]", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], |
1871 | 1968 | |
1872 | 1969 | "react-remove-scroll": ["[email protected]", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], |
| @@ -1939,6 +2036,8 @@ | ||
1939 | 2036 | |
1940 | 2037 | "reusify": ["[email protected]", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], |
1941 | 2038 | |
2039 | + "robust-predicates": ["[email protected]", "", {}, "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg=="], | |
2040 | + | |
1942 | 2041 | "rollup": ["[email protected]", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], |
1943 | 2042 | |
1944 | 2043 | "run-parallel": ["[email protected]", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], |
| @@ -1989,6 +2088,8 @@ | ||
1989 | 2088 | |
1990 | 2089 | "simple-swizzle": ["[email protected]", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw=="], |
1991 | 2090 | |
2091 | + "simplesignal": ["[email protected]", "", {}, "sha512-PEo2qWpUke7IMhlqiBxrulIFvhJRLkl1ih52Rwa+bPjzhJepcd4GIjn2RiQmFSx3dQvsEAgF0/lXMwMN7vODaA=="], | |
2092 | + | |
1992 | 2093 | "sisteransi": ["[email protected]", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="], |
1993 | 2094 | |
1994 | 2095 | "smart-buffer": ["[email protected]", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], |
| @@ -2081,8 +2182,22 @@ | ||
2081 | 2182 | |
2082 | 2183 | "thread-stream": ["[email protected]", "", { "dependencies": { "real-require": "^0.2.0" } }, "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A=="], |
2083 | 2184 | |
2185 | + "three": ["[email protected]", "", {}, "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ=="], | |
2186 | + | |
2187 | + "three-conic-polygon-geometry": ["[email protected]", "", { "dependencies": { "@turf/boolean-point-in-polygon": "^7.2", "d3-array": "1 - 3", "d3-geo": "1 - 3", "d3-geo-voronoi": "2", "d3-scale": "1 - 4", "delaunator": "5", "earcut": "3" }, "peerDependencies": { "three": ">=0.72.0" } }, "sha512-NaP3RWLJIyPGI+zyaZwd0Yj6rkoxm4FJHqAX1Enb4L64oNYLCn4bz1ESgOEYavgcUwCNYINu1AgEoUBJr1wZcA=="], | |
2188 | + | |
2189 | + "three-geojson-geometry": ["[email protected]", "", { "dependencies": { "d3-geo": "1 - 3", "d3-interpolate": "1 - 3", "earcut": "3" }, "peerDependencies": { "three": ">=0.72.0" } }, "sha512-dC7bF3ri1goDcihYhzACHOBQqu7YNNazYLa2bSydVIiJUb3jDFojKSy+gNj2pMkqZNSVjssSmdY9zlmnhEpr1w=="], | |
2190 | + | |
2191 | + "three-globe": ["[email protected]", "", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "d3-array": "3", "d3-color": "3", "d3-geo": "3", "d3-interpolate": "3", "d3-scale": "4", "d3-scale-chromatic": "3", "data-bind-mapper": "1", "frame-ticker": "1", "h3-js": "4", "index-array-by": "1", "kapsule": "^1.16", "three-conic-polygon-geometry": "2", "three-geojson-geometry": "2", "three-slippy-map-globe": "1", "tinycolor2": "1" }, "peerDependencies": { "three": ">=0.154" } }, "sha512-Ur6BVkezvmHnvsEg8fbq6gIscSZtknSQMWwDRbiJ95o6OSDjDbGTc4oO6nP7mOM9aAA3YrF7YZyOwSkP4T56QA=="], | |
2192 | + | |
2193 | + "three-render-objects": ["[email protected]", "", { "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", "float-tooltip": "^1.7", "kapsule": "^1.16", "polished": "4" }, "peerDependencies": { "three": ">=0.168" } }, "sha512-Ukpu1pei3L5r809izvjsZxwuRcYLiyn6Uvy3lZ9bpMTdvj3i6PeX6w++/hs2ZS3KnEzGjb6YvTvh4UQuwHTDJg=="], | |
2194 | + | |
2195 | + "three-slippy-map-globe": ["[email protected]", "", { "dependencies": { "d3-geo": "1 - 3", "d3-octree": "^1.1", "d3-scale": "1 - 4" }, "peerDependencies": { "three": ">=0.154" } }, "sha512-am8A4PP38AfTdrhXBDucwPRHLTbBl93yhpjIs56K1TLs9VuUWzg68oim4Dibs9QC1riXbj5SoBp/okA1VN9eYg=="], | |
2196 | + | |
2084 | 2197 | "tinybench": ["[email protected]", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], |
2085 | 2198 | |
2199 | + "tinycolor2": ["[email protected]", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="], | |
2200 | + | |
2086 | 2201 | "tinyexec": ["[email protected]", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], |
2087 | 2202 | |
2088 | 2203 | "tinyglobby": ["[email protected]", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], |
| @@ -2307,6 +2422,8 @@ | ||
2307 | 2422 | |
2308 | 2423 | "cliui/wrap-ansi": ["[email protected]", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], |
2309 | 2424 | |
2425 | + "cmdk/@radix-ui/react-primitive": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], | |
2426 | + | |
2310 | 2427 | "cross-spawn/which": ["[email protected]", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], |
2311 | 2428 | |
2312 | 2429 | "docker-modem/readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], |
| @@ -2349,6 +2466,8 @@ | ||
2349 | 2466 | |
2350 | 2467 | "postcss/nanoid": ["[email protected]", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], |
2351 | 2468 | |
2469 | + "postcss-nested/postcss-selector-parser": ["[email protected]", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], | |
2470 | + | |
2352 | 2471 | "prompts/kleur": ["[email protected]", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], |
2353 | 2472 | |
2354 | 2473 | "rc/strip-json-comments": ["[email protected]", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], |
| @@ -2367,6 +2486,8 @@ | ||
2367 | 2486 | |
2368 | 2487 | "strip-ansi-cjs/ansi-regex": ["[email protected]", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], |
2369 | 2488 | |
2489 | + "tailwindcss/postcss-selector-parser": ["[email protected]", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="], | |
2490 | + | |
2370 | 2491 | "tar/chownr": ["[email protected]", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], |
2371 | 2492 | |
2372 | 2493 | "tar-stream/readable-stream": ["[email protected]", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], |
2373 | 2494 | |
▼cli/bin/och.ts
+62-0
| @@ -0,0 +1,62 @@ | ||
1 | +#!/usr/bin/env node | |
2 | +/** | |
3 | + * OpenCodeHub CLI | |
4 | + * Stack-first PR workflow tool | |
5 | + */ | |
6 | + | |
7 | +import { Command } from "commander"; | |
8 | +import chalk from "chalk"; | |
9 | +import { stackCommands } from "../src/commands/stack/index.js"; | |
10 | +import { authCommands } from "../src/commands/auth.js"; | |
11 | + | |
12 | +const program = new Command(); | |
13 | + | |
14 | +program | |
15 | + .name("och") | |
16 | + .description("OpenCodeHub CLI - Stack-first PR workflows") | |
17 | + .version("1.0.0"); | |
18 | + | |
19 | +// Auth commands | |
20 | +program | |
21 | + .command("auth") | |
22 | + .description("Authentication commands") | |
23 | + .addCommand(authCommands); | |
24 | + | |
25 | +// Stack commands | |
26 | +program | |
27 | + .command("stack") | |
28 | + .description("Manage stacked PRs") | |
29 | + .addCommand(stackCommands); | |
30 | + | |
31 | +// Repo init | |
32 | +program | |
33 | + .command("init") | |
34 | + .description("Initialize repository for OpenCodeHub") | |
35 | + .option("-u, --url <url>", "OpenCodeHub server URL") | |
36 | + .action(async (options) => { | |
37 | + console.log(chalk.blue("Initializing repository...")); | |
38 | + console.log(`Server: ${options.url || "http://localhost:4321"}`); | |
39 | + // TODO: Create .ochrc config | |
40 | + }); | |
41 | + | |
42 | +// Sync command | |
43 | +program | |
44 | + .command("sync") | |
45 | + .description("Bidirectional sync with remote") | |
46 | + .action(async () => { | |
47 | + console.log(chalk.blue("Syncing with remote...")); | |
48 | + // TODO: Implement sync | |
49 | + }); | |
50 | + | |
51 | +// Status command | |
52 | +program | |
53 | + .command("status") | |
54 | + .alias("st") | |
55 | + .description("Show current stack status") | |
56 | + .action(async () => { | |
57 | + console.log(chalk.blue("Stack Status")); | |
58 | + console.log("─".repeat(40)); | |
59 | + // TODO: Show current stack | |
60 | + }); | |
61 | + | |
62 | +program.parse(); | |
0 | 63 | |
▼cli/package.json
+37-0
| @@ -0,0 +1,37 @@ | ||
1 | +{ | |
2 | + "name": "opencodehub-cli", | |
3 | + "version": "1.0.0", | |
4 | + "description": "CLI for OpenCodeHub - Stack-first PR workflows", | |
5 | + "type": "module", | |
6 | + "bin": { | |
7 | + "och": "./bin/och.js" | |
8 | + }, | |
9 | + "scripts": { | |
10 | + "build": "tsc", | |
11 | + "dev": "tsx bin/och.ts", | |
12 | + "test": "vitest" | |
13 | + }, | |
14 | + "dependencies": { | |
15 | + "commander": "^12.0.0", | |
16 | + "chalk": "^5.3.0", | |
17 | + "ora": "^8.0.0", | |
18 | + "inquirer": "^9.2.0", | |
19 | + "conf": "^12.0.0", | |
20 | + "simple-git": "^3.25.0" | |
21 | + }, | |
22 | + "devDependencies": { | |
23 | + "@types/node": "^20.14.9", | |
24 | + "tsx": "^4.15.0", | |
25 | + "typescript": "^5.5.2", | |
26 | + "vitest": "^4.0.15" | |
27 | + }, | |
28 | + "engines": { | |
29 | + "node": ">=18.0.0" | |
30 | + }, | |
31 | + "keywords": [ | |
32 | + "git", | |
33 | + "stacked-prs", | |
34 | + "code-review", | |
35 | + "cli" | |
36 | + ] | |
37 | +} | |
0 | 38 | |
▼cli/src/commands/auth.ts
+102-0
| @@ -0,0 +1,102 @@ | ||
1 | +/** | |
2 | + * Auth Commands | |
3 | + * Authentication for OpenCodeHub CLI | |
4 | + */ | |
5 | + | |
6 | +import { Command } from "commander"; | |
7 | +import chalk from "chalk"; | |
8 | +import ora from "ora"; | |
9 | +import inquirer from "inquirer"; | |
10 | +import { getConfig, saveConfig } from "../lib/config.js"; | |
11 | +import { apiCall } from "../lib/api.js"; | |
12 | + | |
13 | +export const authCommands = new Command("auth") | |
14 | + .description("Authentication commands"); | |
15 | + | |
16 | +// Login | |
17 | +authCommands | |
18 | + .command("login") | |
19 | + .description("Login to OpenCodeHub") | |
20 | + .option("-u, --url <url>", "OpenCodeHub server URL", "http://localhost:4321") | |
21 | + .option("-t, --token <token>", "Personal access token") | |
22 | + .action(async (options) => { | |
23 | + console.log(chalk.blue("\n🔐 OpenCodeHub Login\n")); | |
24 | + | |
25 | + try { | |
26 | + let token = options.token; | |
27 | + | |
28 | + if (!token) { | |
29 | + // Interactive login | |
30 | + const answers = await inquirer.prompt([ | |
31 | + { | |
32 | + type: "input", | |
33 | + name: "email", | |
34 | + message: "Email:", | |
35 | + }, | |
36 | + { | |
37 | + type: "password", | |
38 | + name: "password", | |
39 | + message: "Password:", | |
40 | + mask: "*", | |
41 | + }, | |
42 | + ]); | |
43 | + | |
44 | + const spinner = ora("Authenticating...").start(); | |
45 | + | |
46 | + // Call auth API | |
47 | + const response = await apiCall(options.url, "/api/auth/login", "POST", { | |
48 | + email: answers.email, | |
49 | + password: answers.password, | |
50 | + }); | |
51 | + | |
52 | + if (response.token) { | |
53 | + token = response.token; | |
54 | + } else { | |
55 | + spinner.fail("Authentication failed"); | |
56 | + console.error(chalk.red(response.error || "Invalid credentials")); | |
57 | + process.exit(1); | |
58 | + } | |
59 | + | |
60 | + spinner.succeed("Authenticated successfully"); | |
61 | + } | |
62 | + | |
63 | + // Save config | |
64 | + saveConfig({ | |
65 | + serverUrl: options.url, | |
66 | + token, | |
67 | + }); | |
68 | + | |
69 | + console.log(chalk.green("\n✓ Logged in to " + options.url)); | |
70 | + } catch (error) { | |
71 | + console.error(chalk.red("Login failed")); | |
72 | + console.error(chalk.dim(error instanceof Error ? error.message : "Unknown error")); | |
73 | + process.exit(1); | |
74 | + } | |
75 | + }); | |
76 | + | |
77 | +// Logout | |
78 | +authCommands | |
79 | + .command("logout") | |
80 | + .description("Logout from OpenCodeHub") | |
81 | + .action(async () => { | |
82 | + saveConfig({ serverUrl: "", token: "" }); | |
83 | + console.log(chalk.green("✓ Logged out successfully")); | |
84 | + }); | |
85 | + | |
86 | +// Status | |
87 | +authCommands | |
88 | + .command("status") | |
89 | + .description("Show authentication status") | |
90 | + .action(async () => { | |
91 | + const config = getConfig(); | |
92 | + | |
93 | + if (config.token) { | |
94 | + console.log(chalk.green("✓ Authenticated")); | |
95 | + console.log(chalk.dim(" Server: " + config.serverUrl)); | |
96 | + } else { | |
97 | + console.log(chalk.yellow("✗ Not authenticated")); | |
98 | + console.log(chalk.dim(" Run ") + chalk.cyan("och auth login") + chalk.dim(" to authenticate")); | |
99 | + } | |
100 | + }); | |
101 | + | |
102 | +export default authCommands; | |
0 | 103 | |
▼cli/src/commands/stack/index.ts
+289-0
| @@ -0,0 +1,289 @@ | ||
1 | +/** | |
2 | + * Stack Commands | |
3 | + * Manage stacked PRs from the CLI | |
4 | + */ | |
5 | + | |
6 | +import { Command } from "commander"; | |
7 | +import chalk from "chalk"; | |
8 | +import ora from "ora"; | |
9 | +import { simpleGit } from "simple-git"; | |
10 | +import { getConfig, saveConfig } from "../../lib/config.js"; | |
11 | +import { apiCall } from "../../lib/api.js"; | |
12 | + | |
13 | +const git = simpleGit(); | |
14 | + | |
15 | +export const stackCommands = new Command("stack") | |
16 | + .description("Manage stacked PRs"); | |
17 | + | |
18 | +// Create a new branch in the stack | |
19 | +stackCommands | |
20 | + .command("create <name>") | |
21 | + .description("Create a new branch in the current stack") | |
22 | + .option("-m, --message <message>", "Commit message for work in progress") | |
23 | + .action(async (name: string, options) => { | |
24 | + const spinner = ora("Creating stacked branch...").start(); | |
25 | + | |
26 | + try { | |
27 | + // Get current branch | |
28 | + const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]); | |
29 | + | |
30 | + // Create new branch | |
31 | + const newBranch = `stack/${name}`; | |
32 | + await git.checkoutLocalBranch(newBranch); | |
33 | + | |
34 | + spinner.succeed(`Created branch ${chalk.green(newBranch)} (stacked on ${chalk.cyan(currentBranch.trim())})`); | |
35 | + | |
36 | + console.log("\n" + chalk.dim("Next steps:")); | |
37 | + console.log(chalk.dim(" • Make your changes")); | |
38 | + console.log(chalk.dim(" • Run ") + chalk.cyan("och stack submit") + chalk.dim(" to push and create PRs")); | |
39 | + } catch (error) { | |
40 | + spinner.fail("Failed to create branch"); | |
41 | + console.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); | |
42 | + process.exit(1); | |
43 | + } | |
44 | + }); | |
45 | + | |
46 | +// Submit stack to remote | |
47 | +stackCommands | |
48 | + .command("submit") | |
49 | + .description("Push stack and create/update PRs") | |
50 | + .option("-d, --draft", "Create PRs as drafts") | |
51 | + .action(async (options) => { | |
52 | + const spinner = ora("Submitting stack...").start(); | |
53 | + | |
54 | + try { | |
55 | + // Get current branch | |
56 | + const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]); | |
57 | + | |
58 | + // Push current branch | |
59 | + spinner.text = "Pushing branch..."; | |
60 | + await git.push(["-u", "origin", currentBranch.trim()]); | |
61 | + | |
62 | + spinner.succeed(`Pushed ${chalk.green(currentBranch.trim())}`); | |
63 | + | |
64 | + console.log("\n" + chalk.dim("Stack submitted! Create PRs at:")); | |
65 | + console.log(chalk.cyan(" http://localhost:4321/your-repo/compare")); | |
66 | + } catch (error) { | |
67 | + spinner.fail("Failed to submit stack"); | |
68 | + console.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); | |
69 | + process.exit(1); | |
70 | + } | |
71 | + }); | |
72 | + | |
73 | +// View current stack | |
74 | +stackCommands | |
75 | + .command("view") | |
76 | + .alias("ls") | |
77 | + .description("View current stack visualization") | |
78 | + .action(async () => { | |
79 | + try { | |
80 | + const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]); | |
81 | + const branches = await git.branchLocal(); | |
82 | + | |
83 | + console.log(chalk.bold("\n📚 Current Stack\n")); | |
84 | + | |
85 | + // Find stack branches | |
86 | + const stackBranches = branches.all.filter(b => b.startsWith("stack/")); | |
87 | + | |
88 | + if (stackBranches.length === 0) { | |
89 | + console.log(chalk.dim(" No stack branches found.")); | |
90 | + console.log(chalk.dim(" Run ") + chalk.cyan("och stack create <name>") + chalk.dim(" to start a stack.")); | |
91 | + return; | |
92 | + } | |
93 | + | |
94 | + // Display stack | |
95 | + console.log(chalk.dim(" ┌─ ") + chalk.green("main") + chalk.dim(" (base)")); | |
96 | + | |
97 | + for (let i = 0; i < stackBranches.length; i++) { | |
98 | + const branch = stackBranches[i]; | |
99 | + const isCurrent = currentBranch.trim() === branch; | |
100 | + const prefix = i === stackBranches.length - 1 ? "└─" : "├─"; | |
101 | + | |
102 | + if (isCurrent) { | |
103 | + console.log(chalk.dim(" │")); | |
104 | + console.log(chalk.dim(` ${prefix} `) + chalk.yellow.bold("● " + branch) + chalk.yellow(" (current)")); | |
105 | + } else { | |
106 | + console.log(chalk.dim(" │")); | |
107 | + console.log(chalk.dim(` ${prefix} `) + chalk.cyan("○ " + branch)); | |
108 | + } | |
109 | + } | |
110 | + | |
111 | + console.log(""); | |
112 | + } catch (error) { | |
113 | + console.error(chalk.red("Failed to view stack")); | |
114 | + process.exit(1); | |
115 | + } | |
116 | + }); | |
117 | + | |
118 | +// Sync stack with remote | |
119 | +stackCommands | |
120 | + .command("sync") | |
121 | + .description("Bidirectional sync with remote") | |
122 | + .option("-p, --push", "Push local changes to remote") | |
123 | + .option("-l, --pull", "Pull remote changes to local") | |
124 | + .option("-f, --force", "Force push (use with caution)") | |
125 | + .action(async (options) => { | |
126 | + const spinner = ora("Syncing stack...").start(); | |
127 | + | |
128 | + try { | |
129 | + // Fetch latest | |
130 | + spinner.text = "Fetching from remote..."; | |
131 | + await git.fetch(); | |
132 | + | |
133 | + const branches = await git.branchLocal(); | |
134 | + const stackBranches = branches.all.filter(b => b.startsWith("stack/")); | |
135 | + | |
136 | + if (stackBranches.length === 0) { | |
137 | + spinner.info("No stack branches found"); | |
138 | + return; | |
139 | + } | |
140 | + | |
141 | + const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]); | |
142 | + | |
143 | + // Default: do both push and pull if neither specified | |
144 | + const doPush = options.push || (!options.push && !options.pull); | |
145 | + const doPull = options.pull || (!options.push && !options.pull); | |
146 | + | |
147 | + let syncedCount = 0; | |
148 | + const errors: string[] = []; | |
149 | + | |
150 | + for (const branch of stackBranches) { | |
151 | + spinner.text = `Syncing ${branch}...`; | |
152 | + | |
153 | + try { | |
154 | + await git.checkout(branch); | |
155 | + | |
156 | + if (doPull) { | |
157 | + // Pull with rebase | |
158 | + try { | |
159 | + await git.pull(["--rebase", "origin", branch]); | |
160 | + } catch (e) { | |
161 | + // Branch might not exist on remote yet | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + if (doPush) { | |
166 | + const pushArgs = ["-u", "origin", branch]; | |
167 | + if (options.force) { | |
168 | + pushArgs.unshift("--force-with-lease"); | |
169 | + } | |
170 | + await git.push(pushArgs); | |
171 | + } | |
172 | + | |
173 | + syncedCount++; | |
174 | + } catch (error) { | |
175 | + errors.push(`${branch}: ${error instanceof Error ? error.message : "Unknown error"}`); | |
176 | + } | |
177 | + } | |
178 | + | |
179 | + // Return to original branch | |
180 | + await git.checkout(currentBranch.trim()); | |
181 | + | |
182 | + if (errors.length > 0) { | |
183 | + spinner.warn(`Synced ${syncedCount}/${stackBranches.length} branches`); | |
184 | + console.log(chalk.yellow("\nIssues:")); | |
185 | + errors.forEach(e => console.log(chalk.dim(` • ${e}`))); | |
186 | + } else { | |
187 | + spinner.succeed(`Synced ${syncedCount} branches successfully`); | |
188 | + } | |
189 | + } catch (error) { | |
190 | + spinner.fail("Failed to sync stack"); | |
191 | + console.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); | |
192 | + console.log(chalk.dim("\nIf there are conflicts, resolve them and run:")); | |
193 | + console.log(chalk.cyan(" git rebase --continue")); | |
194 | + process.exit(1); | |
195 | + } | |
196 | + }); | |
197 | + | |
198 | +// Stack status | |
199 | +stackCommands | |
200 | + .command("status") | |
201 | + .alias("st") | |
202 | + .description("Show sync status for all stack branches") | |
203 | + .action(async () => { | |
204 | + const spinner = ora("Checking status...").start(); | |
205 | + | |
206 | + try { | |
207 | + await git.fetch(); | |
208 | + | |
209 | + const branches = await git.branchLocal(); | |
210 | + const stackBranches = branches.all.filter(b => b.startsWith("stack/")); | |
211 | + const currentBranch = await git.revparse(["--abbrev-ref", "HEAD"]); | |
212 | + | |
213 | + spinner.stop(); | |
214 | + | |
215 | + if (stackBranches.length === 0) { | |
216 | + console.log(chalk.dim("\nNo stack branches found.")); | |
217 | + return; | |
218 | + } | |
219 | + | |
220 | + console.log(chalk.bold("\n📊 Stack Status\n")); | |
221 | + | |
222 | + for (const branch of stackBranches) { | |
223 | + const isCurrent = currentBranch.trim() === branch; | |
224 | + | |
225 | + // Get ahead/behind | |
226 | + let ahead = 0; | |
227 | + let behind = 0; | |
228 | + let hasRemote = true; | |
229 | + | |
230 | + try { | |
231 | + const log = await git.raw([ | |
232 | + "rev-list", "--left-right", "--count", | |
233 | + `${branch}...origin/${branch}` | |
234 | + ]); | |
235 | + [ahead, behind] = log.trim().split("\t").map(Number); | |
236 | + } catch { | |
237 | + hasRemote = false; | |
238 | + } | |
239 | + | |
240 | + // Build status line | |
241 | + let status = ""; | |
242 | + if (!hasRemote) { | |
243 | + status = chalk.yellow("⚠ not pushed"); | |
244 | + } else if (ahead > 0 && behind > 0) { | |
245 | + status = chalk.red(`↑${ahead} ↓${behind} diverged`); | |
246 | + } else if (ahead > 0) { | |
247 | + status = chalk.cyan(`↑${ahead} ahead`); | |
248 | + } else if (behind > 0) { | |
249 | + status = chalk.magenta(`↓${behind} behind`); | |
250 | + } else { | |
251 | + status = chalk.green("✓ synced"); | |
252 | + } | |
253 | + | |
254 | + const marker = isCurrent ? chalk.yellow("● ") : chalk.dim("○ "); | |
255 | + console.log(` ${marker}${isCurrent ? chalk.bold(branch) : branch} ${status}`); | |
256 | + } | |
257 | + | |
258 | + console.log(""); | |
259 | + } catch (error) { | |
260 | + spinner.fail("Failed to get status"); | |
261 | + console.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); | |
262 | + process.exit(1); | |
263 | + } | |
264 | + }); | |
265 | + | |
266 | +// Rebase entire stack | |
267 | +stackCommands | |
268 | + .command("rebase") | |
269 | + .description("Rebase entire stack on base branch") | |
270 | + .option("-b, --base <branch>", "Base branch to rebase on", "main") | |
271 | + .action(async (options) => { | |
272 | + const spinner = ora(`Rebasing stack on ${options.base}...`).start(); | |
273 | + | |
274 | + try { | |
275 | + // Fetch latest | |
276 | + await git.fetch(); | |
277 | + | |
278 | + // Rebase | |
279 | + await git.rebase([options.base]); | |
280 | + | |
281 | + spinner.succeed(`Stack rebased on ${chalk.green(options.base)}`); | |
282 | + } catch (error) { | |
283 | + spinner.fail("Failed to rebase stack"); | |
284 | + console.error(chalk.red(error instanceof Error ? error.message : "Unknown error")); | |
285 | + process.exit(1); | |
286 | + } | |
287 | + }); | |
288 | + | |
289 | +export default stackCommands; | |
0 | 290 | |
▼cli/src/lib/api.ts
+48-0
| @@ -0,0 +1,48 @@ | ||
1 | +/** | |
2 | + * CLI API Client | |
3 | + * Make authenticated requests to OpenCodeHub | |
4 | + */ | |
5 | + | |
6 | +import { getConfig } from "./config.js"; | |
7 | + | |
8 | +export async function apiCall<T = any>( | |
9 | + serverUrl: string, | |
10 | + endpoint: string, | |
11 | + method: "GET" | "POST" | "PATCH" | "DELETE" = "GET", | |
12 | + body?: any | |
13 | +): Promise<T> { | |
14 | + const config = getConfig(); | |
15 | + const url = serverUrl || config.serverUrl; | |
16 | + | |
17 | + const headers: Record<string, string> = { | |
18 | + "Content-Type": "application/json", | |
19 | + }; | |
20 | + | |
21 | + if (config.token) { | |
22 | + headers["Authorization"] = `Bearer ${config.token}`; | |
23 | + } | |
24 | + | |
25 | + const response = await fetch(`${url}${endpoint}`, { | |
26 | + method, | |
27 | + headers, | |
28 | + body: body ? JSON.stringify(body) : undefined, | |
29 | + }); | |
30 | + | |
31 | + const data = await response.json(); | |
32 | + | |
33 | + if (!response.ok) { | |
34 | + throw new Error(data.error || `API error: ${response.status}`); | |
35 | + } | |
36 | + | |
37 | + return data; | |
38 | +} | |
39 | + | |
40 | +export async function getWithAuth<T = any>(endpoint: string): Promise<T> { | |
41 | + const config = getConfig(); | |
42 | + return apiCall<T>(config.serverUrl, endpoint, "GET"); | |
43 | +} | |
44 | + | |
45 | +export async function postWithAuth<T = any>(endpoint: string, body: any): Promise<T> { | |
46 | + const config = getConfig(); | |
47 | + return apiCall<T>(config.serverUrl, endpoint, "POST", body); | |
48 | +} | |
0 | 49 | |
▼cli/src/lib/config.ts
+45-0
| @@ -0,0 +1,45 @@ | ||
1 | +/** | |
2 | + * CLI Configuration | |
3 | + * Store and retrieve CLI settings | |
4 | + */ | |
5 | + | |
6 | +import Conf from "conf"; | |
7 | + | |
8 | +interface OchConfig { | |
9 | + serverUrl: string; | |
10 | + token: string; | |
11 | + defaultBranch?: string; | |
12 | +} | |
13 | + | |
14 | +const config = new Conf<OchConfig>({ | |
15 | + projectName: "opencodehub-cli", | |
16 | + defaults: { | |
17 | + serverUrl: "http://localhost:4321", | |
18 | + token: "", | |
19 | + defaultBranch: "main", | |
20 | + }, | |
21 | +}); | |
22 | + | |
23 | +export function getConfig(): OchConfig { | |
24 | + return { | |
25 | + serverUrl: config.get("serverUrl"), | |
26 | + token: config.get("token"), | |
27 | + defaultBranch: config.get("defaultBranch"), | |
28 | + }; | |
29 | +} | |
30 | + | |
31 | +export function saveConfig(updates: Partial<OchConfig>): void { | |
32 | + if (updates.serverUrl !== undefined) { | |
33 | + config.set("serverUrl", updates.serverUrl); | |
34 | + } | |
35 | + if (updates.token !== undefined) { | |
36 | + config.set("token", updates.token); | |
37 | + } | |
38 | + if (updates.defaultBranch !== undefined) { | |
39 | + config.set("defaultBranch", updates.defaultBranch); | |
40 | + } | |
41 | +} | |
42 | + | |
43 | +export function clearConfig(): void { | |
44 | + config.clear(); | |
45 | +} | |
0 | 46 | |
▼cli/tsconfig.json
+23-0
| @@ -0,0 +1,23 @@ | ||
1 | +{ | |
2 | + "compilerOptions": { | |
3 | + "target": "ES2022", | |
4 | + "module": "ESNext", | |
5 | + "moduleResolution": "node", | |
6 | + "outDir": "./dist", | |
7 | + "rootDir": ".", | |
8 | + "strict": true, | |
9 | + "esModuleInterop": true, | |
10 | + "skipLibCheck": true, | |
11 | + "forceConsistentCasingInFileNames": true, | |
12 | + "declaration": true, | |
13 | + "declarationMap": true | |
14 | + }, | |
15 | + "include": [ | |
16 | + "bin/**/*", | |
17 | + "src/**/*" | |
18 | + ], | |
19 | + "exclude": [ | |
20 | + "node_modules", | |
21 | + "dist" | |
22 | + ] | |
23 | +} | |
0 | 24 | |
▼drizzle.config.ts
+27-4
| @@ -1,12 +1,35 @@ | ||
1 | 1 | import { defineConfig } from "drizzle-kit"; |
2 | 2 | |
3 | +// Get driver type from environment | |
4 | +const driver = process.env.DATABASE_DRIVER || "sqlite"; | |
5 | +const url = process.env.DATABASE_URL || "./data/opencodehub.db"; | |
6 | + | |
7 | +// Map driver to Drizzle dialect | |
8 | +const dialectMap: Record<string, "sqlite" | "postgresql" | "mysql"> = { | |
9 | + sqlite: "sqlite", | |
10 | + postgres: "postgresql", | |
11 | + mysql: "mysql", | |
12 | +}; | |
13 | + | |
14 | +const dialect = dialectMap[driver] || "sqlite"; | |
15 | + | |
16 | +// Build credentials based on driver | |
17 | +function getCredentials() { | |
18 | + switch (driver) { | |
19 | + case "postgres": | |
20 | + case "mysql": | |
21 | + return { url }; | |
22 | + case "sqlite": | |
23 | + default: | |
24 | + return { url }; | |
25 | + } | |
26 | +} | |
27 | + | |
3 | 28 | export default defineConfig({ |
4 | 29 | schema: "./src/db/schema/index.ts", |
5 | 30 | out: "./drizzle", |
6 | - dialect: "sqlite", | |
7 | - dbCredentials: { | |
8 | - url: process.env.DATABASE_URL || "./data/opencodehub.db", | |
9 | - }, | |
31 | + dialect, | |
32 | + dbCredentials: getCredentials(), | |
10 | 33 | verbose: true, |
11 | 34 | strict: true, |
12 | 35 | }); |
13 | 36 | |
▼drizzle/0000_black_monster_badoon.sql
+1055-0
| @@ -0,0 +1,1055 @@ | ||
1 | +CREATE TABLE `email_verification_tokens` ( | |
2 | + `id` text PRIMARY KEY NOT NULL, | |
3 | + `user_id` text NOT NULL, | |
4 | + `token` text NOT NULL, | |
5 | + `expires_at` text NOT NULL, | |
6 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
7 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
8 | +); | |
9 | +--> statement-breakpoint | |
10 | +CREATE UNIQUE INDEX `email_verification_tokens_token_unique` ON `email_verification_tokens` (`token`);--> statement-breakpoint | |
11 | +CREATE TABLE `gpg_keys` ( | |
12 | + `id` text PRIMARY KEY NOT NULL, | |
13 | + `user_id` text NOT NULL, | |
14 | + `name` text NOT NULL, | |
15 | + `key_id` text NOT NULL, | |
16 | + `public_key` text NOT NULL, | |
17 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
18 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
19 | +); | |
20 | +--> statement-breakpoint | |
21 | +CREATE TABLE `oauth_accounts` ( | |
22 | + `id` text PRIMARY KEY NOT NULL, | |
23 | + `user_id` text NOT NULL, | |
24 | + `provider` text NOT NULL, | |
25 | + `provider_account_id` text NOT NULL, | |
26 | + `access_token` text, | |
27 | + `refresh_token` text, | |
28 | + `expires_at` text, | |
29 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
30 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
31 | +); | |
32 | +--> statement-breakpoint | |
33 | +CREATE TABLE `password_reset_tokens` ( | |
34 | + `id` text PRIMARY KEY NOT NULL, | |
35 | + `user_id` text NOT NULL, | |
36 | + `token` text NOT NULL, | |
37 | + `expires_at` text NOT NULL, | |
38 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
39 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
40 | +); | |
41 | +--> statement-breakpoint | |
42 | +CREATE UNIQUE INDEX `password_reset_tokens_token_unique` ON `password_reset_tokens` (`token`);--> statement-breakpoint | |
43 | +CREATE TABLE `personal_access_tokens` ( | |
44 | + `id` text PRIMARY KEY NOT NULL, | |
45 | + `user_id` text NOT NULL, | |
46 | + `name` text NOT NULL, | |
47 | + `token` text NOT NULL, | |
48 | + `expires_at` text, | |
49 | + `last_used_at` text, | |
50 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
51 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
52 | +); | |
53 | +--> statement-breakpoint | |
54 | +CREATE UNIQUE INDEX `personal_access_tokens_token_unique` ON `personal_access_tokens` (`token`);--> statement-breakpoint | |
55 | +CREATE TABLE `sessions` ( | |
56 | + `id` text PRIMARY KEY NOT NULL, | |
57 | + `user_id` text NOT NULL, | |
58 | + `token` text NOT NULL, | |
59 | + `user_agent` text, | |
60 | + `ip_address` text, | |
61 | + `expires_at` text NOT NULL, | |
62 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
63 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
64 | +); | |
65 | +--> statement-breakpoint | |
66 | +CREATE UNIQUE INDEX `sessions_token_unique` ON `sessions` (`token`);--> statement-breakpoint | |
67 | +CREATE TABLE `ssh_keys` ( | |
68 | + `id` text PRIMARY KEY NOT NULL, | |
69 | + `user_id` text NOT NULL, | |
70 | + `title` text NOT NULL, | |
71 | + `fingerprint` text NOT NULL, | |
72 | + `public_key` text NOT NULL, | |
73 | + `key_type` text NOT NULL, | |
74 | + `last_used_at` text, | |
75 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
76 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
77 | +); | |
78 | +--> statement-breakpoint | |
79 | +CREATE UNIQUE INDEX `ssh_keys_fingerprint_unique` ON `ssh_keys` (`fingerprint`);--> statement-breakpoint | |
80 | +CREATE TABLE `users` ( | |
81 | + `id` text PRIMARY KEY NOT NULL, | |
82 | + `username` text NOT NULL, | |
83 | + `email` text NOT NULL, | |
84 | + `password_hash` text, | |
85 | + `display_name` text, | |
86 | + `bio` text, | |
87 | + `avatar_url` text, | |
88 | + `location` text, | |
89 | + `website` text, | |
90 | + `company` text, | |
91 | + `is_admin` integer DEFAULT false, | |
92 | + `is_active` integer DEFAULT true, | |
93 | + `email_verified` integer DEFAULT false, | |
94 | + `two_factor_enabled` integer DEFAULT false, | |
95 | + `two_factor_secret` text, | |
96 | + `last_login_at` text, | |
97 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
98 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL | |
99 | +); | |
100 | +--> statement-breakpoint | |
101 | +CREATE UNIQUE INDEX `users_username_unique` ON `users` (`username`);--> statement-breakpoint | |
102 | +CREATE UNIQUE INDEX `users_email_unique` ON `users` (`email`);--> statement-breakpoint | |
103 | +CREATE TABLE `branches` ( | |
104 | + `id` text PRIMARY KEY NOT NULL, | |
105 | + `repository_id` text NOT NULL, | |
106 | + `name` text NOT NULL, | |
107 | + `commit_sha` text NOT NULL, | |
108 | + `is_protected` integer DEFAULT false, | |
109 | + `is_default` integer DEFAULT false, | |
110 | + `protection_rules` text, | |
111 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
112 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
113 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
114 | +); | |
115 | +--> statement-breakpoint | |
116 | +CREATE TABLE `commits` ( | |
117 | + `id` text PRIMARY KEY NOT NULL, | |
118 | + `repository_id` text NOT NULL, | |
119 | + `sha` text NOT NULL, | |
120 | + `message` text NOT NULL, | |
121 | + `author_name` text NOT NULL, | |
122 | + `author_email` text NOT NULL, | |
123 | + `author_date` text NOT NULL, | |
124 | + `committer_name` text NOT NULL, | |
125 | + `committer_email` text NOT NULL, | |
126 | + `committer_date` text NOT NULL, | |
127 | + `parent_shas` text, | |
128 | + `tree_sha` text, | |
129 | + `user_id` text, | |
130 | + `stats` text, | |
131 | + `signature` text, | |
132 | + `is_verified` integer DEFAULT false, | |
133 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
134 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
135 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
136 | +); | |
137 | +--> statement-breakpoint | |
138 | +CREATE TABLE `releases` ( | |
139 | + `id` text PRIMARY KEY NOT NULL, | |
140 | + `repository_id` text NOT NULL, | |
141 | + `tag_id` text, | |
142 | + `name` text NOT NULL, | |
143 | + `body` text, | |
144 | + `is_draft` integer DEFAULT false, | |
145 | + `is_prerelease` integer DEFAULT false, | |
146 | + `author_id` text, | |
147 | + `assets` text, | |
148 | + `download_count` integer DEFAULT 0, | |
149 | + `published_at` text, | |
150 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
151 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
152 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
153 | + FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON UPDATE no action ON DELETE no action, | |
154 | + FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
155 | +); | |
156 | +--> statement-breakpoint | |
157 | +CREATE TABLE `repositories` ( | |
158 | + `id` text PRIMARY KEY NOT NULL, | |
159 | + `name` text NOT NULL, | |
160 | + `slug` text NOT NULL, | |
161 | + `description` text, | |
162 | + `owner_id` text NOT NULL, | |
163 | + `owner_type` text DEFAULT 'user' NOT NULL, | |
164 | + `visibility` text DEFAULT 'public' NOT NULL, | |
165 | + `default_branch` text DEFAULT 'main' NOT NULL, | |
166 | + `disk_path` text NOT NULL, | |
167 | + `ssh_clone_url` text, | |
168 | + `http_clone_url` text, | |
169 | + `star_count` integer DEFAULT 0, | |
170 | + `fork_count` integer DEFAULT 0, | |
171 | + `watch_count` integer DEFAULT 0, | |
172 | + `open_issue_count` integer DEFAULT 0, | |
173 | + `open_pr_count` integer DEFAULT 0, | |
174 | + `size` integer DEFAULT 0, | |
175 | + `is_fork` integer DEFAULT false, | |
176 | + `forked_from_id` text, | |
177 | + `is_archived` integer DEFAULT false, | |
178 | + `is_mirror` integer DEFAULT false, | |
179 | + `mirror_url` text, | |
180 | + `has_issues` integer DEFAULT true, | |
181 | + `has_wiki` integer DEFAULT true, | |
182 | + `has_actions` integer DEFAULT true, | |
183 | + `allow_forking` integer DEFAULT true, | |
184 | + `license_type` text, | |
185 | + `topics` text, | |
186 | + `language` text, | |
187 | + `languages` text, | |
188 | + `last_activity_at` text, | |
189 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
190 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
191 | + FOREIGN KEY (`owner_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
192 | + FOREIGN KEY (`forked_from_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action | |
193 | +); | |
194 | +--> statement-breakpoint | |
195 | +CREATE TABLE `repository_collaborators` ( | |
196 | + `id` text PRIMARY KEY NOT NULL, | |
197 | + `repository_id` text NOT NULL, | |
198 | + `user_id` text NOT NULL, | |
199 | + `role` text DEFAULT 'developer' NOT NULL, | |
200 | + `added_by_id` text, | |
201 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
202 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
203 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
204 | + FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
205 | +); | |
206 | +--> statement-breakpoint | |
207 | +CREATE TABLE `repository_stars` ( | |
208 | + `id` text PRIMARY KEY NOT NULL, | |
209 | + `repository_id` text NOT NULL, | |
210 | + `user_id` text NOT NULL, | |
211 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
212 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
213 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
214 | +); | |
215 | +--> statement-breakpoint | |
216 | +CREATE TABLE `repository_watchers` ( | |
217 | + `id` text PRIMARY KEY NOT NULL, | |
218 | + `repository_id` text NOT NULL, | |
219 | + `user_id` text NOT NULL, | |
220 | + `watch_level` text DEFAULT 'watching' NOT NULL, | |
221 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
222 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
223 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
224 | +); | |
225 | +--> statement-breakpoint | |
226 | +CREATE TABLE `tags` ( | |
227 | + `id` text PRIMARY KEY NOT NULL, | |
228 | + `repository_id` text NOT NULL, | |
229 | + `name` text NOT NULL, | |
230 | + `commit_sha` text NOT NULL, | |
231 | + `message` text, | |
232 | + `tagger_name` text, | |
233 | + `tagger_email` text, | |
234 | + `tagged_at` text, | |
235 | + `is_release` integer DEFAULT false, | |
236 | + `release_id` text, | |
237 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
238 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
239 | +); | |
240 | +--> statement-breakpoint | |
241 | +CREATE TABLE `webhooks` ( | |
242 | + `id` text PRIMARY KEY NOT NULL, | |
243 | + `repository_id` text NOT NULL, | |
244 | + `url` text NOT NULL, | |
245 | + `secret` text, | |
246 | + `events` text NOT NULL, | |
247 | + `is_active` integer DEFAULT true, | |
248 | + `last_delivery_at` text, | |
249 | + `last_delivery_status` integer, | |
250 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
251 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
252 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
253 | +); | |
254 | +--> statement-breakpoint | |
255 | +CREATE TABLE `issue_assignees` ( | |
256 | + `id` text PRIMARY KEY NOT NULL, | |
257 | + `issue_id` text NOT NULL, | |
258 | + `user_id` text NOT NULL, | |
259 | + `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
260 | + FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
261 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
262 | +); | |
263 | +--> statement-breakpoint | |
264 | +CREATE TABLE `issue_comments` ( | |
265 | + `id` text PRIMARY KEY NOT NULL, | |
266 | + `issue_id` text NOT NULL, | |
267 | + `author_id` text NOT NULL, | |
268 | + `body` text NOT NULL, | |
269 | + `reactions` text, | |
270 | + `is_edited` integer DEFAULT false, | |
271 | + `edited_at` text, | |
272 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
273 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
274 | + FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
275 | + FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
276 | +); | |
277 | +--> statement-breakpoint | |
278 | +CREATE TABLE `issue_labels` ( | |
279 | + `id` text PRIMARY KEY NOT NULL, | |
280 | + `issue_id` text NOT NULL, | |
281 | + `label_id` text NOT NULL, | |
282 | + FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
283 | + FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade | |
284 | +); | |
285 | +--> statement-breakpoint | |
286 | +CREATE TABLE `issue_subscribers` ( | |
287 | + `id` text PRIMARY KEY NOT NULL, | |
288 | + `issue_id` text NOT NULL, | |
289 | + `user_id` text NOT NULL, | |
290 | + `subscribed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
291 | + FOREIGN KEY (`issue_id`) REFERENCES `issues`(`id`) ON UPDATE no action ON DELETE cascade, | |
292 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
293 | +); | |
294 | +--> statement-breakpoint | |
295 | +CREATE TABLE `issues` ( | |
296 | + `id` text PRIMARY KEY NOT NULL, | |
297 | + `repository_id` text NOT NULL, | |
298 | + `number` integer NOT NULL, | |
299 | + `title` text NOT NULL, | |
300 | + `body` text, | |
301 | + `state` text DEFAULT 'open' NOT NULL, | |
302 | + `author_id` text NOT NULL, | |
303 | + `assignee_id` text, | |
304 | + `milestone_id` text, | |
305 | + `is_pinned` integer DEFAULT false, | |
306 | + `is_locked` integer DEFAULT false, | |
307 | + `lock_reason` text, | |
308 | + `comment_count` integer DEFAULT 0, | |
309 | + `reactions` text, | |
310 | + `closed_at` text, | |
311 | + `closed_by_id` text, | |
312 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
313 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
314 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
315 | + FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
316 | + FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
317 | + FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action, | |
318 | + FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
319 | +); | |
320 | +--> statement-breakpoint | |
321 | +CREATE TABLE `labels` ( | |
322 | + `id` text PRIMARY KEY NOT NULL, | |
323 | + `repository_id` text NOT NULL, | |
324 | + `name` text NOT NULL, | |
325 | + `color` text DEFAULT '#6b7280' NOT NULL, | |
326 | + `description` text, | |
327 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
328 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
329 | +); | |
330 | +--> statement-breakpoint | |
331 | +CREATE TABLE `milestones` ( | |
332 | + `id` text PRIMARY KEY NOT NULL, | |
333 | + `repository_id` text NOT NULL, | |
334 | + `title` text NOT NULL, | |
335 | + `description` text, | |
336 | + `state` text DEFAULT 'open' NOT NULL, | |
337 | + `due_date` text, | |
338 | + `open_issue_count` integer DEFAULT 0, | |
339 | + `closed_issue_count` integer DEFAULT 0, | |
340 | + `closed_at` text, | |
341 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
342 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
343 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
344 | +); | |
345 | +--> statement-breakpoint | |
346 | +CREATE TABLE `pull_request_assignees` ( | |
347 | + `id` text PRIMARY KEY NOT NULL, | |
348 | + `pull_request_id` text NOT NULL, | |
349 | + `user_id` text NOT NULL, | |
350 | + `assigned_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
351 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
352 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
353 | +); | |
354 | +--> statement-breakpoint | |
355 | +CREATE TABLE `pull_request_checks` ( | |
356 | + `id` text PRIMARY KEY NOT NULL, | |
357 | + `pull_request_id` text NOT NULL, | |
358 | + `name` text NOT NULL, | |
359 | + `status` text NOT NULL, | |
360 | + `conclusion` text, | |
361 | + `head_sha` text NOT NULL, | |
362 | + `external_id` text, | |
363 | + `details_url` text, | |
364 | + `output` text, | |
365 | + `started_at` text, | |
366 | + `completed_at` text, | |
367 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
368 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
369 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade | |
370 | +); | |
371 | +--> statement-breakpoint | |
372 | +CREATE TABLE `pull_request_comments` ( | |
373 | + `id` text PRIMARY KEY NOT NULL, | |
374 | + `pull_request_id` text NOT NULL, | |
375 | + `review_id` text, | |
376 | + `author_id` text NOT NULL, | |
377 | + `body` text NOT NULL, | |
378 | + `path` text, | |
379 | + `line` integer, | |
380 | + `side` text, | |
381 | + `start_line` integer, | |
382 | + `start_side` text, | |
383 | + `commit_sha` text, | |
384 | + `original_commit_sha` text, | |
385 | + `original_line` integer, | |
386 | + `in_reply_to_id` text, | |
387 | + `reactions` text, | |
388 | + `is_resolved` integer DEFAULT false, | |
389 | + `resolved_by_id` text, | |
390 | + `resolved_at` text, | |
391 | + `is_edited` integer DEFAULT false, | |
392 | + `edited_at` text, | |
393 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
394 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
395 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
396 | + FOREIGN KEY (`review_id`) REFERENCES `pull_request_reviews`(`id`) ON UPDATE no action ON DELETE no action, | |
397 | + FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
398 | + FOREIGN KEY (`in_reply_to_id`) REFERENCES `pull_request_comments`(`id`) ON UPDATE no action ON DELETE no action, | |
399 | + FOREIGN KEY (`resolved_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
400 | +); | |
401 | +--> statement-breakpoint | |
402 | +CREATE TABLE `pull_request_labels` ( | |
403 | + `id` text PRIMARY KEY NOT NULL, | |
404 | + `pull_request_id` text NOT NULL, | |
405 | + `label_id` text NOT NULL, | |
406 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
407 | + FOREIGN KEY (`label_id`) REFERENCES `labels`(`id`) ON UPDATE no action ON DELETE cascade | |
408 | +); | |
409 | +--> statement-breakpoint | |
410 | +CREATE TABLE `pull_request_reviewers` ( | |
411 | + `id` text PRIMARY KEY NOT NULL, | |
412 | + `pull_request_id` text NOT NULL, | |
413 | + `user_id` text NOT NULL, | |
414 | + `is_required` integer DEFAULT false, | |
415 | + `requested_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
416 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
417 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
418 | +); | |
419 | +--> statement-breakpoint | |
420 | +CREATE TABLE `pull_request_reviews` ( | |
421 | + `id` text PRIMARY KEY NOT NULL, | |
422 | + `pull_request_id` text NOT NULL, | |
423 | + `reviewer_id` text NOT NULL, | |
424 | + `state` text NOT NULL, | |
425 | + `body` text, | |
426 | + `commit_sha` text, | |
427 | + `submitted_at` text, | |
428 | + `dismissed_at` text, | |
429 | + `dismissed_by_id` text, | |
430 | + `dismissal_reason` text, | |
431 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
432 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
433 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
434 | + FOREIGN KEY (`reviewer_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
435 | + FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
436 | +); | |
437 | +--> statement-breakpoint | |
438 | +CREATE TABLE `pull_requests` ( | |
439 | + `id` text PRIMARY KEY NOT NULL, | |
440 | + `repository_id` text NOT NULL, | |
441 | + `number` integer NOT NULL, | |
442 | + `title` text NOT NULL, | |
443 | + `body` text, | |
444 | + `state` text DEFAULT 'open' NOT NULL, | |
445 | + `author_id` text NOT NULL, | |
446 | + `assignee_id` text, | |
447 | + `milestone_id` text, | |
448 | + `head_branch` text NOT NULL, | |
449 | + `head_sha` text NOT NULL, | |
450 | + `head_repository_id` text, | |
451 | + `base_branch` text NOT NULL, | |
452 | + `base_sha` text NOT NULL, | |
453 | + `is_draft` integer DEFAULT false, | |
454 | + `is_merged` integer DEFAULT false, | |
455 | + `merged_at` text, | |
456 | + `merged_by_id` text, | |
457 | + `merge_sha` text, | |
458 | + `merge_method` text, | |
459 | + `additions` integer DEFAULT 0, | |
460 | + `deletions` integer DEFAULT 0, | |
461 | + `changed_files` integer DEFAULT 0, | |
462 | + `comment_count` integer DEFAULT 0, | |
463 | + `review_count` integer DEFAULT 0, | |
464 | + `mergeable` integer, | |
465 | + `mergeable_state` text, | |
466 | + `rebaseable` integer, | |
467 | + `maintainer_can_modify` integer DEFAULT true, | |
468 | + `allow_auto_merge` integer DEFAULT false, | |
469 | + `auto_merge_method` text, | |
470 | + `closed_at` text, | |
471 | + `closed_by_id` text, | |
472 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
473 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
474 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
475 | + FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
476 | + FOREIGN KEY (`assignee_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
477 | + FOREIGN KEY (`milestone_id`) REFERENCES `milestones`(`id`) ON UPDATE no action ON DELETE no action, | |
478 | + FOREIGN KEY (`head_repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action, | |
479 | + FOREIGN KEY (`merged_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
480 | + FOREIGN KEY (`closed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
481 | +); | |
482 | +--> statement-breakpoint | |
483 | +CREATE TABLE `scheduled_workflows` ( | |
484 | + `id` text PRIMARY KEY NOT NULL, | |
485 | + `workflow_id` text NOT NULL, | |
486 | + `cron_expression` text NOT NULL, | |
487 | + `timezone` text DEFAULT 'UTC', | |
488 | + `is_enabled` integer DEFAULT true, | |
489 | + `last_run_at` text, | |
490 | + `next_run_at` text, | |
491 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
492 | + FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade | |
493 | +); | |
494 | +--> statement-breakpoint | |
495 | +CREATE TABLE `workflow_artifacts` ( | |
496 | + `id` text PRIMARY KEY NOT NULL, | |
497 | + `run_id` text NOT NULL, | |
498 | + `job_id` text, | |
499 | + `name` text NOT NULL, | |
500 | + `size_bytes` integer NOT NULL, | |
501 | + `storage_path` text NOT NULL, | |
502 | + `mime_type` text, | |
503 | + `download_count` integer DEFAULT 0, | |
504 | + `expires_at` text, | |
505 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
506 | + FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade, | |
507 | + FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE no action | |
508 | +); | |
509 | +--> statement-breakpoint | |
510 | +CREATE TABLE `workflow_jobs` ( | |
511 | + `id` text PRIMARY KEY NOT NULL, | |
512 | + `run_id` text NOT NULL, | |
513 | + `name` text NOT NULL, | |
514 | + `status` text DEFAULT 'queued' NOT NULL, | |
515 | + `conclusion` text, | |
516 | + `runner_id` text, | |
517 | + `runner_name` text, | |
518 | + `runner_group_id` text, | |
519 | + `runner_group_name` text, | |
520 | + `container_id` text, | |
521 | + `container_image` text, | |
522 | + `needs` text, | |
523 | + `environment` text, | |
524 | + `environment_url` text, | |
525 | + `matrix` text, | |
526 | + `started_at` text, | |
527 | + `completed_at` text, | |
528 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
529 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
530 | + FOREIGN KEY (`run_id`) REFERENCES `workflow_runs`(`id`) ON UPDATE no action ON DELETE cascade | |
531 | +); | |
532 | +--> statement-breakpoint | |
533 | +CREATE TABLE `workflow_logs` ( | |
534 | + `id` text PRIMARY KEY NOT NULL, | |
535 | + `job_id` text NOT NULL, | |
536 | + `step_id` text, | |
537 | + `log_level` text DEFAULT 'info' NOT NULL, | |
538 | + `message` text NOT NULL, | |
539 | + `timestamp` text NOT NULL, | |
540 | + `line_number` integer, | |
541 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
542 | + FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade, | |
543 | + FOREIGN KEY (`step_id`) REFERENCES `workflow_steps`(`id`) ON UPDATE no action ON DELETE no action | |
544 | +); | |
545 | +--> statement-breakpoint | |
546 | +CREATE TABLE `workflow_runs` ( | |
547 | + `id` text PRIMARY KEY NOT NULL, | |
548 | + `workflow_id` text NOT NULL, | |
549 | + `repository_id` text NOT NULL, | |
550 | + `run_number` integer NOT NULL, | |
551 | + `run_attempt` integer DEFAULT 1 NOT NULL, | |
552 | + `name` text NOT NULL, | |
553 | + `display_title` text, | |
554 | + `status` text DEFAULT 'queued' NOT NULL, | |
555 | + `conclusion` text, | |
556 | + `event` text NOT NULL, | |
557 | + `head_branch` text, | |
558 | + `head_sha` text NOT NULL, | |
559 | + `base_branch` text, | |
560 | + `base_sha` text, | |
561 | + `pull_request_id` text, | |
562 | + `triggered_by_id` text, | |
563 | + `started_at` text, | |
564 | + `completed_at` text, | |
565 | + `workflow_config` text, | |
566 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
567 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
568 | + FOREIGN KEY (`workflow_id`) REFERENCES `workflows`(`id`) ON UPDATE no action ON DELETE cascade, | |
569 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
570 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action, | |
571 | + FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
572 | +); | |
573 | +--> statement-breakpoint | |
574 | +CREATE TABLE `workflow_secrets` ( | |
575 | + `id` text PRIMARY KEY NOT NULL, | |
576 | + `repository_id` text NOT NULL, | |
577 | + `name` text NOT NULL, | |
578 | + `encrypted_value` text NOT NULL, | |
579 | + `environment` text, | |
580 | + `created_by_id` text, | |
581 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
582 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
583 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
584 | + FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
585 | +); | |
586 | +--> statement-breakpoint | |
587 | +CREATE TABLE `workflow_steps` ( | |
588 | + `id` text PRIMARY KEY NOT NULL, | |
589 | + `job_id` text NOT NULL, | |
590 | + `number` integer NOT NULL, | |
591 | + `name` text NOT NULL, | |
592 | + `status` text DEFAULT 'queued' NOT NULL, | |
593 | + `conclusion` text, | |
594 | + `uses` text, | |
595 | + `run` text, | |
596 | + `shell` text, | |
597 | + `working_directory` text, | |
598 | + `env` text, | |
599 | + `with` text, | |
600 | + `if` text, | |
601 | + `continue_on_error` integer DEFAULT false, | |
602 | + `timeout_minutes` integer, | |
603 | + `started_at` text, | |
604 | + `completed_at` text, | |
605 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
606 | + FOREIGN KEY (`job_id`) REFERENCES `workflow_jobs`(`id`) ON UPDATE no action ON DELETE cascade | |
607 | +); | |
608 | +--> statement-breakpoint | |
609 | +CREATE TABLE `workflow_variables` ( | |
610 | + `id` text PRIMARY KEY NOT NULL, | |
611 | + `repository_id` text NOT NULL, | |
612 | + `name` text NOT NULL, | |
613 | + `value` text NOT NULL, | |
614 | + `environment` text, | |
615 | + `created_by_id` text, | |
616 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
617 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
618 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
619 | + FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
620 | +); | |
621 | +--> statement-breakpoint | |
622 | +CREATE TABLE `workflows` ( | |
623 | + `id` text PRIMARY KEY NOT NULL, | |
624 | + `repository_id` text NOT NULL, | |
625 | + `name` text NOT NULL, | |
626 | + `path` text NOT NULL, | |
627 | + `state` text DEFAULT 'active' NOT NULL, | |
628 | + `badge_url` text, | |
629 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
630 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
631 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
632 | +); | |
633 | +--> statement-breakpoint | |
634 | +CREATE TABLE `wiki_pages` ( | |
635 | + `id` text PRIMARY KEY NOT NULL, | |
636 | + `repository_id` text NOT NULL, | |
637 | + `slug` text NOT NULL, | |
638 | + `title` text NOT NULL, | |
639 | + `content` text NOT NULL, | |
640 | + `format` text DEFAULT 'markdown' NOT NULL, | |
641 | + `parent_id` text, | |
642 | + `order` integer DEFAULT 0, | |
643 | + `last_editor_id` text, | |
644 | + `view_count` integer DEFAULT 0, | |
645 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
646 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
647 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
648 | + FOREIGN KEY (`parent_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE no action, | |
649 | + FOREIGN KEY (`last_editor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
650 | +); | |
651 | +--> statement-breakpoint | |
652 | +CREATE TABLE `wiki_revisions` ( | |
653 | + `id` text PRIMARY KEY NOT NULL, | |
654 | + `page_id` text NOT NULL, | |
655 | + `content` text NOT NULL, | |
656 | + `message` text, | |
657 | + `author_id` text NOT NULL, | |
658 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
659 | + FOREIGN KEY (`page_id`) REFERENCES `wiki_pages`(`id`) ON UPDATE no action ON DELETE cascade, | |
660 | + FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
661 | +); | |
662 | +--> statement-breakpoint | |
663 | +CREATE TABLE `organization_members` ( | |
664 | + `id` text PRIMARY KEY NOT NULL, | |
665 | + `organization_id` text NOT NULL, | |
666 | + `user_id` text NOT NULL, | |
667 | + `role` text DEFAULT 'member' NOT NULL, | |
668 | + `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
669 | + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, | |
670 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
671 | +); | |
672 | +--> statement-breakpoint | |
673 | +CREATE TABLE `organizations` ( | |
674 | + `id` text PRIMARY KEY NOT NULL, | |
675 | + `name` text NOT NULL, | |
676 | + `display_name` text, | |
677 | + `description` text, | |
678 | + `avatar_url` text, | |
679 | + `website` text, | |
680 | + `location` text, | |
681 | + `email` text, | |
682 | + `is_verified` integer DEFAULT false, | |
683 | + `visibility` text DEFAULT 'public' NOT NULL, | |
684 | + `default_repo_permission` text DEFAULT 'read', | |
685 | + `members_can_create_repos` integer DEFAULT true, | |
686 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
687 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL | |
688 | +); | |
689 | +--> statement-breakpoint | |
690 | +CREATE UNIQUE INDEX `organizations_name_unique` ON `organizations` (`name`);--> statement-breakpoint | |
691 | +CREATE TABLE `team_members` ( | |
692 | + `id` text PRIMARY KEY NOT NULL, | |
693 | + `team_id` text NOT NULL, | |
694 | + `user_id` text NOT NULL, | |
695 | + `role` text DEFAULT 'member' NOT NULL, | |
696 | + `joined_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
697 | + FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade, | |
698 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade | |
699 | +); | |
700 | +--> statement-breakpoint | |
701 | +CREATE TABLE `team_repositories` ( | |
702 | + `id` text PRIMARY KEY NOT NULL, | |
703 | + `team_id` text NOT NULL, | |
704 | + `repository_id` text NOT NULL, | |
705 | + `permission` text DEFAULT 'read' NOT NULL, | |
706 | + `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
707 | + FOREIGN KEY (`team_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE cascade, | |
708 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
709 | +); | |
710 | +--> statement-breakpoint | |
711 | +CREATE TABLE `teams` ( | |
712 | + `id` text PRIMARY KEY NOT NULL, | |
713 | + `organization_id` text NOT NULL, | |
714 | + `name` text NOT NULL, | |
715 | + `slug` text NOT NULL, | |
716 | + `description` text, | |
717 | + `privacy` text DEFAULT 'visible' NOT NULL, | |
718 | + `parent_id` text, | |
719 | + `permission` text DEFAULT 'read' NOT NULL, | |
720 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
721 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
722 | + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, | |
723 | + FOREIGN KEY (`parent_id`) REFERENCES `teams`(`id`) ON UPDATE no action ON DELETE no action | |
724 | +); | |
725 | +--> statement-breakpoint | |
726 | +CREATE TABLE `activities` ( | |
727 | + `id` text PRIMARY KEY NOT NULL, | |
728 | + `user_id` text NOT NULL, | |
729 | + `repository_id` text, | |
730 | + `organization_id` text, | |
731 | + `type` text NOT NULL, | |
732 | + `action` text NOT NULL, | |
733 | + `ref_type` text, | |
734 | + `ref_name` text, | |
735 | + `target_type` text, | |
736 | + `target_id` text, | |
737 | + `payload` text, | |
738 | + `is_public` integer DEFAULT true, | |
739 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
740 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
741 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
742 | + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action | |
743 | +); | |
744 | +--> statement-breakpoint | |
745 | +CREATE TABLE `audit_logs` ( | |
746 | + `id` text PRIMARY KEY NOT NULL, | |
747 | + `user_id` text, | |
748 | + `organization_id` text, | |
749 | + `repository_id` text, | |
750 | + `action` text NOT NULL, | |
751 | + `actor_type` text DEFAULT 'user' NOT NULL, | |
752 | + `actor_id` text, | |
753 | + `actor_ip` text, | |
754 | + `actor_user_agent` text, | |
755 | + `target_type` text, | |
756 | + `target_id` text, | |
757 | + `target_name` text, | |
758 | + `data` text, | |
759 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
760 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
761 | + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action, | |
762 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action | |
763 | +); | |
764 | +--> statement-breakpoint | |
765 | +CREATE TABLE `notifications` ( | |
766 | + `id` text PRIMARY KEY NOT NULL, | |
767 | + `user_id` text NOT NULL, | |
768 | + `repository_id` text, | |
769 | + `type` text NOT NULL, | |
770 | + `title` text NOT NULL, | |
771 | + `body` text, | |
772 | + `url` text, | |
773 | + `actor_id` text, | |
774 | + `subject_type` text, | |
775 | + `subject_id` text, | |
776 | + `reason` text NOT NULL, | |
777 | + `is_read` integer DEFAULT false, | |
778 | + `read_at` text, | |
779 | + `is_archived` integer DEFAULT false, | |
780 | + `archived_at` text, | |
781 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
782 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
783 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
784 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE no action, | |
785 | + FOREIGN KEY (`actor_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
786 | +); | |
787 | +--> statement-breakpoint | |
788 | +CREATE TABLE `security_scans` ( | |
789 | + `id` text PRIMARY KEY NOT NULL, | |
790 | + `repository_id` text NOT NULL, | |
791 | + `status` text DEFAULT 'queued' NOT NULL, | |
792 | + `started_at` integer, | |
793 | + `completed_at` integer, | |
794 | + `critical_count` integer DEFAULT 0, | |
795 | + `high_count` integer DEFAULT 0, | |
796 | + `medium_count` integer DEFAULT 0, | |
797 | + `low_count` integer DEFAULT 0, | |
798 | + `unknown_count` integer DEFAULT 0, | |
799 | + `logs` text, | |
800 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
801 | +); | |
802 | +--> statement-breakpoint | |
803 | +CREATE TABLE `security_vulnerabilities` ( | |
804 | + `id` text PRIMARY KEY NOT NULL, | |
805 | + `scan_id` text NOT NULL, | |
806 | + `vulnerability_id` text NOT NULL, | |
807 | + `pkg_name` text NOT NULL, | |
808 | + `installed_version` text, | |
809 | + `fixed_version` text, | |
810 | + `severity` text NOT NULL, | |
811 | + `title` text, | |
812 | + `description` text, | |
813 | + `target` text, | |
814 | + `class` text, | |
815 | + FOREIGN KEY (`scan_id`) REFERENCES `security_scans`(`id`) ON UPDATE no action ON DELETE cascade | |
816 | +); | |
817 | +--> statement-breakpoint | |
818 | +CREATE TABLE `pipeline_runners` ( | |
819 | + `id` text PRIMARY KEY NOT NULL, | |
820 | + `repository_id` text, | |
821 | + `token` text NOT NULL, | |
822 | + `name` text NOT NULL, | |
823 | + `os` text, | |
824 | + `arch` text, | |
825 | + `version` text, | |
826 | + `status` text DEFAULT 'offline' NOT NULL, | |
827 | + `last_seen_at` text, | |
828 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
829 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
830 | +); | |
831 | +--> statement-breakpoint | |
832 | +CREATE TABLE `pr_stack_entries` ( | |
833 | + `id` text PRIMARY KEY NOT NULL, | |
834 | + `stack_id` text NOT NULL, | |
835 | + `pull_request_id` text NOT NULL, | |
836 | + `stack_order` integer NOT NULL, | |
837 | + `parent_pr_id` text, | |
838 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
839 | + FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE cascade, | |
840 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
841 | + FOREIGN KEY (`parent_pr_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE no action | |
842 | +); | |
843 | +--> statement-breakpoint | |
844 | +CREATE TABLE `pr_stacks` ( | |
845 | + `id` text PRIMARY KEY NOT NULL, | |
846 | + `repository_id` text NOT NULL, | |
847 | + `name` text, | |
848 | + `base_branch` text NOT NULL, | |
849 | + `status` text DEFAULT 'active' NOT NULL, | |
850 | + `created_by_id` text NOT NULL, | |
851 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
852 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
853 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
854 | + FOREIGN KEY (`created_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
855 | +); | |
856 | +--> statement-breakpoint | |
857 | +CREATE TABLE `merge_queue` ( | |
858 | + `id` text PRIMARY KEY NOT NULL, | |
859 | + `repository_id` text NOT NULL, | |
860 | + `pull_request_id` text NOT NULL, | |
861 | + `stack_id` text, | |
862 | + `status` text DEFAULT 'pending' NOT NULL, | |
863 | + `priority` integer DEFAULT 0 NOT NULL, | |
864 | + `position` integer, | |
865 | + `ci_status` text DEFAULT 'pending', | |
866 | + `ci_run_id` text, | |
867 | + `added_by_id` text NOT NULL, | |
868 | + `added_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
869 | + `estimated_merge_at` text, | |
870 | + `started_at` text, | |
871 | + `completed_at` text, | |
872 | + `failure_reason` text, | |
873 | + `retry_count` integer DEFAULT 0, | |
874 | + `merge_method` text DEFAULT 'merge', | |
875 | + `delete_on_merge` integer DEFAULT true, | |
876 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
877 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
878 | + FOREIGN KEY (`stack_id`) REFERENCES `pr_stacks`(`id`) ON UPDATE no action ON DELETE no action, | |
879 | + FOREIGN KEY (`added_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
880 | +); | |
881 | +--> statement-breakpoint | |
882 | +CREATE TABLE `ai_review_suggestions` ( | |
883 | + `id` text PRIMARY KEY NOT NULL, | |
884 | + `ai_review_id` text NOT NULL, | |
885 | + `path` text NOT NULL, | |
886 | + `line` integer, | |
887 | + `end_line` integer, | |
888 | + `severity` text NOT NULL, | |
889 | + `type` text NOT NULL, | |
890 | + `category` text, | |
891 | + `title` text NOT NULL, | |
892 | + `message` text NOT NULL, | |
893 | + `suggested_fix` text, | |
894 | + `explanation` text, | |
895 | + `is_applied` integer DEFAULT false, | |
896 | + `is_dismissed` integer DEFAULT false, | |
897 | + `applied_at` text, | |
898 | + `applied_by_id` text, | |
899 | + `dismissed_at` text, | |
900 | + `dismissed_by_id` text, | |
901 | + `dismiss_reason` text, | |
902 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
903 | + FOREIGN KEY (`ai_review_id`) REFERENCES `ai_reviews`(`id`) ON UPDATE no action ON DELETE cascade, | |
904 | + FOREIGN KEY (`applied_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action, | |
905 | + FOREIGN KEY (`dismissed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
906 | +); | |
907 | +--> statement-breakpoint | |
908 | +CREATE TABLE `ai_reviews` ( | |
909 | + `id` text PRIMARY KEY NOT NULL, | |
910 | + `pull_request_id` text NOT NULL, | |
911 | + `status` text DEFAULT 'pending' NOT NULL, | |
912 | + `model` text NOT NULL, | |
913 | + `provider` text NOT NULL, | |
914 | + `stack_context` text, | |
915 | + `includes_stack_context` integer DEFAULT false, | |
916 | + `summary` text, | |
917 | + `overall_severity` text, | |
918 | + `suggestions_count` integer DEFAULT 0, | |
919 | + `tokens_used` integer, | |
920 | + `prompt_tokens` integer, | |
921 | + `completion_tokens` integer, | |
922 | + `cost_cents` integer, | |
923 | + `raw_response` text, | |
924 | + `triggered_by_id` text, | |
925 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
926 | + `started_at` text, | |
927 | + `completed_at` text, | |
928 | + `error_message` text, | |
929 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
930 | + FOREIGN KEY (`triggered_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
931 | +); | |
932 | +--> statement-breakpoint | |
933 | +CREATE TABLE `pr_metrics` ( | |
934 | + `id` text PRIMARY KEY NOT NULL, | |
935 | + `pull_request_id` text NOT NULL, | |
936 | + `repository_id` text NOT NULL, | |
937 | + `author_id` text NOT NULL, | |
938 | + `time_to_first_review` integer, | |
939 | + `time_to_approval` integer, | |
940 | + `time_to_merge` integer, | |
941 | + `total_cycle_time` integer, | |
942 | + `review_rounds` integer DEFAULT 1, | |
943 | + `reviewers_count` integer DEFAULT 0, | |
944 | + `comments_count` integer DEFAULT 0, | |
945 | + `changes_requested_count` integer DEFAULT 0, | |
946 | + `lines_added` integer DEFAULT 0, | |
947 | + `lines_removed` integer DEFAULT 0, | |
948 | + `files_changed` integer DEFAULT 0, | |
949 | + `commits` integer DEFAULT 1, | |
950 | + `is_stacked` integer DEFAULT false, | |
951 | + `stack_position` integer, | |
952 | + `pr_created_at` text, | |
953 | + `first_review_at` text, | |
954 | + `approved_at` text, | |
955 | + `merged_at` text, | |
956 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
957 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
958 | + FOREIGN KEY (`pull_request_id`) REFERENCES `pull_requests`(`id`) ON UPDATE no action ON DELETE cascade, | |
959 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
960 | + FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
961 | +); | |
962 | +--> statement-breakpoint | |
963 | +CREATE TABLE `repo_metrics` ( | |
964 | + `id` text PRIMARY KEY NOT NULL, | |
965 | + `repository_id` text NOT NULL, | |
966 | + `week_of` text NOT NULL, | |
967 | + `prs_opened` integer DEFAULT 0, | |
968 | + `prs_merged` integer DEFAULT 0, | |
969 | + `prs_closed` integer DEFAULT 0, | |
970 | + `avg_time_to_first_review` integer, | |
971 | + `avg_time_to_merge` integer, | |
972 | + `avg_review_rounds` integer, | |
973 | + `avg_lines_changed` integer, | |
974 | + `avg_files_changed` integer, | |
975 | + `stacked_prs` integer DEFAULT 0, | |
976 | + `stacked_prs_percentage` integer DEFAULT 0, | |
977 | + `active_authors` integer DEFAULT 0, | |
978 | + `active_reviewers` integer DEFAULT 0, | |
979 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
980 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
981 | +); | |
982 | +--> statement-breakpoint | |
983 | +CREATE TABLE `review_metrics` ( | |
984 | + `id` text PRIMARY KEY NOT NULL, | |
985 | + `user_id` text NOT NULL, | |
986 | + `repository_id` text, | |
987 | + `organization_id` text, | |
988 | + `week_of` text NOT NULL, | |
989 | + `prs_authored` integer DEFAULT 0, | |
990 | + `prs_authored_merged` integer DEFAULT 0, | |
991 | + `avg_time_to_merge_authored` integer, | |
992 | + `lines_authored_added` integer DEFAULT 0, | |
993 | + `lines_authored_removed` integer DEFAULT 0, | |
994 | + `prs_reviewed` integer DEFAULT 0, | |
995 | + `avg_time_to_review` integer, | |
996 | + `comments_given` integer DEFAULT 0, | |
997 | + `approvals_given` integer DEFAULT 0, | |
998 | + `changes_requested_given` integer DEFAULT 0, | |
999 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
1000 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
1001 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
1002 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade, | |
1003 | + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE no action | |
1004 | +); | |
1005 | +--> statement-breakpoint | |
1006 | +CREATE TABLE `slack_channel_mappings` ( | |
1007 | + `id` text PRIMARY KEY NOT NULL, | |
1008 | + `workspace_id` text NOT NULL, | |
1009 | + `repository_id` text NOT NULL, | |
1010 | + `channel_id` text NOT NULL, | |
1011 | + `channel_name` text, | |
1012 | + `notify_on` text DEFAULT '["pr_created","pr_merged","review_requested","ci_failed"]', | |
1013 | + `notify_branches` text, | |
1014 | + `notify_authors` text, | |
1015 | + `is_active` integer DEFAULT true, | |
1016 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
1017 | + `updated_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
1018 | + FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade, | |
1019 | + FOREIGN KEY (`repository_id`) REFERENCES `repositories`(`id`) ON UPDATE no action ON DELETE cascade | |
1020 | +); | |
1021 | +--> statement-breakpoint | |
1022 | +CREATE TABLE `slack_user_mappings` ( | |
1023 | + `id` text PRIMARY KEY NOT NULL, | |
1024 | + `user_id` text NOT NULL, | |
1025 | + `workspace_id` text NOT NULL, | |
1026 | + `slack_user_id` text NOT NULL, | |
1027 | + `slack_username` text, | |
1028 | + `dm_preferences` text DEFAULT '{"review_requested":true,"pr_approved":true,"ci_failed":true}', | |
1029 | + `dnd_enabled` integer DEFAULT false, | |
1030 | + `dnd_start` text, | |
1031 | + `dnd_end` text, | |
1032 | + `dnd_timezone` text, | |
1033 | + `is_active` integer DEFAULT true, | |
1034 | + `created_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
1035 | + FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE cascade, | |
1036 | + FOREIGN KEY (`workspace_id`) REFERENCES `slack_workspaces`(`id`) ON UPDATE no action ON DELETE cascade | |
1037 | +); | |
1038 | +--> statement-breakpoint | |
1039 | +CREATE TABLE `slack_workspaces` ( | |
1040 | + `id` text PRIMARY KEY NOT NULL, | |
1041 | + `organization_id` text, | |
1042 | + `team_id` text NOT NULL, | |
1043 | + `team_name` text, | |
1044 | + `team_domain` text, | |
1045 | + `access_token` text NOT NULL, | |
1046 | + `bot_user_id` text, | |
1047 | + `bot_access_token` text, | |
1048 | + `scopes` text, | |
1049 | + `installed_by_id` text NOT NULL, | |
1050 | + `installed_at` text DEFAULT 'CURRENT_TIMESTAMP' NOT NULL, | |
1051 | + `is_active` integer DEFAULT true, | |
1052 | + `last_used_at` text, | |
1053 | + FOREIGN KEY (`organization_id`) REFERENCES `organizations`(`id`) ON UPDATE no action ON DELETE cascade, | |
1054 | + FOREIGN KEY (`installed_by_id`) REFERENCES `users`(`id`) ON UPDATE no action ON DELETE no action | |
1055 | +); | |
0 | 1056 | |
▼package-lock.json
+1869-125
| @@ -42,6 +42,7 @@ | ||
42 | 42 | "@radix-ui/react-toggle": "^1.1.0", |
43 | 43 | "@radix-ui/react-toggle-group": "^1.1.0", |
44 | 44 | "@radix-ui/react-tooltip": "^1.1.2", |
45 | + "@types/nodemailer": "^7.0.4", | |
45 | 46 | "astro": "^4.11.0", |
46 | 47 | "bcryptjs": "^2.4.3", |
47 | 48 | "better-sqlite3": "^11.1.2", |
| @@ -49,10 +50,12 @@ | ||
49 | 50 | "class-variance-authority": "^0.7.0", |
50 | 51 | "clsx": "^2.1.1", |
51 | 52 | "cmdk": "^1.1.1", |
53 | + "cobe": "^0.6.5", | |
52 | 54 | "dockerode": "^4.0.2", |
53 | 55 | "drizzle-orm": "^0.45.1", |
54 | - "framer-motion": "^11.2.12", | |
56 | + "framer-motion": "^11.18.2", | |
55 | 57 | "github-markdown-css": "^5.8.1", |
58 | + "googleapis": "^169.0.0", | |
56 | 59 | "ioredis": "^5.8.2", |
57 | 60 | "isomorphic-git": "^1.25.10", |
58 | 61 | "jose": "^5.6.3", |
| @@ -63,6 +66,8 @@ | ||
63 | 66 | "mysql2": "^3.15.3", |
64 | 67 | "nanoid": "^5.0.7", |
65 | 68 | "nodegit": "^0.28.0-alpha.27", |
69 | + "nodemailer": "^7.0.12", | |
70 | + "openpgp": "^6.3.0", | |
66 | 71 | "otplib": "^12.0.1", |
67 | 72 | "pg": "^8.16.3", |
68 | 73 | "pino": "^10.1.0", |
| @@ -70,6 +75,7 @@ | ||
70 | 75 | "prom-client": "^15.1.3", |
71 | 76 | "react": "^18.3.1", |
72 | 77 | "react-dom": "^18.3.1", |
78 | + "react-globe.gl": "^2.37.0", | |
73 | 79 | "rehype-sanitize": "^6.0.0", |
74 | 80 | "rehype-stringify": "^10.0.1", |
75 | 81 | "remark-gfm": "^4.0.1", |
| @@ -77,13 +83,15 @@ | ||
77 | 83 | "remark-rehype": "^11.1.2", |
78 | 84 | "simple-git": "^3.25.0", |
79 | 85 | "ssh2": "^1.15.0", |
80 | - "tailwind-merge": "^2.3.0", | |
86 | + "tailwind-merge": "^2.6.0", | |
81 | 87 | "tailwindcss-animate": "^1.0.7", |
88 | + "three": "^0.182.0", | |
82 | 89 | "unified": "^11.0.5", |
83 | 90 | "zod": "^3.23.8", |
84 | 91 | "zustand": "^4.5.4" |
85 | 92 | }, |
86 | 93 | "devDependencies": { |
94 | + "@faker-js/faker": "^10.1.0", | |
87 | 95 | "@playwright/test": "^1.57.0", |
88 | 96 | "@tailwindcss/typography": "^0.5.19", |
89 | 97 | "@types/bcryptjs": "^2.4.6", |
| @@ -91,10 +99,11 @@ | ||
91 | 99 | "@types/dockerode": "^3.3.29", |
92 | 100 | "@types/js-yaml": "^4.0.9", |
93 | 101 | "@types/node": "^20.14.9", |
94 | - "@types/pg": "^8.11.6", | |
102 | + "@types/pg": "^8.16.0", | |
95 | 103 | "@types/react": "^18.3.3", |
96 | 104 | "@types/react-dom": "^18.3.0", |
97 | 105 | "@types/ssh2": "^1.15.0", |
106 | + "@types/three": "^0.182.0", | |
98 | 107 | "autoprefixer": "^10.4.19", |
99 | 108 | "drizzle-kit": "^0.31.8", |
100 | 109 | "pino-pretty": "^13.1.3", |
| @@ -943,6 +952,567 @@ | ||
943 | 952 | "node": ">=18.0.0" |
944 | 953 | } |
945 | 954 | }, |
955 | + "node_modules/@aws-sdk/client-sesv2": { | |
956 | + "version": "3.958.0", | |
957 | + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.958.0.tgz", | |
958 | + "integrity": "sha512-3x3n8IIxIMAkdpt9wy9zS7MO2lqTcJwQTdHMn6BlD7YUohb+r5Q4KCOEQ2uHWd4WIJv2tlbXnfypHaXReO/WXA==", | |
959 | + "license": "Apache-2.0", | |
960 | + "dependencies": { | |
961 | + "@aws-crypto/sha256-browser": "5.2.0", | |
962 | + "@aws-crypto/sha256-js": "5.2.0", | |
963 | + "@aws-sdk/core": "3.957.0", | |
964 | + "@aws-sdk/credential-provider-node": "3.958.0", | |
965 | + "@aws-sdk/middleware-host-header": "3.957.0", | |
966 | + "@aws-sdk/middleware-logger": "3.957.0", | |
967 | + "@aws-sdk/middleware-recursion-detection": "3.957.0", | |
968 | + "@aws-sdk/middleware-user-agent": "3.957.0", | |
969 | + "@aws-sdk/region-config-resolver": "3.957.0", | |
970 | + "@aws-sdk/signature-v4-multi-region": "3.957.0", | |
971 | + "@aws-sdk/types": "3.957.0", | |
972 | + "@aws-sdk/util-endpoints": "3.957.0", | |
973 | + "@aws-sdk/util-user-agent-browser": "3.957.0", | |
974 | + "@aws-sdk/util-user-agent-node": "3.957.0", | |
975 | + "@smithy/config-resolver": "^4.4.5", | |
976 | + "@smithy/core": "^3.20.0", | |
977 | + "@smithy/fetch-http-handler": "^5.3.8", | |
978 | + "@smithy/hash-node": "^4.2.7", | |
979 | + "@smithy/invalid-dependency": "^4.2.7", | |
980 | + "@smithy/middleware-content-length": "^4.2.7", | |
981 | + "@smithy/middleware-endpoint": "^4.4.1", | |
982 | + "@smithy/middleware-retry": "^4.4.17", | |
983 | + "@smithy/middleware-serde": "^4.2.8", | |
984 | + "@smithy/middleware-stack": "^4.2.7", | |
985 | + "@smithy/node-config-provider": "^4.3.7", | |
986 | + "@smithy/node-http-handler": "^4.4.7", | |
987 | + "@smithy/protocol-http": "^5.3.7", | |
988 | + "@smithy/smithy-client": "^4.10.2", | |
989 | + "@smithy/types": "^4.11.0", | |
990 | + "@smithy/url-parser": "^4.2.7", | |
991 | + "@smithy/util-base64": "^4.3.0", | |
992 | + "@smithy/util-body-length-browser": "^4.2.0", | |
993 | + "@smithy/util-body-length-node": "^4.2.1", | |
994 | + "@smithy/util-defaults-mode-browser": "^4.3.16", | |
995 | + "@smithy/util-defaults-mode-node": "^4.2.19", | |
996 | + "@smithy/util-endpoints": "^3.2.7", | |
997 | + "@smithy/util-middleware": "^4.2.7", | |
998 | + "@smithy/util-retry": "^4.2.7", | |
999 | + "@smithy/util-utf8": "^4.2.0", | |
1000 | + "tslib": "^2.6.2" | |
1001 | + }, | |
1002 | + "engines": { | |
1003 | + "node": ">=18.0.0" | |
1004 | + } | |
1005 | + }, | |
1006 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/client-sso": { | |
1007 | + "version": "3.958.0", | |
1008 | + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.958.0.tgz", | |
1009 | + "integrity": "sha512-6qNCIeaMzKzfqasy2nNRuYnMuaMebCcCPP4J2CVGkA8QYMbIVKPlkn9bpB20Vxe6H/r3jtCCLQaOJjVTx/6dXg==", | |
1010 | + "license": "Apache-2.0", | |
1011 | + "dependencies": { | |
1012 | + "@aws-crypto/sha256-browser": "5.2.0", | |
1013 | + "@aws-crypto/sha256-js": "5.2.0", | |
1014 | + "@aws-sdk/core": "3.957.0", | |
1015 | + "@aws-sdk/middleware-host-header": "3.957.0", | |
1016 | + "@aws-sdk/middleware-logger": "3.957.0", | |
1017 | + "@aws-sdk/middleware-recursion-detection": "3.957.0", | |
1018 | + "@aws-sdk/middleware-user-agent": "3.957.0", | |
1019 | + "@aws-sdk/region-config-resolver": "3.957.0", | |
1020 | + "@aws-sdk/types": "3.957.0", | |
1021 | + "@aws-sdk/util-endpoints": "3.957.0", | |
1022 | + "@aws-sdk/util-user-agent-browser": "3.957.0", | |
1023 | + "@aws-sdk/util-user-agent-node": "3.957.0", | |
1024 | + "@smithy/config-resolver": "^4.4.5", | |
1025 | + "@smithy/core": "^3.20.0", | |
1026 | + "@smithy/fetch-http-handler": "^5.3.8", | |
1027 | + "@smithy/hash-node": "^4.2.7", | |
1028 | + "@smithy/invalid-dependency": "^4.2.7", | |
1029 | + "@smithy/middleware-content-length": "^4.2.7", | |
1030 | + "@smithy/middleware-endpoint": "^4.4.1", | |
1031 | + "@smithy/middleware-retry": "^4.4.17", | |
1032 | + "@smithy/middleware-serde": "^4.2.8", | |
1033 | + "@smithy/middleware-stack": "^4.2.7", | |
1034 | + "@smithy/node-config-provider": "^4.3.7", | |
1035 | + "@smithy/node-http-handler": "^4.4.7", | |
1036 | + "@smithy/protocol-http": "^5.3.7", | |
1037 | + "@smithy/smithy-client": "^4.10.2", | |
1038 | + "@smithy/types": "^4.11.0", | |
1039 | + "@smithy/url-parser": "^4.2.7", | |
1040 | + "@smithy/util-base64": "^4.3.0", | |
1041 | + "@smithy/util-body-length-browser": "^4.2.0", | |
1042 | + "@smithy/util-body-length-node": "^4.2.1", | |
1043 | + "@smithy/util-defaults-mode-browser": "^4.3.16", | |
1044 | + "@smithy/util-defaults-mode-node": "^4.2.19", | |
1045 | + "@smithy/util-endpoints": "^3.2.7", | |
1046 | + "@smithy/util-middleware": "^4.2.7", | |
1047 | + "@smithy/util-retry": "^4.2.7", | |
1048 | + "@smithy/util-utf8": "^4.2.0", | |
1049 | + "tslib": "^2.6.2" | |
1050 | + }, | |
1051 | + "engines": { | |
1052 | + "node": ">=18.0.0" | |
1053 | + } | |
1054 | + }, | |
1055 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/core": { | |
1056 | + "version": "3.957.0", | |
1057 | + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.957.0.tgz", | |
1058 | + "integrity": "sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw==", | |
1059 | + "license": "Apache-2.0", | |
1060 | + "dependencies": { | |
1061 | + "@aws-sdk/types": "3.957.0", | |
1062 | + "@aws-sdk/xml-builder": "3.957.0", | |
1063 | + "@smithy/core": "^3.20.0", | |
1064 | + "@smithy/node-config-provider": "^4.3.7", | |
1065 | + "@smithy/property-provider": "^4.2.7", | |
1066 | + "@smithy/protocol-http": "^5.3.7", | |
1067 | + "@smithy/signature-v4": "^5.3.7", | |
1068 | + "@smithy/smithy-client": "^4.10.2", | |
1069 | + "@smithy/types": "^4.11.0", | |
1070 | + "@smithy/util-base64": "^4.3.0", | |
1071 | + "@smithy/util-middleware": "^4.2.7", | |
1072 | + "@smithy/util-utf8": "^4.2.0", | |
1073 | + "tslib": "^2.6.2" | |
1074 | + }, | |
1075 | + "engines": { | |
1076 | + "node": ">=18.0.0" | |
1077 | + } | |
1078 | + }, | |
1079 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-env": { | |
1080 | + "version": "3.957.0", | |
1081 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.957.0.tgz", | |
1082 | + "integrity": "sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog==", | |
1083 | + "license": "Apache-2.0", | |
1084 | + "dependencies": { | |
1085 | + "@aws-sdk/core": "3.957.0", | |
1086 | + "@aws-sdk/types": "3.957.0", | |
1087 | + "@smithy/property-provider": "^4.2.7", | |
1088 | + "@smithy/types": "^4.11.0", | |
1089 | + "tslib": "^2.6.2" | |
1090 | + }, | |
1091 | + "engines": { | |
1092 | + "node": ">=18.0.0" | |
1093 | + } | |
1094 | + }, | |
1095 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-http": { | |
1096 | + "version": "3.957.0", | |
1097 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.957.0.tgz", | |
1098 | + "integrity": "sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw==", | |
1099 | + "license": "Apache-2.0", | |
1100 | + "dependencies": { | |
1101 | + "@aws-sdk/core": "3.957.0", | |
1102 | + "@aws-sdk/types": "3.957.0", | |
1103 | + "@smithy/fetch-http-handler": "^5.3.8", | |
1104 | + "@smithy/node-http-handler": "^4.4.7", | |
1105 | + "@smithy/property-provider": "^4.2.7", | |
1106 | + "@smithy/protocol-http": "^5.3.7", | |
1107 | + "@smithy/smithy-client": "^4.10.2", | |
1108 | + "@smithy/types": "^4.11.0", | |
1109 | + "@smithy/util-stream": "^4.5.8", | |
1110 | + "tslib": "^2.6.2" | |
1111 | + }, | |
1112 | + "engines": { | |
1113 | + "node": ">=18.0.0" | |
1114 | + } | |
1115 | + }, | |
1116 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-ini": { | |
1117 | + "version": "3.958.0", | |
1118 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.958.0.tgz", | |
1119 | + "integrity": "sha512-u7twvZa1/6GWmPBZs6DbjlegCoNzNjBsMS/6fvh5quByYrcJr/uLd8YEr7S3UIq4kR/gSnHqcae7y2nL2bqZdg==", | |
1120 | + "license": "Apache-2.0", | |
1121 | + "dependencies": { | |
1122 | + "@aws-sdk/core": "3.957.0", | |
1123 | + "@aws-sdk/credential-provider-env": "3.957.0", | |
1124 | + "@aws-sdk/credential-provider-http": "3.957.0", | |
1125 | + "@aws-sdk/credential-provider-login": "3.958.0", | |
1126 | + "@aws-sdk/credential-provider-process": "3.957.0", | |
1127 | + "@aws-sdk/credential-provider-sso": "3.958.0", | |
1128 | + "@aws-sdk/credential-provider-web-identity": "3.958.0", | |
1129 | + "@aws-sdk/nested-clients": "3.958.0", | |
1130 | + "@aws-sdk/types": "3.957.0", | |
1131 | + "@smithy/credential-provider-imds": "^4.2.7", | |
1132 | + "@smithy/property-provider": "^4.2.7", | |
1133 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
1134 | + "@smithy/types": "^4.11.0", | |
1135 | + "tslib": "^2.6.2" | |
1136 | + }, | |
1137 | + "engines": { | |
1138 | + "node": ">=18.0.0" | |
1139 | + } | |
1140 | + }, | |
1141 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-login": { | |
1142 | + "version": "3.958.0", | |
1143 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.958.0.tgz", | |
1144 | + "integrity": "sha512-sDwtDnBSszUIbzbOORGh5gmXGl9aK25+BHb4gb1aVlqB+nNL2+IUEJA62+CE55lXSH8qXF90paivjK8tOHTwPA==", | |
1145 | + "license": "Apache-2.0", | |
1146 | + "dependencies": { | |
1147 | + "@aws-sdk/core": "3.957.0", | |
1148 | + "@aws-sdk/nested-clients": "3.958.0", | |
1149 | + "@aws-sdk/types": "3.957.0", | |
1150 | + "@smithy/property-provider": "^4.2.7", | |
1151 | + "@smithy/protocol-http": "^5.3.7", | |
1152 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
1153 | + "@smithy/types": "^4.11.0", | |
1154 | + "tslib": "^2.6.2" | |
1155 | + }, | |
1156 | + "engines": { | |
1157 | + "node": ">=18.0.0" | |
1158 | + } | |
1159 | + }, | |
1160 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-node": { | |
1161 | + "version": "3.958.0", | |
1162 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.958.0.tgz", | |
1163 | + "integrity": "sha512-vdoZbNG2dt66I7EpN3fKCzi6fp9xjIiwEA/vVVgqO4wXCGw8rKPIdDUus4e13VvTr330uQs2W0UNg/7AgtquEQ==", | |
1164 | + "license": "Apache-2.0", | |
1165 | + "dependencies": { | |
1166 | + "@aws-sdk/credential-provider-env": "3.957.0", | |
1167 | + "@aws-sdk/credential-provider-http": "3.957.0", | |
1168 | + "@aws-sdk/credential-provider-ini": "3.958.0", | |
1169 | + "@aws-sdk/credential-provider-process": "3.957.0", | |
1170 | + "@aws-sdk/credential-provider-sso": "3.958.0", | |
1171 | + "@aws-sdk/credential-provider-web-identity": "3.958.0", | |
1172 | + "@aws-sdk/types": "3.957.0", | |
1173 | + "@smithy/credential-provider-imds": "^4.2.7", | |
1174 | + "@smithy/property-provider": "^4.2.7", | |
1175 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
1176 | + "@smithy/types": "^4.11.0", | |
1177 | + "tslib": "^2.6.2" | |
1178 | + }, | |
1179 | + "engines": { | |
1180 | + "node": ">=18.0.0" | |
1181 | + } | |
1182 | + }, | |
1183 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-process": { | |
1184 | + "version": "3.957.0", | |
1185 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.957.0.tgz", | |
1186 | + "integrity": "sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg==", | |
1187 | + "license": "Apache-2.0", | |
1188 | + "dependencies": { | |
1189 | + "@aws-sdk/core": "3.957.0", | |
1190 | + "@aws-sdk/types": "3.957.0", | |
1191 | + "@smithy/property-provider": "^4.2.7", | |
1192 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
1193 | + "@smithy/types": "^4.11.0", | |
1194 | + "tslib": "^2.6.2" | |
1195 | + }, | |
1196 | + "engines": { | |
1197 | + "node": ">=18.0.0" | |
1198 | + } | |
1199 | + }, | |
1200 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-sso": { | |
1201 | + "version": "3.958.0", | |
1202 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.958.0.tgz", | |
1203 | + "integrity": "sha512-CBYHJ5ufp8HC4q+o7IJejCUctJXWaksgpmoFpXerbjAso7/Fg7LLUu9inXVOxlHKLlvYekDXjIUBXDJS2WYdgg==", | |
1204 | + "license": "Apache-2.0", | |
1205 | + "dependencies": { | |
1206 | + "@aws-sdk/client-sso": "3.958.0", | |
1207 | + "@aws-sdk/core": "3.957.0", | |
1208 | + "@aws-sdk/token-providers": "3.958.0", | |
1209 | + "@aws-sdk/types": "3.957.0", | |
1210 | + "@smithy/property-provider": "^4.2.7", | |
1211 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
1212 | + "@smithy/types": "^4.11.0", | |
1213 | + "tslib": "^2.6.2" | |
1214 | + }, | |
1215 | + "engines": { | |
1216 | + "node": ">=18.0.0" | |
1217 | + } | |
1218 | + }, | |
1219 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/credential-provider-web-identity": { | |
1220 | + "version": "3.958.0", | |
1221 | + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.958.0.tgz", | |
1222 | + "integrity": "sha512-dgnvwjMq5Y66WozzUzxNkCFap+umHUtqMMKlr8z/vl9NYMLem/WUbWNpFFOVFWquXikc+ewtpBMR4KEDXfZ+KA==", | |
1223 | + "license": "Apache-2.0", | |
1224 | + "dependencies": { | |
1225 | + "@aws-sdk/core": "3.957.0", | |
1226 | + "@aws-sdk/nested-clients": "3.958.0", | |
1227 | + "@aws-sdk/types": "3.957.0", | |
1228 | + "@smithy/property-provider": "^4.2.7", | |
1229 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
1230 | + "@smithy/types": "^4.11.0", | |
1231 | + "tslib": "^2.6.2" | |
1232 | + }, | |
1233 | + "engines": { | |
1234 | + "node": ">=18.0.0" | |
1235 | + } | |
1236 | + }, | |
1237 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-host-header": { | |
1238 | + "version": "3.957.0", | |
1239 | + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.957.0.tgz", | |
1240 | + "integrity": "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA==", | |
1241 | + "license": "Apache-2.0", | |
1242 | + "dependencies": { | |
1243 | + "@aws-sdk/types": "3.957.0", | |
1244 | + "@smithy/protocol-http": "^5.3.7", | |
1245 | + "@smithy/types": "^4.11.0", | |
1246 | + "tslib": "^2.6.2" | |
1247 | + }, | |
1248 | + "engines": { | |
1249 | + "node": ">=18.0.0" | |
1250 | + } | |
1251 | + }, | |
1252 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-logger": { | |
1253 | + "version": "3.957.0", | |
1254 | + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.957.0.tgz", | |
1255 | + "integrity": "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ==", | |
1256 | + "license": "Apache-2.0", | |
1257 | + "dependencies": { | |
1258 | + "@aws-sdk/types": "3.957.0", | |
1259 | + "@smithy/types": "^4.11.0", | |
1260 | + "tslib": "^2.6.2" | |
1261 | + }, | |
1262 | + "engines": { | |
1263 | + "node": ">=18.0.0" | |
1264 | + } | |
1265 | + }, | |
1266 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-recursion-detection": { | |
1267 | + "version": "3.957.0", | |
1268 | + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.957.0.tgz", | |
1269 | + "integrity": "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA==", | |
1270 | + "license": "Apache-2.0", | |
1271 | + "dependencies": { | |
1272 | + "@aws-sdk/types": "3.957.0", | |
1273 | + "@aws/lambda-invoke-store": "^0.2.2", | |
1274 | + "@smithy/protocol-http": "^5.3.7", | |
1275 | + "@smithy/types": "^4.11.0", | |
1276 | + "tslib": "^2.6.2" | |
1277 | + }, | |
1278 | + "engines": { | |
1279 | + "node": ">=18.0.0" | |
1280 | + } | |
1281 | + }, | |
1282 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-sdk-s3": { | |
1283 | + "version": "3.957.0", | |
1284 | + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.957.0.tgz", | |
1285 | + "integrity": "sha512-5B2qY2nR2LYpxoQP0xUum5A1UNvH2JQpLHDH1nWFNF/XetV7ipFHksMxPNhtJJ6ARaWhQIDXfOUj0jcnkJxXUg==", | |
1286 | + "license": "Apache-2.0", | |
1287 | + "dependencies": { | |
1288 | + "@aws-sdk/core": "3.957.0", | |
1289 | + "@aws-sdk/types": "3.957.0", | |
1290 | + "@aws-sdk/util-arn-parser": "3.957.0", | |
1291 | + "@smithy/core": "^3.20.0", | |
1292 | + "@smithy/node-config-provider": "^4.3.7", | |
1293 | + "@smithy/protocol-http": "^5.3.7", | |
1294 | + "@smithy/signature-v4": "^5.3.7", | |
1295 | + "@smithy/smithy-client": "^4.10.2", | |
1296 | + "@smithy/types": "^4.11.0", | |
1297 | + "@smithy/util-config-provider": "^4.2.0", | |
1298 | + "@smithy/util-middleware": "^4.2.7", | |
1299 | + "@smithy/util-stream": "^4.5.8", | |
1300 | + "@smithy/util-utf8": "^4.2.0", | |
1301 | + "tslib": "^2.6.2" | |
1302 | + }, | |
1303 | + "engines": { | |
1304 | + "node": ">=18.0.0" | |
1305 | + } | |
1306 | + }, | |
1307 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/middleware-user-agent": { | |
1308 | + "version": "3.957.0", | |
1309 | + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.957.0.tgz", | |
1310 | + "integrity": "sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ==", | |
1311 | + "license": "Apache-2.0", | |
1312 | + "dependencies": { | |
1313 | + "@aws-sdk/core": "3.957.0", | |
1314 | + "@aws-sdk/types": "3.957.0", | |
1315 | + "@aws-sdk/util-endpoints": "3.957.0", | |
1316 | + "@smithy/core": "^3.20.0", | |
1317 | + "@smithy/protocol-http": "^5.3.7", | |
1318 | + "@smithy/types": "^4.11.0", | |
1319 | + "tslib": "^2.6.2" | |
1320 | + }, | |
1321 | + "engines": { | |
1322 | + "node": ">=18.0.0" | |
1323 | + } | |
1324 | + }, | |
1325 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/nested-clients": { | |
1326 | + "version": "3.958.0", | |
1327 | + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.958.0.tgz", | |
1328 | + "integrity": "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw==", | |
1329 | + "license": "Apache-2.0", | |
1330 | + "dependencies": { | |
1331 | + "@aws-crypto/sha256-browser": "5.2.0", | |
1332 | + "@aws-crypto/sha256-js": "5.2.0", | |
1333 | + "@aws-sdk/core": "3.957.0", | |
1334 | + "@aws-sdk/middleware-host-header": "3.957.0", | |
1335 | + "@aws-sdk/middleware-logger": "3.957.0", | |
1336 | + "@aws-sdk/middleware-recursion-detection": "3.957.0", | |
1337 | + "@aws-sdk/middleware-user-agent": "3.957.0", | |
1338 | + "@aws-sdk/region-config-resolver": "3.957.0", | |
1339 | + "@aws-sdk/types": "3.957.0", | |
1340 | + "@aws-sdk/util-endpoints": "3.957.0", | |
1341 | + "@aws-sdk/util-user-agent-browser": "3.957.0", | |
1342 | + "@aws-sdk/util-user-agent-node": "3.957.0", | |
1343 | + "@smithy/config-resolver": "^4.4.5", | |
1344 | + "@smithy/core": "^3.20.0", | |
1345 | + "@smithy/fetch-http-handler": "^5.3.8", | |
1346 | + "@smithy/hash-node": "^4.2.7", | |
1347 | + "@smithy/invalid-dependency": "^4.2.7", | |
1348 | + "@smithy/middleware-content-length": "^4.2.7", | |
1349 | + "@smithy/middleware-endpoint": "^4.4.1", | |
1350 | + "@smithy/middleware-retry": "^4.4.17", | |
1351 | + "@smithy/middleware-serde": "^4.2.8", | |
1352 | + "@smithy/middleware-stack": "^4.2.7", | |
1353 | + "@smithy/node-config-provider": "^4.3.7", | |
1354 | + "@smithy/node-http-handler": "^4.4.7", | |
1355 | + "@smithy/protocol-http": "^5.3.7", | |
1356 | + "@smithy/smithy-client": "^4.10.2", | |
1357 | + "@smithy/types": "^4.11.0", | |
1358 | + "@smithy/url-parser": "^4.2.7", | |
1359 | + "@smithy/util-base64": "^4.3.0", | |
1360 | + "@smithy/util-body-length-browser": "^4.2.0", | |
1361 | + "@smithy/util-body-length-node": "^4.2.1", | |
1362 | + "@smithy/util-defaults-mode-browser": "^4.3.16", | |
1363 | + "@smithy/util-defaults-mode-node": "^4.2.19", | |
1364 | + "@smithy/util-endpoints": "^3.2.7", | |
1365 | + "@smithy/util-middleware": "^4.2.7", | |
1366 | + "@smithy/util-retry": "^4.2.7", | |
1367 | + "@smithy/util-utf8": "^4.2.0", | |
1368 | + "tslib": "^2.6.2" | |
1369 | + }, | |
1370 | + "engines": { | |
1371 | + "node": ">=18.0.0" | |
1372 | + } | |
1373 | + }, | |
1374 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/region-config-resolver": { | |
1375 | + "version": "3.957.0", | |
1376 | + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.957.0.tgz", | |
1377 | + "integrity": "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A==", | |
1378 | + "license": "Apache-2.0", | |
1379 | + "dependencies": { | |
1380 | + "@aws-sdk/types": "3.957.0", | |
1381 | + "@smithy/config-resolver": "^4.4.5", | |
1382 | + "@smithy/node-config-provider": "^4.3.7", | |
1383 | + "@smithy/types": "^4.11.0", | |
1384 | + "tslib": "^2.6.2" | |
1385 | + }, | |
1386 | + "engines": { | |
1387 | + "node": ">=18.0.0" | |
1388 | + } | |
1389 | + }, | |
1390 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/signature-v4-multi-region": { | |
1391 | + "version": "3.957.0", | |
1392 | + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.957.0.tgz", | |
1393 | + "integrity": "sha512-t6UfP1xMUigMMzHcb7vaZcjv7dA2DQkk9C/OAP1dKyrE0vb4lFGDaTApi17GN6Km9zFxJthEMUbBc7DL0hq1Bg==", | |
1394 | + "license": "Apache-2.0", | |
1395 | + "dependencies": { | |
1396 | + "@aws-sdk/middleware-sdk-s3": "3.957.0", | |
1397 | + "@aws-sdk/types": "3.957.0", | |
1398 | + "@smithy/protocol-http": "^5.3.7", | |
1399 | + "@smithy/signature-v4": "^5.3.7", | |
1400 | + "@smithy/types": "^4.11.0", | |
1401 | + "tslib": "^2.6.2" | |
1402 | + }, | |
1403 | + "engines": { | |
1404 | + "node": ">=18.0.0" | |
1405 | + } | |
1406 | + }, | |
1407 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/token-providers": { | |
1408 | + "version": "3.958.0", | |
1409 | + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.958.0.tgz", | |
1410 | + "integrity": "sha512-UCj7lQXODduD1myNJQkV+LYcGYJ9iiMggR8ow8Hva1g3A/Na5imNXzz6O67k7DAee0TYpy+gkNw+SizC6min8Q==", | |
1411 | + "license": "Apache-2.0", | |
1412 | + "dependencies": { | |
1413 | + "@aws-sdk/core": "3.957.0", | |
1414 | + "@aws-sdk/nested-clients": "3.958.0", | |
1415 | + "@aws-sdk/types": "3.957.0", | |
1416 | + "@smithy/property-provider": "^4.2.7", | |
1417 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
1418 | + "@smithy/types": "^4.11.0", | |
1419 | + "tslib": "^2.6.2" | |
1420 | + }, | |
1421 | + "engines": { | |
1422 | + "node": ">=18.0.0" | |
1423 | + } | |
1424 | + }, | |
1425 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/types": { | |
1426 | + "version": "3.957.0", | |
1427 | + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.957.0.tgz", | |
1428 | + "integrity": "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg==", | |
1429 | + "license": "Apache-2.0", | |
1430 | + "dependencies": { | |
1431 | + "@smithy/types": "^4.11.0", | |
1432 | + "tslib": "^2.6.2" | |
1433 | + }, | |
1434 | + "engines": { | |
1435 | + "node": ">=18.0.0" | |
1436 | + } | |
1437 | + }, | |
1438 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-arn-parser": { | |
1439 | + "version": "3.957.0", | |
1440 | + "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.957.0.tgz", | |
1441 | + "integrity": "sha512-Aj6m+AyrhWyg8YQ4LDPg2/gIfGHCEcoQdBt5DeSFogN5k9mmJPOJ+IAmNSWmWRjpOxEy6eY813RNDI6qS97M0g==", | |
1442 | + "license": "Apache-2.0", | |
1443 | + "dependencies": { | |
1444 | + "tslib": "^2.6.2" | |
1445 | + }, | |
1446 | + "engines": { | |
1447 | + "node": ">=18.0.0" | |
1448 | + } | |
1449 | + }, | |
1450 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-endpoints": { | |
1451 | + "version": "3.957.0", | |
1452 | + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.957.0.tgz", | |
1453 | + "integrity": "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw==", | |
1454 | + "license": "Apache-2.0", | |
1455 | + "dependencies": { | |
1456 | + "@aws-sdk/types": "3.957.0", | |
1457 | + "@smithy/types": "^4.11.0", | |
1458 | + "@smithy/url-parser": "^4.2.7", | |
1459 | + "@smithy/util-endpoints": "^3.2.7", | |
1460 | + "tslib": "^2.6.2" | |
1461 | + }, | |
1462 | + "engines": { | |
1463 | + "node": ">=18.0.0" | |
1464 | + } | |
1465 | + }, | |
1466 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-browser": { | |
1467 | + "version": "3.957.0", | |
1468 | + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.957.0.tgz", | |
1469 | + "integrity": "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw==", | |
1470 | + "license": "Apache-2.0", | |
1471 | + "dependencies": { | |
1472 | + "@aws-sdk/types": "3.957.0", | |
1473 | + "@smithy/types": "^4.11.0", | |
1474 | + "bowser": "^2.11.0", | |
1475 | + "tslib": "^2.6.2" | |
1476 | + } | |
1477 | + }, | |
1478 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/util-user-agent-node": { | |
1479 | + "version": "3.957.0", | |
1480 | + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.957.0.tgz", | |
1481 | + "integrity": "sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q==", | |
1482 | + "license": "Apache-2.0", | |
1483 | + "dependencies": { | |
1484 | + "@aws-sdk/middleware-user-agent": "3.957.0", | |
1485 | + "@aws-sdk/types": "3.957.0", | |
1486 | + "@smithy/node-config-provider": "^4.3.7", | |
1487 | + "@smithy/types": "^4.11.0", | |
1488 | + "tslib": "^2.6.2" | |
1489 | + }, | |
1490 | + "engines": { | |
1491 | + "node": ">=18.0.0" | |
1492 | + }, | |
1493 | + "peerDependencies": { | |
1494 | + "aws-crt": ">=1.0.0" | |
1495 | + }, | |
1496 | + "peerDependenciesMeta": { | |
1497 | + "aws-crt": { | |
1498 | + "optional": true | |
1499 | + } | |
1500 | + } | |
1501 | + }, | |
1502 | + "node_modules/@aws-sdk/client-sesv2/node_modules/@aws-sdk/xml-builder": { | |
1503 | + "version": "3.957.0", | |
1504 | + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.957.0.tgz", | |
1505 | + "integrity": "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA==", | |
1506 | + "license": "Apache-2.0", | |
1507 | + "dependencies": { | |
1508 | + "@smithy/types": "^4.11.0", | |
1509 | + "fast-xml-parser": "5.2.5", | |
1510 | + "tslib": "^2.6.2" | |
1511 | + }, | |
1512 | + "engines": { | |
1513 | + "node": ">=18.0.0" | |
1514 | + } | |
1515 | + }, | |
946 | 1516 | "node_modules/@aws-sdk/client-sso": { |
947 | 1517 | "version": "3.948.0", |
948 | 1518 | "license": "Apache-2.0", |
| @@ -1770,6 +2340,15 @@ | ||
1770 | 2340 | "@babel/core": "^7.0.0-0" |
1771 | 2341 | } |
1772 | 2342 | }, |
2343 | + "node_modules/@babel/runtime": { | |
2344 | + "version": "7.28.4", | |
2345 | + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", | |
2346 | + "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", | |
2347 | + "license": "MIT", | |
2348 | + "engines": { | |
2349 | + "node": ">=6.9.0" | |
2350 | + } | |
2351 | + }, | |
1773 | 2352 | "node_modules/@babel/template": { |
1774 | 2353 | "version": "7.27.2", |
1775 | 2354 | "license": "MIT", |
| @@ -1813,6 +2392,13 @@ | ||
1813 | 2392 | "version": "1.0.2", |
1814 | 2393 | "license": "Apache-2.0" |
1815 | 2394 | }, |
2395 | + "node_modules/@dimforge/rapier3d-compat": { | |
2396 | + "version": "0.12.0", | |
2397 | + "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", | |
2398 | + "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", | |
2399 | + "dev": true, | |
2400 | + "license": "Apache-2.0" | |
2401 | + }, | |
1816 | 2402 | "node_modules/@drizzle-team/brocli": { |
1817 | 2403 | "version": "0.10.2", |
1818 | 2404 | "dev": true, |
| @@ -2669,14 +3255,31 @@ | ||
2669 | 3255 | "node": ">=18" |
2670 | 3256 | } |
2671 | 3257 | }, |
2672 | - "node_modules/@floating-ui/core": { | |
2673 | - "version": "1.7.3", | |
2674 | - "license": "MIT", | |
2675 | - "dependencies": { | |
2676 | - "@floating-ui/utils": "^0.2.10" | |
2677 | - } | |
2678 | - }, | |
2679 | - "node_modules/@floating-ui/dom": { | |
3258 | + "node_modules/@faker-js/faker": { | |
3259 | + "version": "10.1.0", | |
3260 | + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-10.1.0.tgz", | |
3261 | + "integrity": "sha512-C3mrr3b5dRVlKPJdfrAXS8+dq+rq8Qm5SNRazca0JKgw1HQERFmrVb0towvMmw5uu8hHKNiQasMaR/tydf3Zsg==", | |
3262 | + "dev": true, | |
3263 | + "funding": [ | |
3264 | + { | |
3265 | + "type": "opencollective", | |
3266 | + "url": "https://opencollective.com/fakerjs" | |
3267 | + } | |
3268 | + ], | |
3269 | + "license": "MIT", | |
3270 | + "engines": { | |
3271 | + "node": "^20.19.0 || ^22.13.0 || ^23.5.0 || >=24.0.0", | |
3272 | + "npm": ">=10" | |
3273 | + } | |
3274 | + }, | |
3275 | + "node_modules/@floating-ui/core": { | |
3276 | + "version": "1.7.3", | |
3277 | + "license": "MIT", | |
3278 | + "dependencies": { | |
3279 | + "@floating-ui/utils": "^0.2.10" | |
3280 | + } | |
3281 | + }, | |
3282 | + "node_modules/@floating-ui/dom": { | |
2680 | 3283 | "version": "1.7.4", |
2681 | 3284 | "license": "MIT", |
2682 | 3285 | "dependencies": { |
| @@ -6509,10 +7112,12 @@ | ||
6509 | 7112 | } |
6510 | 7113 | }, |
6511 | 7114 | "node_modules/@smithy/abort-controller": { |
6512 | - "version": "4.2.5", | |
7115 | + "version": "4.2.7", | |
7116 | + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.7.tgz", | |
7117 | + "integrity": "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw==", | |
6513 | 7118 | "license": "Apache-2.0", |
6514 | 7119 | "dependencies": { |
6515 | - "@smithy/types": "^4.9.0", | |
7120 | + "@smithy/types": "^4.11.0", | |
6516 | 7121 | "tslib": "^2.6.2" |
6517 | 7122 | }, |
6518 | 7123 | "engines": { |
| @@ -6541,14 +7146,16 @@ | ||
6541 | 7146 | } |
6542 | 7147 | }, |
6543 | 7148 | "node_modules/@smithy/config-resolver": { |
6544 | - "version": "4.4.3", | |
7149 | + "version": "4.4.5", | |
7150 | + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.5.tgz", | |
7151 | + "integrity": "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg==", | |
6545 | 7152 | "license": "Apache-2.0", |
6546 | 7153 | "dependencies": { |
6547 | - "@smithy/node-config-provider": "^4.3.5", | |
6548 | - "@smithy/types": "^4.9.0", | |
7154 | + "@smithy/node-config-provider": "^4.3.7", | |
7155 | + "@smithy/types": "^4.11.0", | |
6549 | 7156 | "@smithy/util-config-provider": "^4.2.0", |
6550 | - "@smithy/util-endpoints": "^3.2.5", | |
6551 | - "@smithy/util-middleware": "^4.2.5", | |
7157 | + "@smithy/util-endpoints": "^3.2.7", | |
7158 | + "@smithy/util-middleware": "^4.2.7", | |
6552 | 7159 | "tslib": "^2.6.2" |
6553 | 7160 | }, |
6554 | 7161 | "engines": { |
| @@ -6556,16 +7163,18 @@ | ||
6556 | 7163 | } |
6557 | 7164 | }, |
6558 | 7165 | "node_modules/@smithy/core": { |
6559 | - "version": "3.18.7", | |
7166 | + "version": "3.20.0", | |
7167 | + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.20.0.tgz", | |
7168 | + "integrity": "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ==", | |
6560 | 7169 | "license": "Apache-2.0", |
6561 | 7170 | "dependencies": { |
6562 | - "@smithy/middleware-serde": "^4.2.6", | |
6563 | - "@smithy/protocol-http": "^5.3.5", | |
6564 | - "@smithy/types": "^4.9.0", | |
7171 | + "@smithy/middleware-serde": "^4.2.8", | |
7172 | + "@smithy/protocol-http": "^5.3.7", | |
7173 | + "@smithy/types": "^4.11.0", | |
6565 | 7174 | "@smithy/util-base64": "^4.3.0", |
6566 | 7175 | "@smithy/util-body-length-browser": "^4.2.0", |
6567 | - "@smithy/util-middleware": "^4.2.5", | |
6568 | - "@smithy/util-stream": "^4.5.6", | |
7176 | + "@smithy/util-middleware": "^4.2.7", | |
7177 | + "@smithy/util-stream": "^4.5.8", | |
6569 | 7178 | "@smithy/util-utf8": "^4.2.0", |
6570 | 7179 | "@smithy/uuid": "^1.1.0", |
6571 | 7180 | "tslib": "^2.6.2" |
| @@ -6575,13 +7184,15 @@ | ||
6575 | 7184 | } |
6576 | 7185 | }, |
6577 | 7186 | "node_modules/@smithy/credential-provider-imds": { |
6578 | - "version": "4.2.5", | |
7187 | + "version": "4.2.7", | |
7188 | + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.7.tgz", | |
7189 | + "integrity": "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA==", | |
6579 | 7190 | "license": "Apache-2.0", |
6580 | 7191 | "dependencies": { |
6581 | - "@smithy/node-config-provider": "^4.3.5", | |
6582 | - "@smithy/property-provider": "^4.2.5", | |
6583 | - "@smithy/types": "^4.9.0", | |
6584 | - "@smithy/url-parser": "^4.2.5", | |
7192 | + "@smithy/node-config-provider": "^4.3.7", | |
7193 | + "@smithy/property-provider": "^4.2.7", | |
7194 | + "@smithy/types": "^4.11.0", | |
7195 | + "@smithy/url-parser": "^4.2.7", | |
6585 | 7196 | "tslib": "^2.6.2" |
6586 | 7197 | }, |
6587 | 7198 | "engines": { |
| @@ -6649,12 +7260,14 @@ | ||
6649 | 7260 | } |
6650 | 7261 | }, |
6651 | 7262 | "node_modules/@smithy/fetch-http-handler": { |
6652 | - "version": "5.3.6", | |
7263 | + "version": "5.3.8", | |
7264 | + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.8.tgz", | |
7265 | + "integrity": "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg==", | |
6653 | 7266 | "license": "Apache-2.0", |
6654 | 7267 | "dependencies": { |
6655 | - "@smithy/protocol-http": "^5.3.5", | |
6656 | - "@smithy/querystring-builder": "^4.2.5", | |
6657 | - "@smithy/types": "^4.9.0", | |
7268 | + "@smithy/protocol-http": "^5.3.7", | |
7269 | + "@smithy/querystring-builder": "^4.2.7", | |
7270 | + "@smithy/types": "^4.11.0", | |
6658 | 7271 | "@smithy/util-base64": "^4.3.0", |
6659 | 7272 | "tslib": "^2.6.2" |
6660 | 7273 | }, |
| @@ -6676,10 +7289,12 @@ | ||
6676 | 7289 | } |
6677 | 7290 | }, |
6678 | 7291 | "node_modules/@smithy/hash-node": { |
6679 | - "version": "4.2.5", | |
7292 | + "version": "4.2.7", | |
7293 | + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.7.tgz", | |
7294 | + "integrity": "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw==", | |
6680 | 7295 | "license": "Apache-2.0", |
6681 | 7296 | "dependencies": { |
6682 | - "@smithy/types": "^4.9.0", | |
7297 | + "@smithy/types": "^4.11.0", | |
6683 | 7298 | "@smithy/util-buffer-from": "^4.2.0", |
6684 | 7299 | "@smithy/util-utf8": "^4.2.0", |
6685 | 7300 | "tslib": "^2.6.2" |
| @@ -6701,10 +7316,12 @@ | ||
6701 | 7316 | } |
6702 | 7317 | }, |
6703 | 7318 | "node_modules/@smithy/invalid-dependency": { |
6704 | - "version": "4.2.5", | |
7319 | + "version": "4.2.7", | |
7320 | + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.7.tgz", | |
7321 | + "integrity": "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ==", | |
6705 | 7322 | "license": "Apache-2.0", |
6706 | 7323 | "dependencies": { |
6707 | - "@smithy/types": "^4.9.0", | |
7324 | + "@smithy/types": "^4.11.0", | |
6708 | 7325 | "tslib": "^2.6.2" |
6709 | 7326 | }, |
6710 | 7327 | "engines": { |
| @@ -6734,11 +7351,13 @@ | ||
6734 | 7351 | } |
6735 | 7352 | }, |
6736 | 7353 | "node_modules/@smithy/middleware-content-length": { |
6737 | - "version": "4.2.5", | |
7354 | + "version": "4.2.7", | |
7355 | + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.7.tgz", | |
7356 | + "integrity": "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg==", | |
6738 | 7357 | "license": "Apache-2.0", |
6739 | 7358 | "dependencies": { |
6740 | - "@smithy/protocol-http": "^5.3.5", | |
6741 | - "@smithy/types": "^4.9.0", | |
7359 | + "@smithy/protocol-http": "^5.3.7", | |
7360 | + "@smithy/types": "^4.11.0", | |
6742 | 7361 | "tslib": "^2.6.2" |
6743 | 7362 | }, |
6744 | 7363 | "engines": { |
| @@ -6746,16 +7365,18 @@ | ||
6746 | 7365 | } |
6747 | 7366 | }, |
6748 | 7367 | "node_modules/@smithy/middleware-endpoint": { |
6749 | - "version": "4.3.14", | |
7368 | + "version": "4.4.1", | |
7369 | + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.1.tgz", | |
7370 | + "integrity": "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg==", | |
6750 | 7371 | "license": "Apache-2.0", |
6751 | 7372 | "dependencies": { |
6752 | - "@smithy/core": "^3.18.7", | |
6753 | - "@smithy/middleware-serde": "^4.2.6", | |
6754 | - "@smithy/node-config-provider": "^4.3.5", | |
6755 | - "@smithy/shared-ini-file-loader": "^4.4.0", | |
6756 | - "@smithy/types": "^4.9.0", | |
6757 | - "@smithy/url-parser": "^4.2.5", | |
6758 | - "@smithy/util-middleware": "^4.2.5", | |
7373 | + "@smithy/core": "^3.20.0", | |
7374 | + "@smithy/middleware-serde": "^4.2.8", | |
7375 | + "@smithy/node-config-provider": "^4.3.7", | |
7376 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
7377 | + "@smithy/types": "^4.11.0", | |
7378 | + "@smithy/url-parser": "^4.2.7", | |
7379 | + "@smithy/util-middleware": "^4.2.7", | |
6759 | 7380 | "tslib": "^2.6.2" |
6760 | 7381 | }, |
6761 | 7382 | "engines": { |
| @@ -6763,16 +7384,18 @@ | ||
6763 | 7384 | } |
6764 | 7385 | }, |
6765 | 7386 | "node_modules/@smithy/middleware-retry": { |
6766 | - "version": "4.4.14", | |
7387 | + "version": "4.4.17", | |
7388 | + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.17.tgz", | |
7389 | + "integrity": "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg==", | |
6767 | 7390 | "license": "Apache-2.0", |
6768 | 7391 | "dependencies": { |
6769 | - "@smithy/node-config-provider": "^4.3.5", | |
6770 | - "@smithy/protocol-http": "^5.3.5", | |
6771 | - "@smithy/service-error-classification": "^4.2.5", | |
6772 | - "@smithy/smithy-client": "^4.9.10", | |
6773 | - "@smithy/types": "^4.9.0", | |
6774 | - "@smithy/util-middleware": "^4.2.5", | |
6775 | - "@smithy/util-retry": "^4.2.5", | |
7392 | + "@smithy/node-config-provider": "^4.3.7", | |
7393 | + "@smithy/protocol-http": "^5.3.7", | |
7394 | + "@smithy/service-error-classification": "^4.2.7", | |
7395 | + "@smithy/smithy-client": "^4.10.2", | |
7396 | + "@smithy/types": "^4.11.0", | |
7397 | + "@smithy/util-middleware": "^4.2.7", | |
7398 | + "@smithy/util-retry": "^4.2.7", | |
6776 | 7399 | "@smithy/uuid": "^1.1.0", |
6777 | 7400 | "tslib": "^2.6.2" |
6778 | 7401 | }, |
| @@ -6781,11 +7404,13 @@ | ||
6781 | 7404 | } |
6782 | 7405 | }, |
6783 | 7406 | "node_modules/@smithy/middleware-serde": { |
6784 | - "version": "4.2.6", | |
7407 | + "version": "4.2.8", | |
7408 | + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.8.tgz", | |
7409 | + "integrity": "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w==", | |
6785 | 7410 | "license": "Apache-2.0", |
6786 | 7411 | "dependencies": { |
6787 | - "@smithy/protocol-http": "^5.3.5", | |
6788 | - "@smithy/types": "^4.9.0", | |
7412 | + "@smithy/protocol-http": "^5.3.7", | |
7413 | + "@smithy/types": "^4.11.0", | |
6789 | 7414 | "tslib": "^2.6.2" |
6790 | 7415 | }, |
6791 | 7416 | "engines": { |
| @@ -6793,10 +7418,12 @@ | ||
6793 | 7418 | } |
6794 | 7419 | }, |
6795 | 7420 | "node_modules/@smithy/middleware-stack": { |
6796 | - "version": "4.2.5", | |
7421 | + "version": "4.2.7", | |
7422 | + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.7.tgz", | |
7423 | + "integrity": "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw==", | |
6797 | 7424 | "license": "Apache-2.0", |
6798 | 7425 | "dependencies": { |
6799 | - "@smithy/types": "^4.9.0", | |
7426 | + "@smithy/types": "^4.11.0", | |
6800 | 7427 | "tslib": "^2.6.2" |
6801 | 7428 | }, |
6802 | 7429 | "engines": { |
| @@ -6804,12 +7431,14 @@ | ||
6804 | 7431 | } |
6805 | 7432 | }, |
6806 | 7433 | "node_modules/@smithy/node-config-provider": { |
6807 | - "version": "4.3.5", | |
7434 | + "version": "4.3.7", | |
7435 | + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.7.tgz", | |
7436 | + "integrity": "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw==", | |
6808 | 7437 | "license": "Apache-2.0", |
6809 | 7438 | "dependencies": { |
6810 | - "@smithy/property-provider": "^4.2.5", | |
6811 | - "@smithy/shared-ini-file-loader": "^4.4.0", | |
6812 | - "@smithy/types": "^4.9.0", | |
7439 | + "@smithy/property-provider": "^4.2.7", | |
7440 | + "@smithy/shared-ini-file-loader": "^4.4.2", | |
7441 | + "@smithy/types": "^4.11.0", | |
6813 | 7442 | "tslib": "^2.6.2" |
6814 | 7443 | }, |
6815 | 7444 | "engines": { |
| @@ -6817,13 +7446,15 @@ | ||
6817 | 7446 | } |
6818 | 7447 | }, |
6819 | 7448 | "node_modules/@smithy/node-http-handler": { |
6820 | - "version": "4.4.5", | |
7449 | + "version": "4.4.7", | |
7450 | + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.7.tgz", | |
7451 | + "integrity": "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ==", | |
6821 | 7452 | "license": "Apache-2.0", |
6822 | 7453 | "dependencies": { |
6823 | - "@smithy/abort-controller": "^4.2.5", | |
6824 | - "@smithy/protocol-http": "^5.3.5", | |
6825 | - "@smithy/querystring-builder": "^4.2.5", | |
6826 | - "@smithy/types": "^4.9.0", | |
7454 | + "@smithy/abort-controller": "^4.2.7", | |
7455 | + "@smithy/protocol-http": "^5.3.7", | |
7456 | + "@smithy/querystring-builder": "^4.2.7", | |
7457 | + "@smithy/types": "^4.11.0", | |
6827 | 7458 | "tslib": "^2.6.2" |
6828 | 7459 | }, |
6829 | 7460 | "engines": { |
| @@ -6831,10 +7462,12 @@ | ||
6831 | 7462 | } |
6832 | 7463 | }, |
6833 | 7464 | "node_modules/@smithy/property-provider": { |
6834 | - "version": "4.2.5", | |
7465 | + "version": "4.2.7", | |
7466 | + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.7.tgz", | |
7467 | + "integrity": "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA==", | |
6835 | 7468 | "license": "Apache-2.0", |
6836 | 7469 | "dependencies": { |
6837 | - "@smithy/types": "^4.9.0", | |
7470 | + "@smithy/types": "^4.11.0", | |
6838 | 7471 | "tslib": "^2.6.2" |
6839 | 7472 | }, |
6840 | 7473 | "engines": { |
| @@ -6842,10 +7475,12 @@ | ||
6842 | 7475 | } |
6843 | 7476 | }, |
6844 | 7477 | "node_modules/@smithy/protocol-http": { |
6845 | - "version": "5.3.5", | |
7478 | + "version": "5.3.7", | |
7479 | + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.7.tgz", | |
7480 | + "integrity": "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA==", | |
6846 | 7481 | "license": "Apache-2.0", |
6847 | 7482 | "dependencies": { |
6848 | - "@smithy/types": "^4.9.0", | |
7483 | + "@smithy/types": "^4.11.0", | |
6849 | 7484 | "tslib": "^2.6.2" |
6850 | 7485 | }, |
6851 | 7486 | "engines": { |
| @@ -6853,10 +7488,12 @@ | ||
6853 | 7488 | } |
6854 | 7489 | }, |
6855 | 7490 | "node_modules/@smithy/querystring-builder": { |
6856 | - "version": "4.2.5", | |
7491 | + "version": "4.2.7", | |
7492 | + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.7.tgz", | |
7493 | + "integrity": "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg==", | |
6857 | 7494 | "license": "Apache-2.0", |
6858 | 7495 | "dependencies": { |
6859 | - "@smithy/types": "^4.9.0", | |
7496 | + "@smithy/types": "^4.11.0", | |
6860 | 7497 | "@smithy/util-uri-escape": "^4.2.0", |
6861 | 7498 | "tslib": "^2.6.2" |
6862 | 7499 | }, |
| @@ -6865,10 +7502,12 @@ | ||
6865 | 7502 | } |
6866 | 7503 | }, |
6867 | 7504 | "node_modules/@smithy/querystring-parser": { |
6868 | - "version": "4.2.5", | |
7505 | + "version": "4.2.7", | |
7506 | + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.7.tgz", | |
7507 | + "integrity": "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w==", | |
6869 | 7508 | "license": "Apache-2.0", |
6870 | 7509 | "dependencies": { |
6871 | - "@smithy/types": "^4.9.0", | |
7510 | + "@smithy/types": "^4.11.0", | |
6872 | 7511 | "tslib": "^2.6.2" |
6873 | 7512 | }, |
6874 | 7513 | "engines": { |
| @@ -6876,20 +7515,24 @@ | ||
6876 | 7515 | } |
6877 | 7516 | }, |
6878 | 7517 | "node_modules/@smithy/service-error-classification": { |
6879 | - "version": "4.2.5", | |
7518 | + "version": "4.2.7", | |
7519 | + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.7.tgz", | |
7520 | + "integrity": "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA==", | |
6880 | 7521 | "license": "Apache-2.0", |
6881 | 7522 | "dependencies": { |
6882 | - "@smithy/types": "^4.9.0" | |
7523 | + "@smithy/types": "^4.11.0" | |
6883 | 7524 | }, |
6884 | 7525 | "engines": { |
6885 | 7526 | "node": ">=18.0.0" |
6886 | 7527 | } |
6887 | 7528 | }, |
6888 | 7529 | "node_modules/@smithy/shared-ini-file-loader": { |
6889 | - "version": "4.4.0", | |
7530 | + "version": "4.4.2", | |
7531 | + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.2.tgz", | |
7532 | + "integrity": "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg==", | |
6890 | 7533 | "license": "Apache-2.0", |
6891 | 7534 | "dependencies": { |
6892 | - "@smithy/types": "^4.9.0", | |
7535 | + "@smithy/types": "^4.11.0", | |
6893 | 7536 | "tslib": "^2.6.2" |
6894 | 7537 | }, |
6895 | 7538 | "engines": { |
| @@ -6897,14 +7540,16 @@ | ||
6897 | 7540 | } |
6898 | 7541 | }, |
6899 | 7542 | "node_modules/@smithy/signature-v4": { |
6900 | - "version": "5.3.5", | |
7543 | + "version": "5.3.7", | |
7544 | + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.7.tgz", | |
7545 | + "integrity": "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg==", | |
6901 | 7546 | "license": "Apache-2.0", |
6902 | 7547 | "dependencies": { |
6903 | 7548 | "@smithy/is-array-buffer": "^4.2.0", |
6904 | - "@smithy/protocol-http": "^5.3.5", | |
6905 | - "@smithy/types": "^4.9.0", | |
7549 | + "@smithy/protocol-http": "^5.3.7", | |
7550 | + "@smithy/types": "^4.11.0", | |
6906 | 7551 | "@smithy/util-hex-encoding": "^4.2.0", |
6907 | - "@smithy/util-middleware": "^4.2.5", | |
7552 | + "@smithy/util-middleware": "^4.2.7", | |
6908 | 7553 | "@smithy/util-uri-escape": "^4.2.0", |
6909 | 7554 | "@smithy/util-utf8": "^4.2.0", |
6910 | 7555 | "tslib": "^2.6.2" |
| @@ -6914,15 +7559,17 @@ | ||
6914 | 7559 | } |
6915 | 7560 | }, |
6916 | 7561 | "node_modules/@smithy/smithy-client": { |
6917 | - "version": "4.9.10", | |
7562 | + "version": "4.10.2", | |
7563 | + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.10.2.tgz", | |
7564 | + "integrity": "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g==", | |
6918 | 7565 | "license": "Apache-2.0", |
6919 | 7566 | "dependencies": { |
6920 | - "@smithy/core": "^3.18.7", | |
6921 | - "@smithy/middleware-endpoint": "^4.3.14", | |
6922 | - "@smithy/middleware-stack": "^4.2.5", | |
6923 | - "@smithy/protocol-http": "^5.3.5", | |
6924 | - "@smithy/types": "^4.9.0", | |
6925 | - "@smithy/util-stream": "^4.5.6", | |
7567 | + "@smithy/core": "^3.20.0", | |
7568 | + "@smithy/middleware-endpoint": "^4.4.1", | |
7569 | + "@smithy/middleware-stack": "^4.2.7", | |
7570 | + "@smithy/protocol-http": "^5.3.7", | |
7571 | + "@smithy/types": "^4.11.0", | |
7572 | + "@smithy/util-stream": "^4.5.8", | |
6926 | 7573 | "tslib": "^2.6.2" |
6927 | 7574 | }, |
6928 | 7575 | "engines": { |
| @@ -6930,7 +7577,9 @@ | ||
6930 | 7577 | } |
6931 | 7578 | }, |
6932 | 7579 | "node_modules/@smithy/types": { |
6933 | - "version": "4.9.0", | |
7580 | + "version": "4.11.0", | |
7581 | + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.11.0.tgz", | |
7582 | + "integrity": "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA==", | |
6934 | 7583 | "license": "Apache-2.0", |
6935 | 7584 | "dependencies": { |
6936 | 7585 | "tslib": "^2.6.2" |
| @@ -6940,11 +7589,13 @@ | ||
6940 | 7589 | } |
6941 | 7590 | }, |
6942 | 7591 | "node_modules/@smithy/url-parser": { |
6943 | - "version": "4.2.5", | |
7592 | + "version": "4.2.7", | |
7593 | + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.7.tgz", | |
7594 | + "integrity": "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg==", | |
6944 | 7595 | "license": "Apache-2.0", |
6945 | 7596 | "dependencies": { |
6946 | - "@smithy/querystring-parser": "^4.2.5", | |
6947 | - "@smithy/types": "^4.9.0", | |
7597 | + "@smithy/querystring-parser": "^4.2.7", | |
7598 | + "@smithy/types": "^4.11.0", | |
6948 | 7599 | "tslib": "^2.6.2" |
6949 | 7600 | }, |
6950 | 7601 | "engines": { |
| @@ -7005,12 +7656,14 @@ | ||
7005 | 7656 | } |
7006 | 7657 | }, |
7007 | 7658 | "node_modules/@smithy/util-defaults-mode-browser": { |
7008 | - "version": "4.3.13", | |
7659 | + "version": "4.3.16", | |
7660 | + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.16.tgz", | |
7661 | + "integrity": "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ==", | |
7009 | 7662 | "license": "Apache-2.0", |
7010 | 7663 | "dependencies": { |
7011 | - "@smithy/property-provider": "^4.2.5", | |
7012 | - "@smithy/smithy-client": "^4.9.10", | |
7013 | - "@smithy/types": "^4.9.0", | |
7664 | + "@smithy/property-provider": "^4.2.7", | |
7665 | + "@smithy/smithy-client": "^4.10.2", | |
7666 | + "@smithy/types": "^4.11.0", | |
7014 | 7667 | "tslib": "^2.6.2" |
7015 | 7668 | }, |
7016 | 7669 | "engines": { |
| @@ -7018,15 +7671,17 @@ | ||
7018 | 7671 | } |
7019 | 7672 | }, |
7020 | 7673 | "node_modules/@smithy/util-defaults-mode-node": { |
7021 | - "version": "4.2.16", | |
7674 | + "version": "4.2.19", | |
7675 | + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.19.tgz", | |
7676 | + "integrity": "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA==", | |
7022 | 7677 | "license": "Apache-2.0", |
7023 | 7678 | "dependencies": { |
7024 | - "@smithy/config-resolver": "^4.4.3", | |
7025 | - "@smithy/credential-provider-imds": "^4.2.5", | |
7026 | - "@smithy/node-config-provider": "^4.3.5", | |
7027 | - "@smithy/property-provider": "^4.2.5", | |
7028 | - "@smithy/smithy-client": "^4.9.10", | |
7029 | - "@smithy/types": "^4.9.0", | |
7679 | + "@smithy/config-resolver": "^4.4.5", | |
7680 | + "@smithy/credential-provider-imds": "^4.2.7", | |
7681 | + "@smithy/node-config-provider": "^4.3.7", | |
7682 | + "@smithy/property-provider": "^4.2.7", | |
7683 | + "@smithy/smithy-client": "^4.10.2", | |
7684 | + "@smithy/types": "^4.11.0", | |
7030 | 7685 | "tslib": "^2.6.2" |
7031 | 7686 | }, |
7032 | 7687 | "engines": { |
| @@ -7034,11 +7689,13 @@ | ||
7034 | 7689 | } |
7035 | 7690 | }, |
7036 | 7691 | "node_modules/@smithy/util-endpoints": { |
7037 | - "version": "3.2.5", | |
7692 | + "version": "3.2.7", | |
7693 | + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.7.tgz", | |
7694 | + "integrity": "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg==", | |
7038 | 7695 | "license": "Apache-2.0", |
7039 | 7696 | "dependencies": { |
7040 | - "@smithy/node-config-provider": "^4.3.5", | |
7041 | - "@smithy/types": "^4.9.0", | |
7697 | + "@smithy/node-config-provider": "^4.3.7", | |
7698 | + "@smithy/types": "^4.11.0", | |
7042 | 7699 | "tslib": "^2.6.2" |
7043 | 7700 | }, |
7044 | 7701 | "engines": { |
| @@ -7056,10 +7713,12 @@ | ||
7056 | 7713 | } |
7057 | 7714 | }, |
7058 | 7715 | "node_modules/@smithy/util-middleware": { |
7059 | - "version": "4.2.5", | |
7716 | + "version": "4.2.7", | |
7717 | + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.7.tgz", | |
7718 | + "integrity": "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w==", | |
7060 | 7719 | "license": "Apache-2.0", |
7061 | 7720 | "dependencies": { |
7062 | - "@smithy/types": "^4.9.0", | |
7721 | + "@smithy/types": "^4.11.0", | |
7063 | 7722 | "tslib": "^2.6.2" |
7064 | 7723 | }, |
7065 | 7724 | "engines": { |
| @@ -7067,11 +7726,13 @@ | ||
7067 | 7726 | } |
7068 | 7727 | }, |
7069 | 7728 | "node_modules/@smithy/util-retry": { |
7070 | - "version": "4.2.5", | |
7729 | + "version": "4.2.7", | |
7730 | + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.7.tgz", | |
7731 | + "integrity": "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg==", | |
7071 | 7732 | "license": "Apache-2.0", |
7072 | 7733 | "dependencies": { |
7073 | - "@smithy/service-error-classification": "^4.2.5", | |
7074 | - "@smithy/types": "^4.9.0", | |
7734 | + "@smithy/service-error-classification": "^4.2.7", | |
7735 | + "@smithy/types": "^4.11.0", | |
7075 | 7736 | "tslib": "^2.6.2" |
7076 | 7737 | }, |
7077 | 7738 | "engines": { |
| @@ -7079,12 +7740,14 @@ | ||
7079 | 7740 | } |
7080 | 7741 | }, |
7081 | 7742 | "node_modules/@smithy/util-stream": { |
7082 | - "version": "4.5.6", | |
7743 | + "version": "4.5.8", | |
7744 | + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.8.tgz", | |
7745 | + "integrity": "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w==", | |
7083 | 7746 | "license": "Apache-2.0", |
7084 | 7747 | "dependencies": { |
7085 | - "@smithy/fetch-http-handler": "^5.3.6", | |
7086 | - "@smithy/node-http-handler": "^4.4.5", | |
7087 | - "@smithy/types": "^4.9.0", | |
7748 | + "@smithy/fetch-http-handler": "^5.3.8", | |
7749 | + "@smithy/node-http-handler": "^4.4.7", | |
7750 | + "@smithy/types": "^4.11.0", | |
7088 | 7751 | "@smithy/util-base64": "^4.3.0", |
7089 | 7752 | "@smithy/util-buffer-from": "^4.2.0", |
7090 | 7753 | "@smithy/util-hex-encoding": "^4.2.0", |
| @@ -7170,6 +7833,55 @@ | ||
7170 | 7833 | "node": ">=4" |
7171 | 7834 | } |
7172 | 7835 | }, |
7836 | + "node_modules/@turf/boolean-point-in-polygon": { | |
7837 | + "version": "7.3.1", | |
7838 | + "resolved": "https://registry.npmjs.org/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-7.3.1.tgz", | |
7839 | + "integrity": "sha512-BUPW63vE43LctwkgannjmEFTX1KFR/18SS7WzFahJWK1ZoP0s1jrfxGX+pi0BH/3Dd9mA71hkGKDDnj1Ndcz0g==", | |
7840 | + "license": "MIT", | |
7841 | + "dependencies": { | |
7842 | + "@turf/helpers": "7.3.1", | |
7843 | + "@turf/invariant": "7.3.1", | |
7844 | + "@types/geojson": "^7946.0.10", | |
7845 | + "point-in-polygon-hao": "^1.1.0", | |
7846 | + "tslib": "^2.8.1" | |
7847 | + }, | |
7848 | + "funding": { | |
7849 | + "url": "https://opencollective.com/turf" | |
7850 | + } | |
7851 | + }, | |
7852 | + "node_modules/@turf/helpers": { | |
7853 | + "version": "7.3.1", | |
7854 | + "resolved": "https://registry.npmjs.org/@turf/helpers/-/helpers-7.3.1.tgz", | |
7855 | + "integrity": "sha512-zkL34JVhi5XhsuMEO0MUTIIFEJ8yiW1InMu4hu/oRqamlY4mMoZql0viEmH6Dafh/p+zOl8OYvMJ3Vm3rFshgg==", | |
7856 | + "license": "MIT", | |
7857 | + "dependencies": { | |
7858 | + "@types/geojson": "^7946.0.10", | |
7859 | + "tslib": "^2.8.1" | |
7860 | + }, | |
7861 | + "funding": { | |
7862 | + "url": "https://opencollective.com/turf" | |
7863 | + } | |
7864 | + }, | |
7865 | + "node_modules/@turf/invariant": { | |
7866 | + "version": "7.3.1", | |
7867 | + "resolved": "https://registry.npmjs.org/@turf/invariant/-/invariant-7.3.1.tgz", | |
7868 | + "integrity": "sha512-IdZJfDjIDCLH+Gu2yLFoSM7H23sdetIo5t4ET1/25X8gi3GE2XSqbZwaGjuZgNh02nisBewLqNiJs2bo+hrqZA==", | |
7869 | + "license": "MIT", | |
7870 | + "dependencies": { | |
7871 | + "@turf/helpers": "7.3.1", | |
7872 | + "@types/geojson": "^7946.0.10", | |
7873 | + "tslib": "^2.8.1" | |
7874 | + }, | |
7875 | + "funding": { | |
7876 | + "url": "https://opencollective.com/turf" | |
7877 | + } | |
7878 | + }, | |
7879 | + "node_modules/@tweenjs/tween.js": { | |
7880 | + "version": "25.0.0", | |
7881 | + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-25.0.0.tgz", | |
7882 | + "integrity": "sha512-XKLA6syeBUaPzx4j3qwMqzzq+V4uo72BnlbOjmuljLrRqdsd3qnzvZZoxvMHZ23ndsRS4aufU6JOZYpCbU6T1A==", | |
7883 | + "license": "MIT" | |
7884 | + }, | |
7173 | 7885 | "node_modules/@types/aws-lambda": { |
7174 | 7886 | "version": "8.10.159", |
7175 | 7887 | "license": "MIT" |
| @@ -7283,6 +7995,12 @@ | ||
7283 | 7995 | "version": "1.0.8", |
7284 | 7996 | "license": "MIT" |
7285 | 7997 | }, |
7998 | + "node_modules/@types/geojson": { | |
7999 | + "version": "7946.0.16", | |
8000 | + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", | |
8001 | + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", | |
8002 | + "license": "MIT" | |
8003 | + }, | |
7286 | 8004 | "node_modules/@types/hast": { |
7287 | 8005 | "version": "3.0.4", |
7288 | 8006 | "license": "MIT", |
| @@ -7339,6 +8057,16 @@ | ||
7339 | 8057 | "undici-types": "~6.21.0" |
7340 | 8058 | } |
7341 | 8059 | }, |
8060 | + "node_modules/@types/nodemailer": { | |
8061 | + "version": "7.0.4", | |
8062 | + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.4.tgz", | |
8063 | + "integrity": "sha512-ee8fxWqOchH+Hv6MDDNNy028kwvVnLplrStm4Zf/3uHWw5zzo8FoYYeffpJtGs2wWysEumMH0ZIdMGMY1eMAow==", | |
8064 | + "license": "MIT", | |
8065 | + "dependencies": { | |
8066 | + "@aws-sdk/client-sesv2": "^3.839.0", | |
8067 | + "@types/node": "*" | |
8068 | + } | |
8069 | + }, | |
7342 | 8070 | "node_modules/@types/oracledb": { |
7343 | 8071 | "version": "6.5.2", |
7344 | 8072 | "license": "MIT", |
| @@ -7348,6 +8076,8 @@ | ||
7348 | 8076 | }, |
7349 | 8077 | "node_modules/@types/pg": { |
7350 | 8078 | "version": "8.16.0", |
8079 | + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.16.0.tgz", | |
8080 | + "integrity": "sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==", | |
7351 | 8081 | "license": "MIT", |
7352 | 8082 | "dependencies": { |
7353 | 8083 | "@types/node": "*", |
| @@ -7404,6 +8134,13 @@ | ||
7404 | 8134 | "dev": true, |
7405 | 8135 | "license": "MIT" |
7406 | 8136 | }, |
8137 | + "node_modules/@types/stats.js": { | |
8138 | + "version": "0.17.4", | |
8139 | + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", | |
8140 | + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", | |
8141 | + "dev": true, | |
8142 | + "license": "MIT" | |
8143 | + }, | |
7407 | 8144 | "node_modules/@types/tedious": { |
7408 | 8145 | "version": "4.0.14", |
7409 | 8146 | "license": "MIT", |
| @@ -7411,10 +8148,40 @@ | ||
7411 | 8148 | "@types/node": "*" |
7412 | 8149 | } |
7413 | 8150 | }, |
8151 | + "node_modules/@types/three": { | |
8152 | + "version": "0.182.0", | |
8153 | + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.182.0.tgz", | |
8154 | + "integrity": "sha512-WByN9V3Sbwbe2OkWuSGyoqQO8Du6yhYaXtXLoA5FkKTUJorZ+yOHBZ35zUUPQXlAKABZmbYp5oAqpA4RBjtJ/Q==", | |
8155 | + "dev": true, | |
8156 | + "license": "MIT", | |
8157 | + "dependencies": { | |
8158 | + "@dimforge/rapier3d-compat": "~0.12.0", | |
8159 | + "@tweenjs/tween.js": "~23.1.3", | |
8160 | + "@types/stats.js": "*", | |
8161 | + "@types/webxr": ">=0.5.17", | |
8162 | + "@webgpu/types": "*", | |
8163 | + "fflate": "~0.8.2", | |
8164 | + "meshoptimizer": "~0.22.0" | |
8165 | + } | |
8166 | + }, | |
8167 | + "node_modules/@types/three/node_modules/@tweenjs/tween.js": { | |
8168 | + "version": "23.1.3", | |
8169 | + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", | |
8170 | + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", | |
8171 | + "dev": true, | |
8172 | + "license": "MIT" | |
8173 | + }, | |
7414 | 8174 | "node_modules/@types/unist": { |
7415 | 8175 | "version": "3.0.3", |
7416 | 8176 | "license": "MIT" |
7417 | 8177 | }, |
8178 | + "node_modules/@types/webxr": { | |
8179 | + "version": "0.5.24", | |
8180 | + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", | |
8181 | + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", | |
8182 | + "dev": true, | |
8183 | + "license": "MIT" | |
8184 | + }, | |
7418 | 8185 | "node_modules/@ungap/structured-clone": { |
7419 | 8186 | "version": "1.3.0", |
7420 | 8187 | "license": "ISC" |
| @@ -7534,6 +8301,13 @@ | ||
7534 | 8301 | "url": "https://opencollective.com/vitest" |
7535 | 8302 | } |
7536 | 8303 | }, |
8304 | + "node_modules/@webgpu/types": { | |
8305 | + "version": "0.1.68", | |
8306 | + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.68.tgz", | |
8307 | + "integrity": "sha512-3ab1B59Ojb6RwjOspYLsTpCzbNB3ZaamIAxBMmvnNkiDoLTZUOBXZ9p5nAYVEkQlDdf6qAZWi1pqj9+ypiqznA==", | |
8308 | + "dev": true, | |
8309 | + "license": "BSD-3-Clause" | |
8310 | + }, | |
7537 | 8311 | "node_modules/abbrev": { |
7538 | 8312 | "version": "3.0.1", |
7539 | 8313 | "license": "ISC", |
| @@ -7551,6 +8325,15 @@ | ||
7551 | 8325 | "node": ">=6.5" |
7552 | 8326 | } |
7553 | 8327 | }, |
8328 | + "node_modules/accessor-fn": { | |
8329 | + "version": "1.5.3", | |
8330 | + "resolved": "https://registry.npmjs.org/accessor-fn/-/accessor-fn-1.5.3.tgz", | |
8331 | + "integrity": "sha512-rkAofCwe/FvYFUlMB0v0gWmhqtfAtV1IUkdPbfhTUyYniu5LrC0A0UJkTH0Jv3S8SvwkmfuAlY+mQIJATdocMA==", | |
8332 | + "license": "MIT", | |
8333 | + "engines": { | |
8334 | + "node": ">=12" | |
8335 | + } | |
8336 | + }, | |
7554 | 8337 | "node_modules/acorn": { |
7555 | 8338 | "version": "8.15.0", |
7556 | 8339 | "license": "MIT", |
| @@ -8644,6 +9427,12 @@ | ||
8644 | 9427 | "ieee754": "^1.2.1" |
8645 | 9428 | } |
8646 | 9429 | }, |
9430 | + "node_modules/buffer-equal-constant-time": { | |
9431 | + "version": "1.0.1", | |
9432 | + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", | |
9433 | + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", | |
9434 | + "license": "BSD-3-Clause" | |
9435 | + }, | |
8647 | 9436 | "node_modules/buffer-from": { |
8648 | 9437 | "version": "1.1.2", |
8649 | 9438 | "dev": true, |
| @@ -9038,6 +9827,8 @@ | ||
9038 | 9827 | }, |
9039 | 9828 | "node_modules/clsx": { |
9040 | 9829 | "version": "2.1.1", |
9830 | + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", | |
9831 | + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", | |
9041 | 9832 | "license": "MIT", |
9042 | 9833 | "engines": { |
9043 | 9834 | "node": ">=6" |
| @@ -9066,6 +9857,15 @@ | ||
9066 | 9857 | "react-dom": "^18 || ^19 || ^19.0.0-rc" |
9067 | 9858 | } |
9068 | 9859 | }, |
9860 | + "node_modules/cobe": { | |
9861 | + "version": "0.6.5", | |
9862 | + "resolved": "https://registry.npmjs.org/cobe/-/cobe-0.6.5.tgz", | |
9863 | + "integrity": "sha512-MA8bu81EFY6JjQpj+FovEuhyJ25khx2Q7Lh+ot/UkCJe5yKyDgzdc6u2lGZIOmsZTXK6Itg1i4lQZIJZbPWnAg==", | |
9864 | + "license": "MIT", | |
9865 | + "dependencies": { | |
9866 | + "phenomenon": "^1.6.0" | |
9867 | + } | |
9868 | + }, | |
9069 | 9869 | "node_modules/color": { |
9070 | 9870 | "version": "4.2.3", |
9071 | 9871 | "license": "MIT", |
| @@ -9218,6 +10018,189 @@ | ||
9218 | 10018 | "version": "3.2.3", |
9219 | 10019 | "license": "MIT" |
9220 | 10020 | }, |
10021 | + "node_modules/d3-array": { | |
10022 | + "version": "3.2.4", | |
10023 | + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", | |
10024 | + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", | |
10025 | + "license": "ISC", | |
10026 | + "dependencies": { | |
10027 | + "internmap": "1 - 2" | |
10028 | + }, | |
10029 | + "engines": { | |
10030 | + "node": ">=12" | |
10031 | + } | |
10032 | + }, | |
10033 | + "node_modules/d3-color": { | |
10034 | + "version": "3.1.0", | |
10035 | + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", | |
10036 | + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", | |
10037 | + "license": "ISC", | |
10038 | + "engines": { | |
10039 | + "node": ">=12" | |
10040 | + } | |
10041 | + }, | |
10042 | + "node_modules/d3-delaunay": { | |
10043 | + "version": "6.0.4", | |
10044 | + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", | |
10045 | + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", | |
10046 | + "license": "ISC", | |
10047 | + "dependencies": { | |
10048 | + "delaunator": "5" | |
10049 | + }, | |
10050 | + "engines": { | |
10051 | + "node": ">=12" | |
10052 | + } | |
10053 | + }, | |
10054 | + "node_modules/d3-format": { | |
10055 | + "version": "3.1.0", | |
10056 | + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", | |
10057 | + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", | |
10058 | + "license": "ISC", | |
10059 | + "engines": { | |
10060 | + "node": ">=12" | |
10061 | + } | |
10062 | + }, | |
10063 | + "node_modules/d3-geo": { | |
10064 | + "version": "3.1.1", | |
10065 | + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", | |
10066 | + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", | |
10067 | + "license": "ISC", | |
10068 | + "dependencies": { | |
10069 | + "d3-array": "2.5.0 - 3" | |
10070 | + }, | |
10071 | + "engines": { | |
10072 | + "node": ">=12" | |
10073 | + } | |
10074 | + }, | |
10075 | + "node_modules/d3-geo-voronoi": { | |
10076 | + "version": "2.1.0", | |
10077 | + "resolved": "https://registry.npmjs.org/d3-geo-voronoi/-/d3-geo-voronoi-2.1.0.tgz", | |
10078 | + "integrity": "sha512-kqE4yYuOjPbKdBXG0xztCacPwkVSK2REF1opSNrnqqtXJmNcM++UbwQ8SxvwP6IQTj9RvIjjK4qeiVsEfj0Z2Q==", | |
10079 | + "license": "ISC", | |
10080 | + "dependencies": { | |
10081 | + "d3-array": "3", | |
10082 | + "d3-delaunay": "6", | |
10083 | + "d3-geo": "3", | |
10084 | + "d3-tricontour": "1" | |
10085 | + }, | |
10086 | + "engines": { | |
10087 | + "node": ">=12" | |
10088 | + } | |
10089 | + }, | |
10090 | + "node_modules/d3-interpolate": { | |
10091 | + "version": "3.0.1", | |
10092 | + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", | |
10093 | + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", | |
10094 | + "license": "ISC", | |
10095 | + "dependencies": { | |
10096 | + "d3-color": "1 - 3" | |
10097 | + }, | |
10098 | + "engines": { | |
10099 | + "node": ">=12" | |
10100 | + } | |
10101 | + }, | |
10102 | + "node_modules/d3-octree": { | |
10103 | + "version": "1.1.0", | |
10104 | + "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.1.0.tgz", | |
10105 | + "integrity": "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==", | |
10106 | + "license": "MIT" | |
10107 | + }, | |
10108 | + "node_modules/d3-scale": { | |
10109 | + "version": "4.0.2", | |
10110 | + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", | |
10111 | + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", | |
10112 | + "license": "ISC", | |
10113 | + "dependencies": { | |
10114 | + "d3-array": "2.10.0 - 3", | |
10115 | + "d3-format": "1 - 3", | |
10116 | + "d3-interpolate": "1.2.0 - 3", | |
10117 | + "d3-time": "2.1.1 - 3", | |
10118 | + "d3-time-format": "2 - 4" | |
10119 | + }, | |
10120 | + "engines": { | |
10121 | + "node": ">=12" | |
10122 | + } | |
10123 | + }, | |
10124 | + "node_modules/d3-scale-chromatic": { | |
10125 | + "version": "3.1.0", | |
10126 | + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", | |
10127 | + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", | |
10128 | + "license": "ISC", | |
10129 | + "dependencies": { | |
10130 | + "d3-color": "1 - 3", | |
10131 | + "d3-interpolate": "1 - 3" | |
10132 | + }, | |
10133 | + "engines": { | |
10134 | + "node": ">=12" | |
10135 | + } | |
10136 | + }, | |
10137 | + "node_modules/d3-selection": { | |
10138 | + "version": "3.0.0", | |
10139 | + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", | |
10140 | + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", | |
10141 | + "license": "ISC", | |
10142 | + "engines": { | |
10143 | + "node": ">=12" | |
10144 | + } | |
10145 | + }, | |
10146 | + "node_modules/d3-time": { | |
10147 | + "version": "3.1.0", | |
10148 | + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", | |
10149 | + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", | |
10150 | + "license": "ISC", | |
10151 | + "dependencies": { | |
10152 | + "d3-array": "2 - 3" | |
10153 | + }, | |
10154 | + "engines": { | |
10155 | + "node": ">=12" | |
10156 | + } | |
10157 | + }, | |
10158 | + "node_modules/d3-time-format": { | |
10159 | + "version": "4.1.0", | |
10160 | + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", | |
10161 | + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", | |
10162 | + "license": "ISC", | |
10163 | + "dependencies": { | |
10164 | + "d3-time": "1 - 3" | |
10165 | + }, | |
10166 | + "engines": { | |
10167 | + "node": ">=12" | |
10168 | + } | |
10169 | + }, | |
10170 | + "node_modules/d3-tricontour": { | |
10171 | + "version": "1.1.0", | |
10172 | + "resolved": "https://registry.npmjs.org/d3-tricontour/-/d3-tricontour-1.1.0.tgz", | |
10173 | + "integrity": "sha512-G7gHKj89n2owmkGb6WX6ixcnQ0Kf/0wpa9VIh9DGdbHu8wdrlaHU4ir3/bFNERl8N8nn4G7e7qbtBG8N9caihQ==", | |
10174 | + "license": "ISC", | |
10175 | + "dependencies": { | |
10176 | + "d3-delaunay": "6", | |
10177 | + "d3-scale": "4" | |
10178 | + }, | |
10179 | + "engines": { | |
10180 | + "node": ">=12" | |
10181 | + } | |
10182 | + }, | |
10183 | + "node_modules/data-bind-mapper": { | |
10184 | + "version": "1.0.3", | |
10185 | + "resolved": "https://registry.npmjs.org/data-bind-mapper/-/data-bind-mapper-1.0.3.tgz", | |
10186 | + "integrity": "sha512-QmU3lyEnbENQPo0M1F9BMu4s6cqNNp8iJA+b/HP2sSb7pf3dxwF3+EP1eO69rwBfH9kFJ1apmzrtogAmVt2/Xw==", | |
10187 | + "license": "MIT", | |
10188 | + "dependencies": { | |
10189 | + "accessor-fn": "1" | |
10190 | + }, | |
10191 | + "engines": { | |
10192 | + "node": ">=12" | |
10193 | + } | |
10194 | + }, | |
10195 | + "node_modules/data-uri-to-buffer": { | |
10196 | + "version": "4.0.1", | |
10197 | + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", | |
10198 | + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", | |
10199 | + "license": "MIT", | |
10200 | + "engines": { | |
10201 | + "node": ">= 12" | |
10202 | + } | |
10203 | + }, | |
9221 | 10204 | "node_modules/dateformat": { |
9222 | 10205 | "version": "4.6.3", |
9223 | 10206 | "dev": true, |
| @@ -9287,6 +10270,15 @@ | ||
9287 | 10270 | "url": "https://github.com/sponsors/ljharb" |
9288 | 10271 | } |
9289 | 10272 | }, |
10273 | + "node_modules/delaunator": { | |
10274 | + "version": "5.0.1", | |
10275 | + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", | |
10276 | + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", | |
10277 | + "license": "ISC", | |
10278 | + "dependencies": { | |
10279 | + "robust-predicates": "^3.0.2" | |
10280 | + } | |
10281 | + }, | |
9290 | 10282 | "node_modules/denque": { |
9291 | 10283 | "version": "2.1.0", |
9292 | 10284 | "license": "Apache-2.0", |
| @@ -9579,10 +10571,25 @@ | ||
9579 | 10571 | "node": ">= 0.4" |
9580 | 10572 | } |
9581 | 10573 | }, |
10574 | + "node_modules/earcut": { | |
10575 | + "version": "3.0.2", | |
10576 | + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", | |
10577 | + "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", | |
10578 | + "license": "ISC" | |
10579 | + }, | |
9582 | 10580 | "node_modules/eastasianwidth": { |
9583 | 10581 | "version": "0.2.0", |
9584 | 10582 | "license": "MIT" |
9585 | 10583 | }, |
10584 | + "node_modules/ecdsa-sig-formatter": { | |
10585 | + "version": "1.0.11", | |
10586 | + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", | |
10587 | + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", | |
10588 | + "license": "Apache-2.0", | |
10589 | + "dependencies": { | |
10590 | + "safe-buffer": "^5.0.1" | |
10591 | + } | |
10592 | + }, | |
9586 | 10593 | "node_modules/ee-first": { |
9587 | 10594 | "version": "1.1.1", |
9588 | 10595 | "license": "MIT" |
| @@ -9894,6 +10901,36 @@ | ||
9894 | 10901 | } |
9895 | 10902 | } |
9896 | 10903 | }, |
10904 | + "node_modules/fetch-blob": { | |
10905 | + "version": "3.2.0", | |
10906 | + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", | |
10907 | + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", | |
10908 | + "funding": [ | |
10909 | + { | |
10910 | + "type": "github", | |
10911 | + "url": "https://github.com/sponsors/jimmywarting" | |
10912 | + }, | |
10913 | + { | |
10914 | + "type": "paypal", | |
10915 | + "url": "https://paypal.me/jimmywarting" | |
10916 | + } | |
10917 | + ], | |
10918 | + "license": "MIT", | |
10919 | + "dependencies": { | |
10920 | + "node-domexception": "^1.0.0", | |
10921 | + "web-streams-polyfill": "^3.0.3" | |
10922 | + }, | |
10923 | + "engines": { | |
10924 | + "node": "^12.20 || >= 14.13" | |
10925 | + } | |
10926 | + }, | |
10927 | + "node_modules/fflate": { | |
10928 | + "version": "0.8.2", | |
10929 | + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", | |
10930 | + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", | |
10931 | + "dev": true, | |
10932 | + "license": "MIT" | |
10933 | + }, | |
9897 | 10934 | "node_modules/file-uri-to-path": { |
9898 | 10935 | "version": "1.0.0", |
9899 | 10936 | "license": "MIT" |
| @@ -9944,6 +10981,20 @@ | ||
9944 | 10981 | "node": ">=8" |
9945 | 10982 | } |
9946 | 10983 | }, |
10984 | + "node_modules/float-tooltip": { | |
10985 | + "version": "1.7.5", | |
10986 | + "resolved": "https://registry.npmjs.org/float-tooltip/-/float-tooltip-1.7.5.tgz", | |
10987 | + "integrity": "sha512-/kXzuDnnBqyyWyhDMH7+PfP8J/oXiAavGzcRxASOMRHFuReDtofizLLJsf7nnDLAfEaMW4pVWaXrAjtnglpEkg==", | |
10988 | + "license": "MIT", | |
10989 | + "dependencies": { | |
10990 | + "d3-selection": "2 - 3", | |
10991 | + "kapsule": "^1.16", | |
10992 | + "preact": "10" | |
10993 | + }, | |
10994 | + "engines": { | |
10995 | + "node": ">=12" | |
10996 | + } | |
10997 | + }, | |
9947 | 10998 | "node_modules/for-each": { |
9948 | 10999 | "version": "0.3.5", |
9949 | 11000 | "license": "MIT", |
| @@ -9978,6 +11029,18 @@ | ||
9978 | 11029 | "node": ">= 18" |
9979 | 11030 | } |
9980 | 11031 | }, |
11032 | + "node_modules/formdata-polyfill": { | |
11033 | + "version": "4.0.10", | |
11034 | + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", | |
11035 | + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", | |
11036 | + "license": "MIT", | |
11037 | + "dependencies": { | |
11038 | + "fetch-blob": "^3.1.2" | |
11039 | + }, | |
11040 | + "engines": { | |
11041 | + "node": ">=12.20.0" | |
11042 | + } | |
11043 | + }, | |
9981 | 11044 | "node_modules/forwarded-parse": { |
9982 | 11045 | "version": "2.1.2", |
9983 | 11046 | "license": "MIT" |
| @@ -9993,8 +11056,19 @@ | ||
9993 | 11056 | "url": "https://github.com/sponsors/rawify" |
9994 | 11057 | } |
9995 | 11058 | }, |
11059 | + "node_modules/frame-ticker": { | |
11060 | + "version": "1.0.3", | |
11061 | + "resolved": "https://registry.npmjs.org/frame-ticker/-/frame-ticker-1.0.3.tgz", | |
11062 | + "integrity": "sha512-E0X2u2JIvbEMrqEg5+4BpTqaD22OwojJI63K7MdKHdncjtAhGRbCR8nJCr2vwEt9NWBPCPcu70X9smPviEBy8Q==", | |
11063 | + "license": "MIT", | |
11064 | + "dependencies": { | |
11065 | + "simplesignal": "^2.1.6" | |
11066 | + } | |
11067 | + }, | |
9996 | 11068 | "node_modules/framer-motion": { |
9997 | 11069 | "version": "11.18.2", |
11070 | + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", | |
11071 | + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", | |
9998 | 11072 | "license": "MIT", |
9999 | 11073 | "dependencies": { |
10000 | 11074 | "motion-dom": "^11.18.1", |
| @@ -10263,6 +11337,97 @@ | ||
10263 | 11337 | "node": ">=10.13.0" |
10264 | 11338 | } |
10265 | 11339 | }, |
11340 | + "node_modules/globe.gl": { | |
11341 | + "version": "2.45.0", | |
11342 | + "resolved": "https://registry.npmjs.org/globe.gl/-/globe.gl-2.45.0.tgz", | |
11343 | + "integrity": "sha512-fjkLHVBrnbESkUgklTd4UbcGLciu4nIl49IIi1hclLjI6MU3ASu6JYmf/K5qwPf7I+tNOauQRr4i5Y28JTtHQg==", | |
11344 | + "license": "MIT", | |
11345 | + "dependencies": { | |
11346 | + "@tweenjs/tween.js": "18 - 25", | |
11347 | + "accessor-fn": "1", | |
11348 | + "kapsule": "^1.16", | |
11349 | + "three": ">=0.154 <1", | |
11350 | + "three-globe": "^2.45", | |
11351 | + "three-render-objects": "^1.40" | |
11352 | + }, | |
11353 | + "engines": { | |
11354 | + "node": ">=12" | |
11355 | + } | |
11356 | + }, | |
11357 | + "node_modules/google-auth-library": { | |
11358 | + "version": "10.5.0", | |
11359 | + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz", | |
11360 | + "integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==", | |
11361 | + "license": "Apache-2.0", | |
11362 | + "dependencies": { | |
11363 | + "base64-js": "^1.3.0", | |
11364 | + "ecdsa-sig-formatter": "^1.0.11", | |
11365 | + "gaxios": "^7.0.0", | |
11366 | + "gcp-metadata": "^8.0.0", | |
11367 | + "google-logging-utils": "^1.0.0", | |
11368 | + "gtoken": "^8.0.0", | |
11369 | + "jws": "^4.0.0" | |
11370 | + }, | |
11371 | + "engines": { | |
11372 | + "node": ">=18" | |
11373 | + } | |
11374 | + }, | |
11375 | + "node_modules/google-auth-library/node_modules/gaxios": { | |
11376 | + "version": "7.1.3", | |
11377 | + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", | |
11378 | + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", | |
11379 | + "license": "Apache-2.0", | |
11380 | + "dependencies": { | |
11381 | + "extend": "^3.0.2", | |
11382 | + "https-proxy-agent": "^7.0.1", | |
11383 | + "node-fetch": "^3.3.2", | |
11384 | + "rimraf": "^5.0.1" | |
11385 | + }, | |
11386 | + "engines": { | |
11387 | + "node": ">=18" | |
11388 | + } | |
11389 | + }, | |
11390 | + "node_modules/google-auth-library/node_modules/gcp-metadata": { | |
11391 | + "version": "8.1.2", | |
11392 | + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", | |
11393 | + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", | |
11394 | + "license": "Apache-2.0", | |
11395 | + "dependencies": { | |
11396 | + "gaxios": "^7.0.0", | |
11397 | + "google-logging-utils": "^1.0.0", | |
11398 | + "json-bigint": "^1.0.0" | |
11399 | + }, | |
11400 | + "engines": { | |
11401 | + "node": ">=18" | |
11402 | + } | |
11403 | + }, | |
11404 | + "node_modules/google-auth-library/node_modules/google-logging-utils": { | |
11405 | + "version": "1.1.3", | |
11406 | + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", | |
11407 | + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", | |
11408 | + "license": "Apache-2.0", | |
11409 | + "engines": { | |
11410 | + "node": ">=14" | |
11411 | + } | |
11412 | + }, | |
11413 | + "node_modules/google-auth-library/node_modules/node-fetch": { | |
11414 | + "version": "3.3.2", | |
11415 | + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", | |
11416 | + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", | |
11417 | + "license": "MIT", | |
11418 | + "dependencies": { | |
11419 | + "data-uri-to-buffer": "^4.0.0", | |
11420 | + "fetch-blob": "^3.1.4", | |
11421 | + "formdata-polyfill": "^4.0.10" | |
11422 | + }, | |
11423 | + "engines": { | |
11424 | + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | |
11425 | + }, | |
11426 | + "funding": { | |
11427 | + "type": "opencollective", | |
11428 | + "url": "https://opencollective.com/node-fetch" | |
11429 | + } | |
11430 | + }, | |
10266 | 11431 | "node_modules/google-logging-utils": { |
10267 | 11432 | "version": "0.0.2", |
10268 | 11433 | "license": "Apache-2.0", |
| @@ -10270,6 +11435,68 @@ | ||
10270 | 11435 | "node": ">=14" |
10271 | 11436 | } |
10272 | 11437 | }, |
11438 | + "node_modules/googleapis": { | |
11439 | + "version": "169.0.0", | |
11440 | + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-169.0.0.tgz", | |
11441 | + "integrity": "sha512-IOGMG8tljCZSLvYgdojRu6mB10KEsK0J7X62sXXlQz9koe5BUAW+rqkY3qhQM9wXM6hVL3/Hase7XbxoMyeYiQ==", | |
11442 | + "license": "Apache-2.0", | |
11443 | + "dependencies": { | |
11444 | + "google-auth-library": "^10.2.0", | |
11445 | + "googleapis-common": "^8.0.0" | |
11446 | + }, | |
11447 | + "engines": { | |
11448 | + "node": ">=18" | |
11449 | + } | |
11450 | + }, | |
11451 | + "node_modules/googleapis-common": { | |
11452 | + "version": "8.0.1", | |
11453 | + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-8.0.1.tgz", | |
11454 | + "integrity": "sha512-eCzNACUXPb1PW5l0ULTzMHaL/ltPRADoPgjBlT8jWsTbxkCp6siv+qKJ/1ldaybCthGwsYFYallF7u9AkU4L+A==", | |
11455 | + "license": "Apache-2.0", | |
11456 | + "dependencies": { | |
11457 | + "extend": "^3.0.2", | |
11458 | + "gaxios": "^7.0.0-rc.4", | |
11459 | + "google-auth-library": "^10.1.0", | |
11460 | + "qs": "^6.7.0", | |
11461 | + "url-template": "^2.0.8" | |
11462 | + }, | |
11463 | + "engines": { | |
11464 | + "node": ">=18.0.0" | |
11465 | + } | |
11466 | + }, | |
11467 | + "node_modules/googleapis-common/node_modules/gaxios": { | |
11468 | + "version": "7.1.3", | |
11469 | + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", | |
11470 | + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", | |
11471 | + "license": "Apache-2.0", | |
11472 | + "dependencies": { | |
11473 | + "extend": "^3.0.2", | |
11474 | + "https-proxy-agent": "^7.0.1", | |
11475 | + "node-fetch": "^3.3.2", | |
11476 | + "rimraf": "^5.0.1" | |
11477 | + }, | |
11478 | + "engines": { | |
11479 | + "node": ">=18" | |
11480 | + } | |
11481 | + }, | |
11482 | + "node_modules/googleapis-common/node_modules/node-fetch": { | |
11483 | + "version": "3.3.2", | |
11484 | + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", | |
11485 | + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", | |
11486 | + "license": "MIT", | |
11487 | + "dependencies": { | |
11488 | + "data-uri-to-buffer": "^4.0.0", | |
11489 | + "fetch-blob": "^3.1.4", | |
11490 | + "formdata-polyfill": "^4.0.10" | |
11491 | + }, | |
11492 | + "engines": { | |
11493 | + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | |
11494 | + }, | |
11495 | + "funding": { | |
11496 | + "type": "opencollective", | |
11497 | + "url": "https://opencollective.com/node-fetch" | |
11498 | + } | |
11499 | + }, | |
10273 | 11500 | "node_modules/gopd": { |
10274 | 11501 | "version": "1.2.0", |
10275 | 11502 | "license": "MIT", |
| @@ -10355,11 +11582,68 @@ | ||
10355 | 11582 | "js-yaml": "bin/js-yaml.js" |
10356 | 11583 | } |
10357 | 11584 | }, |
10358 | - "node_modules/gray-matter/node_modules/js-yaml/node_modules/argparse": { | |
10359 | - "version": "1.0.10", | |
10360 | - "license": "MIT", | |
10361 | - "dependencies": { | |
10362 | - "sprintf-js": "~1.0.2" | |
11585 | + "node_modules/gray-matter/node_modules/js-yaml/node_modules/argparse": { | |
11586 | + "version": "1.0.10", | |
11587 | + "license": "MIT", | |
11588 | + "dependencies": { | |
11589 | + "sprintf-js": "~1.0.2" | |
11590 | + } | |
11591 | + }, | |
11592 | + "node_modules/gtoken": { | |
11593 | + "version": "8.0.0", | |
11594 | + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz", | |
11595 | + "integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==", | |
11596 | + "license": "MIT", | |
11597 | + "dependencies": { | |
11598 | + "gaxios": "^7.0.0", | |
11599 | + "jws": "^4.0.0" | |
11600 | + }, | |
11601 | + "engines": { | |
11602 | + "node": ">=18" | |
11603 | + } | |
11604 | + }, | |
11605 | + "node_modules/gtoken/node_modules/gaxios": { | |
11606 | + "version": "7.1.3", | |
11607 | + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", | |
11608 | + "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", | |
11609 | + "license": "Apache-2.0", | |
11610 | + "dependencies": { | |
11611 | + "extend": "^3.0.2", | |
11612 | + "https-proxy-agent": "^7.0.1", | |
11613 | + "node-fetch": "^3.3.2", | |
11614 | + "rimraf": "^5.0.1" | |
11615 | + }, | |
11616 | + "engines": { | |
11617 | + "node": ">=18" | |
11618 | + } | |
11619 | + }, | |
11620 | + "node_modules/gtoken/node_modules/node-fetch": { | |
11621 | + "version": "3.3.2", | |
11622 | + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", | |
11623 | + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", | |
11624 | + "license": "MIT", | |
11625 | + "dependencies": { | |
11626 | + "data-uri-to-buffer": "^4.0.0", | |
11627 | + "fetch-blob": "^3.1.4", | |
11628 | + "formdata-polyfill": "^4.0.10" | |
11629 | + }, | |
11630 | + "engines": { | |
11631 | + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" | |
11632 | + }, | |
11633 | + "funding": { | |
11634 | + "type": "opencollective", | |
11635 | + "url": "https://opencollective.com/node-fetch" | |
11636 | + } | |
11637 | + }, | |
11638 | + "node_modules/h3-js": { | |
11639 | + "version": "4.4.0", | |
11640 | + "resolved": "https://registry.npmjs.org/h3-js/-/h3-js-4.4.0.tgz", | |
11641 | + "integrity": "sha512-DvJh07MhGgY2KcC4OeZc8SSyA+ZXpdvoh6uCzGpoKvWtZxJB+g6VXXC1+eWYkaMIsLz7J/ErhOalHCpcs1KYog==", | |
11642 | + "license": "Apache-2.0", | |
11643 | + "engines": { | |
11644 | + "node": ">=4", | |
11645 | + "npm": ">=3", | |
11646 | + "yarn": ">=1.3.0" | |
10363 | 11647 | } |
10364 | 11648 | }, |
10365 | 11649 | "node_modules/has-property-descriptors": { |
| @@ -10707,6 +11991,15 @@ | ||
10707 | 11991 | "node": ">=0.8.19" |
10708 | 11992 | } |
10709 | 11993 | }, |
11994 | + "node_modules/index-array-by": { | |
11995 | + "version": "1.4.2", | |
11996 | + "resolved": "https://registry.npmjs.org/index-array-by/-/index-array-by-1.4.2.tgz", | |
11997 | + "integrity": "sha512-SP23P27OUKzXWEC/TOyWlwLviofQkCSCKONnc62eItjp69yCZZPqDQtr3Pw5gJDnPeUMqExmKydNZaJO0FU9pw==", | |
11998 | + "license": "MIT", | |
11999 | + "engines": { | |
12000 | + "node": ">=12" | |
12001 | + } | |
12002 | + }, | |
10710 | 12003 | "node_modules/inherits": { |
10711 | 12004 | "version": "2.0.4", |
10712 | 12005 | "license": "ISC" |
| @@ -10715,6 +12008,15 @@ | ||
10715 | 12008 | "version": "1.3.8", |
10716 | 12009 | "license": "ISC" |
10717 | 12010 | }, |
12011 | + "node_modules/internmap": { | |
12012 | + "version": "2.0.3", | |
12013 | + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", | |
12014 | + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", | |
12015 | + "license": "ISC", | |
12016 | + "engines": { | |
12017 | + "node": ">=12" | |
12018 | + } | |
12019 | + }, | |
10718 | 12020 | "node_modules/ioredis": { |
10719 | 12021 | "version": "5.8.2", |
10720 | 12022 | "license": "MIT", |
| @@ -10966,6 +12268,15 @@ | ||
10966 | 12268 | "@pkgjs/parseargs": "^0.11.0" |
10967 | 12269 | } |
10968 | 12270 | }, |
12271 | + "node_modules/jerrypick": { | |
12272 | + "version": "1.1.2", | |
12273 | + "resolved": "https://registry.npmjs.org/jerrypick/-/jerrypick-1.1.2.tgz", | |
12274 | + "integrity": "sha512-YKnxXEekXKzhpf7CLYA0A+oDP8V0OhICNCr5lv96FvSsDEmrb0GKM776JgQvHTMjr7DTTPEVv/1Ciaw0uEWzBA==", | |
12275 | + "license": "MIT", | |
12276 | + "engines": { | |
12277 | + "node": ">=12" | |
12278 | + } | |
12279 | + }, | |
10969 | 12280 | "node_modules/jiti": { |
10970 | 12281 | "version": "1.21.7", |
10971 | 12282 | "license": "MIT", |
| @@ -11037,6 +12348,39 @@ | ||
11037 | 12348 | "graceful-fs": "^4.1.6" |
11038 | 12349 | } |
11039 | 12350 | }, |
12351 | + "node_modules/jwa": { | |
12352 | + "version": "2.0.1", | |
12353 | + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", | |
12354 | + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", | |
12355 | + "license": "MIT", | |
12356 | + "dependencies": { | |
12357 | + "buffer-equal-constant-time": "^1.0.1", | |
12358 | + "ecdsa-sig-formatter": "1.0.11", | |
12359 | + "safe-buffer": "^5.0.1" | |
12360 | + } | |
12361 | + }, | |
12362 | + "node_modules/jws": { | |
12363 | + "version": "4.0.1", | |
12364 | + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", | |
12365 | + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", | |
12366 | + "license": "MIT", | |
12367 | + "dependencies": { | |
12368 | + "jwa": "^2.0.1", | |
12369 | + "safe-buffer": "^5.0.1" | |
12370 | + } | |
12371 | + }, | |
12372 | + "node_modules/kapsule": { | |
12373 | + "version": "1.16.3", | |
12374 | + "resolved": "https://registry.npmjs.org/kapsule/-/kapsule-1.16.3.tgz", | |
12375 | + "integrity": "sha512-4+5mNNf4vZDSwPhKprKwz3330iisPrb08JyMgbsdFrimBCKNHecua/WBwvVg3n7vwx0C1ARjfhwIpbrbd9n5wg==", | |
12376 | + "license": "MIT", | |
12377 | + "dependencies": { | |
12378 | + "lodash-es": "4" | |
12379 | + }, | |
12380 | + "engines": { | |
12381 | + "node": ">=12" | |
12382 | + } | |
12383 | + }, | |
11040 | 12384 | "node_modules/keyv": { |
11041 | 12385 | "version": "5.5.5", |
11042 | 12386 | "license": "MIT", |
| @@ -11117,6 +12461,12 @@ | ||
11117 | 12461 | "version": "4.17.21", |
11118 | 12462 | "license": "MIT" |
11119 | 12463 | }, |
12464 | + "node_modules/lodash-es": { | |
12465 | + "version": "4.17.21", | |
12466 | + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", | |
12467 | + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", | |
12468 | + "license": "MIT" | |
12469 | + }, | |
11120 | 12470 | "node_modules/lodash.camelcase": { |
11121 | 12471 | "version": "4.3.0", |
11122 | 12472 | "license": "MIT" |
| @@ -11492,6 +12842,13 @@ | ||
11492 | 12842 | "node": ">= 8" |
11493 | 12843 | } |
11494 | 12844 | }, |
12845 | + "node_modules/meshoptimizer": { | |
12846 | + "version": "0.22.0", | |
12847 | + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", | |
12848 | + "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", | |
12849 | + "dev": true, | |
12850 | + "license": "MIT" | |
12851 | + }, | |
11495 | 12852 | "node_modules/micromark": { |
11496 | 12853 | "version": "4.0.2", |
11497 | 12854 | "funding": [ |
| @@ -12355,6 +13712,26 @@ | ||
12355 | 13712 | "version": "3.1.1", |
12356 | 13713 | "license": "MIT" |
12357 | 13714 | }, |
13715 | + "node_modules/node-domexception": { | |
13716 | + "version": "1.0.0", | |
13717 | + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", | |
13718 | + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", | |
13719 | + "deprecated": "Use your platform's native DOMException instead", | |
13720 | + "funding": [ | |
13721 | + { | |
13722 | + "type": "github", | |
13723 | + "url": "https://github.com/sponsors/jimmywarting" | |
13724 | + }, | |
13725 | + { | |
13726 | + "type": "github", | |
13727 | + "url": "https://paypal.me/jimmywarting" | |
13728 | + } | |
13729 | + ], | |
13730 | + "license": "MIT", | |
13731 | + "engines": { | |
13732 | + "node": ">=10.5.0" | |
13733 | + } | |
13734 | + }, | |
12358 | 13735 | "node_modules/node-fetch": { |
12359 | 13736 | "version": "2.7.0", |
12360 | 13737 | "license": "MIT", |
| @@ -12451,6 +13828,15 @@ | ||
12451 | 13828 | "streamx": "^2.15.0" |
12452 | 13829 | } |
12453 | 13830 | }, |
13831 | + "node_modules/nodemailer": { | |
13832 | + "version": "7.0.12", | |
13833 | + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.12.tgz", | |
13834 | + "integrity": "sha512-H+rnK5bX2Pi/6ms3sN4/jRQvYSMltV6vqup/0SFOrxYYY/qoNvhXPlYq3e+Pm9RFJRwrMGbMIwi81M4dxpomhA==", | |
13835 | + "license": "MIT-0", | |
13836 | + "engines": { | |
13837 | + "node": ">=6.0.0" | |
13838 | + } | |
13839 | + }, | |
12454 | 13840 | "node_modules/nopt": { |
12455 | 13841 | "version": "8.1.0", |
12456 | 13842 | "license": "ISC", |
| @@ -12502,6 +13888,18 @@ | ||
12502 | 13888 | "node": ">= 6" |
12503 | 13889 | } |
12504 | 13890 | }, |
13891 | + "node_modules/object-inspect": { | |
13892 | + "version": "1.13.4", | |
13893 | + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", | |
13894 | + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", | |
13895 | + "license": "MIT", | |
13896 | + "engines": { | |
13897 | + "node": ">= 0.4" | |
13898 | + }, | |
13899 | + "funding": { | |
13900 | + "url": "https://github.com/sponsors/ljharb" | |
13901 | + } | |
13902 | + }, | |
12505 | 13903 | "node_modules/obug": { |
12506 | 13904 | "version": "2.1.1", |
12507 | 13905 | "dev": true, |
| @@ -12557,6 +13955,15 @@ | ||
12557 | 13955 | "regex-recursion": "^5.1.1" |
12558 | 13956 | } |
12559 | 13957 | }, |
13958 | + "node_modules/openpgp": { | |
13959 | + "version": "6.3.0", | |
13960 | + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-6.3.0.tgz", | |
13961 | + "integrity": "sha512-pLzCU8IgyKXPSO11eeharQkQ4GzOKNWhXq79pQarIRZEMt1/ssyr+MIuWBv1mNoenJLg04gvPx+fi4gcKZ4bag==", | |
13962 | + "license": "LGPL-3.0+", | |
13963 | + "engines": { | |
13964 | + "node": ">= 18.0.0" | |
13965 | + } | |
13966 | + }, | |
12560 | 13967 | "node_modules/ora": { |
12561 | 13968 | "version": "8.2.0", |
12562 | 13969 | "license": "MIT", |
| @@ -12748,6 +14155,8 @@ | ||
12748 | 14155 | }, |
12749 | 14156 | "node_modules/pg": { |
12750 | 14157 | "version": "8.16.3", |
14158 | + "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", | |
14159 | + "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", | |
12751 | 14160 | "license": "MIT", |
12752 | 14161 | "peer": true, |
12753 | 14162 | "dependencies": { |
| @@ -12820,6 +14229,12 @@ | ||
12820 | 14229 | "split2": "^4.1.0" |
12821 | 14230 | } |
12822 | 14231 | }, |
14232 | + "node_modules/phenomenon": { | |
14233 | + "version": "1.6.0", | |
14234 | + "resolved": "https://registry.npmjs.org/phenomenon/-/phenomenon-1.6.0.tgz", | |
14235 | + "integrity": "sha512-7h9/fjPD3qNlgggzm88cY58l9sudZ6Ey+UmZsizfhtawO6E3srZQXywaNm2lBwT72TbpHYRPy7ytIHeBUD/G0A==", | |
14236 | + "license": "MIT" | |
14237 | + }, | |
12823 | 14238 | "node_modules/picocolors": { |
12824 | 14239 | "version": "1.1.1", |
12825 | 14240 | "license": "ISC" |
| @@ -12964,6 +14379,27 @@ | ||
12964 | 14379 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" |
12965 | 14380 | } |
12966 | 14381 | }, |
14382 | + "node_modules/point-in-polygon-hao": { | |
14383 | + "version": "1.2.4", | |
14384 | + "resolved": "https://registry.npmjs.org/point-in-polygon-hao/-/point-in-polygon-hao-1.2.4.tgz", | |
14385 | + "integrity": "sha512-x2pcvXeqhRHlNRdhLs/tgFapAbSSe86wa/eqmj1G6pWftbEs5aVRJhRGM6FYSUERKu0PjekJzMq0gsI2XyiclQ==", | |
14386 | + "license": "MIT", | |
14387 | + "dependencies": { | |
14388 | + "robust-predicates": "^3.0.2" | |
14389 | + } | |
14390 | + }, | |
14391 | + "node_modules/polished": { | |
14392 | + "version": "4.3.1", | |
14393 | + "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", | |
14394 | + "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", | |
14395 | + "license": "MIT", | |
14396 | + "dependencies": { | |
14397 | + "@babel/runtime": "^7.17.8" | |
14398 | + }, | |
14399 | + "engines": { | |
14400 | + "node": ">=10" | |
14401 | + } | |
14402 | + }, | |
12967 | 14403 | "node_modules/possible-typed-array-names": { |
12968 | 14404 | "version": "1.1.0", |
12969 | 14405 | "license": "MIT", |
| @@ -13173,6 +14609,16 @@ | ||
13173 | 14609 | "node": ">=0.10.0" |
13174 | 14610 | } |
13175 | 14611 | }, |
14612 | + "node_modules/preact": { | |
14613 | + "version": "10.28.0", | |
14614 | + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.0.tgz", | |
14615 | + "integrity": "sha512-rytDAoiXr3+t6OIP3WGlDd0ouCUG1iCWzkcY3++Nreuoi17y6T5i/zRhe6uYfoVcxq6YU+sBtJouuRDsq8vvqA==", | |
14616 | + "license": "MIT", | |
14617 | + "funding": { | |
14618 | + "type": "opencollective", | |
14619 | + "url": "https://opencollective.com/preact" | |
14620 | + } | |
14621 | + }, | |
13176 | 14622 | "node_modules/prebuild-install": { |
13177 | 14623 | "version": "7.1.3", |
13178 | 14624 | "license": "MIT", |
| @@ -13284,6 +14730,17 @@ | ||
13284 | 14730 | "node": ">=6" |
13285 | 14731 | } |
13286 | 14732 | }, |
14733 | + "node_modules/prop-types": { | |
14734 | + "version": "15.8.1", | |
14735 | + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", | |
14736 | + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", | |
14737 | + "license": "MIT", | |
14738 | + "dependencies": { | |
14739 | + "loose-envify": "^1.4.0", | |
14740 | + "object-assign": "^4.1.1", | |
14741 | + "react-is": "^16.13.1" | |
14742 | + } | |
14743 | + }, | |
13287 | 14744 | "node_modules/property-information": { |
13288 | 14745 | "version": "7.1.0", |
13289 | 14746 | "license": "MIT", |
| @@ -13322,6 +14779,21 @@ | ||
13322 | 14779 | "once": "^1.3.1" |
13323 | 14780 | } |
13324 | 14781 | }, |
14782 | + "node_modules/qs": { | |
14783 | + "version": "6.14.1", | |
14784 | + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", | |
14785 | + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", | |
14786 | + "license": "BSD-3-Clause", | |
14787 | + "dependencies": { | |
14788 | + "side-channel": "^1.1.0" | |
14789 | + }, | |
14790 | + "engines": { | |
14791 | + "node": ">=0.6" | |
14792 | + }, | |
14793 | + "funding": { | |
14794 | + "url": "https://github.com/sponsors/ljharb" | |
14795 | + } | |
14796 | + }, | |
13325 | 14797 | "node_modules/queue-microtask": { |
13326 | 14798 | "version": "1.2.3", |
13327 | 14799 | "funding": [ |
| @@ -13404,6 +14876,44 @@ | ||
13404 | 14876 | "react": "^18.3.1" |
13405 | 14877 | } |
13406 | 14878 | }, |
14879 | + "node_modules/react-globe.gl": { | |
14880 | + "version": "2.37.0", | |
14881 | + "resolved": "https://registry.npmjs.org/react-globe.gl/-/react-globe.gl-2.37.0.tgz", | |
14882 | + "integrity": "sha512-nN1FDOJBhFvWfKrOY0SnkDuA8wk9FSTBN0HFfAdTqqcFM5R+OXBIxK0BM6t8n3oNVYpEJVxEzjYFwLyk4BC7Cw==", | |
14883 | + "license": "MIT", | |
14884 | + "dependencies": { | |
14885 | + "globe.gl": "^2.45", | |
14886 | + "prop-types": "15", | |
14887 | + "react-kapsule": "^2.5" | |
14888 | + }, | |
14889 | + "engines": { | |
14890 | + "node": ">=12" | |
14891 | + }, | |
14892 | + "peerDependencies": { | |
14893 | + "react": "*" | |
14894 | + } | |
14895 | + }, | |
14896 | + "node_modules/react-is": { | |
14897 | + "version": "16.13.1", | |
14898 | + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", | |
14899 | + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", | |
14900 | + "license": "MIT" | |
14901 | + }, | |
14902 | + "node_modules/react-kapsule": { | |
14903 | + "version": "2.5.7", | |
14904 | + "resolved": "https://registry.npmjs.org/react-kapsule/-/react-kapsule-2.5.7.tgz", | |
14905 | + "integrity": "sha512-kifAF4ZPD77qZKc4CKLmozq6GY1sBzPEJTIJb0wWFK6HsePJatK3jXplZn2eeAt3x67CDozgi7/rO8fNQ/AL7A==", | |
14906 | + "license": "MIT", | |
14907 | + "dependencies": { | |
14908 | + "jerrypick": "^1.1.1" | |
14909 | + }, | |
14910 | + "engines": { | |
14911 | + "node": ">=12" | |
14912 | + }, | |
14913 | + "peerDependencies": { | |
14914 | + "react": ">=16.13.1" | |
14915 | + } | |
14916 | + }, | |
13407 | 14917 | "node_modules/react-refresh": { |
13408 | 14918 | "version": "0.17.0", |
13409 | 14919 | "license": "MIT", |
| @@ -13844,6 +15354,27 @@ | ||
13844 | 15354 | "node": ">=0.10.0" |
13845 | 15355 | } |
13846 | 15356 | }, |
15357 | + "node_modules/rimraf": { | |
15358 | + "version": "5.0.10", | |
15359 | + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", | |
15360 | + "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", | |
15361 | + "license": "ISC", | |
15362 | + "dependencies": { | |
15363 | + "glob": "^10.3.7" | |
15364 | + }, | |
15365 | + "bin": { | |
15366 | + "rimraf": "dist/esm/bin.mjs" | |
15367 | + }, | |
15368 | + "funding": { | |
15369 | + "url": "https://github.com/sponsors/isaacs" | |
15370 | + } | |
15371 | + }, | |
15372 | + "node_modules/robust-predicates": { | |
15373 | + "version": "3.0.2", | |
15374 | + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", | |
15375 | + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==", | |
15376 | + "license": "Unlicense" | |
15377 | + }, | |
13847 | 15378 | "node_modules/rollup": { |
13848 | 15379 | "version": "4.53.3", |
13849 | 15380 | "license": "MIT", |
| @@ -14122,6 +15653,78 @@ | ||
14122 | 15653 | "@types/hast": "^3.0.4" |
14123 | 15654 | } |
14124 | 15655 | }, |
15656 | + "node_modules/side-channel": { | |
15657 | + "version": "1.1.0", | |
15658 | + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", | |
15659 | + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", | |
15660 | + "license": "MIT", | |
15661 | + "dependencies": { | |
15662 | + "es-errors": "^1.3.0", | |
15663 | + "object-inspect": "^1.13.3", | |
15664 | + "side-channel-list": "^1.0.0", | |
15665 | + "side-channel-map": "^1.0.1", | |
15666 | + "side-channel-weakmap": "^1.0.2" | |
15667 | + }, | |
15668 | + "engines": { | |
15669 | + "node": ">= 0.4" | |
15670 | + }, | |
15671 | + "funding": { | |
15672 | + "url": "https://github.com/sponsors/ljharb" | |
15673 | + } | |
15674 | + }, | |
15675 | + "node_modules/side-channel-list": { | |
15676 | + "version": "1.0.0", | |
15677 | + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", | |
15678 | + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", | |
15679 | + "license": "MIT", | |
15680 | + "dependencies": { | |
15681 | + "es-errors": "^1.3.0", | |
15682 | + "object-inspect": "^1.13.3" | |
15683 | + }, | |
15684 | + "engines": { | |
15685 | + "node": ">= 0.4" | |
15686 | + }, | |
15687 | + "funding": { | |
15688 | + "url": "https://github.com/sponsors/ljharb" | |
15689 | + } | |
15690 | + }, | |
15691 | + "node_modules/side-channel-map": { | |
15692 | + "version": "1.0.1", | |
15693 | + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", | |
15694 | + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", | |
15695 | + "license": "MIT", | |
15696 | + "dependencies": { | |
15697 | + "call-bound": "^1.0.2", | |
15698 | + "es-errors": "^1.3.0", | |
15699 | + "get-intrinsic": "^1.2.5", | |
15700 | + "object-inspect": "^1.13.3" | |
15701 | + }, | |
15702 | + "engines": { | |
15703 | + "node": ">= 0.4" | |
15704 | + }, | |
15705 | + "funding": { | |
15706 | + "url": "https://github.com/sponsors/ljharb" | |
15707 | + } | |
15708 | + }, | |
15709 | + "node_modules/side-channel-weakmap": { | |
15710 | + "version": "1.0.2", | |
15711 | + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", | |
15712 | + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", | |
15713 | + "license": "MIT", | |
15714 | + "dependencies": { | |
15715 | + "call-bound": "^1.0.2", | |
15716 | + "es-errors": "^1.3.0", | |
15717 | + "get-intrinsic": "^1.2.5", | |
15718 | + "object-inspect": "^1.13.3", | |
15719 | + "side-channel-map": "^1.0.1" | |
15720 | + }, | |
15721 | + "engines": { | |
15722 | + "node": ">= 0.4" | |
15723 | + }, | |
15724 | + "funding": { | |
15725 | + "url": "https://github.com/sponsors/ljharb" | |
15726 | + } | |
15727 | + }, | |
14125 | 15728 | "node_modules/siginfo": { |
14126 | 15729 | "version": "2.0.0", |
14127 | 15730 | "dev": true, |
| @@ -14199,6 +15802,12 @@ | ||
14199 | 15802 | "is-arrayish": "^0.3.1" |
14200 | 15803 | } |
14201 | 15804 | }, |
15805 | + "node_modules/simplesignal": { | |
15806 | + "version": "2.1.7", | |
15807 | + "resolved": "https://registry.npmjs.org/simplesignal/-/simplesignal-2.1.7.tgz", | |
15808 | + "integrity": "sha512-PEo2qWpUke7IMhlqiBxrulIFvhJRLkl1ih52Rwa+bPjzhJepcd4GIjn2RiQmFSx3dQvsEAgF0/lXMwMN7vODaA==", | |
15809 | + "license": "MIT" | |
15810 | + }, | |
14202 | 15811 | "node_modules/sisteransi": { |
14203 | 15812 | "version": "1.0.5", |
14204 | 15813 | "license": "MIT" |
| @@ -14531,6 +16140,8 @@ | ||
14531 | 16140 | }, |
14532 | 16141 | "node_modules/tailwind-merge": { |
14533 | 16142 | "version": "2.6.0", |
16143 | + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", | |
16144 | + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", | |
14534 | 16145 | "license": "MIT", |
14535 | 16146 | "funding": { |
14536 | 16147 | "type": "github", |
| @@ -14681,11 +16292,129 @@ | ||
14681 | 16292 | "real-require": "^0.2.0" |
14682 | 16293 | } |
14683 | 16294 | }, |
16295 | + "node_modules/three": { | |
16296 | + "version": "0.182.0", | |
16297 | + "resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", | |
16298 | + "integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", | |
16299 | + "license": "MIT", | |
16300 | + "peer": true | |
16301 | + }, | |
16302 | + "node_modules/three-conic-polygon-geometry": { | |
16303 | + "version": "2.1.2", | |
16304 | + "resolved": "https://registry.npmjs.org/three-conic-polygon-geometry/-/three-conic-polygon-geometry-2.1.2.tgz", | |
16305 | + "integrity": "sha512-NaP3RWLJIyPGI+zyaZwd0Yj6rkoxm4FJHqAX1Enb4L64oNYLCn4bz1ESgOEYavgcUwCNYINu1AgEoUBJr1wZcA==", | |
16306 | + "license": "MIT", | |
16307 | + "dependencies": { | |
16308 | + "@turf/boolean-point-in-polygon": "^7.2", | |
16309 | + "d3-array": "1 - 3", | |
16310 | + "d3-geo": "1 - 3", | |
16311 | + "d3-geo-voronoi": "2", | |
16312 | + "d3-scale": "1 - 4", | |
16313 | + "delaunator": "5", | |
16314 | + "earcut": "3" | |
16315 | + }, | |
16316 | + "engines": { | |
16317 | + "node": ">=12" | |
16318 | + }, | |
16319 | + "peerDependencies": { | |
16320 | + "three": ">=0.72.0" | |
16321 | + } | |
16322 | + }, | |
16323 | + "node_modules/three-geojson-geometry": { | |
16324 | + "version": "2.1.1", | |
16325 | + "resolved": "https://registry.npmjs.org/three-geojson-geometry/-/three-geojson-geometry-2.1.1.tgz", | |
16326 | + "integrity": "sha512-dC7bF3ri1goDcihYhzACHOBQqu7YNNazYLa2bSydVIiJUb3jDFojKSy+gNj2pMkqZNSVjssSmdY9zlmnhEpr1w==", | |
16327 | + "license": "MIT", | |
16328 | + "dependencies": { | |
16329 | + "d3-geo": "1 - 3", | |
16330 | + "d3-interpolate": "1 - 3", | |
16331 | + "earcut": "3" | |
16332 | + }, | |
16333 | + "engines": { | |
16334 | + "node": ">=12" | |
16335 | + }, | |
16336 | + "peerDependencies": { | |
16337 | + "three": ">=0.72.0" | |
16338 | + } | |
16339 | + }, | |
16340 | + "node_modules/three-globe": { | |
16341 | + "version": "2.45.0", | |
16342 | + "resolved": "https://registry.npmjs.org/three-globe/-/three-globe-2.45.0.tgz", | |
16343 | + "integrity": "sha512-Ur6BVkezvmHnvsEg8fbq6gIscSZtknSQMWwDRbiJ95o6OSDjDbGTc4oO6nP7mOM9aAA3YrF7YZyOwSkP4T56QA==", | |
16344 | + "license": "MIT", | |
16345 | + "dependencies": { | |
16346 | + "@tweenjs/tween.js": "18 - 25", | |
16347 | + "accessor-fn": "1", | |
16348 | + "d3-array": "3", | |
16349 | + "d3-color": "3", | |
16350 | + "d3-geo": "3", | |
16351 | + "d3-interpolate": "3", | |
16352 | + "d3-scale": "4", | |
16353 | + "d3-scale-chromatic": "3", | |
16354 | + "data-bind-mapper": "1", | |
16355 | + "frame-ticker": "1", | |
16356 | + "h3-js": "4", | |
16357 | + "index-array-by": "1", | |
16358 | + "kapsule": "^1.16", | |
16359 | + "three-conic-polygon-geometry": "2", | |
16360 | + "three-geojson-geometry": "2", | |
16361 | + "three-slippy-map-globe": "1", | |
16362 | + "tinycolor2": "1" | |
16363 | + }, | |
16364 | + "engines": { | |
16365 | + "node": ">=12" | |
16366 | + }, | |
16367 | + "peerDependencies": { | |
16368 | + "three": ">=0.154" | |
16369 | + } | |
16370 | + }, | |
16371 | + "node_modules/three-render-objects": { | |
16372 | + "version": "1.40.4", | |
16373 | + "resolved": "https://registry.npmjs.org/three-render-objects/-/three-render-objects-1.40.4.tgz", | |
16374 | + "integrity": "sha512-Ukpu1pei3L5r809izvjsZxwuRcYLiyn6Uvy3lZ9bpMTdvj3i6PeX6w++/hs2ZS3KnEzGjb6YvTvh4UQuwHTDJg==", | |
16375 | + "license": "MIT", | |
16376 | + "dependencies": { | |
16377 | + "@tweenjs/tween.js": "18 - 25", | |
16378 | + "accessor-fn": "1", | |
16379 | + "float-tooltip": "^1.7", | |
16380 | + "kapsule": "^1.16", | |
16381 | + "polished": "4" | |
16382 | + }, | |
16383 | + "engines": { | |
16384 | + "node": ">=12" | |
16385 | + }, | |
16386 | + "peerDependencies": { | |
16387 | + "three": ">=0.168" | |
16388 | + } | |
16389 | + }, | |
16390 | + "node_modules/three-slippy-map-globe": { | |
16391 | + "version": "1.0.4", | |
16392 | + "resolved": "https://registry.npmjs.org/three-slippy-map-globe/-/three-slippy-map-globe-1.0.4.tgz", | |
16393 | + "integrity": "sha512-am8A4PP38AfTdrhXBDucwPRHLTbBl93yhpjIs56K1TLs9VuUWzg68oim4Dibs9QC1riXbj5SoBp/okA1VN9eYg==", | |
16394 | + "license": "MIT", | |
16395 | + "dependencies": { | |
16396 | + "d3-geo": "1 - 3", | |
16397 | + "d3-octree": "^1.1", | |
16398 | + "d3-scale": "1 - 4" | |
16399 | + }, | |
16400 | + "engines": { | |
16401 | + "node": ">=12" | |
16402 | + }, | |
16403 | + "peerDependencies": { | |
16404 | + "three": ">=0.154" | |
16405 | + } | |
16406 | + }, | |
14684 | 16407 | "node_modules/tinybench": { |
14685 | 16408 | "version": "2.9.0", |
14686 | 16409 | "dev": true, |
14687 | 16410 | "license": "MIT" |
14688 | 16411 | }, |
16412 | + "node_modules/tinycolor2": { | |
16413 | + "version": "1.6.0", | |
16414 | + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", | |
16415 | + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", | |
16416 | + "license": "MIT" | |
16417 | + }, | |
14689 | 16418 | "node_modules/tinyexec": { |
14690 | 16419 | "version": "1.0.2", |
14691 | 16420 | "dev": true, |
| @@ -15034,6 +16763,12 @@ | ||
15034 | 16763 | "browserslist": ">= 4.21.0" |
15035 | 16764 | } |
15036 | 16765 | }, |
16766 | + "node_modules/url-template": { | |
16767 | + "version": "2.0.8", | |
16768 | + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", | |
16769 | + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", | |
16770 | + "license": "BSD" | |
16771 | + }, | |
15037 | 16772 | "node_modules/use-callback-ref": { |
15038 | 16773 | "version": "1.3.3", |
15039 | 16774 | "license": "MIT", |
| @@ -15358,6 +17093,15 @@ | ||
15358 | 17093 | "url": "https://github.com/sponsors/wooorm" |
15359 | 17094 | } |
15360 | 17095 | }, |
17096 | + "node_modules/web-streams-polyfill": { | |
17097 | + "version": "3.3.3", | |
17098 | + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", | |
17099 | + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", | |
17100 | + "license": "MIT", | |
17101 | + "engines": { | |
17102 | + "node": ">= 8" | |
17103 | + } | |
17104 | + }, | |
15361 | 17105 | "node_modules/webidl-conversions": { |
15362 | 17106 | "version": "3.0.1", |
15363 | 17107 | "license": "BSD-2-Clause" |
15364 | 17108 | |
▼package.json
+13-3
| @@ -14,6 +14,7 @@ | ||
14 | 14 | "db:push": "drizzle-kit push", |
15 | 15 | "db:studio": "drizzle-kit studio", |
16 | 16 | "git:init": "node scripts/init-git-server.js", |
17 | + "git:start": "tsx scripts/ssh-server.ts", | |
17 | 18 | "runner:start": "node src/runner/index.js" |
18 | 19 | }, |
19 | 20 | "dependencies": { |
| @@ -51,6 +52,7 @@ | ||
51 | 52 | "@radix-ui/react-toggle": "^1.1.0", |
52 | 53 | "@radix-ui/react-toggle-group": "^1.1.0", |
53 | 54 | "@radix-ui/react-tooltip": "^1.1.2", |
55 | + "@types/nodemailer": "^7.0.4", | |
54 | 56 | "astro": "^4.11.0", |
55 | 57 | "bcryptjs": "^2.4.3", |
56 | 58 | "better-sqlite3": "^11.1.2", |
| @@ -58,10 +60,12 @@ | ||
58 | 60 | "class-variance-authority": "^0.7.0", |
59 | 61 | "clsx": "^2.1.1", |
60 | 62 | "cmdk": "^1.1.1", |
63 | + "cobe": "^0.6.5", | |
61 | 64 | "dockerode": "^4.0.2", |
62 | 65 | "drizzle-orm": "^0.45.1", |
63 | - "framer-motion": "^11.2.12", | |
66 | + "framer-motion": "^11.18.2", | |
64 | 67 | "github-markdown-css": "^5.8.1", |
68 | + "googleapis": "^169.0.0", | |
65 | 69 | "ioredis": "^5.8.2", |
66 | 70 | "isomorphic-git": "^1.25.10", |
67 | 71 | "jose": "^5.6.3", |
| @@ -72,6 +76,8 @@ | ||
72 | 76 | "mysql2": "^3.15.3", |
73 | 77 | "nanoid": "^5.0.7", |
74 | 78 | "nodegit": "^0.28.0-alpha.27", |
79 | + "nodemailer": "^7.0.12", | |
80 | + "openpgp": "^6.3.0", | |
75 | 81 | "otplib": "^12.0.1", |
76 | 82 | "pg": "^8.16.3", |
77 | 83 | "pino": "^10.1.0", |
| @@ -79,6 +85,7 @@ | ||
79 | 85 | "prom-client": "^15.1.3", |
80 | 86 | "react": "^18.3.1", |
81 | 87 | "react-dom": "^18.3.1", |
88 | + "react-globe.gl": "^2.37.0", | |
82 | 89 | "rehype-sanitize": "^6.0.0", |
83 | 90 | "rehype-stringify": "^10.0.1", |
84 | 91 | "remark-gfm": "^4.0.1", |
| @@ -86,13 +93,15 @@ | ||
86 | 93 | "remark-rehype": "^11.1.2", |
87 | 94 | "simple-git": "^3.25.0", |
88 | 95 | "ssh2": "^1.15.0", |
89 | - "tailwind-merge": "^2.3.0", | |
96 | + "tailwind-merge": "^2.6.0", | |
90 | 97 | "tailwindcss-animate": "^1.0.7", |
98 | + "three": "^0.182.0", | |
91 | 99 | "unified": "^11.0.5", |
92 | 100 | "zod": "^3.23.8", |
93 | 101 | "zustand": "^4.5.4" |
94 | 102 | }, |
95 | 103 | "devDependencies": { |
104 | + "@faker-js/faker": "^10.1.0", | |
96 | 105 | "@playwright/test": "^1.57.0", |
97 | 106 | "@tailwindcss/typography": "^0.5.19", |
98 | 107 | "@types/bcryptjs": "^2.4.6", |
| @@ -100,10 +109,11 @@ | ||
100 | 109 | "@types/dockerode": "^3.3.29", |
101 | 110 | "@types/js-yaml": "^4.0.9", |
102 | 111 | "@types/node": "^20.14.9", |
103 | - "@types/pg": "^8.11.6", | |
112 | + "@types/pg": "^8.16.0", | |
104 | 113 | "@types/react": "^18.3.3", |
105 | 114 | "@types/react-dom": "^18.3.0", |
106 | 115 | "@types/ssh2": "^1.15.0", |
116 | + "@types/three": "^0.182.0", | |
107 | 117 | "autoprefixer": "^10.4.19", |
108 | 118 | "drizzle-kit": "^0.31.8", |
109 | 119 | "pino-pretty": "^13.1.3", |
110 | 120 | |
▼pci/1.png
+0-0
Binary file not shown.
▼pci/image copy 2.png
+0-0
Binary file not shown.
▼pci/image copy 3.png
+0-0
Binary file not shown.
▼pci/image copy 4.png
+0-0
Binary file not shown.
▼pci/image copy 5.png
+0-0
Binary file not shown.
▼pci/image copy.png
+0-0
Binary file not shown.
▼pci/image.png
+0-0
Binary file not shown.
▼scripts/seed-admin.ts
+77-0
| @@ -0,0 +1,77 @@ | ||
1 | +import { getDatabase, schema } from "../src/db"; | |
2 | +import { eq } from "drizzle-orm"; | |
3 | +import readline from "readline"; | |
4 | +import bcrypt from "bcryptjs"; | |
5 | + | |
6 | +// Utility to prompt input | |
7 | +const rl = readline.createInterface({ | |
8 | + input: process.stdin, | |
9 | + output: process.stdout | |
10 | +}); | |
11 | + | |
12 | +const ask = (query: string): Promise<string> => | |
13 | + new Promise(resolve => rl.question(query, resolve)); | |
14 | + | |
15 | +async function main() { | |
16 | + console.log("🚀 OpenCodeHub Admin Seeder"); | |
17 | + console.log("---------------------------"); | |
18 | + | |
19 | + const username = process.argv[2] || await ask("Enter username: "); | |
20 | + if (!username) { | |
21 | + console.error("Username required"); | |
22 | + process.exit(1); | |
23 | + } | |
24 | + | |
25 | + const email = process.argv[3] || await ask("Enter email (if creating new): "); | |
26 | + const password = process.argv[4] || await ask("Enter password (if creating new): "); | |
27 | + | |
28 | + const db = getDatabase(); | |
29 | + | |
30 | + // Check if user exists | |
31 | + const user = await db.query.users.findFirst({ | |
32 | + where: eq(schema.users.username, username) | |
33 | + }); | |
34 | + | |
35 | + if (user) { | |
36 | + console.log(`\nUser '${username}' found.`); | |
37 | + if (user.isAdmin) { | |
38 | + console.log("✅ User is already an Admin."); | |
39 | + } else { | |
40 | + console.log("Promoting to Admin..."); | |
41 | + await db.update(schema.users) | |
42 | + .set({ isAdmin: true }) | |
43 | + .where(eq(schema.users.id, user.id)); | |
44 | + console.log("✅ User promoted to Admin successfully."); | |
45 | + } | |
46 | + } else { | |
47 | + console.log(`\nUser '${username}' not found. Creating new admin user...`); | |
48 | + if (!email || !password) { | |
49 | + console.error("Error: Email and Password are required to create a new user."); | |
50 | + process.exit(1); | |
51 | + } | |
52 | + | |
53 | + const hashedPassword = await bcrypt.hash(password, 10); | |
54 | + const userId = crypto.randomUUID(); | |
55 | + | |
56 | + await db.insert(schema.users).values({ | |
57 | + id: userId, | |
58 | + username, | |
59 | + email, | |
60 | + passwordHash: hashedPassword, | |
61 | + displayName: username, // Default display name | |
62 | + isAdmin: true, | |
63 | + isActive: true, // Auto-activate admin | |
64 | + emailVerified: true // Auto-verify admin | |
65 | + }); | |
66 | + | |
67 | + console.log(`✅ Admin user '${username}' created successfully.`); | |
68 | + } | |
69 | + | |
70 | + rl.close(); | |
71 | + process.exit(0); | |
72 | +} | |
73 | + | |
74 | +main().catch(err => { | |
75 | + console.error("Error:", err); | |
76 | + process.exit(1); | |
77 | +}); | |
0 | 78 | |
▼scripts/ssh-server.ts
+119-0
| @@ -0,0 +1,119 @@ | ||
1 | + | |
2 | +import { getDatabase, schema } from "@/db"; | |
3 | +import { canReadRepo, canWriteRepo } from "@/lib/permissions"; | |
4 | +import { startSSHServer } from "@/lib/ssh"; | |
5 | +import { and, eq } from "drizzle-orm"; | |
6 | +import { join } from "path"; | |
7 | + | |
8 | +const DATA_DIR = join(process.cwd(), "data"); | |
9 | +const REPOS_DIR = join(DATA_DIR, "repos"); | |
10 | +const SSH_DIR = join(DATA_DIR, "ssh"); | |
11 | +const HOST_KEY_PATH = join(SSH_DIR, "ssh_host_rsa_key"); | |
12 | + | |
13 | +async function main() { | |
14 | + console.log("Starting OpenCodeHub SSH Server..."); | |
15 | + | |
16 | + await startSSHServer({ | |
17 | + port: 2222, | |
18 | + hostKeyPath: HOST_KEY_PATH, | |
19 | + reposPath: REPOS_DIR, | |
20 | + authenticateUser: async (username, key) => { | |
21 | + // We ignore the username (usually 'git') and authenticate by key | |
22 | + const db = getDatabase(); | |
23 | + | |
24 | + // Iterate over all keys to find a match | |
25 | + // In a production app, we would index by fingerprint or key type to optimize | |
26 | + const allKeys = await db.query.sshKeys.findMany(); | |
27 | + | |
28 | + for (const dbKey of allKeys) { | |
29 | + try { | |
30 | + // Parse DB key | |
31 | + const parts = dbKey.publicKey.trim().split(" "); | |
32 | + if (parts.length < 2) continue; | |
33 | + | |
34 | + const dbKeyData = Buffer.from(parts[1], 'base64'); | |
35 | + | |
36 | + // Compare buffers | |
37 | + if (dbKeyData.equals(key)) { | |
38 | + return { | |
39 | + valid: true, | |
40 | + userId: dbKey.userId, | |
41 | + canRead: true, // Specific repo auth is handled in authorizeRepo | |
42 | + canWrite: true | |
43 | + }; | |
44 | + } | |
45 | + } catch (e) { | |
46 | + console.error("Error checking key:", e); | |
47 | + } | |
48 | + } | |
49 | + | |
50 | + return { valid: false }; | |
51 | + }, | |
52 | + authorizeRepo: async (userId, repoPath, operation) => { | |
53 | + // repoPath is "owner/repo.git" | |
54 | + const [ownerName, repoNameWithGit] = repoPath.split("/"); | |
55 | + const repoName = repoNameWithGit.replace(".git", ""); | |
56 | + | |
57 | + if (!ownerName || !repoName) return false; | |
58 | + | |
59 | + const db = getDatabase(); | |
60 | + | |
61 | + // Find Repo | |
62 | + const owner = await db.query.users.findFirst({ | |
63 | + where: eq(schema.users.username, ownerName) | |
64 | + }); | |
65 | + | |
66 | + if (!owner) return false; | |
67 | + | |
68 | + const repo = await db.query.repositories.findFirst({ | |
69 | + where: and( | |
70 | + eq(schema.repositories.ownerId, owner.id), | |
71 | + eq(schema.repositories.name, repoName) | |
72 | + ) | |
73 | + }); | |
74 | + | |
75 | + if (!repo) return false; | |
76 | + | |
77 | + if (operation === "read") { | |
78 | + return canReadRepo(userId, repo); | |
79 | + } else { | |
80 | + return canWriteRepo(userId, repo); | |
81 | + } | |
82 | + }, | |
83 | + onPush: async (userId, repoPath, refs) => { | |
84 | + console.log(`Push received to ${repoPath} from ${userId}`); | |
85 | + | |
86 | + // Resolve Repo ID | |
87 | + const [ownerName, repoNameWithGit] = repoPath.split("/"); | |
88 | + const repoName = repoNameWithGit.replace(".git", ""); | |
89 | + | |
90 | + const db = getDatabase(); | |
91 | + const owner = await db.query.users.findFirst({ where: eq(schema.users.username, ownerName) }); | |
92 | + if (!owner) return; | |
93 | + | |
94 | + const repo = await db.query.repositories.findFirst({ | |
95 | + where: and(eq(schema.repositories.ownerId, owner.id), eq(schema.repositories.name, repoName)) | |
96 | + }); | |
97 | + | |
98 | + if (repo) { | |
99 | + // 1. Run Analysis | |
100 | + import("@/lib/analysis").then(({ analyzeRepository }) => { | |
101 | + analyzeRepository(repo.id, userId).catch(console.error); | |
102 | + }); | |
103 | + | |
104 | + // 2. Trigger Workflows | |
105 | + import("@/lib/workflows").then(({ triggerRepoWorkflows }) => { | |
106 | + refs.forEach(refLine => { | |
107 | + const [oldSha, newSha, refName] = refLine.split(" "); | |
108 | + // Only trigger on branch updates (not deletions or tags for now unless supported) | |
109 | + if (newSha !== "0000000000000000000000000000000000000000" && refName.startsWith("refs/heads/")) { | |
110 | + triggerRepoWorkflows(repo.id, newSha, refName, userId).catch(console.error); | |
111 | + } | |
112 | + }); | |
113 | + }); | |
114 | + } | |
115 | + } | |
116 | + }); | |
117 | +} | |
118 | + | |
119 | +main().catch(console.error); | |
0 | 120 | |
▼scripts/test-gpg.ts
+25-0
| @@ -0,0 +1,25 @@ | ||
1 | + | |
2 | +import * as openpgp from 'openpgp'; | |
3 | + | |
4 | +async function test() { | |
5 | + try { | |
6 | + console.log("Generating key..."); | |
7 | + const { privateKey, publicKey } = await openpgp.generateKey({ | |
8 | + type: 'ecc', | |
9 | + curve: 'curve25519', | |
10 | + userIDs: [{ name: 'Test', email: '[email protected]' }], | |
11 | + }); | |
12 | + console.log("Key generated."); | |
13 | + | |
14 | + console.log("Reading public key..."); | |
15 | + const key = await openpgp.readKey({ armoredKey: publicKey }); | |
16 | + console.log("Key ID:", key.getKeyID().toHex()); | |
17 | + console.log("Is Private:", key.isPrivate()); | |
18 | + | |
19 | + console.log("Test passed."); | |
20 | + } catch (e) { | |
21 | + console.error("Test failed:", e); | |
22 | + } | |
23 | +} | |
24 | + | |
25 | +test(); | |
0 | 26 | |
▼src/components/admin/AdminDashboard.tsx
+158-0
| @@ -0,0 +1,158 @@ | ||
1 | +import { useEffect, useState, lazy, Suspense, Component, ReactNode } from "react"; | |
2 | +import DashboardStats from "./DashboardStats"; | |
3 | +import TrendingDevelopers from "./TrendingDevelopers"; | |
4 | +import LiveLogStream from "./LiveLogStream"; | |
5 | +import BottomPanel from "./BottomPanel"; | |
6 | +import SystemStatus from "./SystemStatus"; | |
7 | +import QuickStatsRow from "./QuickStatsRow"; | |
8 | +import AlertBanner from "./AlertBanner"; | |
9 | +import RecentActivity from "./RecentActivity"; | |
10 | +import { Github } from "lucide-react"; | |
11 | + | |
12 | +// Error boundary for WebGL/Globe issues | |
13 | +class GlobeErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> { | |
14 | + constructor(props: { children: ReactNode }) { | |
15 | + super(props); | |
16 | + this.state = { hasError: false }; | |
17 | + } | |
18 | + | |
19 | + static getDerivedStateFromError() { | |
20 | + return { hasError: true }; | |
21 | + } | |
22 | + | |
23 | + componentDidCatch(error: Error) { | |
24 | + console.warn("Globe component error:", error.message); | |
25 | + } | |
26 | + | |
27 | + render() { | |
28 | + if (this.state.hasError) { | |
29 | + return ( | |
30 | + <div className="w-full h-full flex items-center justify-center"> | |
31 | + <div className="text-center text-white/50"> | |
32 | + <div className="text-6xl mb-4">🌐</div> | |
33 | + <p className="text-sm">3D Globe unavailable</p> | |
34 | + <p className="text-xs mt-1 opacity-50">WebGL may not be supported</p> | |
35 | + </div> | |
36 | + </div> | |
37 | + ); | |
38 | + } | |
39 | + return this.props.children; | |
40 | + } | |
41 | +} | |
42 | + | |
43 | +const GlobeViz = lazy(() => import("./GlobeViz").catch(() => ({ | |
44 | + default: () => ( | |
45 | + <div className="w-full h-full flex items-center justify-center"> | |
46 | + <div className="text-center text-white/50"> | |
47 | + <div className="text-6xl mb-4">🌐</div> | |
48 | + <p className="text-sm">Globe failed to load</p> | |
49 | + </div> | |
50 | + </div> | |
51 | + ) | |
52 | +}))); | |
53 | + | |
54 | +export default function AdminDashboard() { | |
55 | + const [data, setData] = useState<any>({ | |
56 | + totalRepos: 0, | |
57 | + totalUsers: 0, | |
58 | + collaborations: 0, | |
59 | + trendingDevelopers: [], | |
60 | + activityLog: [], | |
61 | + codeStats: {}, | |
62 | + reviewStats: {}, | |
63 | + languages: [], | |
64 | + quickStats: {} | |
65 | + }); | |
66 | + | |
67 | + useEffect(() => { | |
68 | + const fetchData = async () => { | |
69 | + try { | |
70 | + const res = await fetch("/api/admin/stats"); | |
71 | + const json = await res.json(); | |
72 | + setData(json); | |
73 | + } catch (e) { | |
74 | + console.error("Failed to fetch admin stats"); | |
75 | + } | |
76 | + }; | |
77 | + | |
78 | + fetchData(); | |
79 | + const interval = setInterval(fetchData, 10000); | |
80 | + return () => clearInterval(interval); | |
81 | + }, []); | |
82 | + | |
83 | + return ( | |
84 | + <div className="relative h-full w-full"> | |
85 | + {/* 3D Globe Background */} | |
86 | + <div className="absolute inset-0 z-0 opacity-80 pointer-events-auto"> | |
87 | + <GlobeErrorBoundary> | |
88 | + <Suspense fallback={ | |
89 | + <div className="w-full h-full flex items-center justify-center"> | |
90 | + <div className="text-white/30 text-sm">Loading globe...</div> | |
91 | + </div> | |
92 | + }> | |
93 | + <GlobeViz activities={data.activityLog || []} /> | |
94 | + </Suspense> | |
95 | + </GlobeErrorBoundary> | |
96 | + </div> | |
97 | + | |
98 | + <div className="relative z-10 flex flex-col h-full p-6 pb-8 pointer-events-none"> | |
99 | + | |
100 | + {/* Top Alert Banner */} | |
101 | + <div className="mb-4 pointer-events-auto shrink-0"> | |
102 | + <AlertBanner /> | |
103 | + </div> | |
104 | + | |
105 | + {/* Quick Stats Row - Top Center */} | |
106 | + <div className="mb-4 pointer-events-auto shrink-0 flex justify-center"> | |
107 | + <QuickStatsRow stats={data.quickStats || {}} /> | |
108 | + </div> | |
109 | + | |
110 | + {/* Main Content Grid */} | |
111 | + <div className="flex-1 grid grid-cols-12 gap-6 min-h-0"> | |
112 | + {/* Left Column: Stats & Trending */} | |
113 | + <div className="col-span-3 flex flex-col gap-4 pointer-events-auto overflow-hidden"> | |
114 | + {/* Stats Block */} | |
115 | + <div className="glass-panel rounded-xl shrink-0"> | |
116 | + <DashboardStats stats={data} /> | |
117 | + </div> | |
118 | + | |
119 | + {/* Trending Devs Block */} | |
120 | + <div className="glass-panel rounded-xl flex-1 overflow-y-auto custom-scrollbar min-h-0"> | |
121 | + <TrendingDevelopers developers={data.trendingDevelopers || []} /> | |
122 | + </div> | |
123 | + </div> | |
124 | + | |
125 | + {/* Middle Column: Globe Area with System Status */} | |
126 | + <div className="col-span-6 flex flex-col justify-between items-center pointer-events-none py-4"> | |
127 | + {/* Top area - Recent Activity floating */} | |
128 | + <div className="pointer-events-auto self-start"> | |
129 | + <RecentActivity activities={data.recentActivity || []} /> | |
130 | + </div> | |
131 | + | |
132 | + {/* Bottom - System Status HUD */} | |
133 | + <div className="pointer-events-auto"> | |
134 | + <SystemStatus status={data.systemStatus} /> | |
135 | + </div> | |
136 | + </div> | |
137 | + | |
138 | + {/* Right Column: Log Stream */} | |
139 | + <div className="col-span-3 flex flex-col gap-4 pointer-events-auto overflow-hidden"> | |
140 | + <div className="flex justify-end shrink-0"> | |
141 | + <Github className="h-8 w-8 text-white opacity-30 hover:opacity-70 transition-opacity cursor-pointer" /> | |
142 | + </div> | |
143 | + | |
144 | + <div className="glass-panel rounded-xl flex-1 overflow-hidden min-h-0 relative"> | |
145 | + <LiveLogStream logs={data.activityLog || []} /> | |
146 | + </div> | |
147 | + </div> | |
148 | + </div> | |
149 | + | |
150 | + {/* Bottom Section: Carousel Panel */} | |
151 | + <div className="h-48 shrink-0 pointer-events-auto mt-4"> | |
152 | + <BottomPanel stats={data} /> | |
153 | + </div> | |
154 | + | |
155 | + </div> | |
156 | + </div> | |
157 | + ); | |
158 | +} | |
0 | 159 | |
▼src/components/admin/AlertBanner.tsx
+108-0
| @@ -0,0 +1,108 @@ | ||
1 | +import { motion } from "framer-motion"; | |
2 | +import { AlertTriangle, CheckCircle, Info, Bell, X } from "lucide-react"; | |
3 | +import { useState } from "react"; | |
4 | + | |
5 | +interface Alert { | |
6 | + id: string; | |
7 | + type: "success" | "warning" | "info" | "error"; | |
8 | + message: string; | |
9 | + timestamp: string; | |
10 | +} | |
11 | + | |
12 | +interface Props { | |
13 | + alerts?: Alert[]; | |
14 | +} | |
15 | + | |
16 | +const alertStyles = { | |
17 | + success: { | |
18 | + bg: "from-green-500/10 via-emerald-500/5 to-transparent", | |
19 | + border: "border-green-500/30", | |
20 | + icon: CheckCircle, | |
21 | + iconColor: "text-green-400", | |
22 | + glow: "shadow-green-500/20" | |
23 | + }, | |
24 | + warning: { | |
25 | + bg: "from-yellow-500/10 via-amber-500/5 to-transparent", | |
26 | + border: "border-yellow-500/30", | |
27 | + icon: AlertTriangle, | |
28 | + iconColor: "text-yellow-400", | |
29 | + glow: "shadow-yellow-500/20" | |
30 | + }, | |
31 | + info: { | |
32 | + bg: "from-blue-500/10 via-cyan-500/5 to-transparent", | |
33 | + border: "border-blue-500/30", | |
34 | + icon: Info, | |
35 | + iconColor: "text-blue-400", | |
36 | + glow: "shadow-blue-500/20" | |
37 | + }, | |
38 | + error: { | |
39 | + bg: "from-red-500/10 via-rose-500/5 to-transparent", | |
40 | + border: "border-red-500/30", | |
41 | + icon: AlertTriangle, | |
42 | + iconColor: "text-red-400", | |
43 | + glow: "shadow-red-500/20" | |
44 | + } | |
45 | +}; | |
46 | + | |
47 | +const defaultAlerts: Alert[] = [ | |
48 | + { | |
49 | + id: "1", | |
50 | + type: "success", | |
51 | + message: "All systems operational • 99.9% uptime this month", | |
52 | + timestamp: "Just now" | |
53 | + } | |
54 | +]; | |
55 | + | |
56 | +export default function AlertBanner({ alerts = defaultAlerts }: Props) { | |
57 | + const [dismissedIds, setDismissedIds] = useState<Set<string>>(new Set()); | |
58 | + | |
59 | + const visibleAlerts = alerts.filter(a => !dismissedIds.has(a.id)); | |
60 | + | |
61 | + if (visibleAlerts.length === 0) return null; | |
62 | + | |
63 | + const alert = visibleAlerts[0]; | |
64 | + const style = alertStyles[alert.type]; | |
65 | + const Icon = style.icon; | |
66 | + | |
67 | + return ( | |
68 | + <motion.div | |
69 | + initial={{ opacity: 0, y: -10 }} | |
70 | + animate={{ opacity: 1, y: 0 }} | |
71 | + exit={{ opacity: 0, y: -10 }} | |
72 | + className="w-full max-w-2xl mx-auto" | |
73 | + > | |
74 | + <div className={`relative overflow-hidden rounded-lg border ${style.border} bg-gradient-to-r ${style.bg} backdrop-blur-sm shadow-lg ${style.glow}`}> | |
75 | + {/* Animated shimmer */} | |
76 | + <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/5 to-transparent animate-shimmer" /> | |
77 | + | |
78 | + <div className="relative flex items-center gap-3 px-4 py-2.5"> | |
79 | + {/* Pulsing icon */} | |
80 | + <motion.div | |
81 | + animate={{ scale: [1, 1.1, 1] }} | |
82 | + transition={{ duration: 2, repeat: Infinity }} | |
83 | + > | |
84 | + <Icon className={`w-4 h-4 ${style.iconColor}`} /> | |
85 | + </motion.div> | |
86 | + | |
87 | + {/* Message */} | |
88 | + <span className="flex-1 text-sm text-gray-200"> | |
89 | + {alert.message} | |
90 | + </span> | |
91 | + | |
92 | + {/* Timestamp */} | |
93 | + <span className="text-xs text-gray-500 font-mono"> | |
94 | + {alert.timestamp} | |
95 | + </span> | |
96 | + | |
97 | + {/* Dismiss button */} | |
98 | + <button | |
99 | + onClick={() => setDismissedIds(prev => new Set([...prev, alert.id]))} | |
100 | + className="p-1 rounded hover:bg-white/10 transition-colors" | |
101 | + > | |
102 | + <X className="w-3 h-3 text-gray-500 hover:text-gray-300" /> | |
103 | + </button> | |
104 | + </div> | |
105 | + </div> | |
106 | + </motion.div> | |
107 | + ); | |
108 | +} | |
0 | 109 | |
▼src/components/admin/BottomPanel.tsx
+311-0
| @@ -0,0 +1,311 @@ | ||
1 | +import { useState, useEffect } from "react"; | |
2 | +import { motion, AnimatePresence } from "framer-motion"; | |
3 | +import { Code2, GitCommit, MessageSquare, Users, TrendingUp, TrendingDown, Sparkles, Globe } from "lucide-react"; | |
4 | + | |
5 | +interface Props { | |
6 | + stats: any; | |
7 | +} | |
8 | + | |
9 | +// Animated number component | |
10 | +function AnimatedNumber({ value, className, prefix = "", suffix = "" }: { | |
11 | + value: number; | |
12 | + className?: string; | |
13 | + prefix?: string; | |
14 | + suffix?: string; | |
15 | +}) { | |
16 | + const [displayValue, setDisplayValue] = useState(0); | |
17 | + | |
18 | + useEffect(() => { | |
19 | + const duration = 1200; | |
20 | + const startTime = Date.now(); | |
21 | + const startValue = displayValue; | |
22 | + | |
23 | + const animate = () => { | |
24 | + const now = Date.now(); | |
25 | + const progress = Math.min((now - startTime) / duration, 1); | |
26 | + const eased = 1 - Math.pow(1 - progress, 3); | |
27 | + setDisplayValue(Math.floor(startValue + (value - startValue) * eased)); | |
28 | + | |
29 | + if (progress < 1) { | |
30 | + requestAnimationFrame(animate); | |
31 | + } | |
32 | + }; | |
33 | + | |
34 | + requestAnimationFrame(animate); | |
35 | + }, [value]); | |
36 | + | |
37 | + return <span className={className}>{prefix}{displayValue.toLocaleString()}{suffix}</span>; | |
38 | +} | |
39 | + | |
40 | +export default function BottomPanel({ stats }: Props) { | |
41 | + const [activeSlide, setActiveSlide] = useState(0); | |
42 | + | |
43 | + const SLIDES = 4; | |
44 | + | |
45 | + useEffect(() => { | |
46 | + const interval = setInterval(() => { | |
47 | + setActiveSlide((prev) => (prev + 1) % SLIDES); | |
48 | + }, 15000); | |
49 | + return () => clearInterval(interval); | |
50 | + }, []); | |
51 | + | |
52 | + const variants = { | |
53 | + enter: { opacity: 0, y: 30, filter: "blur(10px)" }, | |
54 | + center: { opacity: 1, y: 0, filter: "blur(0px)" }, | |
55 | + exit: { opacity: 0, y: -30, filter: "blur(10px)" }, | |
56 | + }; | |
57 | + | |
58 | + return ( | |
59 | + <div className="relative rounded-2xl h-48 overflow-hidden flex items-center w-full"> | |
60 | + {/* Animated gradient border */} | |
61 | + <div className="absolute inset-0 rounded-2xl p-[1px] bg-gradient-to-r from-cyan-500/50 via-purple-500/50 to-pink-500/50 animate-gradient-x bg-[length:200%_auto]"> | |
62 | + <div className="h-full w-full rounded-2xl bg-[#050510]" /> | |
63 | + </div> | |
64 | + | |
65 | + {/* Content */} | |
66 | + <div className="relative h-full w-full p-6 glass-panel rounded-2xl"> | |
67 | + <AnimatePresence mode="wait"> | |
68 | + | |
69 | + {/* VIEW 1: Code Frequency */} | |
70 | + {activeSlide === 0 && ( | |
71 | + <motion.div | |
72 | + key="code-freq" | |
73 | + variants={variants} | |
74 | + initial="enter" | |
75 | + animate="center" | |
76 | + exit="exit" | |
77 | + transition={{ duration: 0.5 }} | |
78 | + className="w-full h-full flex items-center justify-between px-8" | |
79 | + > | |
80 | + <div className="relative"> | |
81 | + {/* Glow effect */} | |
82 | + <div className="absolute -inset-4 bg-gradient-to-r from-green-500/20 to-emerald-500/20 rounded-xl blur-xl" /> | |
83 | + | |
84 | + <div className="relative"> | |
85 | + <div className="text-xs uppercase tracking-widest text-emerald-400/60 mb-3 flex items-center gap-2"> | |
86 | + <Code2 className="h-4 w-4 text-emerald-400" /> | |
87 | + Code Lines Added / Deleted | |
88 | + </div> | |
89 | + <div className="flex items-end gap-5"> | |
90 | + <div className="flex items-center gap-2"> | |
91 | + <TrendingUp className="h-6 w-6 text-green-400" /> | |
92 | + <span className="text-5xl font-mono font-light bg-gradient-to-r from-green-300 to-emerald-400 bg-clip-text text-transparent"> | |
93 | + <AnimatedNumber value={stats.codeStats?.added || 124475} prefix="+" /> | |
94 | + </span> | |
95 | + </div> | |
96 | + <div className="flex items-center gap-2 mb-1"> | |
97 | + <TrendingDown className="h-5 w-5 text-red-400 opacity-60" /> | |
98 | + <span className="text-3xl font-mono text-red-400/70 font-light"> | |
99 | + <AnimatedNumber value={stats.codeStats?.deleted || 48468} prefix="-" /> | |
100 | + </span> | |
101 | + </div> | |
102 | + </div> | |
103 | + <div className="text-xs text-gray-500 mt-3 flex items-center gap-1.5"> | |
104 | + <Globe className="h-3 w-3" /> | |
105 | + Global repository statistics (Last 24h) | |
106 | + </div> | |
107 | + </div> | |
108 | + </div> | |
109 | + | |
110 | + {/* Contribution bars */} | |
111 | + <div className="flex gap-1 items-end h-24"> | |
112 | + {Array.from({ length: 45 }).map((_, i) => { | |
113 | + const height = Math.random() * 100; | |
114 | + return ( | |
115 | + <motion.div | |
116 | + key={i} | |
117 | + className="w-1.5 rounded-full bg-gradient-to-t from-green-600 to-emerald-400" | |
118 | + initial={{ height: 0 }} | |
119 | + animate={{ height: `${height}%` }} | |
120 | + transition={{ | |
121 | + delay: i * 0.02, | |
122 | + duration: 0.8, | |
123 | + ease: "easeOut" | |
124 | + }} | |
125 | + style={{ opacity: 0.3 + (height / 100) * 0.7 }} | |
126 | + /> | |
127 | + ); | |
128 | + })} | |
129 | + </div> | |
130 | + </motion.div> | |
131 | + )} | |
132 | + | |
133 | + {/* VIEW 2: Contribution Graph */} | |
134 | + {activeSlide === 1 && ( | |
135 | + <motion.div | |
136 | + key="contrib-graph" | |
137 | + variants={variants} | |
138 | + initial="enter" | |
139 | + animate="center" | |
140 | + exit="exit" | |
141 | + transition={{ duration: 0.5 }} | |
142 | + className="w-full h-full flex flex-col justify-center px-8" | |
143 | + > | |
144 | + <div className="text-xs uppercase tracking-widest text-blue-400/60 mb-4 flex items-center gap-2"> | |
145 | + <GitCommit className="h-4 w-4 text-blue-400" /> | |
146 | + System-wide Contribution Graph | |
147 | + </div> | |
148 | + <div className="flex gap-1 flex-wrap justify-center"> | |
149 | + {Array.from({ length: 168 }).map((_, i) => { | |
150 | + const level = Math.floor(Math.random() * 5); | |
151 | + const colors = ['#161b22', '#0e4429', '#006d32', '#26a641', '#39d353']; | |
152 | + return ( | |
153 | + <motion.div | |
154 | + key={i} | |
155 | + className="w-3 h-3 rounded-sm" | |
156 | + initial={{ opacity: 0, scale: 0 }} | |
157 | + animate={{ opacity: 1, scale: 1 }} | |
158 | + transition={{ delay: i * 0.005 }} | |
159 | + style={{ backgroundColor: colors[level] }} | |
160 | + /> | |
161 | + ); | |
162 | + })} | |
163 | + </div> | |
164 | + </motion.div> | |
165 | + )} | |
166 | + | |
167 | + {/* VIEW 3: User Engagement */} | |
168 | + {activeSlide === 2 && ( | |
169 | + <motion.div | |
170 | + key="engagement" | |
171 | + variants={variants} | |
172 | + initial="enter" | |
173 | + animate="center" | |
174 | + exit="exit" | |
175 | + transition={{ duration: 0.5 }} | |
176 | + className="w-full h-full flex items-center justify-between px-8" | |
177 | + > | |
178 | + <div className="relative"> | |
179 | + <div className="absolute -inset-4 bg-gradient-to-r from-purple-500/20 to-violet-500/20 rounded-xl blur-xl" /> | |
180 | + <div className="relative"> | |
181 | + <div className="text-xs uppercase tracking-widest text-purple-400/60 mb-3 flex items-center gap-2"> | |
182 | + <Users className="h-4 w-4 text-purple-400" /> | |
183 | + Users Reviewing & Commenting | |
184 | + </div> | |
185 | + <div className="text-5xl font-mono font-light bg-gradient-to-r from-purple-300 via-violet-400 to-fuchsia-400 bg-clip-text text-transparent"> | |
186 | + <AnimatedNumber value={stats.reviewStats?.count || 156} /> | |
187 | + </div> | |
188 | + <div className="text-xs text-gray-500 mt-3 flex items-center gap-1.5"> | |
189 | + <Sparkles className="h-3 w-3 text-purple-400/60" /> | |
190 | + Active participants in code review | |
191 | + </div> | |
192 | + </div> | |
193 | + </div> | |
194 | + | |
195 | + {/* Timeline */} | |
196 | + <div className="flex-1 px-16 flex items-center justify-center relative"> | |
197 | + <div className="absolute w-full h-[2px] bg-gradient-to-r from-transparent via-purple-500/30 to-transparent" /> | |
198 | + <div className="flex gap-10 relative z-10"> | |
199 | + {Array.from({ length: 6 }).map((_, i) => { | |
200 | + const colors = ['#8957e5', '#238636', '#da3633', '#e3b341', '#3fb950', '#f778ba']; | |
201 | + return ( | |
202 | + <motion.div | |
203 | + key={i} | |
204 | + className="h-4 w-4 rounded-full relative" | |
205 | + initial={{ scale: 0 }} | |
206 | + animate={{ scale: 1 }} | |
207 | + transition={{ delay: 0.3 + i * 0.1, type: "spring" }} | |
208 | + style={{ backgroundColor: colors[i % colors.length] }} | |
209 | + > | |
210 | + <div | |
211 | + className="absolute inset-0 rounded-full animate-ping" | |
212 | + style={{ | |
213 | + backgroundColor: colors[i % colors.length], | |
214 | + animationDelay: `${i * 0.2}s`, | |
215 | + animationDuration: "2s" | |
216 | + }} | |
217 | + /> | |
218 | + </motion.div> | |
219 | + ); | |
220 | + })} | |
221 | + </div> | |
222 | + </div> | |
223 | + | |
224 | + {/* Avatars */} | |
225 | + <div className="flex items-center -space-x-3"> | |
226 | + {(stats.reviewStats?.activeReviewers || Array.from({ length: 4 })).slice(0, 5).map((u: any, i: number) => ( | |
227 | + <motion.div | |
228 | + key={i} | |
229 | + className="h-12 w-12 rounded-full border-2 border-[#050510] overflow-hidden bg-gradient-to-br from-purple-500 to-pink-500 flex items-center justify-center" | |
230 | + initial={{ scale: 0, x: 20 }} | |
231 | + animate={{ scale: 1, x: 0 }} | |
232 | + transition={{ delay: 0.2 + i * 0.1 }} | |
233 | + > | |
234 | + {u?.avatar ? ( | |
235 | + <img src={u.avatar} className="h-full w-full object-cover" /> | |
236 | + ) : ( | |
237 | + <span className="text-sm font-bold text-white">{String.fromCharCode(65 + i)}</span> | |
238 | + )} | |
239 | + </motion.div> | |
240 | + ))} | |
241 | + </div> | |
242 | + </motion.div> | |
243 | + )} | |
244 | + | |
245 | + {/* VIEW 4: Languages */} | |
246 | + {activeSlide === 3 && ( | |
247 | + <motion.div | |
248 | + key="languages" | |
249 | + variants={variants} | |
250 | + initial="enter" | |
251 | + animate="center" | |
252 | + exit="exit" | |
253 | + transition={{ duration: 0.5 }} | |
254 | + className="w-full h-full flex flex-col justify-center px-8" | |
255 | + > | |
256 | + <div className="text-xs uppercase tracking-widest text-orange-400/60 mb-4 flex items-center gap-2"> | |
257 | + <MessageSquare className="h-4 w-4 text-orange-400" /> | |
258 | + Usage by Language | |
259 | + </div> | |
260 | + <div className="space-y-3 max-w-4xl w-full"> | |
261 | + {(stats.languages || []).map((lang: any, i: number) => ( | |
262 | + <div key={lang.name} className="flex items-center gap-4"> | |
263 | + <div className="w-24 text-sm text-gray-300 font-mono">{lang.name}</div> | |
264 | + <div className="flex-1 h-2 bg-white/5 rounded-full overflow-hidden"> | |
265 | + <motion.div | |
266 | + initial={{ width: 0 }} | |
267 | + animate={{ width: `${lang.percentage}%` }} | |
268 | + transition={{ delay: i * 0.15, duration: 0.8, ease: "easeOut" }} | |
269 | + className="h-full rounded-full relative overflow-hidden" | |
270 | + style={{ backgroundColor: lang.color }} | |
271 | + > | |
272 | + {/* Shimmer effect */} | |
273 | + <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/30 to-transparent animate-shimmer" /> | |
274 | + </motion.div> | |
275 | + </div> | |
276 | + <div className="w-12 text-sm text-gray-400 text-right font-mono">{lang.percentage}%</div> | |
277 | + </div> | |
278 | + ))} | |
279 | + </div> | |
280 | + </motion.div> | |
281 | + )} | |
282 | + | |
283 | + </AnimatePresence> | |
284 | + | |
285 | + {/* Progress indicators with glow */} | |
286 | + <div className="absolute bottom-4 left-1/2 -translate-x-1/2 flex gap-2"> | |
287 | + {[0, 1, 2, 3].map(i => ( | |
288 | + <button | |
289 | + key={i} | |
290 | + onClick={() => setActiveSlide(i)} | |
291 | + className="relative" | |
292 | + > | |
293 | + {activeSlide === i && ( | |
294 | + <motion.div | |
295 | + layoutId="activeIndicator" | |
296 | + className="absolute -inset-1 bg-blue-500/30 rounded-full blur-sm" | |
297 | + /> | |
298 | + )} | |
299 | + <div | |
300 | + className={`relative h-1.5 rounded-full transition-all duration-300 ${activeSlide === i | |
301 | + ? 'w-8 bg-gradient-to-r from-cyan-400 to-blue-500' | |
302 | + : 'w-2 bg-gray-700 hover:bg-gray-600' | |
303 | + }`} | |
304 | + /> | |
305 | + </button> | |
306 | + ))} | |
307 | + </div> | |
308 | + </div> | |
309 | + </div> | |
310 | + ); | |
311 | +} | |
0 | 312 | |
▼src/components/admin/DashboardStats.tsx
+108-0
| @@ -0,0 +1,108 @@ | ||
1 | +import { motion, useMotionValue, useSpring, useTransform } from "framer-motion"; | |
2 | +import { useEffect, useState } from "react"; | |
3 | +import { Database, GitPullRequest, Sparkles } from "lucide-react"; | |
4 | + | |
5 | +interface Props { | |
6 | + stats: { | |
7 | + totalRepos: number; | |
8 | + collaborations: number; | |
9 | + } | |
10 | +} | |
11 | + | |
12 | +// Animated counter that smoothly transitions between numbers | |
13 | +function AnimatedNumber({ value, className }: { value: number; className?: string }) { | |
14 | + const [displayValue, setDisplayValue] = useState(0); | |
15 | + | |
16 | + useEffect(() => { | |
17 | + const duration = 1500; | |
18 | + const startTime = Date.now(); | |
19 | + const startValue = displayValue; | |
20 | + | |
21 | + const animate = () => { | |
22 | + const now = Date.now(); | |
23 | + const progress = Math.min((now - startTime) / duration, 1); | |
24 | + // Easing function for smooth animation | |
25 | + const eased = 1 - Math.pow(1 - progress, 3); | |
26 | + setDisplayValue(Math.floor(startValue + (value - startValue) * eased)); | |
27 | + | |
28 | + if (progress < 1) { | |
29 | + requestAnimationFrame(animate); | |
30 | + } | |
31 | + }; | |
32 | + | |
33 | + requestAnimationFrame(animate); | |
34 | + }, [value]); | |
35 | + | |
36 | + return <span className={className}>{displayValue.toLocaleString()}</span>; | |
37 | +} | |
38 | + | |
39 | +export default function DashboardStats({ stats }: Props) { | |
40 | + return ( | |
41 | + <div className="flex flex-col gap-8 p-6"> | |
42 | + {/* Total Repositories - with glowing effect */} | |
43 | + <motion.div | |
44 | + initial={{ opacity: 0, x: -20 }} | |
45 | + animate={{ opacity: 1, x: 0 }} | |
46 | + transition={{ duration: 0.5 }} | |
47 | + className="relative group" | |
48 | + > | |
49 | + {/* Glow effect on hover */} | |
50 | + <div className="absolute -inset-2 bg-gradient-to-r from-cyan-500/20 via-blue-500/20 to-purple-500/20 rounded-lg blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500" /> | |
51 | + | |
52 | + <div className="relative"> | |
53 | + <div className="flex items-center gap-2 mb-2"> | |
54 | + <Database className="w-4 h-4 text-cyan-400" /> | |
55 | + <h3 className="text-sm uppercase tracking-widest text-cyan-400/80"> | |
56 | + Total Repositories | |
57 | + </h3> | |
58 | + </div> | |
59 | + <div className="text-5xl font-light font-mono"> | |
60 | + <span className="bg-gradient-to-r from-white via-cyan-200 to-cyan-400 bg-clip-text text-transparent"> | |
61 | + <AnimatedNumber value={stats.totalRepos} /> | |
62 | + </span> | |
63 | + </div> | |
64 | + <div className="flex items-center gap-1.5 mt-2"> | |
65 | + <motion.span | |
66 | + className="relative flex h-2 w-2" | |
67 | + animate={{ scale: [1, 1.2, 1] }} | |
68 | + transition={{ duration: 2, repeat: Infinity }} | |
69 | + > | |
70 | + <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75" /> | |
71 | + <span className="relative inline-flex rounded-full h-2 w-2 bg-cyan-500" /> | |
72 | + </motion.span> | |
73 | + <span className="text-xs text-cyan-400/60">Live Count</span> | |
74 | + </div> | |
75 | + </div> | |
76 | + </motion.div> | |
77 | + | |
78 | + {/* Active Collaborations - with pink/rose theme */} | |
79 | + <motion.div | |
80 | + initial={{ opacity: 0, x: -20 }} | |
81 | + animate={{ opacity: 1, x: 0 }} | |
82 | + transition={{ duration: 0.5, delay: 0.1 }} | |
83 | + className="relative group" | |
84 | + > | |
85 | + {/* Glow effect on hover */} | |
86 | + <div className="absolute -inset-2 bg-gradient-to-r from-pink-500/20 via-rose-500/20 to-orange-500/20 rounded-lg blur-xl opacity-0 group-hover:opacity-100 transition-opacity duration-500" /> | |
87 | + | |
88 | + <div className="relative"> | |
89 | + <div className="flex items-center gap-2 mb-2"> | |
90 | + <GitPullRequest className="w-4 h-4 text-rose-400" /> | |
91 | + <h3 className="text-sm uppercase tracking-widest text-rose-400/80"> | |
92 | + Active Collaborations | |
93 | + </h3> | |
94 | + </div> | |
95 | + <div className="text-5xl font-light font-mono"> | |
96 | + <span className="bg-gradient-to-r from-rose-200 via-pink-300 to-rose-400 bg-clip-text text-transparent"> | |
97 | + <AnimatedNumber value={stats.collaborations} /> | |
98 | + </span> | |
99 | + </div> | |
100 | + <div className="flex items-center gap-1.5 mt-2"> | |
101 | + <Sparkles className="w-3 h-3 text-rose-400/60" /> | |
102 | + <span className="text-xs text-rose-400/60">Pull Requests</span> | |
103 | + </div> | |
104 | + </div> | |
105 | + </motion.div> | |
106 | + </div> | |
107 | + ); | |
108 | +} | |
0 | 109 | |
▼src/components/admin/GlobeViz.tsx
+256-0
| @@ -0,0 +1,256 @@ | ||
1 | +"use client"; | |
2 | +import { useEffect, useRef, useState, Component, ReactNode } from "react"; | |
3 | +import createGlobe from "cobe"; | |
4 | + | |
5 | +// Error boundary for WebGL issues | |
6 | +class GlobeErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> { | |
7 | + constructor(props: { children: ReactNode }) { | |
8 | + super(props); | |
9 | + this.state = { hasError: false }; | |
10 | + } | |
11 | + | |
12 | + static getDerivedStateFromError() { | |
13 | + return { hasError: true }; | |
14 | + } | |
15 | + | |
16 | + componentDidCatch(error: Error) { | |
17 | + console.warn("GitHub Globe error:", error.message); | |
18 | + } | |
19 | + | |
20 | + render() { | |
21 | + if (this.state.hasError) { | |
22 | + return ( | |
23 | + <div className="w-full h-full flex items-center justify-center"> | |
24 | + <div className="text-center text-white/50"> | |
25 | + <div className="text-6xl mb-4">🌐</div> | |
26 | + <p className="text-sm">Globe unavailable</p> | |
27 | + </div> | |
28 | + </div> | |
29 | + ); | |
30 | + } | |
31 | + return this.props.children; | |
32 | + } | |
33 | +} | |
34 | + | |
35 | +// Check WebGL support | |
36 | +function isWebGLSupported(): boolean { | |
37 | + try { | |
38 | + const canvas = document.createElement("canvas"); | |
39 | + const gl = canvas.getContext("webgl") || canvas.getContext("experimental-webgl"); | |
40 | + return !!gl; | |
41 | + } catch { | |
42 | + return false; | |
43 | + } | |
44 | +} | |
45 | + | |
46 | +interface Activity { | |
47 | + user: string; | |
48 | + repo: string; | |
49 | + type: string; | |
50 | +} | |
51 | + | |
52 | +interface Props { | |
53 | + activities?: Activity[]; | |
54 | +} | |
55 | + | |
56 | +function GlobeContent({ activities = [] }: Props) { | |
57 | + const canvasRef = useRef<HTMLCanvasElement>(null); | |
58 | + const pointerInteracting = useRef<number | null>(null); | |
59 | + const pointerInteractionMovement = useRef(0); | |
60 | + const [rotation, setRotation] = useState(0); | |
61 | + | |
62 | + useEffect(() => { | |
63 | + if (!canvasRef.current) return; | |
64 | + | |
65 | + let phi = 0; | |
66 | + let width = 0; | |
67 | + | |
68 | + const onResize = () => { | |
69 | + if (canvasRef.current) { | |
70 | + width = canvasRef.current.offsetWidth; | |
71 | + } | |
72 | + }; | |
73 | + window.addEventListener("resize", onResize); | |
74 | + onResize(); | |
75 | + | |
76 | + const globe = createGlobe(canvasRef.current, { | |
77 | + devicePixelRatio: 2, | |
78 | + width: width * 2, | |
79 | + height: width * 2, | |
80 | + phi: 0, | |
81 | + theta: 0.3, | |
82 | + dark: 1, | |
83 | + diffuse: 1.2, | |
84 | + mapSamples: 16000, | |
85 | + mapBrightness: 6, | |
86 | + baseColor: [0.3, 0.3, 0.3], | |
87 | + markerColor: [0.1, 0.8, 1], | |
88 | + glowColor: [0.1, 0.1, 0.3], | |
89 | + markers: activities.slice(0, 20).map((_, i) => ({ | |
90 | + location: [ | |
91 | + (Math.random() - 0.5) * 180, | |
92 | + (Math.random() - 0.5) * 360 | |
93 | + ], | |
94 | + size: 0.03 + Math.random() * 0.05, | |
95 | + })), | |
96 | + onRender: (state) => { | |
97 | + if (!pointerInteracting.current) { | |
98 | + phi += 0.005; | |
99 | + } | |
100 | + state.phi = phi + rotation; | |
101 | + state.width = width * 2; | |
102 | + state.height = width * 2; | |
103 | + }, | |
104 | + }); | |
105 | + | |
106 | + setTimeout(() => { | |
107 | + if (canvasRef.current) { | |
108 | + canvasRef.current.style.opacity = "1"; | |
109 | + } | |
110 | + }, 100); | |
111 | + | |
112 | + return () => { | |
113 | + globe.destroy(); | |
114 | + window.removeEventListener("resize", onResize); | |
115 | + }; | |
116 | + }, [activities, rotation]); | |
117 | + | |
118 | + return ( | |
119 | + <div className="w-full h-full flex items-center justify-center cursor-move"> | |
120 | + <canvas | |
121 | + ref={canvasRef} | |
122 | + onPointerDown={(e) => { | |
123 | + pointerInteracting.current = e.clientX - pointerInteractionMovement.current; | |
124 | + if (canvasRef.current) { | |
125 | + canvasRef.current.style.cursor = "grabbing"; | |
126 | + } | |
127 | + }} | |
128 | + onPointerUp={() => { | |
129 | + pointerInteracting.current = null; | |
130 | + if (canvasRef.current) { | |
131 | + canvasRef.current.style.cursor = "grab"; | |
132 | + } | |
133 | + }} | |
134 | + onPointerOut={() => { | |
135 | + pointerInteracting.current = null; | |
136 | + if (canvasRef.current) { | |
137 | + canvasRef.current.style.cursor = "grab"; | |
138 | + } | |
139 | + }} | |
140 | + onMouseMove={(e) => { | |
141 | + if (pointerInteracting.current !== null) { | |
142 | + const delta = e.clientX - pointerInteracting.current; | |
143 | + pointerInteractionMovement.current = delta; | |
144 | + setRotation(delta / 200); | |
145 | + } | |
146 | + }} | |
147 | + onTouchMove={(e) => { | |
148 | + if (pointerInteracting.current !== null && e.touches[0]) { | |
149 | + const delta = e.touches[0].clientX - pointerInteracting.current; | |
150 | + pointerInteractionMovement.current = delta; | |
151 | + setRotation(delta / 100); | |
152 | + } | |
153 | + }} | |
154 | + style={{ | |
155 | + width: "100%", | |
156 | + height: "100%", | |
157 | + maxWidth: "600px", | |
158 | + aspectRatio: "1", | |
159 | + opacity: 0, | |
160 | + transition: "opacity 1s ease", | |
161 | + cursor: "grab", | |
162 | + }} | |
163 | + /> | |
164 | + </div> | |
165 | + ); | |
166 | +} | |
167 | + | |
168 | +export default function GlobeViz({ activities = [] }: Props) { | |
169 | + const [webglSupported, setWebglSupported] = useState<boolean | null>(null); | |
170 | + const [mounted, setMounted] = useState(false); | |
171 | + | |
172 | + useEffect(() => { | |
173 | + setMounted(true); | |
174 | + setWebglSupported(isWebGLSupported()); | |
175 | + }, []); | |
176 | + | |
177 | + // Still loading | |
178 | + if (!mounted || webglSupported === null) { | |
179 | + return ( | |
180 | + <div className="w-full h-full flex items-center justify-center"> | |
181 | + <div className="text-white/30 text-sm">Loading globe...</div> | |
182 | + </div> | |
183 | + ); | |
184 | + } | |
185 | + | |
186 | + // No WebGL support - show CSS fallback | |
187 | + if (!webglSupported) { | |
188 | + return <CSSGlobeFallback activities={activities} />; | |
189 | + } | |
190 | + | |
191 | + return ( | |
192 | + <GlobeErrorBoundary> | |
193 | + <GlobeContent activities={activities} /> | |
194 | + </GlobeErrorBoundary> | |
195 | + ); | |
196 | +} | |
197 | + | |
198 | +// CSS-based fallback for systems without WebGL | |
199 | +function CSSGlobeFallback({ activities = [] }: Props) { | |
200 | + const [points, setPoints] = useState<Array<{ x: number; y: number; delay: number }>>([]); | |
201 | + | |
202 | + useEffect(() => { | |
203 | + const newPoints = Array.from({ length: 30 }).map(() => ({ | |
204 | + x: Math.random() * 100, | |
205 | + y: Math.random() * 100, | |
206 | + delay: Math.random() * 3 | |
207 | + })); | |
208 | + setPoints(newPoints); | |
209 | + }, []); | |
210 | + | |
211 | + return ( | |
212 | + <div className="w-full h-full flex items-center justify-center overflow-hidden"> | |
213 | + <div className="relative w-[500px] h-[500px]"> | |
214 | + {/* Outer glow */} | |
215 | + <div className="absolute inset-0 rounded-full bg-gradient-to-r from-blue-500/20 via-cyan-500/20 to-blue-500/20 blur-3xl animate-pulse" /> | |
216 | + | |
217 | + {/* Globe sphere */} | |
218 | + <div className="absolute inset-8 rounded-full bg-gradient-to-br from-slate-900 via-blue-900/30 to-slate-900 border border-cyan-500/20 shadow-2xl overflow-hidden"> | |
219 | + {/* Grid lines */} | |
220 | + <div className="absolute inset-0 opacity-20"> | |
221 | + {[...Array(8)].map((_, i) => ( | |
222 | + <div | |
223 | + key={`h-${i}`} | |
224 | + className="absolute left-0 right-0 border-t border-cyan-400/30" | |
225 | + style={{ top: `${(i + 1) * 12}%` }} | |
226 | + /> | |
227 | + ))} | |
228 | + </div> | |
229 | + | |
230 | + {/* Rotating highlight */} | |
231 | + <div | |
232 | + className="absolute inset-0 bg-gradient-to-r from-transparent via-cyan-400/10 to-transparent animate-spin" | |
233 | + style={{ animationDuration: '20s' }} | |
234 | + /> | |
235 | + | |
236 | + {/* Activity points */} | |
237 | + {points.map((point, i) => ( | |
238 | + <div | |
239 | + key={i} | |
240 | + className="absolute w-2 h-2 rounded-full bg-cyan-400 animate-ping" | |
241 | + style={{ | |
242 | + left: `${point.x}%`, | |
243 | + top: `${point.y}%`, | |
244 | + animationDelay: `${point.delay}s`, | |
245 | + animationDuration: '2s' | |
246 | + }} | |
247 | + /> | |
248 | + ))} | |
249 | + </div> | |
250 | + | |
251 | + {/* Atmosphere */} | |
252 | + <div className="absolute inset-4 rounded-full border-2 border-cyan-400/20 animate-pulse" /> | |
253 | + </div> | |
254 | + </div> | |
255 | + ); | |
256 | +} | |
0 | 257 | |
▼src/components/admin/LiveLogStream.tsx
+143-0
| @@ -0,0 +1,143 @@ | ||
1 | +import { motion, AnimatePresence } from "framer-motion"; | |
2 | +import { Radio, Clock, GitCommit, GitPullRequest, GitFork, Star, MessageSquare } from "lucide-react"; | |
3 | +import { useEffect, useState } from "react"; | |
4 | + | |
5 | +interface LogEntry { | |
6 | + id: string; | |
7 | + timestamp: string; | |
8 | + type: string; | |
9 | + message: string; | |
10 | + user: string; | |
11 | + repo: string; | |
12 | +} | |
13 | + | |
14 | +interface Props { | |
15 | + logs: LogEntry[]; | |
16 | +} | |
17 | + | |
18 | +const getTypeIcon = (type: string) => { | |
19 | + const iconClass = "w-3 h-3"; | |
20 | + switch (type.toLowerCase()) { | |
21 | + case "push": return <GitCommit className={`${iconClass} text-green-400`} />; | |
22 | + case "pr_open": return <GitPullRequest className={`${iconClass} text-blue-400`} />; | |
23 | + case "fork": return <GitFork className={`${iconClass} text-purple-400`} />; | |
24 | + case "star": return <Star className={`${iconClass} text-yellow-400`} />; | |
25 | + case "comment": return <MessageSquare className={`${iconClass} text-cyan-400`} />; | |
26 | + default: return <Radio className={`${iconClass} text-gray-400`} />; | |
27 | + } | |
28 | +}; | |
29 | + | |
30 | +const getTypeColor = (type: string) => { | |
31 | + switch (type.toLowerCase()) { | |
32 | + case "push": return "text-green-400"; | |
33 | + case "pr_open": return "text-blue-400"; | |
34 | + case "fork": return "text-purple-400"; | |
35 | + case "star": return "text-yellow-400"; | |
36 | + case "comment": return "text-cyan-400"; | |
37 | + default: return "text-gray-400"; | |
38 | + } | |
39 | +}; | |
40 | + | |
41 | +const getTypeBg = (type: string) => { | |
42 | + switch (type.toLowerCase()) { | |
43 | + case "push": return "from-green-500/10 to-transparent"; | |
44 | + case "pr_open": return "from-blue-500/10 to-transparent"; | |
45 | + case "fork": return "from-purple-500/10 to-transparent"; | |
46 | + case "star": return "from-yellow-500/10 to-transparent"; | |
47 | + case "comment": return "from-cyan-500/10 to-transparent"; | |
48 | + default: return "from-gray-500/10 to-transparent"; | |
49 | + } | |
50 | +}; | |
51 | + | |
52 | +export default function LiveLogStream({ logs }: Props) { | |
53 | + const [currentTime, setCurrentTime] = useState(new Date().toLocaleTimeString()); | |
54 | + | |
55 | + useEffect(() => { | |
56 | + const interval = setInterval(() => { | |
57 | + setCurrentTime(new Date().toLocaleTimeString()); | |
58 | + }, 1000); | |
59 | + return () => clearInterval(interval); | |
60 | + }, []); | |
61 | + | |
62 | + return ( | |
63 | + <div className="p-6 h-full flex flex-col"> | |
64 | + {/* Header */} | |
65 | + <div className="flex justify-between items-center mb-4 pb-3 border-b border-white/5"> | |
66 | + <div className="flex items-center gap-2"> | |
67 | + <motion.div | |
68 | + animate={{ scale: [1, 1.2, 1] }} | |
69 | + transition={{ duration: 2, repeat: Infinity }} | |
70 | + > | |
71 | + <Radio className="w-4 h-4 text-red-400" /> | |
72 | + </motion.div> | |
73 | + <div> | |
74 | + <h3 className="text-sm font-semibold bg-gradient-to-r from-gray-100 to-gray-400 bg-clip-text text-transparent"> | |
75 | + Data Stream | |
76 | + </h3> | |
77 | + <div className="text-[10px] text-gray-500 uppercase tracking-wider">Real-Time</div> | |
78 | + </div> | |
79 | + </div> | |
80 | + <div className="flex items-center gap-2 text-xs text-gray-500 font-mono"> | |
81 | + <Clock className="w-3 h-3" /> | |
82 | + {currentTime} | |
83 | + </div> | |
84 | + </div> | |
85 | + | |
86 | + {/* Log entries */} | |
87 | + <div className="flex-1 overflow-y-auto custom-scrollbar space-y-2"> | |
88 | + <AnimatePresence initial={false}> | |
89 | + {logs.map((log, index) => ( | |
90 | + <motion.div | |
91 | + key={log.id} | |
92 | + initial={{ opacity: 0, height: 0, x: 20 }} | |
93 | + animate={{ opacity: 1, height: "auto", x: 0 }} | |
94 | + exit={{ opacity: 0, height: 0, x: -20 }} | |
95 | + transition={{ type: "spring", stiffness: 200, damping: 20 }} | |
96 | + className={`relative overflow-hidden rounded-lg bg-gradient-to-r ${getTypeBg(log.type)} p-2`} | |
97 | + > | |
98 | + {/* Timestamp */} | |
99 | + <div className="text-[10px] text-gray-600 font-mono mb-1"> | |
100 | + {log.timestamp} | |
101 | + </div> | |
102 | + | |
103 | + {/* Content */} | |
104 | + <div className="flex items-start gap-2"> | |
105 | + {/* Icon */} | |
106 | + <div className="mt-0.5"> | |
107 | + {getTypeIcon(log.type)} | |
108 | + </div> | |
109 | + | |
110 | + {/* Details */} | |
111 | + <div className="flex-1 min-w-0 text-xs"> | |
112 | + <div className="flex flex-wrap gap-x-1.5 items-center"> | |
113 | + <span className="text-gray-600 font-mono">#{log.id.substring(0, 7)}</span> | |
114 | + <span className="text-gray-600">:</span> | |
115 | + <span className={`font-semibold ${getTypeColor(log.type)}`}> | |
116 | + {log.type.toUpperCase().replace("_", " ")} | |
117 | + </span> | |
118 | + </div> | |
119 | + <div className="text-gray-400 truncate mt-0.5"> | |
120 | + {log.message} | |
121 | + </div> | |
122 | + </div> | |
123 | + </div> | |
124 | + | |
125 | + {/* Animated line for new entries */} | |
126 | + {index === 0 && ( | |
127 | + <motion.div | |
128 | + className={`absolute left-0 top-0 bottom-0 w-0.5 bg-gradient-to-b ${getTypeColor(log.type).replace("text", "from")} to-transparent`} | |
129 | + initial={{ opacity: 0 }} | |
130 | + animate={{ opacity: [0, 1, 0] }} | |
131 | + transition={{ duration: 2 }} | |
132 | + /> | |
133 | + )} | |
134 | + </motion.div> | |
135 | + ))} | |
136 | + </AnimatePresence> | |
137 | + </div> | |
138 | + | |
139 | + {/* Fade effect at bottom */} | |
140 | + <div className="absolute bottom-0 left-0 right-0 h-12 bg-gradient-to-t from-[#0a0a15] to-transparent pointer-events-none" /> | |
141 | + </div> | |
142 | + ); | |
143 | +} | |
0 | 144 | |
▼src/components/admin/QuickStatsRow.tsx
+130-0
| @@ -0,0 +1,130 @@ | ||
1 | +import { motion } from "framer-motion"; | |
2 | +import { GitCommit, GitMerge, CircleDot, Users, Clock, TrendingUp, Activity, Zap } from "lucide-react"; | |
3 | +import { useEffect, useState } from "react"; | |
4 | + | |
5 | +interface Props { | |
6 | + stats: { | |
7 | + commitsToday?: number; | |
8 | + prsMerged?: number; | |
9 | + issuesClosed?: number; | |
10 | + activeUsers?: number; | |
11 | + }; | |
12 | +} | |
13 | + | |
14 | +function AnimatedNumber({ value }: { value: number }) { | |
15 | + const [display, setDisplay] = useState(0); | |
16 | + | |
17 | + useEffect(() => { | |
18 | + const duration = 1000; | |
19 | + const start = Date.now(); | |
20 | + const startVal = display; | |
21 | + | |
22 | + const tick = () => { | |
23 | + const elapsed = Date.now() - start; | |
24 | + const progress = Math.min(elapsed / duration, 1); | |
25 | + const eased = 1 - Math.pow(1 - progress, 3); | |
26 | + setDisplay(Math.floor(startVal + (value - startVal) * eased)); | |
27 | + if (progress < 1) requestAnimationFrame(tick); | |
28 | + }; | |
29 | + | |
30 | + requestAnimationFrame(tick); | |
31 | + }, [value]); | |
32 | + | |
33 | + return <>{display.toLocaleString()}</>; | |
34 | +} | |
35 | + | |
36 | +const widgets = [ | |
37 | + { | |
38 | + icon: GitCommit, | |
39 | + label: "Commits Today", | |
40 | + key: "commitsToday", | |
41 | + color: "from-green-400 to-emerald-500", | |
42 | + iconColor: "text-green-400", | |
43 | + bgGlow: "bg-green-500/20" | |
44 | + }, | |
45 | + { | |
46 | + icon: GitMerge, | |
47 | + label: "PRs Merged", | |
48 | + key: "prsMerged", | |
49 | + color: "from-purple-400 to-violet-500", | |
50 | + iconColor: "text-purple-400", | |
51 | + bgGlow: "bg-purple-500/20" | |
52 | + }, | |
53 | + { | |
54 | + icon: CircleDot, | |
55 | + label: "Issues Closed", | |
56 | + key: "issuesClosed", | |
57 | + color: "from-orange-400 to-amber-500", | |
58 | + iconColor: "text-orange-400", | |
59 | + bgGlow: "bg-orange-500/20" | |
60 | + }, | |
61 | + { | |
62 | + icon: Users, | |
63 | + label: "Active Users", | |
64 | + key: "activeUsers", | |
65 | + color: "from-cyan-400 to-blue-500", | |
66 | + iconColor: "text-cyan-400", | |
67 | + bgGlow: "bg-cyan-500/20" | |
68 | + } | |
69 | +]; | |
70 | + | |
71 | +export default function QuickStatsRow({ stats }: Props) { | |
72 | + // Use real data from API only, default to 0 | |
73 | + const data = { | |
74 | + commitsToday: stats.commitsToday ?? 0, | |
75 | + prsMerged: stats.prsMerged ?? 0, | |
76 | + issuesClosed: stats.issuesClosed ?? 0, | |
77 | + activeUsers: stats.activeUsers ?? 0 | |
78 | + }; | |
79 | + | |
80 | + return ( | |
81 | + <div className="flex items-center justify-center gap-4"> | |
82 | + {widgets.map((widget, i) => { | |
83 | + const Icon = widget.icon; | |
84 | + const value = data[widget.key as keyof typeof data]; | |
85 | + | |
86 | + return ( | |
87 | + <motion.div | |
88 | + key={widget.key} | |
89 | + initial={{ opacity: 0, y: -20 }} | |
90 | + animate={{ opacity: 1, y: 0 }} | |
91 | + transition={{ delay: i * 0.1, type: "spring", stiffness: 100 }} | |
92 | + className="relative group" | |
93 | + > | |
94 | + {/* Glow effect */} | |
95 | + <div className={`absolute -inset-1 ${widget.bgGlow} rounded-xl blur-lg opacity-0 group-hover:opacity-100 transition-opacity duration-300`} /> | |
96 | + | |
97 | + <div className="relative glass-panel rounded-xl px-4 py-3 flex items-center gap-3 hover:border-white/10 transition-colors cursor-default"> | |
98 | + {/* Icon with gradient background */} | |
99 | + <div className={`w-10 h-10 rounded-lg bg-gradient-to-br ${widget.color} p-[1px]`}> | |
100 | + <div className="w-full h-full rounded-lg bg-[#0a0a15] flex items-center justify-center"> | |
101 | + <Icon className={`w-5 h-5 ${widget.iconColor}`} /> | |
102 | + </div> | |
103 | + </div> | |
104 | + | |
105 | + {/* Stats */} | |
106 | + <div> | |
107 | + <div className={`text-xl font-bold font-mono bg-gradient-to-r ${widget.color} bg-clip-text text-transparent`}> | |
108 | + <AnimatedNumber value={value} /> | |
109 | + </div> | |
110 | + <div className="text-[10px] uppercase tracking-wider text-gray-500"> | |
111 | + {widget.label} | |
112 | + </div> | |
113 | + </div> | |
114 | + | |
115 | + {/* Trend indicator */} | |
116 | + <div className="ml-2"> | |
117 | + <motion.div | |
118 | + animate={{ y: [0, -2, 0] }} | |
119 | + transition={{ duration: 2, repeat: Infinity }} | |
120 | + > | |
121 | + <TrendingUp className={`w-3 h-3 ${widget.iconColor} opacity-60`} /> | |
122 | + </motion.div> | |
123 | + </div> | |
124 | + </div> | |
125 | + </motion.div> | |
126 | + ); | |
127 | + })} | |
128 | + </div> | |
129 | + ); | |
130 | +} | |
0 | 131 | |
▼src/components/admin/RecentActivity.tsx
+91-0
| @@ -0,0 +1,91 @@ | ||
1 | +import { motion } from "framer-motion"; | |
2 | +import { GitCommit, GitPullRequest, GitFork, Star, User, Clock } from "lucide-react"; | |
3 | + | |
4 | +interface Activity { | |
5 | + id: string; | |
6 | + type: "commit" | "pr" | "fork" | "star" | "user"; | |
7 | + user: string; | |
8 | + action: string; | |
9 | + target: string; | |
10 | + time: string; | |
11 | +} | |
12 | + | |
13 | +interface Props { | |
14 | + activities?: Activity[]; | |
15 | +} | |
16 | + | |
17 | +const activityIcons = { | |
18 | + commit: { icon: GitCommit, color: "text-green-400", bg: "bg-green-500/10" }, | |
19 | + pr: { icon: GitPullRequest, color: "text-blue-400", bg: "bg-blue-500/10" }, | |
20 | + fork: { icon: GitFork, color: "text-purple-400", bg: "bg-purple-500/10" }, | |
21 | + star: { icon: Star, color: "text-yellow-400", bg: "bg-yellow-500/10" }, | |
22 | + user: { icon: User, color: "text-cyan-400", bg: "bg-cyan-500/10" } | |
23 | +}; | |
24 | + | |
25 | +// Use real activities from API only | |
26 | +export default function RecentActivity({ activities = [] }: Props) { | |
27 | + if (activities.length === 0) { | |
28 | + return ( | |
29 | + <div className="glass-panel rounded-xl p-4 w-72"> | |
30 | + <div className="flex items-center gap-2 mb-4"> | |
31 | + <Clock className="w-4 h-4 text-cyan-400" /> | |
32 | + <h3 className="text-sm font-semibold text-white">Recent Activity</h3> | |
33 | + </div> | |
34 | + <div className="text-center py-4 text-gray-500 text-sm"> | |
35 | + No recent activity | |
36 | + </div> | |
37 | + </div> | |
38 | + ); | |
39 | + } | |
40 | + return ( | |
41 | + <div className="glass-panel rounded-xl p-4 w-72"> | |
42 | + <div className="flex items-center gap-2 mb-4"> | |
43 | + <motion.div | |
44 | + animate={{ rotate: 360 }} | |
45 | + transition={{ duration: 10, repeat: Infinity, ease: "linear" }} | |
46 | + > | |
47 | + <Clock className="w-4 h-4 text-cyan-400" /> | |
48 | + </motion.div> | |
49 | + <h3 className="text-sm font-semibold text-white">Recent Activity</h3> | |
50 | + </div> | |
51 | + | |
52 | + <div className="space-y-3"> | |
53 | + {activities.slice(0, 4).map((activity, i) => { | |
54 | + const { icon: Icon, color, bg } = activityIcons[activity.type]; | |
55 | + | |
56 | + return ( | |
57 | + <motion.div | |
58 | + key={activity.id} | |
59 | + initial={{ opacity: 0, x: -10 }} | |
60 | + animate={{ opacity: 1, x: 0 }} | |
61 | + transition={{ delay: i * 0.1 }} | |
62 | + className="flex items-start gap-3 group" | |
63 | + > | |
64 | + {/* Icon */} | |
65 | + <div className={`w-7 h-7 rounded-lg ${bg} flex items-center justify-center shrink-0`}> | |
66 | + <Icon className={`w-3.5 h-3.5 ${color}`} /> | |
67 | + </div> | |
68 | + | |
69 | + {/* Content */} | |
70 | + <div className="flex-1 min-w-0"> | |
71 | + <div className="text-xs text-gray-300 truncate"> | |
72 | + <span className="font-medium text-white">{activity.user}</span> | |
73 | + <span className="text-gray-500"> {activity.action} </span> | |
74 | + <span className="text-cyan-400">{activity.target}</span> | |
75 | + </div> | |
76 | + <div className="text-[10px] text-gray-600 mt-0.5"> | |
77 | + {activity.time} | |
78 | + </div> | |
79 | + </div> | |
80 | + | |
81 | + {/* Line connector */} | |
82 | + {i < activities.length - 1 && ( | |
83 | + <div className="absolute left-[13px] top-8 w-[1px] h-4 bg-gradient-to-b from-gray-700 to-transparent" /> | |
84 | + )} | |
85 | + </motion.div> | |
86 | + ); | |
87 | + })} | |
88 | + </div> | |
89 | + </div> | |
90 | + ); | |
91 | +} | |
0 | 92 | |
▼src/components/admin/SystemStatus.tsx
+176-0
| @@ -0,0 +1,176 @@ | ||
1 | +import { HardDrive, Cpu, Zap, Activity, Wifi, Server } from "lucide-react"; | |
2 | +import { motion } from "framer-motion"; | |
3 | + | |
4 | +interface SystemStatusProps { | |
5 | + status?: { | |
6 | + cpuLoad: number; | |
7 | + memoryUsage: number; | |
8 | + memoryTotal: number; | |
9 | + storageUsage: number; | |
10 | + activeRunners: number; | |
11 | + uptime: string; | |
12 | + }; | |
13 | +} | |
14 | + | |
15 | +// Glowing ring gauge component | |
16 | +function GlowingGauge({ | |
17 | + value, | |
18 | + color, | |
19 | + icon: Icon, | |
20 | + label, | |
21 | + gradientColors | |
22 | +}: { | |
23 | + value: number; | |
24 | + color: string; | |
25 | + icon: typeof Cpu; | |
26 | + label: string; | |
27 | + gradientColors: [string, string]; | |
28 | +}) { | |
29 | + const radius = 40; | |
30 | + const circumference = 2 * Math.PI * radius; | |
31 | + const strokeDashoffset = circumference - (circumference * value) / 100; | |
32 | + | |
33 | + return ( | |
34 | + <motion.div | |
35 | + className="relative w-28 h-28 flex items-center justify-center group" | |
36 | + initial={{ scale: 0.8, opacity: 0 }} | |
37 | + animate={{ scale: 1, opacity: 1 }} | |
38 | + transition={{ duration: 0.5, type: "spring" }} | |
39 | + > | |
40 | + {/* Outer glow */} | |
41 | + <div | |
42 | + className="absolute inset-0 rounded-full blur-xl opacity-40 group-hover:opacity-60 transition-opacity" | |
43 | + style={{ background: `radial-gradient(circle, ${gradientColors[0]}40, transparent 70%)` }} | |
44 | + /> | |
45 | + | |
46 | + <svg className="w-full h-full transform -rotate-90"> | |
47 | + {/* Background circle */} | |
48 | + <circle | |
49 | + cx="56" cy="56" r={radius} | |
50 | + stroke="rgba(255,255,255,0.05)" | |
51 | + strokeWidth="8" | |
52 | + fill="transparent" | |
53 | + /> | |
54 | + {/* Animated progress circle */} | |
55 | + <motion.circle | |
56 | + cx="56" cy="56" r={radius} | |
57 | + stroke={`url(#gradient-${label})`} | |
58 | + strokeWidth="8" | |
59 | + fill="transparent" | |
60 | + strokeLinecap="round" | |
61 | + initial={{ strokeDasharray: circumference, strokeDashoffset: circumference }} | |
62 | + animate={{ strokeDashoffset }} | |
63 | + transition={{ duration: 1.5, ease: "easeOut" }} | |
64 | + /> | |
65 | + {/* Gradient definition */} | |
66 | + <defs> | |
67 | + <linearGradient id={`gradient-${label}`} x1="0%" y1="0%" x2="100%" y2="100%"> | |
68 | + <stop offset="0%" stopColor={gradientColors[0]} /> | |
69 | + <stop offset="100%" stopColor={gradientColors[1]} /> | |
70 | + </linearGradient> | |
71 | + </defs> | |
72 | + </svg> | |
73 | + | |
74 | + {/* Center content */} | |
75 | + <div className="absolute inset-0 flex flex-col items-center justify-center"> | |
76 | + <Icon className={`w-4 h-4 mb-1`} style={{ color: gradientColors[0] }} /> | |
77 | + <motion.span | |
78 | + className="text-2xl font-bold font-mono text-white" | |
79 | + initial={{ opacity: 0 }} | |
80 | + animate={{ opacity: 1 }} | |
81 | + transition={{ delay: 0.5 }} | |
82 | + > | |
83 | + {value}% | |
84 | + </motion.span> | |
85 | + <span className="text-[10px] text-gray-400 uppercase tracking-wider mt-0.5">{label}</span> | |
86 | + </div> | |
87 | + </motion.div> | |
88 | + ); | |
89 | +} | |
90 | + | |
91 | +export default function SystemStatus({ status }: SystemStatusProps) { | |
92 | + if (!status) return null; | |
93 | + | |
94 | + return ( | |
95 | + <div className="flex flex-col items-center gap-6 w-full"> | |
96 | + {/* Main HUD Gauge Cluster */} | |
97 | + <div className="flex items-center gap-10"> | |
98 | + <GlowingGauge | |
99 | + value={status.cpuLoad} | |
100 | + color="#3b82f6" | |
101 | + icon={Cpu} | |
102 | + label="CPU" | |
103 | + gradientColors={status.cpuLoad > 80 ? ["#ef4444", "#f97316"] : ["#3b82f6", "#06b6d4"]} | |
104 | + /> | |
105 | + <GlowingGauge | |
106 | + value={status.memoryUsage} | |
107 | + color="#8b5cf6" | |
108 | + icon={Zap} | |
109 | + label="RAM" | |
110 | + gradientColors={status.memoryUsage > 80 ? ["#eab308", "#f97316"] : ["#8b5cf6", "#ec4899"]} | |
111 | + /> | |
112 | + </div> | |
113 | + | |
114 | + {/* Sub-metrics panel with glowing border */} | |
115 | + <motion.div | |
116 | + className="relative w-full max-w-md" | |
117 | + initial={{ opacity: 0, y: 20 }} | |
118 | + animate={{ opacity: 1, y: 0 }} | |
119 | + transition={{ delay: 0.3, duration: 0.5 }} | |
120 | + > | |
121 | + {/* Glowing border effect */} | |
122 | + <div className="absolute -inset-[1px] bg-gradient-to-r from-cyan-500/30 via-purple-500/30 to-pink-500/30 rounded-2xl blur-sm" /> | |
123 | + | |
124 | + <div className="relative glass-panel px-6 py-4 rounded-2xl flex items-center justify-between gap-6 text-xs"> | |
125 | + {/* Storage Linear Bar */} | |
126 | + <div className="flex-1"> | |
127 | + <div className="flex justify-between mb-1.5 text-gray-400"> | |
128 | + <span className="flex items-center gap-1.5"> | |
129 | + <HardDrive className="w-3 h-3 text-emerald-400" /> | |
130 | + <span className="text-emerald-400/80">Storage</span> | |
131 | + </span> | |
132 | + <span className="text-emerald-400 font-mono">{status.storageUsage}%</span> | |
133 | + </div> | |
134 | + <div className="h-2 bg-white/5 rounded-full overflow-hidden"> | |
135 | + <motion.div | |
136 | + className="h-full rounded-full bg-gradient-to-r from-emerald-500 to-cyan-500" | |
137 | + initial={{ width: 0 }} | |
138 | + animate={{ width: `${status.storageUsage}%` }} | |
139 | + transition={{ duration: 1, ease: "easeOut" }} | |
140 | + /> | |
141 | + </div> | |
142 | + </div> | |
143 | + | |
144 | + <div className="w-px h-10 bg-gradient-to-b from-transparent via-white/10 to-transparent" /> | |
145 | + | |
146 | + {/* Uptime */} | |
147 | + <div className="flex flex-col items-center"> | |
148 | + <span className="text-gray-500 uppercase tracking-widest text-[10px] mb-1">Uptime</span> | |
149 | + <div className="flex items-center gap-1.5"> | |
150 | + <Activity className="w-3 h-3 text-blue-400" /> | |
151 | + <span className="font-mono text-white font-medium">{status.uptime}</span> | |
152 | + </div> | |
153 | + </div> | |
154 | + | |
155 | + <div className="w-px h-10 bg-gradient-to-b from-transparent via-white/10 to-transparent" /> | |
156 | + | |
157 | + {/* Runners */} | |
158 | + <div className="flex flex-col items-center"> | |
159 | + <span className="text-gray-500 uppercase tracking-widest text-[10px] mb-1">Runners</span> | |
160 | + <div className="flex items-center gap-2"> | |
161 | + <motion.span | |
162 | + className="relative flex h-2.5 w-2.5" | |
163 | + animate={{ scale: [1, 1.2, 1] }} | |
164 | + transition={{ duration: 2, repeat: Infinity }} | |
165 | + > | |
166 | + <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" /> | |
167 | + <span className="relative inline-flex rounded-full h-2.5 w-2.5 bg-green-500" /> | |
168 | + </motion.span> | |
169 | + <span className="font-mono text-green-400 font-medium">{status.activeRunners} Active</span> | |
170 | + </div> | |
171 | + </div> | |
172 | + </div> | |
173 | + </motion.div> | |
174 | + </div> | |
175 | + ); | |
176 | +} | |
0 | 177 | |
▼src/components/admin/TrendingDevelopers.tsx
+103-0
| @@ -0,0 +1,103 @@ | ||
1 | +import { motion } from "framer-motion"; | |
2 | +import { TrendingUp, Flame, Star } from "lucide-react"; | |
3 | + | |
4 | +interface Developer { | |
5 | + rank: number; | |
6 | + name: string; | |
7 | + lang: string; | |
8 | + color: string; | |
9 | +} | |
10 | + | |
11 | +interface Props { | |
12 | + developers: Developer[]; | |
13 | +} | |
14 | + | |
15 | +export default function TrendingDevelopers({ developers }: Props) { | |
16 | + const getRankIcon = (rank: number) => { | |
17 | + if (rank === 1) return <Flame className="w-4 h-4 text-orange-400" />; | |
18 | + if (rank === 2) return <Star className="w-4 h-4 text-yellow-400" />; | |
19 | + if (rank === 3) return <TrendingUp className="w-4 h-4 text-green-400" />; | |
20 | + return <span className="text-xs font-mono text-gray-500 w-4 text-center">{rank}</span>; | |
21 | + }; | |
22 | + | |
23 | + const getGradient = (index: number) => { | |
24 | + const gradients = [ | |
25 | + "from-orange-500 via-amber-500 to-yellow-500", | |
26 | + "from-purple-500 via-violet-500 to-indigo-500", | |
27 | + "from-cyan-500 via-blue-500 to-indigo-500", | |
28 | + "from-green-500 via-emerald-500 to-teal-500", | |
29 | + "from-pink-500 via-rose-500 to-red-500", | |
30 | + ]; | |
31 | + return gradients[index % gradients.length]; | |
32 | + }; | |
33 | + | |
34 | + return ( | |
35 | + <div className="p-6"> | |
36 | + <motion.div | |
37 | + initial={{ opacity: 0 }} | |
38 | + animate={{ opacity: 1 }} | |
39 | + className="flex items-center gap-2 mb-6" | |
40 | + > | |
41 | + <Flame className="w-4 h-4 text-rose-400" /> | |
42 | + <h3 className="text-sm uppercase tracking-widest bg-gradient-to-r from-rose-400 to-orange-400 bg-clip-text text-transparent font-semibold"> | |
43 | + Trending Developers | |
44 | + </h3> | |
45 | + </motion.div> | |
46 | + | |
47 | + <div className="space-y-3"> | |
48 | + {developers.map((dev, i) => ( | |
49 | + <motion.div | |
50 | + key={dev.name} | |
51 | + initial={{ opacity: 0, x: -20 }} | |
52 | + animate={{ opacity: 1, x: 0 }} | |
53 | + transition={{ delay: 0.2 + i * 0.08, type: "spring", stiffness: 100 }} | |
54 | + className="relative group cursor-pointer" | |
55 | + > | |
56 | + {/* Hover glow effect */} | |
57 | + <div className={`absolute -inset-1 bg-gradient-to-r ${getGradient(i)} rounded-lg opacity-0 group-hover:opacity-20 blur-md transition-opacity duration-300`} /> | |
58 | + | |
59 | + <div className="relative flex items-center gap-3 p-2 rounded-lg hover:bg-white/5 transition-colors"> | |
60 | + {/* Rank */} | |
61 | + <div className="w-6 flex justify-center"> | |
62 | + {getRankIcon(dev.rank)} | |
63 | + </div> | |
64 | + | |
65 | + {/* Avatar with gradient border */} | |
66 | + <div className={`relative h-10 w-10 rounded-full p-[2px] bg-gradient-to-br ${getGradient(i)}`}> | |
67 | + <div className="h-full w-full rounded-full bg-[#0a0a15] flex items-center justify-center"> | |
68 | + <span className="text-sm font-bold text-white"> | |
69 | + {dev.name[0].toUpperCase()} | |
70 | + </span> | |
71 | + </div> | |
72 | + </div> | |
73 | + | |
74 | + {/* Info */} | |
75 | + <div className="flex-1 min-w-0"> | |
76 | + <div className="text-sm text-gray-200 font-medium group-hover:text-white transition-colors truncate"> | |
77 | + {dev.name} | |
78 | + </div> | |
79 | + <div className="flex items-center gap-2 mt-1"> | |
80 | + {/* Progress bar */} | |
81 | + <div className="h-1.5 w-12 bg-gray-800 rounded-full overflow-hidden"> | |
82 | + <motion.div | |
83 | + className={`h-full rounded-full bg-gradient-to-r ${getGradient(i)}`} | |
84 | + initial={{ width: 0 }} | |
85 | + animate={{ width: `${70 + Math.random() * 30}%` }} | |
86 | + transition={{ delay: 0.5 + i * 0.1, duration: 0.8 }} | |
87 | + /> | |
88 | + </div> | |
89 | + <span | |
90 | + className="text-[10px] font-medium" | |
91 | + style={{ color: dev.color }} | |
92 | + > | |
93 | + {dev.lang} | |
94 | + </span> | |
95 | + </div> | |
96 | + </div> | |
97 | + </div> | |
98 | + </motion.div> | |
99 | + ))} | |
100 | + </div> | |
101 | + </div> | |
102 | + ); | |
103 | +} | |
0 | 104 | |
▼src/components/dashboard/ActivityStream.tsx
+113-0
| @@ -0,0 +1,113 @@ | ||
1 | + | |
2 | +import * as React from "react"; | |
3 | +import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; | |
4 | +import { cn } from "@/lib/utils"; | |
5 | + | |
6 | +interface ActivityUser { | |
7 | + id: string; | |
8 | + username: string; | |
9 | + avatarUrl?: string; | |
10 | +} | |
11 | + | |
12 | +interface ActivityStreamProps { | |
13 | + activeCount: number; | |
14 | + users: ActivityUser[]; | |
15 | + className?: string; | |
16 | +} | |
17 | + | |
18 | +export function ActivityStream({ activeCount, users, className }: ActivityStreamProps) { | |
19 | + // Mock timeline dots for visual effect matching the image | |
20 | + // In a real app, these could represent specific timestamps of events | |
21 | + // Professional, minimal palette (monochromatic with accents) | |
22 | + const dots = [ | |
23 | + { color: "bg-indigo-500", left: "10%", delay: "0s" }, | |
24 | + { color: "bg-indigo-400", left: "30%", delay: "1s" }, | |
25 | + { color: "bg-violet-500", left: "50%", delay: "2s" }, | |
26 | + { color: "bg-indigo-300", left: "65%", delay: "1.5s" }, | |
27 | + { color: "bg-white", left: "85%", delay: "0.5s" }, | |
28 | + ]; | |
29 | + | |
30 | + return ( | |
31 | + <div className={cn("w-full relative overflow-hidden rounded-xl border border-white/10 bg-white/5 p-6 backdrop-blur-xl transition-all hover:bg-white/10 hover:border-white/20", className)}> | |
32 | + {/* Subtle background glow effect using CSS gradients */} | |
33 | + <div className="absolute -top-24 -right-24 w-48 h-48 bg-indigo-500/20 rounded-full blur-3xl pointer-events-none" /> | |
34 | + <div className="absolute -bottom-24 -left-24 w-48 h-48 bg-purple-500/10 rounded-full blur-3xl pointer-events-none" /> | |
35 | + | |
36 | + <div className="flex flex-col space-y-6 relative z-10"> | |
37 | + <div className="flex items-center justify-between"> | |
38 | + <h3 className="text-[10px] font-bold tracking-[0.2em] text-muted-foreground/70 uppercase"> | |
39 | + Live Code Review Activity | |
40 | + </h3> | |
41 | + <div className="flex items-center gap-2"> | |
42 | + <span className="relative flex h-2 w-2"> | |
43 | + <span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span> | |
44 | + <span className="relative inline-flex rounded-full h-2 w-2 bg-green-500"></span> | |
45 | + </span> | |
46 | + <span className="text-[10px] font-medium text-muted-foreground/70 uppercase tracking-wider">Real-time</span> | |
47 | + </div> | |
48 | + </div> | |
49 | + | |
50 | + <div className="flex items-center gap-12"> | |
51 | + {/* Left Stat - Clean & Big */} | |
52 | + <div className="flex flex-col"> | |
53 | + <span className="text-5xl font-bold tracking-tighter text-transparent bg-clip-text bg-gradient-to-br from-white to-white/50"> | |
54 | + {activeCount} | |
55 | + </span> | |
56 | + <span className="text-xs font-medium text-muted-foreground mt-1"> | |
57 | + Developers shipping code | |
58 | + </span> | |
59 | + </div> | |
60 | + | |
61 | + {/* Timeline Visualization - Minimal & Tech */} | |
62 | + <div className="flex-1 relative h-px bg-gradient-to-r from-transparent via-white/20 to-transparent flex items-center"> | |
63 | + {/* Moving light effect */} | |
64 | + <div className="absolute inset-0 bg-gradient-to-r from-transparent via-indigo-500/50 to-transparent w-1/2 blur-[1px] animate-[shimmer_3s_infinite_linear]" /> | |
65 | + | |
66 | + {/* Dots with pulse */} | |
67 | + {dots.map((dot, i) => ( | |
68 | + <div | |
69 | + key={i} | |
70 | + className={cn("absolute w-1.5 h-1.5 rounded-full shadow-[0_0_10px_rgba(99,102,241,0.5)] transition-all duration-1000", dot.color)} | |
71 | + style={{ left: dot.left }} | |
72 | + > | |
73 | + <div className={cn("absolute inset-0 rounded-full animate-ping opacity-20", dot.color)} style={{ animationDelay: dot.delay }} /> | |
74 | + </div> | |
75 | + ))} | |
76 | + </div> | |
77 | + | |
78 | + {/* User Avatars - Tighter & Cleaner */} | |
79 | + <div className="flex items-center -space-x-4 pl-4"> | |
80 | + {users.slice(0, 5).map((user, i) => ( | |
81 | + <div key={user.id} className="relative group transition-transform hover:-translate-y-1 hover:z-20"> | |
82 | + <Avatar className="w-12 h-12 border-2 border-[#09090b] ring-1 ring-white/10 transition-shadow group-hover:ring-indigo-500/50"> | |
83 | + <AvatarImage src={user.avatarUrl} alt={user.username} className="object-cover" /> | |
84 | + <AvatarFallback className="text-xs bg-zinc-900 font-medium text-zinc-400"> | |
85 | + {user.username.slice(0, 2).toUpperCase()} | |
86 | + </AvatarFallback> | |
87 | + </Avatar> | |
88 | + {/* Tooltip on hover */} | |
89 | + <div className="absolute -bottom-8 left-1/2 -translate-x-1/2 opacity-0 group-hover:opacity-100 transition-opacity bg-black/90 text-white text-[10px] px-2 py-1 rounded whitespace-nowrap pointer-events-none border border-white/10"> | |
90 | + @{user.username} | |
91 | + </div> | |
92 | + </div> | |
93 | + ))} | |
94 | + {users.length > 5 && ( | |
95 | + <div className="relative z-0 group"> | |
96 | + <div className="w-12 h-12 rounded-full bg-[#09090b] border-2 border-[#09090b] ring-1 ring-white/10 flex items-center justify-center text-xs font-medium text-zinc-400 group-hover:border-indigo-500/50 transition-colors"> | |
97 | + +{users.length - 5} | |
98 | + </div> | |
99 | + </div> | |
100 | + )} | |
101 | + </div> | |
102 | + </div> | |
103 | + </div> | |
104 | + | |
105 | + <style>{` | |
106 | + @keyframes shimmer { | |
107 | + 0% { transform: translateX(-100%); } | |
108 | + 100% { transform: translateX(200%); } | |
109 | + } | |
110 | + `}</style> | |
111 | + </div> | |
112 | + ); | |
113 | +} | |
0 | 114 | |
▼src/components/home/FeaturesSection.tsx
+145-0
| @@ -0,0 +1,145 @@ | ||
1 | +"use client"; | |
2 | +import { HoverEffect } from "@/components/ui/card-hover-effect"; | |
3 | +import { motion } from "framer-motion"; | |
4 | +import { | |
5 | + Code, | |
6 | + GitBranch, | |
7 | + Activity, | |
8 | + Users, | |
9 | + BookOpen, | |
10 | + Settings, | |
11 | + Layers, | |
12 | + Bot, | |
13 | + GitMerge, | |
14 | + BarChart3, | |
15 | + MessageSquare, | |
16 | + Terminal, | |
17 | +} from "lucide-react"; | |
18 | + | |
19 | +const graphiteFeatures = [ | |
20 | + { | |
21 | + title: "Stacked Pull Requests", | |
22 | + description: | |
23 | + "Break large changes into reviewable stacks. Each PR builds on the previous, enabling faster feedback cycles.", | |
24 | + icon: <Layers className="h-6 w-6 text-primary" />, | |
25 | + link: "/inbox", | |
26 | + }, | |
27 | + { | |
28 | + title: "AI Code Review", | |
29 | + description: | |
30 | + "Get instant feedback from AI reviewers. Catch bugs, suggest improvements, and enforce best practices automatically.", | |
31 | + icon: <Bot className="h-6 w-6 text-purple-400" />, | |
32 | + }, | |
33 | + { | |
34 | + title: "Smart Merge Queue", | |
35 | + description: | |
36 | + "Stack-aware merge queue with automatic rebasing. Never deal with merge conflicts again.", | |
37 | + icon: <GitMerge className="h-6 w-6 text-green-400" />, | |
38 | + }, | |
39 | + { | |
40 | + title: "Developer Metrics", | |
41 | + description: | |
42 | + "Track PR velocity, review efficiency, and team performance with actionable insights.", | |
43 | + icon: <BarChart3 className="h-6 w-6 text-orange-400" />, | |
44 | + }, | |
45 | + { | |
46 | + title: "Slack Notifications", | |
47 | + description: | |
48 | + "Get actionable PR notifications in Slack. Review, approve, and merge without leaving chat.", | |
49 | + icon: <MessageSquare className="h-6 w-6 text-blue-400" />, | |
50 | + }, | |
51 | + { | |
52 | + title: "och CLI", | |
53 | + description: | |
54 | + "Powerful CLI for managing stacks. Create, sync, and submit PRs from your terminal.", | |
55 | + icon: <Terminal className="h-6 w-6 text-cyan-400" />, | |
56 | + }, | |
57 | +]; | |
58 | + | |
59 | +const coreFeatures = [ | |
60 | + { | |
61 | + title: "Git Hosting", | |
62 | + description: | |
63 | + "Full Git support with SSH and HTTP protocols. Manage branches, tags, and commits with ease.", | |
64 | + icon: <Code className="h-6 w-6 text-primary" />, | |
65 | + }, | |
66 | + { | |
67 | + title: "Pull Requests", | |
68 | + description: | |
69 | + "Review code changes with inline comments, approvals, and merge strategies.", | |
70 | + icon: <GitBranch className="h-6 w-6 text-primary" />, | |
71 | + }, | |
72 | + { | |
73 | + title: "CI/CD Pipelines", | |
74 | + description: | |
75 | + "GitHub Actions compatible workflows. Build, test, and deploy automatically.", | |
76 | + icon: <Activity className="h-6 w-6 text-primary" />, | |
77 | + }, | |
78 | + { | |
79 | + title: "Team Collaboration", | |
80 | + description: | |
81 | + "Collaborators, teams, and fine-grained permissions for enterprise workflows.", | |
82 | + icon: <Users className="h-6 w-6 text-primary" />, | |
83 | + }, | |
84 | + { | |
85 | + title: "Documentation", | |
86 | + description: | |
87 | + "Built-in wiki, README rendering, and project documentation support.", | |
88 | + icon: <BookOpen className="h-6 w-6 text-primary" />, | |
89 | + }, | |
90 | + { | |
91 | + title: "Administration", | |
92 | + description: | |
93 | + "Advanced controls, audit logs, and monitoring for system administrators.", | |
94 | + icon: <Settings className="h-6 w-6 text-primary" />, | |
95 | + }, | |
96 | +]; | |
97 | + | |
98 | +export default function FeaturesSection() { | |
99 | + return ( | |
100 | + <div className="py-20 relative"> | |
101 | + {/* Graphite Features */} | |
102 | + <div className="max-w-7xl mx-auto px-4 mb-20"> | |
103 | + <motion.div | |
104 | + initial={{ opacity: 0, y: 20 }} | |
105 | + whileInView={{ opacity: 1, y: 0 }} | |
106 | + transition={{ duration: 0.5 }} | |
107 | + viewport={{ once: true }} | |
108 | + className="text-center mb-10" | |
109 | + > | |
110 | + <span className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-primary/20 bg-primary/5 text-sm text-primary mb-4"> | |
111 | + ✨ New Features | |
112 | + </span> | |
113 | + <h2 className="text-3xl md:text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-b from-neutral-50 to-neutral-400"> | |
114 | + Graphite-Inspired Developer Experience | |
115 | + </h2> | |
116 | + <p className="mt-4 text-muted-foreground max-w-2xl mx-auto"> | |
117 | + Stack your PRs, automate reviews, and ship faster with our modern code review workflow | |
118 | + </p> | |
119 | + </motion.div> | |
120 | + | |
121 | + <HoverEffect items={graphiteFeatures} /> | |
122 | + </div> | |
123 | + | |
124 | + {/* Core Features */} | |
125 | + <div className="max-w-7xl mx-auto px-4 border-t border-white/5 pt-20"> | |
126 | + <motion.div | |
127 | + initial={{ opacity: 0, y: 20 }} | |
128 | + whileInView={{ opacity: 1, y: 0 }} | |
129 | + transition={{ duration: 0.5 }} | |
130 | + viewport={{ once: true }} | |
131 | + className="text-center mb-10" | |
132 | + > | |
133 | + <h2 className="text-2xl md:text-3xl font-bold text-foreground"> | |
134 | + Everything you need to ship code | |
135 | + </h2> | |
136 | + <p className="mt-4 text-muted-foreground"> | |
137 | + A complete platform for version control, code review, and continuous integration | |
138 | + </p> | |
139 | + </motion.div> | |
140 | + | |
141 | + <HoverEffect items={coreFeatures} /> | |
142 | + </div> | |
143 | + </div> | |
144 | + ); | |
145 | +} | |
0 | 146 | |
▼src/components/home/HeroSection.tsx
+120-0
| @@ -0,0 +1,120 @@ | ||
1 | +"use client"; | |
2 | +import { FlipWords } from "@/components/ui/flip-words"; | |
3 | +import { Spotlight } from "@/components/ui/spotlight"; | |
4 | +import { ColourfulText } from "@/components/ui/colourful-text"; | |
5 | +import { motion } from "framer-motion"; | |
6 | +import { ArrowRight, Github, Star, GitBranch, Zap } from "lucide-react"; | |
7 | + | |
8 | +export default function HeroSection() { | |
9 | + const words = ["Teams", "Developers", "Enterprises", "Startups"]; | |
10 | + | |
11 | + return ( | |
12 | + <div className="h-[40rem] w-full rounded-md flex md:items-center md:justify-center bg-background/[0.96] antialiased bg-grid-white/[0.02] relative overflow-hidden"> | |
13 | + {/* Spotlight effect */} | |
14 | + <Spotlight | |
15 | + className="-top-40 left-0 md:left-60 md:-top-20" | |
16 | + fill="hsl(var(--primary))" | |
17 | + /> | |
18 | + | |
19 | + {/* Ambient glow effects */} | |
20 | + <div className="absolute top-0 left-1/4 w-96 h-96 bg-primary/10 rounded-full blur-[120px] pointer-events-none" /> | |
21 | + <div className="absolute bottom-0 right-1/4 w-80 h-80 bg-purple-500/10 rounded-full blur-[100px] pointer-events-none" /> | |
22 | + | |
23 | + <div className="p-4 max-w-7xl mx-auto relative z-10 w-full pt-20 md:pt-0"> | |
24 | + {/* Badge */} | |
25 | + <motion.div | |
26 | + initial={{ opacity: 0, y: 20 }} | |
27 | + animate={{ opacity: 1, y: 0 }} | |
28 | + transition={{ duration: 0.5 }} | |
29 | + className="flex justify-center mb-6" | |
30 | + > | |
31 | + <span className="inline-flex items-center gap-2 px-4 py-2 rounded-full border border-primary/20 bg-primary/5 text-sm text-primary"> | |
32 | + <Star className="h-4 w-4 fill-primary" /> | |
33 | + Open Source & Self-Hosted | |
34 | + </span> | |
35 | + </motion.div> | |
36 | + | |
37 | + {/* Main heading with animated blue gradient */} | |
38 | + <motion.h1 | |
39 | + initial={{ opacity: 0, y: 20 }} | |
40 | + animate={{ opacity: 1, y: 0 }} | |
41 | + transition={{ duration: 0.5, delay: 0.1 }} | |
42 | + className="text-4xl md:text-7xl font-bold text-center" | |
43 | + > | |
44 | + <span className="bg-clip-text text-transparent bg-gradient-to-b from-neutral-50 to-neutral-400"> | |
45 | + Self-hosted{" "} | |
46 | + </span> | |
47 | + <span className="bg-clip-text text-transparent bg-gradient-to-r from-blue-400 via-cyan-400 to-blue-600 bg-[length:200%_auto] animate-gradient-x"> | |
48 | + Git Platform | |
49 | + </span> | |
50 | + <br /> | |
51 | + <span className="text-3xl md:text-6xl bg-clip-text text-transparent bg-gradient-to-b from-neutral-50 to-neutral-400">for Modern{" "}</span> | |
52 | + <FlipWords words={words} className="text-3xl md:text-6xl" /> | |
53 | + </motion.h1> | |
54 | + | |
55 | + {/* Description */} | |
56 | + <motion.p | |
57 | + initial={{ opacity: 0, y: 20 }} | |
58 | + animate={{ opacity: 1, y: 0 }} | |
59 | + transition={{ duration: 0.5, delay: 0.2 }} | |
60 | + className="mt-6 font-normal text-base text-neutral-300 max-w-xl text-center mx-auto" | |
61 | + > | |
62 | + Host your code, manage pull requests, run CI/CD pipelines, and collaborate securely. | |
63 | + GitHub-compatible, fully self-hosted, and open source. | |
64 | + </motion.p> | |
65 | + | |
66 | + {/* CTA Buttons */} | |
67 | + <motion.div | |
68 | + initial={{ opacity: 0, y: 20 }} | |
69 | + animate={{ opacity: 1, y: 0 }} | |
70 | + transition={{ duration: 0.5, delay: 0.3 }} | |
71 | + className="flex flex-col sm:flex-row gap-4 justify-center mt-10" | |
72 | + > | |
73 | + <a | |
74 | + href="/register" | |
75 | + className="group relative inline-flex items-center justify-center gap-2 rounded-lg bg-primary px-8 py-3.5 text-sm font-semibold text-primary-foreground transition-all hover:bg-primary/90 hover:scale-105" | |
76 | + > | |
77 | + Get Started | |
78 | + <ArrowRight className="h-4 w-4 group-hover:translate-x-1 transition-transform" /> | |
79 | + | |
80 | + {/* Glow effect */} | |
81 | + <span className="absolute inset-0 rounded-lg bg-primary blur-xl opacity-40 group-hover:opacity-60 transition-opacity" /> | |
82 | + </a> | |
83 | + <a | |
84 | + href="/explore" | |
85 | + className="inline-flex items-center justify-center gap-2 rounded-lg border border-white/10 bg-white/5 px-8 py-3.5 text-sm font-medium text-foreground backdrop-blur-sm hover:bg-white/10 hover:border-white/20 transition-all" | |
86 | + > | |
87 | + <Github className="h-4 w-4" /> | |
88 | + Explore Repositories | |
89 | + </a> | |
90 | + </motion.div> | |
91 | + | |
92 | + {/* Stats */} | |
93 | + <motion.div | |
94 | + initial={{ opacity: 0, y: 20 }} | |
95 | + animate={{ opacity: 1, y: 0 }} | |
96 | + transition={{ duration: 0.5, delay: 0.4 }} | |
97 | + className="flex items-center justify-center gap-8 mt-16" | |
98 | + > | |
99 | + <div className="flex items-center gap-2 text-muted-foreground"> | |
100 | + <GitBranch className="h-5 w-5 text-primary" /> | |
101 | + <span className="text-2xl font-bold text-foreground">100%</span> | |
102 | + <span className="text-sm">Open Source</span> | |
103 | + </div> | |
104 | + <div className="w-px h-8 bg-white/10" /> | |
105 | + <div className="flex items-center gap-2 text-muted-foreground"> | |
106 | + <Zap className="h-5 w-5 text-yellow-500" /> | |
107 | + <span className="text-2xl font-bold text-foreground">Graphite</span> | |
108 | + <span className="text-sm">Features</span> | |
109 | + </div> | |
110 | + <div className="w-px h-8 bg-white/10" /> | |
111 | + <div className="flex items-center gap-2 text-muted-foreground"> | |
112 | + <Star className="h-5 w-5 text-orange-500" /> | |
113 | + <span className="text-2xl font-bold text-foreground">AI</span> | |
114 | + <span className="text-sm">Powered Reviews</span> | |
115 | + </div> | |
116 | + </motion.div> | |
117 | + </div> | |
118 | + </div> | |
119 | + ); | |
120 | +} | |
0 | 121 | |
▼src/components/issues/IssueDetail.tsx
+347-0
| @@ -0,0 +1,347 @@ | ||
1 | +"use client"; | |
2 | +import { motion } from "framer-motion"; | |
3 | +import { | |
4 | + AlertCircle, | |
5 | + CheckCircle2, | |
6 | + MessageSquare, | |
7 | + User, | |
8 | + Tag, | |
9 | + Calendar, | |
10 | + Clock, | |
11 | + Link2, | |
12 | + GitBranch, | |
13 | + MoreHorizontal, | |
14 | + Edit3, | |
15 | + Pin, | |
16 | + Lock, | |
17 | + Trash2 | |
18 | +} from "lucide-react"; | |
19 | +import { useState } from "react"; | |
20 | + | |
21 | +interface Issue { | |
22 | + id: string; | |
23 | + number: number; | |
24 | + title: string; | |
25 | + body: string; | |
26 | + state: "open" | "closed"; | |
27 | + createdAt: string; | |
28 | + author: { | |
29 | + username: string; | |
30 | + avatarUrl?: string; | |
31 | + }; | |
32 | + assignees?: Array<{ | |
33 | + username: string; | |
34 | + avatarUrl?: string; | |
35 | + }>; | |
36 | + labels?: Array<{ | |
37 | + name: string; | |
38 | + color: string; | |
39 | + }>; | |
40 | +} | |
41 | + | |
42 | +interface Props { | |
43 | + issue: Issue; | |
44 | + bodyHtml: string; | |
45 | + repoOwner: string; | |
46 | + repoName: string; | |
47 | +} | |
48 | + | |
49 | +function timeAgo(date: string): string { | |
50 | + const now = new Date(); | |
51 | + const past = new Date(date); | |
52 | + const diffMs = now.getTime() - past.getTime(); | |
53 | + const diffMins = Math.floor(diffMs / 60000); | |
54 | + | |
55 | + if (diffMins < 1) return 'just now'; | |
56 | + if (diffMins < 60) return `${diffMins} minutes ago`; | |
57 | + | |
58 | + const diffHours = Math.floor(diffMins / 60); | |
59 | + if (diffHours < 24) return `${diffHours} hours ago`; | |
60 | + | |
61 | + const diffDays = Math.floor(diffHours / 24); | |
62 | + if (diffDays < 30) return `${diffDays} days ago`; | |
63 | + | |
64 | + const diffMonths = Math.floor(diffDays / 30); | |
65 | + return `${diffMonths} months ago`; | |
66 | +} | |
67 | + | |
68 | +export default function IssueDetail({ issue, bodyHtml, repoOwner, repoName }: Props) { | |
69 | + const [showMenu, setShowMenu] = useState(false); | |
70 | + | |
71 | + return ( | |
72 | + <div className="max-w-6xl mx-auto"> | |
73 | + {/* Title Section */} | |
74 | + <motion.div | |
75 | + initial={{ opacity: 0, y: -20 }} | |
76 | + animate={{ opacity: 1, y: 0 }} | |
77 | + className="mb-8 pb-6 border-b border-white/10" | |
78 | + > | |
79 | + <div className="flex items-start justify-between gap-4 mb-4"> | |
80 | + <div className="flex items-center gap-3"> | |
81 | + <h1 className="text-2xl md:text-3xl font-bold text-white"> | |
82 | + {issue.title} | |
83 | + </h1> | |
84 | + <span className="text-2xl md:text-3xl text-gray-500 font-light"> | |
85 | + #{issue.number} | |
86 | + </span> | |
87 | + </div> | |
88 | + | |
89 | + {/* Edit Button */} | |
90 | + <motion.button | |
91 | + whileHover={{ scale: 1.05 }} | |
92 | + whileTap={{ scale: 0.95 }} | |
93 | + className="flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/5 text-sm text-gray-300 hover:bg-white/10 transition-all" | |
94 | + > | |
95 | + <Edit3 className="h-4 w-4" /> | |
96 | + Edit | |
97 | + </motion.button> | |
98 | + </div> | |
99 | + | |
100 | + <div className="flex items-center gap-3 text-sm"> | |
101 | + {/* Status Badge */} | |
102 | + <motion.div | |
103 | + initial={{ scale: 0.9 }} | |
104 | + animate={{ scale: 1 }} | |
105 | + className={`inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full font-medium ${issue.state === "open" | |
106 | + ? "bg-green-500/20 text-green-400 border border-green-500/30" | |
107 | + : "bg-purple-500/20 text-purple-400 border border-purple-500/30" | |
108 | + }`} | |
109 | + > | |
110 | + {issue.state === "open" ? ( | |
111 | + <AlertCircle className="h-4 w-4" /> | |
112 | + ) : ( | |
113 | + <CheckCircle2 className="h-4 w-4" /> | |
114 | + )} | |
115 | + {issue.state === "open" ? "Open" : "Closed"} | |
116 | + </motion.div> | |
117 | + | |
118 | + <span className="text-gray-400"> | |
119 | + <span className="font-medium text-white hover:text-cyan-400 cursor-pointer transition-colors"> | |
120 | + {issue.author.username} | |
121 | + </span> | |
122 | + {" "}opened this issue {timeAgo(issue.createdAt)} | |
123 | + </span> | |
124 | + </div> | |
125 | + </motion.div> | |
126 | + | |
127 | + {/* Main Content Grid */} | |
128 | + <div className="grid grid-cols-1 lg:grid-cols-[1fr_280px] gap-8"> | |
129 | + {/* Left Column - Issue Body & Comments */} | |
130 | + <div className="space-y-6"> | |
131 | + {/* Issue Body Card */} | |
132 | + <motion.div | |
133 | + initial={{ opacity: 0, y: 20 }} | |
134 | + animate={{ opacity: 1, y: 0 }} | |
135 | + transition={{ delay: 0.1 }} | |
136 | + className="relative" | |
137 | + > | |
138 | + {/* Gradient border */} | |
139 | + <div className="absolute -inset-[1px] bg-gradient-to-r from-cyan-500/20 via-transparent to-purple-500/20 rounded-xl opacity-50" /> | |
140 | + | |
141 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/80 backdrop-blur-sm overflow-hidden"> | |
142 | + {/* Author Header */} | |
143 | + <div className="flex items-center justify-between px-4 py-3 bg-white/[0.02] border-b border-white/5"> | |
144 | + <div className="flex items-center gap-3"> | |
145 | + {issue.author.avatarUrl ? ( | |
146 | + <img | |
147 | + src={issue.author.avatarUrl} | |
148 | + alt="" | |
149 | + className="h-8 w-8 rounded-full ring-2 ring-white/10" | |
150 | + /> | |
151 | + ) : ( | |
152 | + <div className="h-8 w-8 rounded-full bg-gradient-to-br from-cyan-400 to-blue-600 flex items-center justify-center text-sm font-bold text-white ring-2 ring-white/10"> | |
153 | + {issue.author.username[0].toUpperCase()} | |
154 | + </div> | |
155 | + )} | |
156 | + <div> | |
157 | + <span className="font-medium text-white text-sm"> | |
158 | + {issue.author.username} | |
159 | + </span> | |
160 | + <span className="text-xs text-gray-500 ml-2"> | |
161 | + commented {timeAgo(issue.createdAt)} | |
162 | + </span> | |
163 | + </div> | |
164 | + </div> | |
165 | + | |
166 | + {/* Actions Menu */} | |
167 | + <div className="relative"> | |
168 | + <button | |
169 | + onClick={() => setShowMenu(!showMenu)} | |
170 | + className="p-1.5 rounded-lg hover:bg-white/10 transition-colors" | |
171 | + > | |
172 | + <MoreHorizontal className="h-4 w-4 text-gray-500" /> | |
173 | + </button> | |
174 | + | |
175 | + {showMenu && ( | |
176 | + <motion.div | |
177 | + initial={{ opacity: 0, scale: 0.95 }} | |
178 | + animate={{ opacity: 1, scale: 1 }} | |
179 | + className="absolute right-0 top-full mt-1 w-48 rounded-lg border border-white/10 bg-[#161b22] shadow-xl z-10" | |
180 | + > | |
181 | + <div className="p-1"> | |
182 | + <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md transition-colors"> | |
183 | + <Link2 className="h-4 w-4" /> | |
184 | + Copy link | |
185 | + </button> | |
186 | + <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md transition-colors"> | |
187 | + <Pin className="h-4 w-4" /> | |
188 | + Pin issue | |
189 | + </button> | |
190 | + <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md transition-colors"> | |
191 | + <Lock className="h-4 w-4" /> | |
192 | + Lock conversation | |
193 | + </button> | |
194 | + <div className="border-t border-white/5 my-1" /> | |
195 | + <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-red-400 hover:bg-red-500/10 rounded-md transition-colors"> | |
196 | + <Trash2 className="h-4 w-4" /> | |
197 | + Delete issue | |
198 | + </button> | |
199 | + </div> | |
200 | + </motion.div> | |
201 | + )} | |
202 | + </div> | |
203 | + </div> | |
204 | + | |
205 | + {/* Body Content */} | |
206 | + <div | |
207 | + className="p-5 prose prose-invert prose-sm max-w-none prose-p:text-gray-300 prose-headings:text-white prose-a:text-cyan-400 prose-code:text-pink-400 prose-pre:bg-black/30" | |
208 | + dangerouslySetInnerHTML={{ __html: bodyHtml }} | |
209 | + /> | |
210 | + </div> | |
211 | + </motion.div> | |
212 | + | |
213 | + {/* Add Comment Section */} | |
214 | + <motion.div | |
215 | + initial={{ opacity: 0, y: 20 }} | |
216 | + animate={{ opacity: 1, y: 0 }} | |
217 | + transition={{ delay: 0.2 }} | |
218 | + className="relative" | |
219 | + > | |
220 | + <div className="rounded-xl border border-white/10 bg-[#0d1117]/80 p-4"> | |
221 | + <textarea | |
222 | + placeholder="Leave a comment..." | |
223 | + className="w-full h-24 bg-transparent border border-white/10 rounded-lg px-4 py-3 text-sm text-white placeholder:text-gray-600 focus:outline-none focus:border-cyan-500/50 resize-none" | |
224 | + /> | |
225 | + <div className="flex justify-end mt-3"> | |
226 | + <motion.button | |
227 | + whileHover={{ scale: 1.02 }} | |
228 | + whileTap={{ scale: 0.98 }} | |
229 | + className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-green-500 to-emerald-600 text-sm font-medium text-white shadow-lg shadow-green-500/20" | |
230 | + > | |
231 | + <MessageSquare className="h-4 w-4" /> | |
232 | + Comment | |
233 | + </motion.button> | |
234 | + </div> | |
235 | + </div> | |
236 | + </motion.div> | |
237 | + </div> | |
238 | + | |
239 | + {/* Right Sidebar */} | |
240 | + <motion.div | |
241 | + initial={{ opacity: 0, x: 20 }} | |
242 | + animate={{ opacity: 1, x: 0 }} | |
243 | + transition={{ delay: 0.15 }} | |
244 | + className="space-y-6" | |
245 | + > | |
246 | + {/* Assignees */} | |
247 | + <div className="relative"> | |
248 | + <div className="absolute -inset-[1px] bg-gradient-to-b from-cyan-500/10 to-transparent rounded-xl opacity-50" /> | |
249 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4"> | |
250 | + <div className="flex items-center justify-between mb-3"> | |
251 | + <h3 className="text-sm font-semibold text-white flex items-center gap-2"> | |
252 | + <User className="h-4 w-4 text-cyan-400" /> | |
253 | + Assignees | |
254 | + </h3> | |
255 | + <button className="text-xs text-gray-500 hover:text-cyan-400 transition-colors"> | |
256 | + Edit | |
257 | + </button> | |
258 | + </div> | |
259 | + {issue.assignees && issue.assignees.length > 0 ? ( | |
260 | + <div className="space-y-2"> | |
261 | + {issue.assignees.map(assignee => ( | |
262 | + <div key={assignee.username} className="flex items-center gap-2"> | |
263 | + <div className="h-6 w-6 rounded-full bg-gradient-to-br from-purple-400 to-pink-500 flex items-center justify-center text-xs font-bold text-white"> | |
264 | + {assignee.username[0].toUpperCase()} | |
265 | + </div> | |
266 | + <span className="text-sm text-gray-300">{assignee.username}</span> | |
267 | + </div> | |
268 | + ))} | |
269 | + </div> | |
270 | + ) : ( | |
271 | + <p className="text-sm text-gray-500">No one assigned</p> | |
272 | + )} | |
273 | + </div> | |
274 | + </div> | |
275 | + | |
276 | + {/* Labels */} | |
277 | + <div className="relative"> | |
278 | + <div className="absolute -inset-[1px] bg-gradient-to-b from-purple-500/10 to-transparent rounded-xl opacity-50" /> | |
279 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4"> | |
280 | + <div className="flex items-center justify-between mb-3"> | |
281 | + <h3 className="text-sm font-semibold text-white flex items-center gap-2"> | |
282 | + <Tag className="h-4 w-4 text-purple-400" /> | |
283 | + Labels | |
284 | + </h3> | |
285 | + <button className="text-xs text-gray-500 hover:text-purple-400 transition-colors"> | |
286 | + Edit | |
287 | + </button> | |
288 | + </div> | |
289 | + {issue.labels && issue.labels.length > 0 ? ( | |
290 | + <div className="flex flex-wrap gap-2"> | |
291 | + {issue.labels.map(label => ( | |
292 | + <span | |
293 | + key={label.name} | |
294 | + className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium" | |
295 | + style={{ | |
296 | + backgroundColor: `${label.color}20`, | |
297 | + color: label.color, | |
298 | + border: `1px solid ${label.color}40` | |
299 | + }} | |
300 | + > | |
301 | + {label.name} | |
302 | + </span> | |
303 | + ))} | |
304 | + </div> | |
305 | + ) : ( | |
306 | + <p className="text-sm text-gray-500">None yet</p> | |
307 | + )} | |
308 | + </div> | |
309 | + </div> | |
310 | + | |
311 | + {/* Development */} | |
312 | + <div className="relative"> | |
313 | + <div className="absolute -inset-[1px] bg-gradient-to-b from-orange-500/10 to-transparent rounded-xl opacity-50" /> | |
314 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4"> | |
315 | + <div className="flex items-center justify-between mb-3"> | |
316 | + <h3 className="text-sm font-semibold text-white flex items-center gap-2"> | |
317 | + <GitBranch className="h-4 w-4 text-orange-400" /> | |
318 | + Development | |
319 | + </h3> | |
320 | + </div> | |
321 | + <p className="text-sm text-gray-500"> | |
322 | + No branches or pull requests linked | |
323 | + </p> | |
324 | + </div> | |
325 | + </div> | |
326 | + | |
327 | + {/* Timeline Info */} | |
328 | + <div className="relative"> | |
329 | + <div className="absolute -inset-[1px] bg-gradient-to-b from-green-500/10 to-transparent rounded-xl opacity-50" /> | |
330 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/60 p-4"> | |
331 | + <h3 className="text-sm font-semibold text-white flex items-center gap-2 mb-3"> | |
332 | + <Clock className="h-4 w-4 text-green-400" /> | |
333 | + Timeline | |
334 | + </h3> | |
335 | + <div className="space-y-2 text-sm text-gray-500"> | |
336 | + <div className="flex items-center gap-2"> | |
337 | + <Calendar className="h-3.5 w-3.5" /> | |
338 | + <span>Created {timeAgo(issue.createdAt)}</span> | |
339 | + </div> | |
340 | + </div> | |
341 | + </div> | |
342 | + </div> | |
343 | + </motion.div> | |
344 | + </div> | |
345 | + </div> | |
346 | + ); | |
347 | +} | |
0 | 348 | |
▼src/components/issues/IssuesList.tsx
+299-0
| @@ -0,0 +1,299 @@ | ||
1 | +"use client"; | |
2 | +import { motion, AnimatePresence } from "framer-motion"; | |
3 | +import { | |
4 | + AlertCircle, | |
5 | + CheckCircle2, | |
6 | + MessageSquare, | |
7 | + Search, | |
8 | + Tag, | |
9 | + Calendar, | |
10 | + User, | |
11 | + ArrowUpDown, | |
12 | + Plus, | |
13 | + Sparkles, | |
14 | + Filter | |
15 | +} from "lucide-react"; | |
16 | +import { useState } from "react"; | |
17 | + | |
18 | +interface Issue { | |
19 | + id: string; | |
20 | + number: number; | |
21 | + title: string; | |
22 | + state: "open" | "closed"; | |
23 | + createdAt: string; | |
24 | + commentCount: number; | |
25 | + author: { | |
26 | + username: string; | |
27 | + avatarUrl?: string; | |
28 | + }; | |
29 | + labels?: Array<{ | |
30 | + name: string; | |
31 | + color: string; | |
32 | + }>; | |
33 | +} | |
34 | + | |
35 | +interface Props { | |
36 | + issues: Issue[]; | |
37 | + openCount: number; | |
38 | + closedCount: number; | |
39 | + repoOwner: string; | |
40 | + repoName: string; | |
41 | +} | |
42 | + | |
43 | +function timeAgo(date: string): string { | |
44 | + const now = new Date(); | |
45 | + const past = new Date(date); | |
46 | + const diffMs = now.getTime() - past.getTime(); | |
47 | + const diffMins = Math.floor(diffMs / 60000); | |
48 | + | |
49 | + if (diffMins < 1) return 'just now'; | |
50 | + if (diffMins < 60) return `${diffMins} minutes ago`; | |
51 | + | |
52 | + const diffHours = Math.floor(diffMins / 60); | |
53 | + if (diffHours < 24) return `${diffHours} hours ago`; | |
54 | + | |
55 | + const diffDays = Math.floor(diffHours / 24); | |
56 | + if (diffDays < 30) return `${diffDays} days ago`; | |
57 | + | |
58 | + const diffMonths = Math.floor(diffDays / 30); | |
59 | + return `${diffMonths} months ago`; | |
60 | +} | |
61 | + | |
62 | +export default function IssuesList({ issues, openCount, closedCount, repoOwner, repoName }: Props) { | |
63 | + const [filter, setFilter] = useState<"open" | "closed" | "all">("open"); | |
64 | + const [searchQuery, setSearchQuery] = useState(""); | |
65 | + | |
66 | + const filteredIssues = issues.filter(issue => { | |
67 | + const matchesFilter = filter === "all" || issue.state === filter; | |
68 | + const matchesSearch = issue.title.toLowerCase().includes(searchQuery.toLowerCase()); | |
69 | + return matchesFilter && matchesSearch; | |
70 | + }); | |
71 | + | |
72 | + return ( | |
73 | + <div className="space-y-6"> | |
74 | + {/* Enhanced Header with Glow */} | |
75 | + <motion.div | |
76 | + initial={{ opacity: 0, y: -20 }} | |
77 | + animate={{ opacity: 1, y: 0 }} | |
78 | + className="flex flex-col lg:flex-row lg:items-center justify-between gap-4" | |
79 | + > | |
80 | + {/* Search Input with Glass Effect */} | |
81 | + <div className="relative flex-1 max-w-xl group"> | |
82 | + <div className="absolute -inset-0.5 bg-gradient-to-r from-cyan-500/20 via-purple-500/20 to-pink-500/20 rounded-xl blur opacity-0 group-focus-within:opacity-100 transition-opacity" /> | |
83 | + <div className="relative"> | |
84 | + <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-500" /> | |
85 | + <input | |
86 | + type="search" | |
87 | + placeholder="Search all issues..." | |
88 | + value={searchQuery} | |
89 | + onChange={(e) => setSearchQuery(e.target.value)} | |
90 | + className="w-full rounded-xl border border-white/10 bg-white/5 backdrop-blur-sm pl-10 pr-4 py-2.5 text-sm text-white placeholder:text-gray-500 focus:outline-none focus:border-cyan-500/50 focus:ring-2 focus:ring-cyan-500/20 transition-all" | |
91 | + /> | |
92 | + </div> | |
93 | + </div> | |
94 | + | |
95 | + {/* Action Buttons */} | |
96 | + <div className="flex gap-3"> | |
97 | + <motion.a | |
98 | + href={`/${repoOwner}/${repoName}/labels`} | |
99 | + whileHover={{ scale: 1.02 }} | |
100 | + whileTap={{ scale: 0.98 }} | |
101 | + className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/5 text-sm text-gray-300 hover:bg-white/10 hover:border-white/20 transition-all" | |
102 | + > | |
103 | + <Tag className="h-4 w-4" /> | |
104 | + Labels | |
105 | + </motion.a> | |
106 | + <motion.a | |
107 | + href={`/${repoOwner}/${repoName}/milestones`} | |
108 | + whileHover={{ scale: 1.02 }} | |
109 | + whileTap={{ scale: 0.98 }} | |
110 | + className="inline-flex items-center gap-2 px-4 py-2 rounded-lg border border-white/10 bg-white/5 text-sm text-gray-300 hover:bg-white/10 hover:border-white/20 transition-all" | |
111 | + > | |
112 | + <Calendar className="h-4 w-4" /> | |
113 | + Milestones | |
114 | + </motion.a> | |
115 | + <motion.a | |
116 | + href={`/${repoOwner}/${repoName}/issues/new`} | |
117 | + whileHover={{ scale: 1.05 }} | |
118 | + whileTap={{ scale: 0.95 }} | |
119 | + className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-gradient-to-r from-green-500 to-emerald-600 text-sm font-medium text-white shadow-lg shadow-green-500/25 hover:shadow-green-500/40 transition-all" | |
120 | + > | |
121 | + <Plus className="h-4 w-4" /> | |
122 | + New Issue | |
123 | + </motion.a> | |
124 | + </div> | |
125 | + </motion.div> | |
126 | + | |
127 | + {/* Issues Container with Glass Effect */} | |
128 | + <motion.div | |
129 | + initial={{ opacity: 0 }} | |
130 | + animate={{ opacity: 1 }} | |
131 | + transition={{ delay: 0.1 }} | |
132 | + className="relative" | |
133 | + > | |
134 | + {/* Gradient border effect */} | |
135 | + <div className="absolute -inset-[1px] bg-gradient-to-r from-cyan-500/30 via-purple-500/20 to-pink-500/30 rounded-xl blur-sm opacity-50" /> | |
136 | + | |
137 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/80 backdrop-blur-sm overflow-hidden"> | |
138 | + {/* Filter Header */} | |
139 | + <div className="flex items-center justify-between gap-4 border-b border-white/5 bg-white/[0.02] px-4 py-3"> | |
140 | + <div className="flex items-center gap-1"> | |
141 | + <button | |
142 | + onClick={() => setFilter("open")} | |
143 | + className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${filter === "open" | |
144 | + ? "bg-green-500/10 text-green-400 border border-green-500/30" | |
145 | + : "text-gray-400 hover:text-white hover:bg-white/5" | |
146 | + }`} | |
147 | + > | |
148 | + <AlertCircle className="h-4 w-4" /> | |
149 | + {openCount} Open | |
150 | + </button> | |
151 | + <button | |
152 | + onClick={() => setFilter("closed")} | |
153 | + className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-sm font-medium transition-all ${filter === "closed" | |
154 | + ? "bg-purple-500/10 text-purple-400 border border-purple-500/30" | |
155 | + : "text-gray-400 hover:text-white hover:bg-white/5" | |
156 | + }`} | |
157 | + > | |
158 | + <CheckCircle2 className="h-4 w-4" /> | |
159 | + {closedCount} Closed | |
160 | + </button> | |
161 | + </div> | |
162 | + | |
163 | + <div className="flex items-center gap-3 text-sm text-gray-500"> | |
164 | + <button className="flex items-center gap-1 hover:text-white transition-colors"> | |
165 | + <User className="h-3.5 w-3.5" /> | |
166 | + Author | |
167 | + </button> | |
168 | + <button className="flex items-center gap-1 hover:text-white transition-colors"> | |
169 | + <Tag className="h-3.5 w-3.5" /> | |
170 | + Label | |
171 | + </button> | |
172 | + <button className="flex items-center gap-1 hover:text-white transition-colors"> | |
173 | + <ArrowUpDown className="h-3.5 w-3.5" /> | |
174 | + Sort | |
175 | + </button> | |
176 | + </div> | |
177 | + </div> | |
178 | + | |
179 | + {/* Issues List */} | |
180 | + <div className="divide-y divide-white/5"> | |
181 | + <AnimatePresence mode="popLayout"> | |
182 | + {filteredIssues.length > 0 ? ( | |
183 | + filteredIssues.map((issue, index) => ( | |
184 | + <motion.a | |
185 | + key={issue.id} | |
186 | + href={`/${repoOwner}/${repoName}/issues/${issue.number}`} | |
187 | + initial={{ opacity: 0, x: -20 }} | |
188 | + animate={{ opacity: 1, x: 0 }} | |
189 | + exit={{ opacity: 0, x: 20 }} | |
190 | + transition={{ delay: index * 0.03 }} | |
191 | + className="flex items-start gap-3 p-4 hover:bg-white/[0.02] transition-colors group" | |
192 | + > | |
193 | + {/* Status Icon */} | |
194 | + <div className="mt-0.5"> | |
195 | + {issue.state === "open" ? ( | |
196 | + <div className="relative"> | |
197 | + <AlertCircle className="h-5 w-5 text-green-500" /> | |
198 | + <div className="absolute inset-0 bg-green-500 blur-md opacity-0 group-hover:opacity-30 transition-opacity" /> | |
199 | + </div> | |
200 | + ) : ( | |
201 | + <div className="relative"> | |
202 | + <CheckCircle2 className="h-5 w-5 text-purple-500" /> | |
203 | + <div className="absolute inset-0 bg-purple-500 blur-md opacity-0 group-hover:opacity-30 transition-opacity" /> | |
204 | + </div> | |
205 | + )} | |
206 | + </div> | |
207 | + | |
208 | + {/* Content */} | |
209 | + <div className="flex-1 min-w-0"> | |
210 | + <div className="flex items-center gap-2 mb-1"> | |
211 | + <span className="font-semibold text-white group-hover:text-cyan-400 transition-colors truncate"> | |
212 | + {issue.title} | |
213 | + </span> | |
214 | + {/* Labels */} | |
215 | + {issue.labels?.map(label => ( | |
216 | + <span | |
217 | + key={label.name} | |
218 | + className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium" | |
219 | + style={{ | |
220 | + backgroundColor: `${label.color}20`, | |
221 | + color: label.color, | |
222 | + border: `1px solid ${label.color}40` | |
223 | + }} | |
224 | + > | |
225 | + {label.name} | |
226 | + </span> | |
227 | + ))} | |
228 | + </div> | |
229 | + <div className="flex items-center gap-2 text-xs text-gray-500"> | |
230 | + <span>#{issue.number}</span> | |
231 | + <span>•</span> | |
232 | + <span>opened {timeAgo(issue.createdAt)}</span> | |
233 | + <span>•</span> | |
234 | + <span className="flex items-center gap-1"> | |
235 | + {issue.author.avatarUrl ? ( | |
236 | + <img | |
237 | + src={issue.author.avatarUrl} | |
238 | + alt="" | |
239 | + className="h-4 w-4 rounded-full" | |
240 | + /> | |
241 | + ) : ( | |
242 | + <div className="h-4 w-4 rounded-full bg-gradient-to-br from-cyan-400 to-blue-500 flex items-center justify-center text-[8px] text-white font-bold"> | |
243 | + {issue.author.username[0].toUpperCase()} | |
244 | + </div> | |
245 | + )} | |
246 | + <span className="hover:text-cyan-400 transition-colors"> | |
247 | + {issue.author.username} | |
248 | + </span> | |
249 | + </span> | |
250 | + </div> | |
251 | + </div> | |
252 | + | |
253 | + {/* Comment Count */} | |
254 | + {issue.commentCount > 0 && ( | |
255 | + <div className="flex items-center gap-1 text-xs text-gray-500 group-hover:text-gray-400 transition-colors"> | |
256 | + <MessageSquare className="h-4 w-4" /> | |
257 | + {issue.commentCount} | |
258 | + </div> | |
259 | + )} | |
260 | + </motion.a> | |
261 | + )) | |
262 | + ) : ( | |
263 | + <motion.div | |
264 | + initial={{ opacity: 0 }} | |
265 | + animate={{ opacity: 1 }} | |
266 | + className="p-12 text-center" | |
267 | + > | |
268 | + <div className="relative inline-block mb-4"> | |
269 | + <Sparkles className="h-12 w-12 text-cyan-400" /> | |
270 | + <div className="absolute inset-0 bg-cyan-500 blur-xl opacity-30" /> | |
271 | + </div> | |
272 | + <h3 className="text-lg font-semibold text-white mb-2"> | |
273 | + {searchQuery ? "No matching issues" : "Welcome to issues!"} | |
274 | + </h3> | |
275 | + <p className="text-gray-500 mb-6 max-w-md mx-auto"> | |
276 | + {searchQuery | |
277 | + ? "Try adjusting your search or filter to find what you're looking for." | |
278 | + : "Issues are used to track todos, bugs, feature requests, and more."} | |
279 | + </p> | |
280 | + {!searchQuery && ( | |
281 | + <motion.a | |
282 | + href={`/${repoOwner}/${repoName}/issues/new`} | |
283 | + whileHover={{ scale: 1.05 }} | |
284 | + whileTap={{ scale: 0.95 }} | |
285 | + className="inline-flex items-center gap-2 px-6 py-2.5 rounded-lg bg-gradient-to-r from-cyan-500 to-blue-600 text-sm font-medium text-white shadow-lg shadow-cyan-500/25" | |
286 | + > | |
287 | + <Plus className="h-4 w-4" /> | |
288 | + Create the first issue | |
289 | + </motion.a> | |
290 | + )} | |
291 | + </motion.div> | |
292 | + )} | |
293 | + </AnimatePresence> | |
294 | + </div> | |
295 | + </div> | |
296 | + </motion.div> | |
297 | + </div> | |
298 | + ); | |
299 | +} | |
0 | 300 | |
▼src/components/layout/Header.astro
+6-6
| @@ -39,6 +39,10 @@ const user = Astro.locals.user; | ||
39 | 39 | <a href="/actions" class="text-muted-foreground transition-colors hover:text-foreground"> |
40 | 40 | Actions |
41 | 41 | </a> |
42 | + <a href="/admin" class="text-muted-foreground transition-colors hover:text-purple-400 font-semibold flex items-center gap-1"> | |
43 | + <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-activity"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg> | |
44 | + Mission Control | |
45 | + </a> | |
42 | 46 | </nav> |
43 | 47 | |
44 | 48 | <!-- Spacer --> |
| @@ -75,9 +79,7 @@ const user = Astro.locals.user; | ||
75 | 79 | <a href="/new" class="flex items-center rounded-sm px-2 py-1.5 text-sm hover:bg-accent"> |
76 | 80 | New Repository |
77 | 81 | </a> |
78 | - <a href="/new/organization" class="flex items-center rounded-sm px-2 py-1.5 text-sm hover:bg-accent"> | |
79 | - New Organization | |
80 | - </a> | |
82 | + | |
81 | 83 | <a href="/new/import" class="flex items-center rounded-sm px-2 py-1.5 text-sm hover:bg-accent"> |
82 | 84 | Import Repository |
83 | 85 | </a> |
| @@ -120,9 +122,7 @@ const user = Astro.locals.user; | ||
120 | 122 | <a href="/repositories" class="flex items-center rounded-sm px-2 py-1.5 text-sm hover:bg-accent"> |
121 | 123 | Your Repositories |
122 | 124 | </a> |
123 | - <a href="/organizations" class="flex items-center rounded-sm px-2 py-1.5 text-sm hover:bg-accent"> | |
124 | - Your Organizations | |
125 | - </a> | |
125 | + | |
126 | 126 | <a href="/stars" class="flex items-center rounded-sm px-2 py-1.5 text-sm hover:bg-accent"> |
127 | 127 | Your Stars |
128 | 128 | </a> |
129 | 129 | |
▼src/components/milestones/MilestonesList.tsx
+276-0
| @@ -0,0 +1,276 @@ | ||
1 | +"use client"; | |
2 | +import { motion, AnimatePresence } from "framer-motion"; | |
3 | +import { | |
4 | + Target, | |
5 | + Calendar, | |
6 | + CheckCircle2, | |
7 | + Circle, | |
8 | + Plus, | |
9 | + Clock, | |
10 | + AlertCircle, | |
11 | + Milestone, | |
12 | + TrendingUp, | |
13 | + MoreHorizontal, | |
14 | + Edit3, | |
15 | + Trash2, | |
16 | + Archive | |
17 | +} from "lucide-react"; | |
18 | +import { useState } from "react"; | |
19 | + | |
20 | +interface MilestoneItem { | |
21 | + id: string; | |
22 | + title: string; | |
23 | + description?: string; | |
24 | + dueDate?: string; | |
25 | + state: "open" | "closed"; | |
26 | + openIssues: number; | |
27 | + closedIssues: number; | |
28 | + progress: number; | |
29 | +} | |
30 | + | |
31 | +interface Props { | |
32 | + milestones: MilestoneItem[]; | |
33 | + repoOwner: string; | |
34 | + repoName: string; | |
35 | +} | |
36 | + | |
37 | +function formatDate(date: string): string { | |
38 | + return new Date(date).toLocaleDateString('en-US', { | |
39 | + month: 'short', | |
40 | + day: 'numeric', | |
41 | + year: 'numeric' | |
42 | + }); | |
43 | +} | |
44 | + | |
45 | +function getDaysRemaining(dueDate: string): { text: string; color: string } { | |
46 | + const now = new Date(); | |
47 | + const due = new Date(dueDate); | |
48 | + const diffDays = Math.ceil((due.getTime() - now.getTime()) / (1000 * 60 * 60 * 24)); | |
49 | + | |
50 | + if (diffDays < 0) return { text: `${Math.abs(diffDays)} days overdue`, color: "text-red-400" }; | |
51 | + if (diffDays === 0) return { text: "Due today", color: "text-orange-400" }; | |
52 | + if (diffDays <= 7) return { text: `${diffDays} days left`, color: "text-yellow-400" }; | |
53 | + return { text: `${diffDays} days left`, color: "text-green-400" }; | |
54 | +} | |
55 | + | |
56 | +export default function MilestonesList({ milestones, repoOwner, repoName }: Props) { | |
57 | + const [filter, setFilter] = useState<"open" | "closed">("open"); | |
58 | + const [showMenu, setShowMenu] = useState<string | null>(null); | |
59 | + | |
60 | + const filteredMilestones = milestones.filter(m => m.state === filter); | |
61 | + const openCount = milestones.filter(m => m.state === "open").length; | |
62 | + const closedCount = milestones.filter(m => m.state === "closed").length; | |
63 | + | |
64 | + return ( | |
65 | + <div className="space-y-6"> | |
66 | + {/* Header */} | |
67 | + <motion.div | |
68 | + initial={{ opacity: 0, y: -20 }} | |
69 | + animate={{ opacity: 1, y: 0 }} | |
70 | + className="flex flex-col sm:flex-row sm:items-center justify-between gap-4" | |
71 | + > | |
72 | + <div> | |
73 | + <h1 className="text-2xl font-bold text-white flex items-center gap-3"> | |
74 | + <div className="p-2 rounded-lg bg-gradient-to-br from-purple-500/20 to-pink-500/20 border border-purple-500/30"> | |
75 | + <Target className="h-6 w-6 text-purple-400" /> | |
76 | + </div> | |
77 | + Milestones | |
78 | + </h1> | |
79 | + <p className="text-gray-500 text-sm mt-1"> | |
80 | + Track progress and manage project goals | |
81 | + </p> | |
82 | + </div> | |
83 | + | |
84 | + <motion.a | |
85 | + href={`/${repoOwner}/${repoName}/milestones/new`} | |
86 | + whileHover={{ scale: 1.02 }} | |
87 | + whileTap={{ scale: 0.98 }} | |
88 | + className="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg bg-gradient-to-r from-purple-500 to-pink-600 text-sm font-medium text-white shadow-lg shadow-purple-500/25 hover:shadow-purple-500/40 transition-all" | |
89 | + > | |
90 | + <Plus className="h-4 w-4" /> | |
91 | + New Milestone | |
92 | + </motion.a> | |
93 | + </motion.div> | |
94 | + | |
95 | + {/* Filter Tabs */} | |
96 | + <motion.div | |
97 | + initial={{ opacity: 0 }} | |
98 | + animate={{ opacity: 1 }} | |
99 | + transition={{ delay: 0.1 }} | |
100 | + className="flex items-center gap-2" | |
101 | + > | |
102 | + <button | |
103 | + onClick={() => setFilter("open")} | |
104 | + className={`inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-all ${filter === "open" | |
105 | + ? "bg-green-500/10 text-green-400 border border-green-500/30" | |
106 | + : "text-gray-400 hover:text-white hover:bg-white/5" | |
107 | + }`} | |
108 | + > | |
109 | + <AlertCircle className="h-4 w-4" /> | |
110 | + {openCount} Open | |
111 | + </button> | |
112 | + <button | |
113 | + onClick={() => setFilter("closed")} | |
114 | + className={`inline-flex items-center gap-1.5 px-4 py-2 rounded-lg text-sm font-medium transition-all ${filter === "closed" | |
115 | + ? "bg-purple-500/10 text-purple-400 border border-purple-500/30" | |
116 | + : "text-gray-400 hover:text-white hover:bg-white/5" | |
117 | + }`} | |
118 | + > | |
119 | + <CheckCircle2 className="h-4 w-4" /> | |
120 | + {closedCount} Closed | |
121 | + </button> | |
122 | + </motion.div> | |
123 | + | |
124 | + {/* Milestones List */} | |
125 | + <div className="space-y-4"> | |
126 | + <AnimatePresence mode="popLayout"> | |
127 | + {filteredMilestones.length > 0 ? ( | |
128 | + filteredMilestones.map((milestone, index) => ( | |
129 | + <motion.div | |
130 | + key={milestone.id} | |
131 | + initial={{ opacity: 0, y: 20 }} | |
132 | + animate={{ opacity: 1, y: 0 }} | |
133 | + exit={{ opacity: 0, y: -20 }} | |
134 | + transition={{ delay: index * 0.05 }} | |
135 | + className="relative group" | |
136 | + > | |
137 | + {/* Gradient border */} | |
138 | + <div className="absolute -inset-[1px] bg-gradient-to-r from-purple-500/20 via-transparent to-pink-500/20 rounded-xl opacity-50 group-hover:opacity-100 transition-opacity" /> | |
139 | + | |
140 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/80 backdrop-blur-sm p-5"> | |
141 | + <div className="flex items-start justify-between gap-4"> | |
142 | + <div className="flex-1"> | |
143 | + {/* Title */} | |
144 | + <a | |
145 | + href={`/${repoOwner}/${repoName}/milestones/${milestone.id}`} | |
146 | + className="text-lg font-semibold text-white hover:text-purple-400 transition-colors" | |
147 | + > | |
148 | + {milestone.title} | |
149 | + </a> | |
150 | + | |
151 | + {/* Description */} | |
152 | + {milestone.description && ( | |
153 | + <p className="text-sm text-gray-400 mt-1 line-clamp-2"> | |
154 | + {milestone.description} | |
155 | + </p> | |
156 | + )} | |
157 | + | |
158 | + {/* Meta Info */} | |
159 | + <div className="flex items-center gap-4 mt-3 text-xs text-gray-500"> | |
160 | + {milestone.dueDate && ( | |
161 | + <span className="flex items-center gap-1"> | |
162 | + <Calendar className="h-3.5 w-3.5" /> | |
163 | + Due {formatDate(milestone.dueDate)} | |
164 | + </span> | |
165 | + )} | |
166 | + {milestone.dueDate && ( | |
167 | + <span className={`flex items-center gap-1 ${getDaysRemaining(milestone.dueDate).color}`}> | |
168 | + <Clock className="h-3.5 w-3.5" /> | |
169 | + {getDaysRemaining(milestone.dueDate).text} | |
170 | + </span> | |
171 | + )} | |
172 | + </div> | |
173 | + </div> | |
174 | + | |
175 | + {/* Actions Menu */} | |
176 | + <div className="relative"> | |
177 | + <button | |
178 | + onClick={() => setShowMenu(showMenu === milestone.id ? null : milestone.id)} | |
179 | + className="p-2 rounded-lg hover:bg-white/10 transition-colors" | |
180 | + > | |
181 | + <MoreHorizontal className="h-4 w-4 text-gray-500" /> | |
182 | + </button> | |
183 | + | |
184 | + {showMenu === milestone.id && ( | |
185 | + <motion.div | |
186 | + initial={{ opacity: 0, scale: 0.95 }} | |
187 | + animate={{ opacity: 1, scale: 1 }} | |
188 | + className="absolute right-0 top-full mt-1 w-40 rounded-lg border border-white/10 bg-[#161b22] shadow-xl z-10" | |
189 | + > | |
190 | + <div className="p-1"> | |
191 | + <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md"> | |
192 | + <Edit3 className="h-4 w-4" /> | |
193 | + Edit | |
194 | + </button> | |
195 | + <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-300 hover:bg-white/5 rounded-md"> | |
196 | + <Archive className="h-4 w-4" /> | |
197 | + Close | |
198 | + </button> | |
199 | + <button className="w-full flex items-center gap-2 px-3 py-2 text-sm text-red-400 hover:bg-red-500/10 rounded-md"> | |
200 | + <Trash2 className="h-4 w-4" /> | |
201 | + Delete | |
202 | + </button> | |
203 | + </div> | |
204 | + </motion.div> | |
205 | + )} | |
206 | + </div> | |
207 | + </div> | |
208 | + | |
209 | + {/* Progress Section */} | |
210 | + <div className="mt-4"> | |
211 | + <div className="flex items-center justify-between text-xs text-gray-500 mb-2"> | |
212 | + <span className="flex items-center gap-3"> | |
213 | + <span className="flex items-center gap-1 text-green-400"> | |
214 | + <CheckCircle2 className="h-3.5 w-3.5" /> | |
215 | + {milestone.closedIssues} closed | |
216 | + </span> | |
217 | + <span className="flex items-center gap-1"> | |
218 | + <Circle className="h-3.5 w-3.5" /> | |
219 | + {milestone.openIssues} open | |
220 | + </span> | |
221 | + </span> | |
222 | + <span className="flex items-center gap-1 text-purple-400"> | |
223 | + <TrendingUp className="h-3.5 w-3.5" /> | |
224 | + {milestone.progress}% complete | |
225 | + </span> | |
226 | + </div> | |
227 | + | |
228 | + {/* Progress Bar */} | |
229 | + <div className="h-2 bg-white/5 rounded-full overflow-hidden"> | |
230 | + <motion.div | |
231 | + initial={{ width: 0 }} | |
232 | + animate={{ width: `${milestone.progress}%` }} | |
233 | + transition={{ duration: 0.8, ease: "easeOut" }} | |
234 | + className="h-full rounded-full bg-gradient-to-r from-purple-500 to-pink-500" | |
235 | + /> | |
236 | + </div> | |
237 | + </div> | |
238 | + </div> | |
239 | + </motion.div> | |
240 | + )) | |
241 | + ) : ( | |
242 | + <motion.div | |
243 | + initial={{ opacity: 0 }} | |
244 | + animate={{ opacity: 1 }} | |
245 | + className="text-center py-16" | |
246 | + > | |
247 | + <div className="relative inline-block mb-4"> | |
248 | + <Milestone className="h-16 w-16 text-purple-400" /> | |
249 | + <div className="absolute inset-0 bg-purple-500 blur-xl opacity-30" /> | |
250 | + </div> | |
251 | + <h3 className="text-xl font-semibold text-white mb-2"> | |
252 | + {filter === "closed" ? "No closed milestones" : "No milestones yet"} | |
253 | + </h3> | |
254 | + <p className="text-gray-500 mb-6 max-w-md mx-auto"> | |
255 | + {filter === "closed" | |
256 | + ? "Closed milestones will appear here." | |
257 | + : "Milestones help you track progress on groups of issues and pull requests."} | |
258 | + </p> | |
259 | + {filter === "open" && ( | |
260 | + <motion.a | |
261 | + href={`/${repoOwner}/${repoName}/milestones/new`} | |
262 | + whileHover={{ scale: 1.05 }} | |
263 | + whileTap={{ scale: 0.95 }} | |
264 | + className="inline-flex items-center gap-2 px-6 py-2.5 rounded-lg bg-gradient-to-r from-purple-500 to-pink-600 text-sm font-medium text-white shadow-lg shadow-purple-500/25" | |
265 | + > | |
266 | + <Plus className="h-4 w-4" /> | |
267 | + Create a milestone | |
268 | + </motion.a> | |
269 | + )} | |
270 | + </motion.div> | |
271 | + )} | |
272 | + </AnimatePresence> | |
273 | + </div> | |
274 | + </div> | |
275 | + ); | |
276 | +} | |
0 | 277 | |
▼src/components/milestones/NewMilestoneForm.tsx
+169-0
| @@ -0,0 +1,169 @@ | ||
1 | +"use client"; | |
2 | +import { motion } from "framer-motion"; | |
3 | +import { | |
4 | + Target, | |
5 | + Calendar, | |
6 | + FileText, | |
7 | + ArrowLeft, | |
8 | + Save, | |
9 | + X | |
10 | +} from "lucide-react"; | |
11 | +import { useState } from "react"; | |
12 | + | |
13 | +interface Props { | |
14 | + repoOwner: string; | |
15 | + repoName: string; | |
16 | +} | |
17 | + | |
18 | +export default function NewMilestoneForm({ repoOwner, repoName }: Props) { | |
19 | + const [title, setTitle] = useState(""); | |
20 | + const [description, setDescription] = useState(""); | |
21 | + const [dueDate, setDueDate] = useState(""); | |
22 | + const [isSubmitting, setIsSubmitting] = useState(false); | |
23 | + | |
24 | + const handleSubmit = async (e: React.FormEvent) => { | |
25 | + e.preventDefault(); | |
26 | + if (!title.trim()) return; | |
27 | + | |
28 | + setIsSubmitting(true); | |
29 | + | |
30 | + try { | |
31 | + const res = await fetch(`/api/repos/${repoOwner}/${repoName}/milestones`, { | |
32 | + method: 'POST', | |
33 | + headers: { 'Content-Type': 'application/json' }, | |
34 | + body: JSON.stringify({ | |
35 | + title: title.trim(), | |
36 | + description: description.trim() || null, | |
37 | + dueOn: dueDate || null, | |
38 | + }), | |
39 | + }); | |
40 | + | |
41 | + if (res.ok) { | |
42 | + window.location.href = `/${repoOwner}/${repoName}/milestones`; | |
43 | + } else { | |
44 | + alert("Failed to create milestone"); | |
45 | + } | |
46 | + } catch (error) { | |
47 | + console.error("Error creating milestone:", error); | |
48 | + alert("Failed to create milestone"); | |
49 | + } finally { | |
50 | + setIsSubmitting(false); | |
51 | + } | |
52 | + }; | |
53 | + | |
54 | + return ( | |
55 | + <div className="max-w-3xl mx-auto"> | |
56 | + {/* Header */} | |
57 | + <motion.div | |
58 | + initial={{ opacity: 0, y: -20 }} | |
59 | + animate={{ opacity: 1, y: 0 }} | |
60 | + className="mb-8" | |
61 | + > | |
62 | + <a | |
63 | + href={`/${repoOwner}/${repoName}/milestones`} | |
64 | + className="inline-flex items-center gap-2 text-sm text-gray-400 hover:text-white transition-colors mb-4" | |
65 | + > | |
66 | + <ArrowLeft className="h-4 w-4" /> | |
67 | + Back to milestones | |
68 | + </a> | |
69 | + | |
70 | + <h1 className="text-2xl font-bold text-white flex items-center gap-3"> | |
71 | + <div className="p-2 rounded-lg bg-gradient-to-br from-purple-500/20 to-pink-500/20 border border-purple-500/30"> | |
72 | + <Target className="h-6 w-6 text-purple-400" /> | |
73 | + </div> | |
74 | + New Milestone | |
75 | + </h1> | |
76 | + <p className="text-gray-500 text-sm mt-2"> | |
77 | + Create a milestone to track progress on a collection of issues and pull requests. | |
78 | + </p> | |
79 | + </motion.div> | |
80 | + | |
81 | + {/* Form */} | |
82 | + <motion.form | |
83 | + initial={{ opacity: 0, y: 20 }} | |
84 | + animate={{ opacity: 1, y: 0 }} | |
85 | + transition={{ delay: 0.1 }} | |
86 | + onSubmit={handleSubmit} | |
87 | + className="space-y-6" | |
88 | + > | |
89 | + {/* Title */} | |
90 | + <div className="relative"> | |
91 | + <div className="absolute -inset-[1px] bg-gradient-to-r from-purple-500/20 via-transparent to-pink-500/20 rounded-xl opacity-50" /> | |
92 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/80 backdrop-blur-sm p-6"> | |
93 | + <label className="block text-sm font-medium text-white mb-2 flex items-center gap-2"> | |
94 | + <FileText className="h-4 w-4 text-purple-400" /> | |
95 | + Title * | |
96 | + </label> | |
97 | + <input | |
98 | + type="text" | |
99 | + value={title} | |
100 | + onChange={(e) => setTitle(e.target.value)} | |
101 | + placeholder="Milestone title" | |
102 | + required | |
103 | + className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-white placeholder:text-gray-600 focus:outline-none focus:border-purple-500/50 focus:ring-2 focus:ring-purple-500/20 transition-all" | |
104 | + /> | |
105 | + </div> | |
106 | + </div> | |
107 | + | |
108 | + {/* Due Date */} | |
109 | + <div className="relative"> | |
110 | + <div className="absolute -inset-[1px] bg-gradient-to-r from-cyan-500/20 via-transparent to-blue-500/20 rounded-xl opacity-50" /> | |
111 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/80 backdrop-blur-sm p-6"> | |
112 | + <label className="block text-sm font-medium text-white mb-2 flex items-center gap-2"> | |
113 | + <Calendar className="h-4 w-4 text-cyan-400" /> | |
114 | + Due date (optional) | |
115 | + </label> | |
116 | + <input | |
117 | + type="date" | |
118 | + value={dueDate} | |
119 | + onChange={(e) => setDueDate(e.target.value)} | |
120 | + className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-white placeholder:text-gray-600 focus:outline-none focus:border-cyan-500/50 focus:ring-2 focus:ring-cyan-500/20 transition-all [color-scheme:dark]" | |
121 | + /> | |
122 | + <p className="text-xs text-gray-500 mt-2"> | |
123 | + Set a target date for this milestone to track deadlines. | |
124 | + </p> | |
125 | + </div> | |
126 | + </div> | |
127 | + | |
128 | + {/* Description */} | |
129 | + <div className="relative"> | |
130 | + <div className="absolute -inset-[1px] bg-gradient-to-r from-green-500/20 via-transparent to-emerald-500/20 rounded-xl opacity-50" /> | |
131 | + <div className="relative rounded-xl border border-white/10 bg-[#0d1117]/80 backdrop-blur-sm p-6"> | |
132 | + <label className="block text-sm font-medium text-white mb-2 flex items-center gap-2"> | |
133 | + <FileText className="h-4 w-4 text-green-400" /> | |
134 | + Description (optional) | |
135 | + </label> | |
136 | + <textarea | |
137 | + value={description} | |
138 | + onChange={(e) => setDescription(e.target.value)} | |
139 | + placeholder="Describe the goals of this milestone..." | |
140 | + rows={4} | |
141 | + className="w-full rounded-lg border border-white/10 bg-white/5 px-4 py-3 text-white placeholder:text-gray-600 focus:outline-none focus:border-green-500/50 focus:ring-2 focus:ring-green-500/20 transition-all resize-none" | |
142 | + /> | |
143 | + </div> | |
144 | + </div> | |
145 | + | |
146 | + {/* Action Buttons */} | |
147 | + <div className="flex items-center justify-end gap-3 pt-4"> | |
148 | + <a | |
149 | + href={`/${repoOwner}/${repoName}/milestones`} | |
150 | + className="inline-flex items-center gap-2 px-4 py-2.5 rounded-lg border border-white/10 bg-white/5 text-sm font-medium text-gray-300 hover:bg-white/10 transition-all" | |
151 | + > | |
152 | + <X className="h-4 w-4" /> | |
153 | + Cancel | |
154 | + </a> | |
155 | + <motion.button | |
156 | + type="submit" | |
157 | + disabled={!title.trim() || isSubmitting} | |
158 | + whileHover={{ scale: 1.02 }} | |
159 | + whileTap={{ scale: 0.98 }} | |
160 | + className="inline-flex items-center gap-2 px-6 py-2.5 rounded-lg bg-gradient-to-r from-purple-500 to-pink-600 text-sm font-medium text-white shadow-lg shadow-purple-500/25 hover:shadow-purple-500/40 transition-all disabled:opacity-50 disabled:cursor-not-allowed" | |
161 | + > | |
162 | + <Save className="h-4 w-4" /> | |
163 | + {isSubmitting ? "Creating..." : "Create Milestone"} | |
164 | + </motion.button> | |
165 | + </div> | |
166 | + </motion.form> | |
167 | + </div> | |
168 | + ); | |
169 | +} | |
0 | 170 | |
▼src/components/pr/AIReviewPanel.tsx
+348-0
| @@ -0,0 +1,348 @@ | ||
1 | +/** | |
2 | + * AI Review Panel Component | |
3 | + * Display AI review suggestions with apply/dismiss actions | |
4 | + */ | |
5 | + | |
6 | +import React, { useState } from "react"; | |
7 | +import { | |
8 | + Bot, | |
9 | + AlertTriangle, | |
10 | + AlertCircle, | |
11 | + Info, | |
12 | + XCircle, | |
13 | + Check, | |
14 | + X, | |
15 | + ChevronDown, | |
16 | + ChevronRight, | |
17 | + Sparkles, | |
18 | + Loader2, | |
19 | + RefreshCw, | |
20 | +} from "lucide-react"; | |
21 | + | |
22 | +interface Suggestion { | |
23 | + id: string; | |
24 | + path: string; | |
25 | + line?: number; | |
26 | + endLine?: number; | |
27 | + severity: "info" | "warning" | "error" | "critical"; | |
28 | + type: string; | |
29 | + title: string; | |
30 | + message: string; | |
31 | + suggestedFix?: string; | |
32 | + explanation?: string; | |
33 | + isApplied?: boolean; | |
34 | + isDismissed?: boolean; | |
35 | +} | |
36 | + | |
37 | +interface AIReviewPanelProps { | |
38 | + reviewId: string; | |
39 | + status: "pending" | "running" | "completed" | "failed"; | |
40 | + model: string; | |
41 | + summary?: string; | |
42 | + suggestions: Suggestion[]; | |
43 | + onApply?: (suggestionId: string) => Promise<void>; | |
44 | + onDismiss?: (suggestionId: string, reason?: string) => Promise<void>; | |
45 | + onRefresh?: () => Promise<void>; | |
46 | +} | |
47 | + | |
48 | +const severityConfig = { | |
49 | + info: { | |
50 | + icon: Info, | |
51 | + color: "text-blue-500", | |
52 | + bg: "bg-blue-500/10", | |
53 | + border: "border-blue-500/20", | |
54 | + }, | |
55 | + warning: { | |
56 | + icon: AlertTriangle, | |
57 | + color: "text-yellow-500", | |
58 | + bg: "bg-yellow-500/10", | |
59 | + border: "border-yellow-500/20", | |
60 | + }, | |
61 | + error: { | |
62 | + icon: AlertCircle, | |
63 | + color: "text-orange-500", | |
64 | + bg: "bg-orange-500/10", | |
65 | + border: "border-orange-500/20", | |
66 | + }, | |
67 | + critical: { | |
68 | + icon: XCircle, | |
69 | + color: "text-red-500", | |
70 | + bg: "bg-red-500/10", | |
71 | + border: "border-red-500/20", | |
72 | + }, | |
73 | +}; | |
74 | + | |
75 | +export function AIReviewPanel({ | |
76 | + reviewId, | |
77 | + status, | |
78 | + model, | |
79 | + summary, | |
80 | + suggestions, | |
81 | + onApply, | |
82 | + onDismiss, | |
83 | + onRefresh, | |
84 | +}: AIReviewPanelProps) { | |
85 | + const [expandedSuggestions, setExpandedSuggestions] = useState<Set<string>>( | |
86 | + new Set() | |
87 | + ); | |
88 | + const [applyingId, setApplyingId] = useState<string | null>(null); | |
89 | + const [dismissingId, setDismissingId] = useState<string | null>(null); | |
90 | + | |
91 | + const toggleExpanded = (id: string) => { | |
92 | + const newExpanded = new Set(expandedSuggestions); | |
93 | + if (newExpanded.has(id)) { | |
94 | + newExpanded.delete(id); | |
95 | + } else { | |
96 | + newExpanded.add(id); | |
97 | + } | |
98 | + setExpandedSuggestions(newExpanded); | |
99 | + }; | |
100 | + | |
101 | + const handleApply = async (suggestionId: string) => { | |
102 | + if (!onApply) return; | |
103 | + setApplyingId(suggestionId); | |
104 | + try { | |
105 | + await onApply(suggestionId); | |
106 | + } finally { | |
107 | + setApplyingId(null); | |
108 | + } | |
109 | + }; | |
110 | + | |
111 | + const handleDismiss = async (suggestionId: string) => { | |
112 | + if (!onDismiss) return; | |
113 | + setDismissingId(suggestionId); | |
114 | + try { | |
115 | + await onDismiss(suggestionId); | |
116 | + } finally { | |
117 | + setDismissingId(null); | |
118 | + } | |
119 | + }; | |
120 | + | |
121 | + const activeSuggestions = suggestions.filter( | |
122 | + (s) => !s.isApplied && !s.isDismissed | |
123 | + ); | |
124 | + const resolvedSuggestions = suggestions.filter( | |
125 | + (s) => s.isApplied || s.isDismissed | |
126 | + ); | |
127 | + | |
128 | + const severityCounts = activeSuggestions.reduce( | |
129 | + (acc, s) => { | |
130 | + acc[s.severity] = (acc[s.severity] || 0) + 1; | |
131 | + return acc; | |
132 | + }, | |
133 | + {} as Record<string, number> | |
134 | + ); | |
135 | + | |
136 | + return ( | |
137 | + <div className="rounded-lg border bg-card text-card-foreground shadow-sm"> | |
138 | + {/* Header */} | |
139 | + <div className="flex items-center justify-between border-b p-4"> | |
140 | + <div className="flex items-center gap-3"> | |
141 | + <div className="flex h-10 w-10 items-center justify-center rounded-full bg-primary/10"> | |
142 | + <Bot className="h-5 w-5 text-primary" /> | |
143 | + </div> | |
144 | + <div> | |
145 | + <h3 className="font-semibold flex items-center gap-2"> | |
146 | + AI Code Review | |
147 | + <Sparkles className="h-4 w-4 text-yellow-500" /> | |
148 | + </h3> | |
149 | + <p className="text-sm text-muted-foreground"> | |
150 | + Powered by {model} | |
151 | + </p> | |
152 | + </div> | |
153 | + </div> | |
154 | + | |
155 | + <div className="flex items-center gap-2"> | |
156 | + {status === "running" && ( | |
157 | + <div className="flex items-center gap-2 text-sm text-muted-foreground"> | |
158 | + <Loader2 className="h-4 w-4 animate-spin" /> | |
159 | + Analyzing... | |
160 | + </div> | |
161 | + )} | |
162 | + {status === "completed" && onRefresh && ( | |
163 | + <button | |
164 | + onClick={onRefresh} | |
165 | + className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors" | |
166 | + > | |
167 | + <RefreshCw className="h-4 w-4" /> | |
168 | + Re-run | |
169 | + </button> | |
170 | + )} | |
171 | + </div> | |
172 | + </div> | |
173 | + | |
174 | + {/* Status */} | |
175 | + {status === "pending" && ( | |
176 | + <div className="p-6 text-center text-muted-foreground"> | |
177 | + <Loader2 className="h-8 w-8 animate-spin mx-auto mb-2" /> | |
178 | + <p>Preparing AI review...</p> | |
179 | + </div> | |
180 | + )} | |
181 | + | |
182 | + {status === "failed" && ( | |
183 | + <div className="p-6 text-center"> | |
184 | + <XCircle className="h-8 w-8 text-red-500 mx-auto mb-2" /> | |
185 | + <p className="text-red-500 font-medium">Review failed</p> | |
186 | + <p className="text-sm text-muted-foreground mt-1"> | |
187 | + Please try again or check your configuration | |
188 | + </p> | |
189 | + </div> | |
190 | + )} | |
191 | + | |
192 | + {status === "completed" && ( | |
193 | + <> | |
194 | + {/* Summary */} | |
195 | + {summary && ( | |
196 | + <div className="p-4 border-b bg-muted/30"> | |
197 | + <p className="text-sm">{summary}</p> | |
198 | + </div> | |
199 | + )} | |
200 | + | |
201 | + {/* Stats */} | |
202 | + <div className="flex items-center gap-4 p-4 border-b"> | |
203 | + <div className="text-sm text-muted-foreground"> | |
204 | + {activeSuggestions.length} active suggestions | |
205 | + </div> | |
206 | + <div className="flex items-center gap-2"> | |
207 | + {severityCounts.critical && ( | |
208 | + <span className="flex items-center gap-1 text-xs text-red-500"> | |
209 | + <XCircle className="h-3 w-3" /> | |
210 | + {severityCounts.critical} critical | |
211 | + </span> | |
212 | + )} | |
213 | + {severityCounts.error && ( | |
214 | + <span className="flex items-center gap-1 text-xs text-orange-500"> | |
215 | + <AlertCircle className="h-3 w-3" /> | |
216 | + {severityCounts.error} errors | |
217 | + </span> | |
218 | + )} | |
219 | + {severityCounts.warning && ( | |
220 | + <span className="flex items-center gap-1 text-xs text-yellow-500"> | |
221 | + <AlertTriangle className="h-3 w-3" /> | |
222 | + {severityCounts.warning} warnings | |
223 | + </span> | |
224 | + )} | |
225 | + </div> | |
226 | + </div> | |
227 | + | |
228 | + {/* Suggestions List */} | |
229 | + <div className="divide-y"> | |
230 | + {activeSuggestions.length === 0 ? ( | |
231 | + <div className="p-6 text-center text-muted-foreground"> | |
232 | + <Check className="h-8 w-8 text-green-500 mx-auto mb-2" /> | |
233 | + <p>All clear! No issues found.</p> | |
234 | + </div> | |
235 | + ) : ( | |
236 | + activeSuggestions.map((suggestion) => { | |
237 | + const config = severityConfig[suggestion.severity]; | |
238 | + const Icon = config.icon; | |
239 | + const isExpanded = expandedSuggestions.has(suggestion.id); | |
240 | + | |
241 | + return ( | |
242 | + <div | |
243 | + key={suggestion.id} | |
244 | + className={`${config.bg} ${config.border} border-l-4`} | |
245 | + > | |
246 | + {/* Suggestion Header */} | |
247 | + <button | |
248 | + className="w-full flex items-start gap-3 p-4 text-left hover:bg-muted/50 transition-colors" | |
249 | + onClick={() => toggleExpanded(suggestion.id)} | |
250 | + > | |
251 | + <Icon className={`h-5 w-5 mt-0.5 ${config.color}`} /> | |
252 | + <div className="flex-1 min-w-0"> | |
253 | + <div className="flex items-center gap-2"> | |
254 | + <span className="font-medium">{suggestion.title}</span> | |
255 | + <span className="text-xs bg-muted px-1.5 py-0.5 rounded"> | |
256 | + {suggestion.type} | |
257 | + </span> | |
258 | + </div> | |
259 | + <div className="text-sm text-muted-foreground mt-1"> | |
260 | + <code className="bg-muted px-1 rounded"> | |
261 | + {suggestion.path} | |
262 | + {suggestion.line && `:${suggestion.line}`} | |
263 | + </code> | |
264 | + </div> | |
265 | + </div> | |
266 | + {isExpanded ? ( | |
267 | + <ChevronDown className="h-5 w-5 text-muted-foreground" /> | |
268 | + ) : ( | |
269 | + <ChevronRight className="h-5 w-5 text-muted-foreground" /> | |
270 | + )} | |
271 | + </button> | |
272 | + | |
273 | + {/* Expanded Content */} | |
274 | + {isExpanded && ( | |
275 | + <div className="px-4 pb-4 pl-12"> | |
276 | + <p className="text-sm mb-3">{suggestion.message}</p> | |
277 | + | |
278 | + {suggestion.explanation && ( | |
279 | + <div className="text-sm text-muted-foreground mb-3 p-2 bg-muted/50 rounded"> | |
280 | + <strong>Why:</strong> {suggestion.explanation} | |
281 | + </div> | |
282 | + )} | |
283 | + | |
284 | + {suggestion.suggestedFix && ( | |
285 | + <div className="mb-3"> | |
286 | + <p className="text-xs text-muted-foreground mb-1"> | |
287 | + Suggested fix: | |
288 | + </p> | |
289 | + <pre className="text-sm bg-muted p-2 rounded overflow-x-auto"> | |
290 | + <code>{suggestion.suggestedFix}</code> | |
291 | + </pre> | |
292 | + </div> | |
293 | + )} | |
294 | + | |
295 | + {/* Actions */} | |
296 | + <div className="flex items-center gap-2"> | |
297 | + {suggestion.suggestedFix && onApply && ( | |
298 | + <button | |
299 | + onClick={() => handleApply(suggestion.id)} | |
300 | + disabled={applyingId === suggestion.id} | |
301 | + className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm bg-green-600 text-white rounded-md hover:bg-green-700 disabled:opacity-50 transition-colors" | |
302 | + > | |
303 | + {applyingId === suggestion.id ? ( | |
304 | + <Loader2 className="h-4 w-4 animate-spin" /> | |
305 | + ) : ( | |
306 | + <Check className="h-4 w-4" /> | |
307 | + )} | |
308 | + Apply Fix | |
309 | + </button> | |
310 | + )} | |
311 | + {onDismiss && ( | |
312 | + <button | |
313 | + onClick={() => handleDismiss(suggestion.id)} | |
314 | + disabled={dismissingId === suggestion.id} | |
315 | + className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm border rounded-md hover:bg-muted transition-colors" | |
316 | + > | |
317 | + {dismissingId === suggestion.id ? ( | |
318 | + <Loader2 className="h-4 w-4 animate-spin" /> | |
319 | + ) : ( | |
320 | + <X className="h-4 w-4" /> | |
321 | + )} | |
322 | + Dismiss | |
323 | + </button> | |
324 | + )} | |
325 | + </div> | |
326 | + </div> | |
327 | + )} | |
328 | + </div> | |
329 | + ); | |
330 | + }) | |
331 | + )} | |
332 | + </div> | |
333 | + | |
334 | + {/* Resolved suggestions */} | |
335 | + {resolvedSuggestions.length > 0 && ( | |
336 | + <div className="border-t p-4"> | |
337 | + <p className="text-sm text-muted-foreground"> | |
338 | + {resolvedSuggestions.length} resolved suggestions | |
339 | + </p> | |
340 | + </div> | |
341 | + )} | |
342 | + </> | |
343 | + )} | |
344 | + </div> | |
345 | + ); | |
346 | +} | |
347 | + | |
348 | +export default AIReviewPanel; | |
0 | 349 | |
▼src/components/pr/StackVisualization.tsx
+230-0
| @@ -0,0 +1,230 @@ | ||
1 | +/** | |
2 | + * Stack Visualization Component | |
3 | + * Visual tree/timeline showing stacked PRs with status and navigation | |
4 | + */ | |
5 | + | |
6 | +import React from "react"; | |
7 | +import { | |
8 | + GitPullRequest, | |
9 | + GitMerge, | |
10 | + CircleDot, | |
11 | + Circle, | |
12 | + FileEdit, | |
13 | + ChevronRight, | |
14 | + Layers, | |
15 | + ArrowUp | |
16 | +} from "lucide-react"; | |
17 | + | |
18 | +interface StackEntry { | |
19 | + order: number; | |
20 | + pr: { | |
21 | + id: string; | |
22 | + number: number; | |
23 | + title: string; | |
24 | + state: string; | |
25 | + isDraft: boolean; | |
26 | + headBranch: string; | |
27 | + isMerged: boolean; | |
28 | + }; | |
29 | + parentPrId: string | null; | |
30 | +} | |
31 | + | |
32 | +interface StackVisualizationProps { | |
33 | + stackId: string; | |
34 | + stackName?: string; | |
35 | + baseBranch: string; | |
36 | + entries: StackEntry[]; | |
37 | + currentPrId?: string; | |
38 | + owner: string; | |
39 | + repo: string; | |
40 | + onReorder?: (newOrder: string[]) => void; | |
41 | + compact?: boolean; | |
42 | +} | |
43 | + | |
44 | +export function StackVisualization({ | |
45 | + stackId, | |
46 | + stackName, | |
47 | + baseBranch, | |
48 | + entries, | |
49 | + currentPrId, | |
50 | + owner, | |
51 | + repo, | |
52 | + onReorder, | |
53 | + compact = false, | |
54 | +}: StackVisualizationProps) { | |
55 | + const getStatusIcon = (entry: StackEntry) => { | |
56 | + if (entry.pr.isMerged) { | |
57 | + return <GitMerge className="h-4 w-4 text-purple-500" />; | |
58 | + } | |
59 | + if (entry.pr.isDraft) { | |
60 | + return <FileEdit className="h-4 w-4 text-gray-400" />; | |
61 | + } | |
62 | + if (entry.pr.state === "open") { | |
63 | + return <GitPullRequest className="h-4 w-4 text-green-500" />; | |
64 | + } | |
65 | + return <Circle className="h-4 w-4 text-red-500" />; | |
66 | + }; | |
67 | + | |
68 | + const getStatusColor = (entry: StackEntry) => { | |
69 | + if (entry.pr.isMerged) return "border-purple-500 bg-purple-500/10"; | |
70 | + if (entry.pr.isDraft) return "border-gray-400 bg-gray-400/10"; | |
71 | + if (entry.pr.state === "open") return "border-green-500 bg-green-500/10"; | |
72 | + return "border-red-500 bg-red-500/10"; | |
73 | + }; | |
74 | + | |
75 | + const sortedEntries = [...entries].sort((a, b) => a.order - b.order); | |
76 | + | |
77 | + if (compact) { | |
78 | + return ( | |
79 | + <div className="flex items-center gap-2 text-sm text-muted-foreground"> | |
80 | + <Layers className="h-4 w-4" /> | |
81 | + <span>Stack of {entries.length}</span> | |
82 | + <div className="flex items-center gap-1"> | |
83 | + {sortedEntries.map((entry, i) => ( | |
84 | + <React.Fragment key={entry.pr.id}> | |
85 | + <a | |
86 | + href={`/${owner}/${repo}/pulls/${entry.pr.number}`} | |
87 | + className={`px-1.5 py-0.5 rounded text-xs font-medium transition-colors ${entry.pr.id === currentPrId | |
88 | + ? "bg-primary text-primary-foreground" | |
89 | + : "bg-muted hover:bg-muted/80" | |
90 | + }`} | |
91 | + > | |
92 | + #{entry.pr.number} | |
93 | + </a> | |
94 | + {i < sortedEntries.length - 1 && ( | |
95 | + <ChevronRight className="h-3 w-3 text-muted-foreground/50" /> | |
96 | + )} | |
97 | + </React.Fragment> | |
98 | + ))} | |
99 | + </div> | |
100 | + </div> | |
101 | + ); | |
102 | + } | |
103 | + | |
104 | + return ( | |
105 | + <div className="rounded-lg border bg-card text-card-foreground shadow-sm"> | |
106 | + {/* Header */} | |
107 | + <div className="flex items-center justify-between border-b p-4"> | |
108 | + <div className="flex items-center gap-2"> | |
109 | + <Layers className="h-5 w-5 text-primary" /> | |
110 | + <h3 className="font-semibold"> | |
111 | + {stackName || "Stacked Changes"} | |
112 | + </h3> | |
113 | + <span className="text-xs text-muted-foreground bg-muted px-2 py-0.5 rounded-full"> | |
114 | + {entries.length} PRs | |
115 | + </span> | |
116 | + </div> | |
117 | + <div className="text-xs text-muted-foreground"> | |
118 | + Base: <code className="bg-muted px-1.5 py-0.5 rounded">{baseBranch}</code> | |
119 | + </div> | |
120 | + </div> | |
121 | + | |
122 | + {/* Stack Visualization */} | |
123 | + <div className="p-4"> | |
124 | + <div className="relative"> | |
125 | + {/* Vertical line */} | |
126 | + <div className="absolute left-4 top-0 bottom-0 w-0.5 bg-border" /> | |
127 | + | |
128 | + {/* Base branch indicator */} | |
129 | + <div className="relative flex items-center gap-3 mb-4"> | |
130 | + <div className="relative z-10 flex h-8 w-8 items-center justify-center rounded-full border-2 border-primary bg-background"> | |
131 | + <CircleDot className="h-4 w-4 text-primary" /> | |
132 | + </div> | |
133 | + <div className="flex-1"> | |
134 | + <div className="text-sm font-medium">{baseBranch}</div> | |
135 | + <div className="text-xs text-muted-foreground">Base branch</div> | |
136 | + </div> | |
137 | + </div> | |
138 | + | |
139 | + {/* Stack entries */} | |
140 | + {sortedEntries.map((entry, index) => { | |
141 | + const isCurrent = entry.pr.id === currentPrId; | |
142 | + const isLast = index === sortedEntries.length - 1; | |
143 | + | |
144 | + return ( | |
145 | + <div | |
146 | + key={entry.pr.id} | |
147 | + className={`relative flex items-start gap-3 ${isLast ? "" : "mb-3" | |
148 | + }`} | |
149 | + > | |
150 | + {/* Arrow indicator */} | |
151 | + <div className="absolute -left-0.5 -top-6 flex items-center justify-center w-9"> | |
152 | + <ArrowUp className="h-4 w-4 text-muted-foreground/50" /> | |
153 | + </div> | |
154 | + | |
155 | + {/* Node */} | |
156 | + <div | |
157 | + className={`relative z-10 flex h-8 w-8 items-center justify-center rounded-full border-2 bg-background transition-all ${isCurrent | |
158 | + ? "ring-2 ring-primary ring-offset-2 ring-offset-background" | |
159 | + : "" | |
160 | + } ${getStatusColor(entry)}`} | |
161 | + > | |
162 | + {getStatusIcon(entry)} | |
163 | + </div> | |
164 | + | |
165 | + {/* PR Info */} | |
166 | + <a | |
167 | + href={`/${owner}/${repo}/pulls/${entry.pr.number}`} | |
168 | + className={`flex-1 rounded-lg border p-3 transition-all hover:shadow-md ${isCurrent | |
169 | + ? "border-primary bg-primary/5" | |
170 | + : "hover:border-primary/50" | |
171 | + }`} | |
172 | + > | |
173 | + <div className="flex items-start justify-between gap-2"> | |
174 | + <div className="flex-1 min-w-0"> | |
175 | + <div className="flex items-center gap-2"> | |
176 | + <span className="text-sm font-medium text-muted-foreground"> | |
177 | + #{entry.pr.number} | |
178 | + </span> | |
179 | + {entry.pr.isDraft && ( | |
180 | + <span className="text-xs bg-muted text-muted-foreground px-1.5 py-0.5 rounded"> | |
181 | + Draft | |
182 | + </span> | |
183 | + )} | |
184 | + {entry.pr.isMerged && ( | |
185 | + <span className="text-xs bg-purple-500/10 text-purple-500 px-1.5 py-0.5 rounded"> | |
186 | + Merged | |
187 | + </span> | |
188 | + )} | |
189 | + {isCurrent && ( | |
190 | + <span className="text-xs bg-primary/10 text-primary px-1.5 py-0.5 rounded"> | |
191 | + Current | |
192 | + </span> | |
193 | + )} | |
194 | + </div> | |
195 | + <h4 className="font-medium mt-1 line-clamp-1"> | |
196 | + {entry.pr.title} | |
197 | + </h4> | |
198 | + <div className="text-xs text-muted-foreground mt-1"> | |
199 | + <code className="bg-muted px-1 py-0.5 rounded"> | |
200 | + {entry.pr.headBranch} | |
201 | + </code> | |
202 | + </div> | |
203 | + </div> | |
204 | + <div className="text-xs text-muted-foreground"> | |
205 | + {entry.order} of {entries.length} | |
206 | + </div> | |
207 | + </div> | |
208 | + </a> | |
209 | + </div> | |
210 | + ); | |
211 | + })} | |
212 | + </div> | |
213 | + </div> | |
214 | + | |
215 | + {/* Actions */} | |
216 | + <div className="border-t p-3 flex items-center justify-between"> | |
217 | + <div className="text-xs text-muted-foreground"> | |
218 | + {sortedEntries.filter((e) => e.pr.isMerged).length} of {entries.length} merged | |
219 | + </div> | |
220 | + <div className="flex items-center gap-2"> | |
221 | + <button className="text-xs text-primary hover:underline"> | |
222 | + View all changes | |
223 | + </button> | |
224 | + </div> | |
225 | + </div> | |
226 | + </div> | |
227 | + ); | |
228 | +} | |
229 | + | |
230 | +export default StackVisualization; | |
0 | 231 | |