chore: eslint & ai rules

This commit is contained in:
ccbikai 2025-07-20 11:34:36 +08:00
parent ebd69c074f
commit 225579ba68
26 changed files with 253 additions and 1548 deletions

1
.clinerules Symbolic link
View file

@ -0,0 +1 @@
AGENT.md

1
.cursor/rules/AGENT.md Symbolic link
View file

@ -0,0 +1 @@
../../AGENT.md

View file

@ -1,88 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# API Design Patterns
## API Route Structure
### Link Management API
Located in the `server/api/link/` directory, containing the following endpoints:
- [create.post.ts](mdc:server/api/link/create.post.ts): Create new short links
- [edit.put.ts](mdc:server/api/link/edit.put.ts): Edit existing links
- [delete.post.ts](mdc:server/api/link/delete.post.ts): Delete links
- [list.get.ts](mdc:server/api/link/list.get.ts): Get link lists
- [query.get.ts](mdc:server/api/link/query.get.ts): Query specific links
- [search.get.ts](mdc:server/api/link/search.get.ts): Search links
- [upsert.post.ts](mdc:server/api/link/upsert.post.ts): Create or update links
- [ai.get.ts](mdc:server/api/link/ai.get.ts): AI-generated slugs
### Other APIs
- [location.ts](mdc:server/api/location.ts): Geographic location information
- [verify.ts](mdc:server/api/verify.ts): Authentication
- `stats/`: Analytics-related APIs
- `logs/`: Logging-related APIs
- `ws/`: WebSocket-related APIs
## API Design Conventions
### HTTP Methods
- `GET`: Retrieve data (queries, lists)
- `POST`: Create resources or perform operations
- `PUT`: Update resources
- `DELETE`: Remove resources
### Response Format
```typescript
// Success response
{
success: true,
data: any
}
// Error response
{
success: false,
error: string,
code?: number
}
```
### Authentication
- Use Site Token for authentication
- Pass token in request header: `Authorization: Bearer <token>`
### Data Validation
- Use Zod for input validation
- Define validation schemas in `schemas/` directory
- Unified error handling and response format
### Cloudflare Integration
- Use Cloudflare Workers KV for data storage
- Leverage Cloudflare Analytics Engine for statistics
- Use Cloudflare AI for slug generation
## Error Handling
### Common Error Codes
- `400`: Bad request parameters
- `401`: Unauthorized access
- `403`: Insufficient permissions
- `404`: Resource not found
- `429`: Rate limit exceeded
- `500`: Internal server error
### Error Handling Pattern
```typescript
try {
// API logic
return { success: true, data: result }
} catch (error) {
return createError({
statusCode: 400,
statusMessage: error.message
})
}
```

View file

@ -1,393 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# Coding Standards
## Code Quality
### TypeScript Requirements
- All new code must use TypeScript
- Enable strict type checking
- Avoid using `any` type, prefer specific types
- Define interfaces or type aliases for complex objects
```typescript
// ✅ Good practice
interface LinkData {
slug: string
url: string
createdAt: Date
expiresAt?: Date
}
// ❌ Avoid
const linkData: any = { ... }
```
### Function and Variable Naming
- Use descriptive names
- Function names should start with verbs
- Boolean variables use `is`, `has`, `should` prefixes
- Constants use UPPER_SNAKE_CASE
```typescript
// ✅ Good practice
const isValidUrl = (url: string): boolean => { ... }
const hasExpired = computed(() => { ... })
const MAX_SLUG_LENGTH = 50
// ❌ Avoid
const check = (u: string) => { ... }
const expired = computed(() => { ... })
const maxLen = 50
```
## Security Requirements
### Input Validation
- All user input must be validated using Zod
- API endpoints must validate request parameters
- Frontend forms use vee-validate + Zod
```typescript
// API input validation
import { z } from 'zod'
const createLinkSchema = z.object({
url: z.string().url(),
slug: z.string().min(1).max(50).optional(),
expiresAt: z.string().datetime().optional()
})
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const validatedData = createLinkSchema.parse(body)
// ...
})
```
### Authentication
- Use Site Token for authentication
- Sensitive operations require permission verification
- Don't store sensitive information on the client
### Data Sanitization
- Sanitize user-input HTML content
- Validate URL format and security
- Prevent XSS and injection attacks
## Performance Best Practices
### Reactive Data
- Use `ref` and `reactive` appropriately
- Avoid unnecessary reactive wrapping
- Use `readonly` to protect data
```typescript
// ✅ Good practice
const { data: links, pending } = await $fetch('/api/links')
const filteredLinks = computed(() =>
links.value.filter(link => link.isActive)
)
// ❌ Avoid
const links = ref([])
const filteredLinks = ref([])
watch(links, () => {
filteredLinks.value = links.value.filter(link => link.isActive)
})
```
### Async Operations
- Use `async/await` instead of Promise chains
- Handle errors and loading states properly
- Avoid blocking UI operations
```typescript
// ✅ Good practice
const { data, error, pending } = await useFetch('/api/links')
// Error handling
try {
const result = await $fetch('/api/link/create', { ... })
} catch (error) {
// Handle error
}
```
## Code Organization
### File Structure
- Each file should export only one main functionality
- Group related functionality in the same directory
- Use `index.ts` as directory entry point
### Import/Export
- Use ES6 module syntax
- Organize imports alphabetically
- Separate third-party and local imports
```typescript
// Third-party libraries
import { ref, computed } from 'vue'
import { z } from 'zod'
// Local imports
import type { LinkData } from '~/types'
import { validateUrl } from '~/utils/validation'
```
### Comments and Documentation
- Add comments for complex logic
- Use JSDoc comments for public APIs
- Keep comments in sync with code
```typescript
/**
* Create a new short link
* @param url - Original URL
* @param slug - Custom slug (optional)
* @returns Created link data
*/
export async function createLink(url: string, slug?: string): Promise<LinkData> {
// Implementation logic
}
```
## Testing Standards
### Unit Testing Requirements
- **Mandatory for all `server/api` endpoints** - Every API route must have comprehensive unit tests
- Use Vitest as the primary testing framework with Nuxt Test Utils
- Follow the Test-Driven Development (TDD) approach when possible
- Maintain test coverage above 80% for API endpoints
### Testing Scope and Strategy
- **API Layer**: Test all endpoints in [server/api/](mdc:server/api/) directory
- **Validation Logic**: Test Zod schemas and input validation
- **Error Handling**: Verify error responses and status codes
- **Authentication**: Test token validation and authorization flows
- **Business Logic**: Cover edge cases and critical functionality
### Test File Organization
```typescript
// File naming convention
tests/api/link/create.test.ts // Unit tests for API endpoints
tests/api/helpers/fixtures.ts // Shared test data
tests/sink.spec.ts // Integration/E2E tests
```
### Testing Best Practices
- **Write descriptive test names** that explain the behavior being tested
- **Test both happy path and error scenarios**
- **Use meaningful assertions** that validate expected behavior
- **Keep tests independent** - no test should depend on another test's state
- **Mock external dependencies** to ensure test reliability
```typescript
// ✅ Good test structure
describe('POST /api/link/create', () => {
it('should create a new link with valid URL and custom slug', async () => {
const response = await $fetch('/api/link/create', {
method: 'POST',
body: { url: 'https://example.com', slug: 'my-link' }
})
expect(response.success).toBe(true)
expect(response.data.slug).toBe('my-link')
expect(response.data.url).toBe('https://example.com')
})
it('should reject requests with invalid URL format', async () => {
await expect($fetch('/api/link/create', {
method: 'POST',
body: { url: 'not-a-valid-url' }
})).rejects.toThrow()
})
})
```
### Test Data Management
- Use factory functions for creating test data
- Store common test fixtures in `tests/api/helpers/fixtures.ts`
- Avoid hardcoded values in tests; use constants and variables
```typescript
// ✅ Good test data management
const validLinkPayload = {
url: 'https://example.com',
slug: 'test-slug',
expiresAt: new Date(Date.now() + 86400000).toISOString()
}
const invalidUrlPayload = {
url: 'invalid-url-format',
slug: 'test-slug'
}
```
### Running and Maintaining Tests
- Run tests locally before pushing changes: `npm run test`
- Use watch mode during development: `npm run test:watch`
- Generate coverage reports: `npm run test:coverage`
- Tests must pass in CI/CD pipeline before merging
## Git Commit Conventions
### Commit Message Format
Follow the conventional commit specification:
```
type(scope): brief description
Optional body explaining what and why vs how
Optional footer with issue references
```
### Commit Types
- **feat**: New feature implementation
- **fix**: Bug fix or issue resolution
- **docs**: Documentation updates only
- **style**: Code formatting changes (no functional impact)
- **refactor**: Code restructuring without changing functionality
- **perf**: Performance optimization
- **test**: Adding or modifying tests
- **chore**: Build process, dependency updates, or auxiliary tool changes
- **ci**: Continuous integration configuration changes
### Scope Examples
- **api**: Changes to server/api endpoints
- **ui**: Frontend components or styling
- **auth**: Authentication related changes
- **db**: Database or data storage changes
- **config**: Configuration file updates
### Writing Good Commit Messages
#### Good Examples
```bash
feat(api): add AI-powered slug generation endpoint
Integrate Cloudflare AI Workers to automatically generate
meaningful slugs based on URL content and page titles.
- Add new /api/link/ai endpoint
- Implement content analysis logic
- Update link creation flow to support AI suggestions
- Add comprehensive test coverage
Closes #123
```
```bash
fix(auth): resolve token validation edge case
Handle expired tokens more gracefully by returning proper
401 status code instead of throwing unhandled exceptions.
Fixes #456
```
```bash
test(api): add comprehensive coverage for link endpoints
- Add unit tests for create, edit, delete operations
- Include validation error scenarios
- Test authentication requirements
- Achieve 85% coverage for link management APIs
```
#### Avoid These Patterns
```bash
# ❌ Too vague
fix: stuff
# ❌ Not descriptive
update code
# ❌ Multiple concerns in one commit
feat: add new API endpoint and fix UI bug and update docs
# ❌ All caps or inconsistent formatting
FIX: BROKEN AUTH SYSTEM
```
### Commit Best Practices
#### Single Responsibility
Each commit should represent one logical change:
- One feature addition
- One bug fix
- One refactoring operation
- One documentation update
#### Atomic Commits
Commits should be complete and functional:
- Code compiles without errors
- Tests pass
- No broken functionality introduced
#### Meaningful Descriptions
- Use imperative mood ("add feature" not "added feature")
- Capitalize the first letter
- No period at the end of the subject line
- Keep subject line under 50 characters
- Use body for detailed explanations when needed
### Branch Naming Conventions
```bash
# Feature branches
feature/ai-slug-generation
feature/link-expiration
# Bug fix branches
fix/auth-token-validation
fix/mobile-responsive-layout
# Documentation branches
docs/api-endpoint-guide
docs/deployment-instructions
# Hotfix branches
hotfix/security-vulnerability
hotfix/production-crash
```
### Pull Request Guidelines
#### PR Title Format
Use the same format as commit messages:
```
feat(api): add AI-powered slug generation
fix(auth): resolve token validation edge case
docs(readme): update installation instructions
```
#### PR Description Template
```markdown
## Summary
Brief description of changes made
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Documentation update
- [ ] Refactoring
- [ ] Performance improvement
## Testing
- [ ] Unit tests added/updated
- [ ] Integration tests pass
- [ ] Manual testing completed
## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Documentation updated if needed
- [ ] Tests cover new functionality
- [ ] No breaking changes introduced
```
This standardized approach ensures clean, readable commit history and makes it easier to track changes, generate changelogs, and collaborate effectively with team members.

View file

@ -1,156 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# Vue Component Design Patterns
## Component Directory Structure
### UI Components (`app/components/ui/`)
Reusable UI components based on shadcn-vue:
- Buttons, inputs, dialogs, and other fundamental components
- Follow shadcn-vue design system
- Styled with Tailwind CSS
- Support theme switching (light/dark mode)
### Business Components
- `dashboard/`: Dashboard-related components
- `home/`: Homepage-related components
- `login/`: Login-related components
- `layouts/`: Layout components
- `spark-ui/`: Special UI components
### Global Components
- [SwitchLanguage.vue](mdc:app/components/SwitchLanguage.vue): Language switching component
- [SwitchTheme.vue](mdc:app/components/SwitchTheme.vue): Theme switching component
## Component Development Standards
### Component Structure
```vue
<script setup lang="ts">
// Imports
import { ref, computed } from 'vue'
// Type definitions
interface Props {
// props types
}
// Props and Emits
const props = defineProps<Props>()
const emit = defineEmits<{
// event types
}>()
// Reactive data
const state = ref()
// Computed properties
const computed = computed(() => {
// computation logic
})
// Methods
function handleAction() {
// handler logic
}
</script>
<template>
<!-- template -->
</template>
<style scoped>
/* styles (if needed) */
</style>
```
### Naming Conventions
- Component files use PascalCase: `MyComponent.vue`
- Internal variables use camelCase
- CSS class names use Tailwind CSS utility classes
- Event names use kebab-case
### Props and Events
```typescript
// Props type definition
interface Props {
title: string
isVisible?: boolean
items: Array<Item>
}
// Events type definition
const emit = defineEmits<{
update: [value: string]
close: []
select: [item: Item]
}>()
```
## State Management
### Composables (`app/composables/`)
- Use VueUse utility functions
- Create custom composables for shared state management
- Follow `use` prefix naming convention
### Reactive State
```typescript
// composables/useAuth.ts
export function useAuth() {
const isAuthenticated = ref(false)
const user = ref(null)
const login = async (token: string) => {
// login logic
}
const logout = () => {
// logout logic
}
return {
isAuthenticated: readonly(isAuthenticated),
user: readonly(user),
login,
logout
}
}
```
## Styling and Theming
### Tailwind CSS
- Use Tailwind utility classes for styling
- Configuration file: [tailwind.config.js](mdc:tailwind.config.js)
- Support responsive design and dark mode
### Theme System
- Use `@nuxtjs/color-mode` module
- Support automatic system theme detection
- Theme switching component: [SwitchTheme.vue](mdc:app/components/SwitchTheme.vue)
### Animation Effects
- Use `@vueuse/motion` for animations
- Use animation classes provided by `tailwindcss-animate`
- Keep animations simple and performance-friendly
## Internationalization
### Multi-language Support
- Use `@nuxtjs/i18n` module
- Language switching component: [SwitchLanguage.vue](mdc:app/components/SwitchLanguage.vue)
- Store translation files in `i18n/` directory
### Usage
```vue
<template>
<div>
<h1>{{ $t('welcome.title') }}</h1>
<p>{{ $t('welcome.description') }}</p>
</div>
</template>
```

View file

@ -1,139 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# Deployment and Infrastructure
## Cloudflare Platform
Sink runs entirely on the Cloudflare platform, leveraging the following services:
### Cloudflare Workers
- Serverless computing platform
- Global edge network deployment
- Configuration file: [wrangler.jsonc](mdc:wrangler.jsonc)
### Cloudflare Pages
- Static website hosting
- Automatic build and deployment
- Preview deployment support
### Cloudflare Workers KV
- Key-value storage database
- Globally distributed storage
- Used for storing link data and configuration
### Cloudflare Analytics Engine
- Real-time analytics data collection
- High-performance time-series database
- Used for link click statistics
### Cloudflare AI
- AI model inference service
- Used for automatic slug generation
- Model: `@cf/meta/llama-3.1-8b-instruct`
## Deployment Configuration
### Environment Variables
Defined in `runtimeConfig` in [nuxt.config.ts](mdc:nuxt.config.ts):
```typescript
runtimeConfig: {
siteToken: 'SinkCool', // Site access token
redirectStatusCode: '301', // Redirect status code
linkCacheTtl: 60, // Link cache time
redirectWithQuery: false, // Whether to preserve query parameters
homeURL: '', // Home URL
cfAccountId: '', // Cloudflare Account ID
cfApiToken: '', // Cloudflare API token
dataset: 'sink', // Analytics dataset name
aiModel: '@cf/meta/llama-3.1-8b-instruct', // AI model
caseSensitive: false, // Case sensitivity
listQueryLimit: 500, // List query limit
disableBotAccessLog: false, // Disable bot access logging
}
```
### Build Scripts
- `pnpm build`: Build for production
- `pnpm build:map`: Build map data
- `pnpm build:colo`: Build Cloudflare data center information
### Deployment Commands
```bash
# Preview deployment
pnpm preview
# Production deployment
pnpm deploy
# Using Wrangler CLI
wrangler pages deploy dist
```
## Performance Optimization
### Caching Strategy
- Static assets cached using Cloudflare CDN
- Configurable link data cache TTL
- Leverage edge computing to reduce latency
### Route Rules
Configured in [nuxt.config.ts](mdc:nuxt.config.ts):
```typescript
routeRules: {
'/': {
prerender: true, // Prerender homepage
},
'/dashboard/**': {
prerender: true, // Prerender dashboard
ssr: false, // Disable server-side rendering
},
'/dashboard': {
redirect: '/dashboard/links', // Redirect to links page
},
}
```
### Build Optimization
- Use `NODE_OPTIONS=--max-old-space-size=8192` to increase memory limit
- Enable Nuxt's built-in optimization features
- Code splitting and lazy loading
## Monitoring and Logging
### Analytics Engine
- Real-time click data collection
- Geographic location statistics
- User agent analysis
- Referrer statistics
### Error Handling
- Unified error response format
- Error logging
- Performance monitoring
### Security
- Site Token authentication
- CORS configuration
- Input validation and sanitization
- Malicious link prevention
## Development Environment
### Local Development
```bash
# Start development server
pnpm dev
# Code style checking
pnpm lint
```
### Environment Configuration
- Use `.env` file for local environment variables
- Use Cloudflare Workers local simulator during development
- Support hot reload and live preview

View file

@ -1,64 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# Development Guidelines
## Code Style and Standards
### TypeScript/JavaScript
- Use TypeScript for type-safe development
- Follow ESLint rules defined in [eslint.config.mjs](mdc:eslint.config.mjs)
- Use `@antfu/eslint-config` as the base configuration
- Run `pnpm lint` to check code style, `pnpm lint:fix` to auto-fix issues
### Vue Components
- Use Composition API with `<script setup>` syntax
- Name component files using PascalCase
- Place UI components in `app/components/ui/` directory
- Place business components in `app/components/` directory
### API Development
- Place API routes in `server/api/` directory
- Use Nitro's file-system routing
- Use consistent error handling format for API responses
- Leverage Cloudflare Workers KV for data storage
## Development Tools and Scripts
### Common Commands
```bash
pnpm dev # Start development server
pnpm build # Build for production
pnpm preview # Preview built application
pnpm deploy # Deploy to Cloudflare
pnpm lint # Code style checking
pnpm lint:fix # Auto-fix code style issues
```
### Environment Configuration
- Use [wrangler.jsonc](mdc:wrangler.jsonc) to configure Cloudflare Workers
- Define environment variables in `runtimeConfig` in [nuxt.config.ts](mdc:nuxt.config.ts)
- Use `.env` file for development (not committed to version control)
## Best Practices
### Performance Optimization
- Utilize Nuxt's built-in optimization features
- Properly configure `prerender` and SSR settings
- Leverage Cloudflare's global CDN network
### Security
- Use Site Token for authentication
- Validate inputs using Zod schemas
- Follow Cloudflare Workers security best practices
### Internationalization
- Use `@nuxtjs/i18n` module
- Store translation files in [i18n/](mdc:i18n/) directory
- Support multi-language detection and switching
### Testing
- Use lint-staged to automatically check code style on commit
- Follow Git commit conventions

View file

@ -1,105 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# Sink Project Overview
Sink is a simple, speedy, and secure link shortener with analytics, running 100% on Cloudflare.
## Project Architecture
This is a full-stack application built with Nuxt.js, utilizing the following tech stack:
- **Frontend Framework**: Nuxt.js 3 ([nuxt.config.ts](mdc:nuxt.config.ts))
- **UI Components**: shadcn-vue + Tailwind CSS
- **Database**: Cloudflare Workers KV
- **Analytics Engine**: Cloudflare Workers Analytics Engine
- **Deployment Platform**: Cloudflare Workers/Pages
- **Testing Framework**: Vitest with Nuxt Test Utils
- **Type Safety**: TypeScript with strict mode enabled
## Core Features
1. **URL Shortening**: Compress long URLs into short links
2. **Analytics**: Monitor link click data and gather insightful statistics
3. **Custom Slugs**: Support for personalized slugs and case sensitivity
4. **AI Slug Generation**: Leverage AI to automatically generate slugs
5. **Link Expiration**: Set expiration dates for links
## Project Structure
- **app/**: Nuxt application frontend code
- `components/`: Vue components
- `pages/`: Page routes
- `composables/`: Composable functions
- `layouts/`: Layout components
- `middleware/`: Middleware
- **server/**: Backend API code
- `api/`: API route handlers ([server/api/](mdc:server/api/))
- `middleware/`: Server middleware
- `utils/`: Server utility functions
- **tests/**: Testing infrastructure
- `api/`: Unit tests for API endpoints
- `helpers/`: Test utilities and fixtures
- [sink.spec.ts](mdc:tests/sink.spec.ts): Integration tests
- **i18n/**: Internationalization configuration
- **schemas/**: Data validation schemas
- **scripts/**: Build scripts
## Testing Strategy
### Testing Philosophy
The project follows a **focused testing approach** where unit testing is exclusively applied to the `server/api` directory. This ensures:
- **API Reliability**: All backend endpoints are thoroughly tested
- **Data Integrity**: Input validation and business logic are verified
- **Error Handling**: Proper error responses and status codes
- **Security**: Authentication and authorization mechanisms
### Testing Architecture
- **Unit Tests**: Comprehensive coverage of all API endpoints in [server/api/](mdc:server/api/)
- **Integration Tests**: E2E testing for critical user flows
- **Test Framework**: Vitest with Nuxt Test Utils for API testing
- **Coverage Target**: Minimum 80% coverage for API endpoints
- **CI/CD Integration**: Automated testing on every pull request
### Key Test Areas
1. **Link Management APIs**: Create, edit, delete, query operations
2. **Statistics APIs**: Analytics data retrieval and processing
3. **Authentication**: Token validation and security checks
4. **Input Validation**: Zod schema validation testing
5. **Error Scenarios**: Comprehensive error handling verification
## Configuration Files
- [package.json](mdc:package.json): Project dependencies and scripts
- [nuxt.config.ts](mdc:nuxt.config.ts): Nuxt configuration
- [wrangler.jsonc](mdc:wrangler.jsonc): Cloudflare Workers configuration
- [tailwind.config.js](mdc:tailwind.config.js): Tailwind CSS configuration
- [eslint.config.mjs](mdc:eslint.config.mjs): ESLint configuration
## Development Workflow
### Quality Assurance
The project maintains high code quality through:
- **Type Safety**: Strict TypeScript configuration
- **Code Standards**: ESLint and Prettier enforcement
- **Testing Requirements**: Mandatory unit tests for all API endpoints
- **Pre-commit Hooks**: Automated linting and testing before commits
- **Code Reviews**: Comprehensive review process for all changes
### Development Commands
```bash
# Development
npm run dev # Start development server
npm run test # Run all tests
# Quality Checks
npm run lint # Run ESLint
npm run lint:fix # Fix ESLint issues
npm run build # Build for production
# Deployment
npm run deploy # Deploy to Cloudflare Pages
npm run preview # Preview with Wrangler
```

View file

@ -1,565 +0,0 @@
---
description:
globs:
alwaysApply: false
---
# Testing Guidelines for Sink
## Overview
This project implements comprehensive testing for the backend API layer using Vitest with Cloudflare Workers Pool. Testing focuses exclusively on API endpoint functionality, data validation, authentication, and business logic for the URL shortening service.
## Testing Stack
- **Test Framework**: [Vitest](https://vitest.dev/) with [`@cloudflare/vitest-pool-workers`](https://github.com/cloudflare/workers-sdk/tree/main/packages/vitest-pool-workers)
- **Mock Generation**: [`@anatine/zod-mock`](https://github.com/anatine/zod-plugins/tree/main/packages/zod-mock) for generating realistic test data
- **Fake Data**: [`@faker-js/faker`](https://fakerjs.dev/) for additional test data generation
- **Environment**: Cloudflare Workers runtime with isolated storage
## Testing Scope
### What We Test
- **API Endpoints**: All routes in [server/api/](mdc:server/api/)
- Link management APIs: [create.post.ts](mdc:server/api/link/create.post.ts), [edit.put.ts](mdc:server/api/link/edit.put.ts), [delete.post.ts](mdc:server/api/link/delete.post.ts)
- Link retrieval APIs: [list.get.ts](mdc:server/api/link/list.get.ts), [query.get.ts](mdc:server/api/link/query.get.ts), [search.get.ts](mdc:server/api/link/search.get.ts)
- Advanced features: [upsert.post.ts](mdc:server/api/link/upsert.post.ts), [ai.get.ts](mdc:server/api/link/ai.get.ts)
- Statistics endpoints: [views.get.ts](mdc:server/api/stats/views.get.ts), [counters.get.ts](mdc:server/api/stats/counters.get.ts), [metrics.get.ts](mdc:server/api/stats/metrics.get.ts)
- Utility endpoints: [location.ts](mdc:server/api/location.ts), [verify.ts](mdc:server/api/verify.ts)
- **Authentication**: Bearer token validation and authorization flows
- **Input Validation**: Zod schema validation for all API inputs
- **Error Handling**: HTTP status codes and error response structures
- **Business Logic**: URL shortening, analytics, and link management features
### What We Don't Test
- Frontend components, pages, or layouts (Vue.js application layer)
- Third-party service integrations (external API dependencies)
- Cloudflare Workers runtime internals
- Database connection layer specifics
- Build and deployment processes
## Project Structure
### Current Test Organization
```
tests/
├── api/
│ ├── link.spec.ts # All link-related endpoint tests
│ ├── location.spec.ts # Location API tests
│ └── verify.spec.ts # Authentication verification tests
├── setup.ts # Global test setup and configuration
├── utils.ts # Test utility functions and helpers
└── sink.spec.ts # End-to-end integration tests
```
### Recommended Test Structure (for expansion)
```
tests/
├── api/
│ ├── link/
│ │ ├── create.test.ts # POST /api/link/create
│ │ ├── edit.test.ts # PUT /api/link/edit
│ │ ├── delete.test.ts # POST /api/link/delete
│ │ ├── list.test.ts # GET /api/link/list
│ │ ├── query.test.ts # GET /api/link/query
│ │ ├── search.test.ts # GET /api/link/search
│ │ ├── upsert.test.ts # POST /api/link/upsert
│ │ └── ai.test.ts # GET /api/link/ai
│ ├── stats/
│ │ ├── views.test.ts # GET /api/stats/views
│ │ ├── counters.test.ts # GET /api/stats/counters
│ │ └── metrics.test.ts # GET /api/stats/metrics
│ ├── location.test.ts # GET /api/location
│ ├── verify.test.ts # GET /api/verify
│ └── helpers/
│ ├── fixtures.ts # Test data fixtures
│ ├── mocks.ts # Mock implementations
│ └── assertions.ts # Custom assertion helpers
├── setup.ts
├── utils.ts
└── integration/
└── sink.spec.ts # End-to-end workflow tests
```
## Testing Patterns
### Standard API Test Template
```typescript
import { describe, it, expect } from 'vitest'
import { generateMock } from '@anatine/zod-mock'
import { z } from 'zod'
import { fetch, fetchWithAuth } from '../utils'
// Define schema for test data generation
const linkSchema = z.object({
url: z.string().url(),
slug: z.string().min(1).max(50),
})
describe('/api/link/create', () => {
it('creates a new link with valid data', async () => {
const payload = generateMock(linkSchema)
const response = await fetchWithAuth('/api/link/create', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
})
expect(response.status).toBe(201)
const data = await response.json()
expect(data.link).toBeDefined()
expect(data.link.url).toBe(payload.url)
expect(data.link.slug).toBe(payload.slug)
expect(data.shortLink).toContain(payload.slug)
})
it('returns 409 when slug already exists', async () => {
const payload = generateMock(linkSchema)
// Create the link first
await fetchWithAuth('/api/link/create', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
})
// Attempt to create duplicate
const duplicateResponse = await fetchWithAuth('/api/link/create', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
})
expect(duplicateResponse.status).toBe(409)
})
it('returns 401 without authentication', async () => {
const payload = generateMock(linkSchema)
const response = await fetch('/api/link/create', {
method: 'POST',
body: JSON.stringify(payload),
headers: { 'Content-Type': 'application/json' },
})
expect(response.status).toBe(401)
})
it('validates required fields', async () => {
const invalidPayload = { slug: 'test' } // Missing URL
const response = await fetchWithAuth('/api/link/create', {
method: 'POST',
body: JSON.stringify(invalidPayload),
headers: { 'Content-Type': 'application/json' },
})
expect(response.status).toBe(400)
})
})
```
### Authentication Testing Pattern
```typescript
describe('Authentication', () => {
it('accepts valid bearer token', async () => {
const response = await fetchWithAuth('/api/link/list')
expect(response.status).toBe(200)
})
it('rejects missing authorization header', async () => {
const response = await fetch('/api/link/list')
expect(response.status).toBe(401)
})
it('rejects invalid bearer token', async () => {
const response = await fetch('/api/link/list', {
headers: { Authorization: 'Bearer invalid-token' }
})
expect(response.status).toBe(401)
})
it('rejects malformed authorization header', async () => {
const response = await fetch('/api/link/list', {
headers: { Authorization: 'InvalidFormat' }
})
expect(response.status).toBe(401)
})
})
```
### Input Validation Testing
```typescript
import { z } from 'zod'
describe('Link validation', () => {
const createLinkSchema = z.object({
url: z.string().url(),
slug: z.string().min(1).max(50).optional(),
expiresAt: z.string().datetime().optional(),
})
it('accepts valid URL formats', () => {
const testCases = [
'https://example.com',
'http://sub.domain.com/path',
'https://site.com/path?query=value#anchor'
]
testCases.forEach(url => {
expect(() => createLinkSchema.parse({ url })).not.toThrow()
})
})
it('rejects invalid URL formats', () => {
const testCases = [
'not-a-url',
'ftp://invalid-protocol.com',
'javascript:alert("xss")',
''
]
testCases.forEach(url => {
expect(() => createLinkSchema.parse({ url })).toThrow()
})
})
it('validates slug constraints', () => {
// Valid slugs
expect(() => createLinkSchema.parse({
url: 'https://example.com',
slug: 'valid-slug'
})).not.toThrow()
// Invalid slugs
expect(() => createLinkSchema.parse({
url: 'https://example.com',
slug: ''
})).toThrow()
expect(() => createLinkSchema.parse({
url: 'https://example.com',
slug: 'a'.repeat(51)
})).toThrow()
})
})
```
### Statistics and Analytics Testing
```typescript
describe('/api/stats/views', () => {
it('returns view statistics for valid slug', async () => {
// First create a link to get stats for
const linkPayload = generateMock(linkSchema)
await fetchWithAuth('/api/link/create', {
method: 'POST',
body: JSON.stringify(linkPayload),
headers: { 'Content-Type': 'application/json' },
})
const response = await fetchWithAuth(
`/api/stats/views?slug=${linkPayload.slug}&timeframe=7d`
)
expect(response.status).toBe(200)
const data = await response.json()
expect(data).toHaveProperty('total')
expect(data).toHaveProperty('unique')
expect(data).toHaveProperty('timeframe')
expect(typeof data.total).toBe('number')
expect(typeof data.unique).toBe('number')
})
it('handles non-existent slug gracefully', async () => {
const response = await fetchWithAuth('/api/stats/views?slug=non-existent')
// Should either return 404 or empty stats
expect([200, 404]).toContain(response.status)
if (response.status === 200) {
const data = await response.json()
expect(data.total).toBe(0)
expect(data.unique).toBe(0)
}
})
})
```
## Test Configuration
### Vitest Configuration
The [vitest.config.ts](mdc:vitest.config.ts) uses Cloudflare Workers pool:
```typescript
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config'
import { loadEnv } from 'vite'
export default defineWorkersConfig(({ mode }) => ({
test: {
env: loadEnv(mode, process.cwd(), ''),
globalSetup: './tests/setup.ts',
poolOptions: {
workers: {
singleWorker: true,
isolatedStorage: false,
wrangler: {
configPath: './wrangler.jsonc',
},
miniflare: {
cf: true,
},
},
},
},
}))
```
### Test Utilities
The [tests/utils.ts](mdc:tests/utils.ts) provides essential helpers:
```typescript
import { SELF } from 'cloudflare:test'
export async function fetchWithAuth(path: string, options?: RequestInit) {
return SELF.fetch(`http://localhost${path}`, {
...options,
headers: {
...options?.headers,
Authorization: `Bearer ${import.meta.env.NUXT_SITE_TOKEN}`,
},
})
}
export async function fetch(path: string, options?: RequestInit) {
return SELF.fetch(`http://localhost${path}`, options)
}
```
### Environment Setup
Configure test environment variables in [tests/setup.ts](mdc:tests/setup.ts):
```typescript
// Global test setup for environment variables and shared state
export default function setup() {
// Set up test-specific environment variables
process.env.NUXT_SITE_TOKEN = 'test-token-value'
// Additional setup as needed
}
```
## Test Data Management
### Using Zod Mock for Realistic Data
```typescript
import { generateMock } from '@anatine/zod-mock'
import { z } from 'zod'
// Schema-based mock generation
const linkSchema = z.object({
url: z.string().url(),
slug: z.string().min(1).max(50),
description: z.string().optional(),
})
const testLink = generateMock(linkSchema)
// Generates: { url: "https://example.com", slug: "abc123", description: "..." }
```
### Creating Test Fixtures
```typescript
// tests/api/helpers/fixtures.ts
import { faker } from '@faker-js/faker'
export const linkFixtures = {
valid: {
url: 'https://example.com',
slug: 'test-link',
description: 'A test link for unit testing'
},
withExpiry: {
url: 'https://temporary.com',
slug: 'temp-link',
expiresAt: new Date(Date.now() + 86400000).toISOString() // 24 hours
},
longUrl: {
url: 'https://very-long-domain-name.example.com/very/long/path/with/many/segments',
slug: 'long-url'
}
}
export const statsFixtures = {
basicStats: {
total: 150,
unique: 123,
timeframe: '7d'
},
geoStats: [
{ country: 'US', count: 45 },
{ country: 'UK', count: 30 },
{ country: 'DE', count: 25 }
]
}
// Dynamic fixture generation
export function createRandomLink() {
return {
url: faker.internet.url(),
slug: faker.internet.domainWord(),
description: faker.lorem.sentence()
}
}
```
## Error Testing Patterns
### HTTP Status Code Testing
```typescript
describe('Error handling', () => {
it('returns 400 for malformed JSON', async () => {
const response = await fetchWithAuth('/api/link/create', {
method: 'POST',
body: 'invalid-json{',
headers: { 'Content-Type': 'application/json' },
})
expect(response.status).toBe(400)
})
it('returns 422 for validation errors', async () => {
const response = await fetchWithAuth('/api/link/create', {
method: 'POST',
body: JSON.stringify({ url: 'not-a-url' }),
headers: { 'Content-Type': 'application/json' },
})
expect(response.status).toBe(422)
})
it('returns 404 for non-existent resources', async () => {
const response = await fetchWithAuth('/api/link/query?slug=non-existent')
expect(response.status).toBe(404)
})
it('returns 405 for unsupported methods', async () => {
const response = await fetchWithAuth('/api/link/create', {
method: 'GET', // Should be POST
})
expect(response.status).toBe(405)
})
})
```
### Rate Limiting and Performance
```typescript
describe('Performance and limits', () => {
it('handles multiple concurrent requests', async () => {
const requests = Array.from({ length: 10 }, (_, i) =>
fetchWithAuth('/api/link/create', {
method: 'POST',
body: JSON.stringify({
url: `https://example-${i}.com`,
slug: `test-${i}`
}),
headers: { 'Content-Type': 'application/json' },
})
)
const responses = await Promise.all(requests)
// All requests should succeed
responses.forEach(response => {
expect(response.status).toBe(201)
})
})
})
```
## Best Practices
### Test Organization
- **Sequential Testing**: Use `describe.sequential()` for tests that modify shared state
- **Isolation**: Each test should be independent and not rely on others
- **Cleanup**: Clean up created resources when necessary
- **Descriptive Names**: Use clear, behavior-focused test descriptions
### Data Management
- **Mock Generation**: Use `@anatine/zod-mock` for schema-consistent test data
- **Realistic Data**: Generate realistic URLs, slugs, and user inputs
- **Edge Cases**: Test boundary conditions, empty values, and extreme inputs
- **State Management**: Be mindful of shared KV storage in sequential tests
### Performance Considerations
- **Focused Tests**: Keep tests fast and focused on specific functionality
- **Parallel Execution**: Use parallel execution where state doesn't conflict
- **Resource Usage**: Monitor test execution time and resource consumption
- **Mock External Services**: Avoid real external API calls in tests
### Assertion Strategies
- **Comprehensive Checks**: Verify response status, structure, and data
- **Type Safety**: Ensure response data matches expected TypeScript types
- **Error Messages**: Test error response content and structure
- **Business Logic**: Verify core URL shortening and analytics logic
## Running Tests
### Available Commands
```bash
# Run all tests
npm run test
# Run tests in watch mode (for development)
npm run test -- --watch
# Run specific test file
npm run test tests/api/link.spec.ts
# Run tests with verbose output
npm run test -- --reporter=verbose
# Run tests matching pattern
npm run test -- --grep "authentication"
# Run tests for specific API group
npm run test tests/api/link/ tests/api/stats/
```
### Debugging Tests
```bash
# Run tests with debugging
npm run test -- --inspect-brk
# Run single test with detailed output
npm run test -- --run --reporter=verbose tests/api/link.spec.ts
```
### CI/CD Integration
- Tests run automatically on every pull request
- Enforce test passage before merging
- Consider adding coverage reporting and thresholds
- Use test results to gate deployments
## Coverage Goals
### Target Areas
- **API Endpoints**: 100% coverage of all public endpoints
- **Authentication**: Complete coverage of auth flows and edge cases
- **Validation**: All input validation paths and error scenarios
- **Business Logic**: Core URL shortening and analytics functionality
### Coverage Exclusions
- Configuration files and build scripts
- Type definitions and interfaces
- Third-party integrations (mock instead)
- Development-only utilities
### Monitoring
- Track coverage trends over time
- Identify uncovered critical paths
- Balance coverage percentage with test quality
- Focus on meaningful coverage over arbitrary numbers

1
.cursorrules Symbolic link
View file

@ -0,0 +1 @@
AGENT.md

1
.github/copilot-instructions.md vendored Symbolic link
View file

@ -0,0 +1 @@
../AGENT.md

1
.kiro/steering/AGENT.md Symbolic link
View file

@ -0,0 +1 @@
../../AGENT.md

1
.windsurfrules Symbolic link
View file

@ -0,0 +1 @@
AGENT.md

122
AGENT.md Normal file
View file

@ -0,0 +1,122 @@
# AGENT.md
This file provides guidance for AI assistants working with the Sink project.
## Project Overview
Sink is a URL shortening service built with Nuxt 3 and Cloudflare Workers. It provides analytics, real-time tracking, and a modern web interface.
## Key Technologies
- **Frontend**: Nuxt 3, Vue 3, TypeScript, Tailwind CSS, shadcn/ui
- **Backend**: Cloudflare Workers, H3 (Nitro)
- **Database**: Cloudflare KV
- **Analytics**: Cloudflare Analytics Engine
- **Deployment**: Cloudflare Pages/Workers
## Project Structure
```txt
Sink/
├── app/ # Nuxt 3 application
│ ├── components/ # Vue components
│ ├── pages/ # File-based routing
│ ├── layouts/ # Layout components
│ ├── utils/ # Utility functions
│ └── middleware/ # Route middleware
├── server/ # Server-side API
│ ├── api/ # API endpoints
│ ├── middleware/ # Server middleware
│ └── utils/ # Server utilities
├── public/ # Static assets
├── schemas/ # TypeScript schemas
├── tests/ # Test files
└── docs/ # Documentation
```
## Development Commands
- `pnpm dev` - Start development server
- `pnpm build` - Build for production
- `pnpm preview` - Preview production build
- `pnpm test` - Run tests
- `pnpm lint` - Run ESLint
## Key Conventions
### Code Style
- Use TypeScript for all new code
- Follow Vue 3 Composition API patterns
- Use PascalCase for component names
- Use camelCase for variables and functions
- Use kebab-case for CSS classes
### API Structure
- RESTful endpoints in `/server/api/`
- Use Zod for schema validation in `/schemas/`
- Implement proper error handling
- Use Cloudflare environment variables for configuration
### Database
- Use Cloudflare KV
### Testing
- Use Vitest for unit tests
- Test files should be co-located with source files
- Use the existing test utilities in `/tests/utils.ts`
## Environment Variables
Key environment variables to be aware of:
- `NUXT_CF_ACCOUNT_ID` - Cloudflare account ID
- `NUXT_CF_API_TOKEN` - Cloudflare API token
- `NUXT_DATASET` - Analytics engine ID
- `NUXT_SITE_TOKEN` - Site token for management
## Common Tasks
### Adding a New API Endpoint
1. Create file in `/server/api/` with appropriate HTTP method
2. Add Zod schema validation in `/schemas/`
3. Add tests in `/tests/api/`
4. Update documentation if needed
### Adding a New Component
1. Use existing UI components from `/app/components/ui/`
2. Follow the established component patterns
3. Add TypeScript interfaces for props
4. Consider adding to storybook if applicable
## Security Guidelines
- Never commit secrets or API keys
- Use environment variables for sensitive configuration
- Validate all user input with Zod schemas
- Implement rate limiting on API endpoints
- Follow OWASP guidelines for web security
## Performance Considerations
- Use Cloudflare's edge caching where appropriate
- Minimize bundle size by using dynamic imports
- Optimize images and assets
- Use the existing analytics for monitoring
## Troubleshooting
- Check Cloudflare dashboard for worker logs
- Use `wrangler tail` for real-time logs
- Verify environment variables are properly set
## Deployment
- Staging: Automatic on push to `main` branch
- Production: Manual trigger via Cloudflare dashboard
- Use `wrangler deploy` for manual deployments

1
AGENTS.md Symbolic link
View file

@ -0,0 +1 @@
AGENT.md

1
CLAUDE.md Symbolic link
View file

@ -0,0 +1 @@
AGENT.md

1
GEMINI.md Symbolic link
View file

@ -0,0 +1 @@
AGENT.md

View file

@ -43,7 +43,7 @@
![Hero](./public/image.png)
----
---
## ✨ Features
@ -82,14 +82,10 @@ Site Token: SinkCool
We welcome your contributions and PRs.
- [x] Browser Extension
- [Sink Tool](https://github.com/zhuzhuyule/sink-extension)
- [x] Raycast Extension
- [Raycast-Sink](https://github.com/foru17/raycast-sink)
- [x] Apple Shortcuts
- [Sink Shortcuts](https://s.search1api.com/sink001)
- [x] iOS App
- [Sink](https://apps.apple.com/app/id6745417598)
- [x] Browser Extension - [Sink Tool](https://github.com/zhuzhuyule/sink-extension)
- [x] Raycast Extension - [Raycast-Sink](https://github.com/foru17/raycast-sink)
- [x] Apple Shortcuts - [Sink Shortcuts](https://s.search1api.com/sink001)
- [x] iOS App - [Sink](https://apps.apple.com/app/id6745417598)
- [ ] Enhanced Link Management (with Cloudflare D1)
- [ ] Analytics Enhancements (Support for merging filter conditions)
- [ ] Dashboard Performance Optimization (Infinite loading)

View file

@ -43,7 +43,7 @@
--vis-tooltip-padding: none !important;
--vis-primary-color: 158 64% 52%;
--vis-secondary-color: 198 93% 60%;
--vis-secondary-color: 198 93% 60%;
/* --vis-secondary-color: 160 81% 40%; */
/* --vis-secondary-color: var(--primary); */
--vis-text-color: var(--muted-foreground);

View file

@ -12,10 +12,10 @@ This place provides an example of creating a short link API. Other APIs are curr
POST /api/link/create
```
| Header | Description |
| :----- | :------------------------- |
| `authorization` | `Bearer SinkCool` |
| `content-type` | `application/json` |
| Header | Description |
| :-------------- | :----------------- |
| `authorization` | `Bearer SinkCool` |
| `content-type` | `application/json` |
#### Example
@ -44,10 +44,10 @@ The BODY data must be JSON.
}
```
| Parameter | Type | Description |
| :-------- | :------- | :------------------------- |
| `id` | `string` | This is automatically generated by Sink |
| `url` | `string` | This is confirmation of the submitted URL and is required. |
| `slug` | `string` | This is slug generated by the system, either automatically or from the input (if provided) |
| `createdAt` | `timestamp` | This is automatically generated with a UNIX Timestamp. |
| `updatedAt` | `timestamp` | This is automatically generated with a UNIX Timestamp. |
| Parameter | Type | Description |
| :---------- | :---------- | :----------------------------------------------------------------------------------------- |
| `id` | `string` | This is automatically generated by Sink |
| `url` | `string` | This is confirmation of the submitted URL and is required. |
| `slug` | `string` | This is slug generated by the system, either automatically or from the input (if provided) |
| `createdAt` | `timestamp` | This is automatically generated with a UNIX Timestamp. |
| `updatedAt` | `timestamp` | This is automatically generated with a UNIX Timestamp. |

View file

@ -6,13 +6,13 @@ Sink provides some configuration options, which can be referred to in [.env.exam
## `NUXT_PUBLIC_PREVIEW_MODE`
> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.
> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.
Sets the site to demo mode, the generated links will expire after 5 minutes, and the links cannot be edited or deleted.
## `NUXT_PUBLIC_SLUG_DEFAULT_LENGTH`
> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.
> If you are using Worker deployment, this variable needs to be configured in **Settings** -> **Build** -> **Variables and Secrets** and **Settings** -> **Variables and Secrets**.
Sets the default length of the generated SLUG.

View file

@ -20,4 +20,4 @@
7. Add Compatibility flags
- Go to **Settings** -> **Runtime** -> **Compatibility flags** and set the following flags `nodejs_compat`.
8. Redeploy the project.
9. To update code, refer to the official GitHub documentation [Syncing a fork branch from the web UI](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork#syncing-a-fork-branch-from-the-web-ui "GitHub: Syncing a fork").
9. To update code, refer to the official GitHub documentation [Syncing a fork branch from the web UI](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork#syncing-a-fork-branch-from-the-web-ui 'GitHub: Syncing a fork').

View file

@ -5,8 +5,8 @@
3. Update the `kv_namespaces` ID in `wrangler.jsonc` with your own namespace ID.
4. Create a project in [Cloudflare Workers](https://developers.cloudflare.com/workers/).
5. Select the `Sink` repository and use the following build and deploy commands:
- **Build command**: `pnpm run build` or `npm run build`
- **Deploy command**: `npx wrangler deploy`
- **Build command**: `pnpm run build` or `npm run build`
- **Deploy command**: `npx wrangler deploy`
6. Save and deploy the project.
7. After deployment, go to **Settings** -> **Variables and Secrets** -> **Add**, and configure the following environment variables:
@ -16,4 +16,4 @@
8. Enable Analytics Engine. In **Workers & Pages**, go to **Account details** in the right panel, locate **Analytics Engine**, and click **Set up** to enable the free tier.
9. Redeploy the project.
10. To update your code, refer to the official GitHub documentation: [Syncing a fork branch from the web UI](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork#syncing-a-fork-branch-from-the-web-ui "GitHub: Syncing a fork").
10. To update your code, refer to the official GitHub documentation: [Syncing a fork branch from the web UI](https://docs.github.com/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork#syncing-a-fork-branch-from-the-web-ui 'GitHub: Syncing a fork').

View file

@ -1,13 +1,12 @@
// @ts-check
import antfu from '@antfu/eslint-config'
import withNuxt from './.nuxt/eslint.config.mjs'
export default withNuxt(
antfu(),
antfu({
formatters: true,
}),
{
ignores: ['app/components/ui', '.data', 'public/*.json'],
},
{
rules: {
'@typescript-eslint/ban-ts-comment': 'off',
'no-console': 'off',

View file

@ -65,6 +65,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"eslint": "^9.27.0",
"eslint-plugin-format": "^1.0.1",
"lint-staged": "^16.0.0",
"nuxt": "^3.17.4",
"shadcn-nuxt": "^2.1.0",

View file

@ -92,16 +92,16 @@ importers:
version: 3.14.0(@faker-js/faker@9.8.0)(zod@3.25.20)
'@antfu/eslint-config':
specifier: ^4.13.2
version: 4.13.2(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
version: 4.13.2(@vue/compiler-sfc@3.5.14)(eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
'@cloudflare/vitest-pool-workers':
specifier: ^0.8.33
version: 0.8.33(@vitest/runner@3.1.4)(@vitest/snapshot@3.1.4)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
'@nuxt/eslint':
specifier: ^1.4.1
version: 1.4.1(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(magicast@0.3.5)(typescript@5.7.2)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
version: 1.4.1(@vue/compiler-sfc@3.5.14)(eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(magicast@0.3.5)(typescript@5.7.2)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
'@nuxt/eslint-config':
specifier: ^1.4.1
version: 1.4.1(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
version: 1.4.1(@vue/compiler-sfc@3.5.14)(eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
'@nuxthub/core':
specifier: ^0.8.27
version: 0.8.27(db0@0.3.2)(ioredis@5.6.1)(magicast@0.3.5)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
@ -126,6 +126,9 @@ importers:
eslint:
specifier: ^9.27.0
version: 9.27.0(jiti@2.4.2)
eslint-plugin-format:
specifier: ^1.0.1
version: 1.0.1(eslint@9.27.0(jiti@2.4.2))
lint-staged:
specifier: ^16.0.0
version: 16.0.0
@ -459,6 +462,15 @@ packages:
resolution: {integrity: sha512-KrkT6qO5NxqNfy68sBl6CTSoJ4SNDIS5iQArkibhlbGU4LaDukZ3q2HIkh8aUKDio6o4itU4xDR7t82Y2eP1Bg==}
engines: {node: '>=14'}
'@dprint/formatter@0.3.0':
resolution: {integrity: sha512-N9fxCxbaBOrDkteSOzaCqwWjso5iAe+WJPsHC021JfHNj2ThInPNEF13ORDKta3llq5D1TlclODCvOvipH7bWQ==}
'@dprint/markdown@0.17.8':
resolution: {integrity: sha512-ukHFOg+RpG284aPdIg7iPrCYmMs3Dqy43S1ejybnwlJoFiW02b+6Bbr5cfZKFRYNP3dKGM86BqHEnMzBOyLvvA==}
'@dprint/toml@0.6.4':
resolution: {integrity: sha512-bZXIUjxr0LIuHWshZr/5mtUkOrnh0NKVZEF6ACojW5z7zkJu7s9sV2mMXm8XQDqN4cJzdHYUYzUyEGdfciaLJA==}
'@emnapi/core@1.4.3':
resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==}
@ -1481,6 +1493,10 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@pkgr/core@0.1.2':
resolution: {integrity: sha512-fdDH1LSGfZdTH2sxdpVMw31BanV28K/Gry0cVFxaNP77neJSkd82mM8ErPNYs9e+0O7SdHBLTDzDgwUuy18RnQ==}
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
'@polka/url@1.0.0-next.25':
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
@ -3518,6 +3534,11 @@ packages:
eslint-flat-config-utils@2.1.0:
resolution: {integrity: sha512-6fjOJ9tS0k28ketkUcQ+kKptB4dBZY2VijMZ9rGn8Cwnn1SH0cZBoPXT8AHBFHxmHcLFQK9zbELDinZ2Mr1rng==}
eslint-formatting-reporter@0.0.0:
resolution: {integrity: sha512-k9RdyTqxqN/wNYVaTk/ds5B5rA8lgoAmvceYN7bcZMBwU7TuXx5ntewJv81eF3pIL/CiJE+pJZm36llG8yhyyw==}
peerDependencies:
eslint: '>=8.40.0'
eslint-import-resolver-node@0.3.9:
resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
@ -3537,6 +3558,9 @@ packages:
peerDependencies:
eslint: '*'
eslint-parser-plain@0.1.1:
resolution: {integrity: sha512-KRgd6wuxH4U8kczqPp+Oyk4irThIhHWxgFgLDtpgjUGVIS3wGrJntvZW/p6hHq1T4FOwnOtCNkvAI4Kr+mQ/Hw==}
eslint-plugin-antfu@3.1.1:
resolution: {integrity: sha512-7Q+NhwLfHJFvopI2HBZbSxWXngTwBLKxW1AGXLr2lEGxcEIK/AsDs8pn8fvIizl5aZjBbVbVK5ujmMpBe4Tvdg==}
peerDependencies:
@ -3553,6 +3577,11 @@ packages:
peerDependencies:
eslint: '>=8'
eslint-plugin-format@1.0.1:
resolution: {integrity: sha512-Tdns+CDjS+m7QrM85wwRi2yLae88XiWVdIOXjp9mDII0pmTBQlczPCmjpKnjiUIY3yPZNLqb5Ms/A/JXcBF2Dw==}
peerDependencies:
eslint: ^8.40.0 || ^9.0.0
eslint-plugin-import-x@4.12.2:
resolution: {integrity: sha512-0jVUgJQipbs0yUfLe7LwYD6p8rIGqCysWZdyJFgkPzDyJgiKpuCaXlywKUAWgJ6u1nLpfrdt21B60OUkupyBrQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@ -3748,6 +3777,9 @@ packages:
fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
fast-diff@1.3.0:
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
fast-fifo@1.3.2:
resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
@ -5541,6 +5573,15 @@ packages:
resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
engines: {node: '>= 0.8.0'}
prettier-linter-helpers@1.0.0:
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
engines: {node: '>=6.0.0'}
prettier@3.6.2:
resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
engines: {node: '>=14'}
hasBin: true
pretty-bytes@6.1.1:
resolution: {integrity: sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==}
engines: {node: ^14.13.1 || >=16.0.0}
@ -6082,6 +6123,10 @@ packages:
resolution: {integrity: sha512-Vhf+bUa//YSTYKseDiiEuQmhGCoIF3CVBhunm3r/DQnYiGT4JssmnKQc44BIyOZRK2pKjXXAgbhfmbeoC9CJpA==}
engines: {node: '>=12.20'}
synckit@0.9.3:
resolution: {integrity: sha512-JJoOEKTfL1urb1mDoEblhD9NhEbWmq9jHEMEnxoC4ujUaZ4itA8vKgwkFAyNClgxplLi9tsUKX+EduK0p/l7sg==}
engines: {node: ^14.18.0 || >=16.0.0}
system-architecture@0.1.0:
resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==}
engines: {node: '>=18'}
@ -6938,7 +6983,7 @@ snapshots:
randexp: 0.5.3
zod: 3.25.20
'@antfu/eslint-config@4.13.2(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))':
'@antfu/eslint-config@4.13.2(@vue/compiler-sfc@3.5.14)(eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)(vitest@3.1.4(@types/debug@4.1.12)(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))':
dependencies:
'@antfu/install-pkg': 1.1.0
'@clack/prompts': 0.10.1
@ -6977,6 +7022,8 @@ snapshots:
toml-eslint-parser: 0.10.0
vue-eslint-parser: 10.1.3(eslint@9.27.0(jiti@2.4.2))
yaml-eslint-parser: 1.3.0
optionalDependencies:
eslint-plugin-format: 1.0.1(eslint@9.27.0(jiti@2.4.2))
transitivePeerDependencies:
- '@eslint/json'
- '@vue/compiler-sfc'
@ -7265,6 +7312,12 @@ snapshots:
gonzales-pe: 4.3.0
node-source-walk: 6.0.2
'@dprint/formatter@0.3.0': {}
'@dprint/markdown@0.17.8': {}
'@dprint/toml@0.6.4': {}
'@emnapi/core@1.4.3':
dependencies:
'@emnapi/wasi-threads': 1.0.2
@ -8070,7 +8123,7 @@ snapshots:
- utf-8-validate
- vue
'@nuxt/eslint-config@1.4.1(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)':
'@nuxt/eslint-config@1.4.1(@vue/compiler-sfc@3.5.14)(eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)':
dependencies:
'@antfu/install-pkg': 1.1.0
'@clack/prompts': 0.10.1
@ -8093,6 +8146,8 @@ snapshots:
local-pkg: 1.1.1
pathe: 2.0.3
vue-eslint-parser: 10.1.3(eslint@9.27.0(jiti@2.4.2))
optionalDependencies:
eslint-plugin-format: 1.0.1(eslint@9.27.0(jiti@2.4.2))
transitivePeerDependencies:
- '@vue/compiler-sfc'
- supports-color
@ -8107,11 +8162,11 @@ snapshots:
- supports-color
- typescript
'@nuxt/eslint@1.4.1(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(magicast@0.3.5)(typescript@5.7.2)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))':
'@nuxt/eslint@1.4.1(@vue/compiler-sfc@3.5.14)(eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(magicast@0.3.5)(typescript@5.7.2)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))':
dependencies:
'@eslint/config-inspector': 1.0.2(eslint@9.27.0(jiti@2.4.2))
'@nuxt/devtools-kit': 2.4.1(magicast@0.3.5)(vite@6.3.5(@types/node@20.12.11)(jiti@2.4.2)(terser@5.31.0)(yaml@2.8.0))
'@nuxt/eslint-config': 1.4.1(@vue/compiler-sfc@3.5.14)(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
'@nuxt/eslint-config': 1.4.1(@vue/compiler-sfc@3.5.14)(eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)))(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
'@nuxt/eslint-plugin': 1.4.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
'@nuxt/kit': 3.17.4(magicast@0.3.5)
chokidar: 4.0.3
@ -8513,6 +8568,8 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@pkgr/core@0.1.2': {}
'@polka/url@1.0.0-next.25': {}
'@poppinss/colors@4.1.4':
@ -10659,6 +10716,11 @@ snapshots:
dependencies:
pathe: 2.0.3
eslint-formatting-reporter@0.0.0(eslint@9.27.0(jiti@2.4.2)):
dependencies:
eslint: 9.27.0(jiti@2.4.2)
prettier-linter-helpers: 1.0.0
eslint-import-resolver-node@0.3.9:
dependencies:
debug: 3.2.7
@ -10677,6 +10739,8 @@ snapshots:
dependencies:
eslint: 9.27.0(jiti@2.4.2)
eslint-parser-plain@0.1.1: {}
eslint-plugin-antfu@3.1.1(eslint@9.27.0(jiti@2.4.2)):
dependencies:
eslint: 9.27.0(jiti@2.4.2)
@ -10693,6 +10757,17 @@ snapshots:
eslint: 9.27.0(jiti@2.4.2)
eslint-compat-utils: 0.5.1(eslint@9.27.0(jiti@2.4.2))
eslint-plugin-format@1.0.1(eslint@9.27.0(jiti@2.4.2)):
dependencies:
'@dprint/formatter': 0.3.0
'@dprint/markdown': 0.17.8
'@dprint/toml': 0.6.4
eslint: 9.27.0(jiti@2.4.2)
eslint-formatting-reporter: 0.0.0(eslint@9.27.0(jiti@2.4.2))
eslint-parser-plain: 0.1.1
prettier: 3.6.2
synckit: 0.9.3
eslint-plugin-import-x@4.12.2(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2):
dependencies:
'@typescript-eslint/utils': 8.32.1(eslint@9.27.0(jiti@2.4.2))(typescript@5.7.2)
@ -10998,6 +11073,8 @@ snapshots:
fast-deep-equal@3.1.3: {}
fast-diff@1.3.0: {}
fast-fifo@1.3.2: {}
fast-glob@3.3.3:
@ -13195,6 +13272,12 @@ snapshots:
prelude-ls@1.2.1: {}
prettier-linter-helpers@1.0.0:
dependencies:
fast-diff: 1.3.0
prettier@3.6.2: {}
pretty-bytes@6.1.1: {}
printable-characters@1.0.42: {}
@ -13809,6 +13892,11 @@ snapshots:
dependencies:
tslib: 2.8.1
synckit@0.9.3:
dependencies:
'@pkgr/core': 0.1.2
tslib: 2.8.1
system-architecture@0.1.0: {}
tailwind-config-viewer@2.0.4(tailwindcss@3.4.17):