|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +cnpmcore is a TypeScript-based private NPM registry implementation for enterprise use. It's built on the Egg.js framework using Domain-Driven Design (DDD) architecture principles and supports both MySQL and PostgreSQL databases. |
| 8 | + |
| 9 | +## Essential Commands |
| 10 | + |
| 11 | +### Development |
| 12 | +```bash |
| 13 | +# Start development server (MySQL) |
| 14 | +npm run dev |
| 15 | + |
| 16 | +# Start development server (PostgreSQL) |
| 17 | +npm run dev:postgresql |
| 18 | + |
| 19 | +# Lint code |
| 20 | +npm run lint |
| 21 | + |
| 22 | +# Fix linting issues |
| 23 | +npm run lint:fix |
| 24 | + |
| 25 | +# TypeScript type checking |
| 26 | +npm run typecheck |
| 27 | +``` |
| 28 | + |
| 29 | +### Testing |
| 30 | +```bash |
| 31 | +# Run all tests with MySQL (takes 4+ minutes) |
| 32 | +npm run test |
| 33 | + |
| 34 | +# Run all tests with PostgreSQL (takes 4+ minutes) |
| 35 | +npm run test:postgresql |
| 36 | + |
| 37 | +# Run single test file (faster iteration, ~12 seconds) |
| 38 | +npm run test:local test/path/to/file.test.ts |
| 39 | + |
| 40 | +# Generate coverage report |
| 41 | +npm run cov |
| 42 | +``` |
| 43 | + |
| 44 | +### Database Setup |
| 45 | +```bash |
| 46 | +# MySQL setup |
| 47 | +docker compose -f docker-compose.yml up -d |
| 48 | +CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-mysql.sh |
| 49 | + |
| 50 | +# PostgreSQL setup |
| 51 | +docker compose -f docker-compose-postgres.yml up -d |
| 52 | +CNPMCORE_DATABASE_NAME=cnpmcore bash ./prepare-database-postgresql.sh |
| 53 | +``` |
| 54 | + |
| 55 | +### Build |
| 56 | +```bash |
| 57 | +# Clean build artifacts |
| 58 | +npm run clean |
| 59 | + |
| 60 | +# Development build |
| 61 | +npm run tsc |
| 62 | + |
| 63 | +# Production build |
| 64 | +npm run tsc:prod |
| 65 | +``` |
| 66 | + |
| 67 | +## Architecture - Domain-Driven Design (DDD) |
| 68 | + |
| 69 | +The codebase follows strict DDD layering with clear separation of concerns: |
| 70 | + |
| 71 | +``` |
| 72 | +Controller (app/port/controller/) ← HTTP interface, validation, auth |
| 73 | + ↓ depends on |
| 74 | +Service (app/core/service/) ← Business logic orchestration |
| 75 | + ↓ depends on |
| 76 | +Repository (app/repository/) ← Data access layer |
| 77 | + ↓ depends on |
| 78 | +Model (app/repository/model/) ← ORM/Database mapping |
| 79 | +
|
| 80 | +Entity (app/core/entity/) ← Pure domain models (no dependencies) |
| 81 | +Common (app/common/) ← Utilities and adapters (all layers) |
| 82 | +``` |
| 83 | + |
| 84 | +### Layer Responsibilities |
| 85 | + |
| 86 | +**Controller Layer** (`app/port/controller/`): |
| 87 | +- Handle HTTP requests/responses |
| 88 | +- Validate inputs using `@eggjs/typebox-validate` |
| 89 | +- Authenticate users and verify authorization |
| 90 | +- Delegate business logic to Services |
| 91 | +- All controllers extend `AbstractController` |
| 92 | + |
| 93 | +**Service Layer** (`app/core/service/`): |
| 94 | +- Implement core business logic |
| 95 | +- Orchestrate multiple repositories |
| 96 | +- Publish domain events |
| 97 | +- Manage transactions |
| 98 | + |
| 99 | +**Repository Layer** (`app/repository/`): |
| 100 | +- CRUD operations on Models |
| 101 | +- Data access and persistence |
| 102 | +- Query building and optimization |
| 103 | +- Methods named: `findX`, `saveX`, `removeX`, `listXs` |
| 104 | + |
| 105 | +**Entity Layer** (`app/core/entity/`): |
| 106 | +- Pure domain models with business behavior |
| 107 | +- No infrastructure dependencies |
| 108 | +- Immutable data structures preferred |
| 109 | + |
| 110 | +**Model Layer** (`app/repository/model/`): |
| 111 | +- ORM definitions using Leoric |
| 112 | +- Database schema mapping |
| 113 | +- No business logic |
| 114 | + |
| 115 | +### Infrastructure Adapters (`app/infra/`) |
| 116 | +Enterprise customization layer for PaaS integration: |
| 117 | +- **NFSClientAdapter**: File storage (local/S3/OSS) |
| 118 | +- **QueueAdapter**: Message queue integration |
| 119 | +- **AuthAdapter**: Authentication system |
| 120 | +- **BinaryAdapter**: Binary package storage |
| 121 | + |
| 122 | +## Key Development Patterns |
| 123 | + |
| 124 | +### Request Validation Trilogy |
| 125 | +Always validate requests in this exact order: |
| 126 | +1. **Parameter Validation** - Use `@eggjs/typebox-validate` for type-safe validation |
| 127 | +2. **Authentication** - Get authorized user with token role verification |
| 128 | +3. **Authorization** - Check resource-level permissions to prevent privilege escalation |
| 129 | + |
| 130 | +```typescript |
| 131 | +// Example controller method |
| 132 | +async someMethod(@HTTPQuery() params: QueryType) { |
| 133 | + // 1. Params already validated by @HTTPQuery with typebox |
| 134 | + // 2. Authenticate |
| 135 | + const user = await this.userRoleManager.requiredAuthorizedUser(this.ctx, 'publish'); |
| 136 | + // 3. Authorize (if needed) |
| 137 | + const { pkg } = await this.ensurePublishAccess(this.ctx, fullname); |
| 138 | + // 4. Execute business logic |
| 139 | + return await this.service.doSomething(params); |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +### Repository Method Naming |
| 144 | +- `findSomething` - Query single entity |
| 145 | +- `saveSomething` - Create or update entity |
| 146 | +- `removeSomething` - Delete entity |
| 147 | +- `listSomethings` - Query multiple entities (plural) |
| 148 | + |
| 149 | +### Modifying Database Models |
| 150 | +When changing a Model, update all 3 locations: |
| 151 | +1. SQL migrations: `sql/mysql/*.sql` AND `sql/postgresql/*.sql` |
| 152 | +2. ORM Model: `app/repository/model/*.ts` |
| 153 | +3. Domain Entity: `app/core/entity/*.ts` |
| 154 | + |
| 155 | +## Code Style |
| 156 | + |
| 157 | +### Linting |
| 158 | +- **Linter**: Oxlint (Rust-based, very fast) |
| 159 | +- **Formatter**: Prettier |
| 160 | +- **Pre-commit**: Husky + lint-staged (auto-format on commit) |
| 161 | + |
| 162 | +Style rules: |
| 163 | +- Single quotes (`'`) |
| 164 | +- 2-space indentation |
| 165 | +- 120 character line width |
| 166 | +- ES5 trailing commas |
| 167 | +- Max 6 function parameters |
| 168 | +- No console statements (use logger) |
| 169 | + |
| 170 | +### TypeScript |
| 171 | +- Strict TypeScript enabled |
| 172 | +- Avoid `any` types - use proper typing or `unknown` |
| 173 | +- ES modules (`import/export`) throughout |
| 174 | +- Comprehensive type definitions in all files |
| 175 | + |
| 176 | +### Testing |
| 177 | +- Test files use `.test.ts` suffix |
| 178 | +- Tests mirror source structure in `test/` directory |
| 179 | +- Use `@eggjs/mock` for mocking |
| 180 | +- Use `assert` from `node:assert/strict` |
| 181 | +- Test both success and error cases |
| 182 | + |
| 183 | +Pattern: |
| 184 | +```typescript |
| 185 | +describe('test/path/to/SourceFile.test.ts', () => { |
| 186 | + describe('[HTTP_METHOD /api/path] functionName()', () => { |
| 187 | + it('should handle expected behavior', async () => { |
| 188 | + // Test implementation |
| 189 | + }); |
| 190 | + }); |
| 191 | +}); |
| 192 | +``` |
| 193 | + |
| 194 | +## Project Structure |
| 195 | + |
| 196 | +``` |
| 197 | +app/ |
| 198 | +├── common/ # Global utilities and adapters |
| 199 | +│ ├── adapter/ # External service adapters |
| 200 | +│ └── enum/ # Shared enumerations |
| 201 | +├── core/ # Business logic layer |
| 202 | +│ ├── entity/ # Domain models |
| 203 | +│ ├── event/ # Event handlers |
| 204 | +│ ├── service/ # Business services |
| 205 | +│ └── util/ # Internal utilities |
| 206 | +├── port/ # Interface layer |
| 207 | +│ ├── controller/ # HTTP controllers |
| 208 | +│ ├── middleware/ # Middleware |
| 209 | +│ └── schedule/ # Background jobs |
| 210 | +├── repository/ # Data access layer |
| 211 | +│ └── model/ # ORM models |
| 212 | +└── infra/ # Infrastructure adapters |
| 213 | +
|
| 214 | +config/ # Configuration files |
| 215 | +sql/ # Database migrations |
| 216 | + ├── mysql/ # MySQL migrations |
| 217 | + └── postgresql/ # PostgreSQL migrations |
| 218 | +test/ # Test files (mirrors app/ structure) |
| 219 | +``` |
| 220 | + |
| 221 | +## Important Configuration |
| 222 | + |
| 223 | +- `config/config.default.ts` - Main application configuration |
| 224 | +- `config/database.ts` - Database connection settings |
| 225 | +- `config/binaries.ts` - Binary package mirror configurations |
| 226 | +- `.env` - Environment-specific variables (copy from `.env.example`) |
| 227 | +- `tsconfig.json` - TypeScript settings (target: ES2021 for Leoric compatibility) |
| 228 | + |
| 229 | +## Development Workflow |
| 230 | + |
| 231 | +1. **Setup**: Copy `.env.example` to `.env`, start Docker services, initialize database |
| 232 | +2. **Feature Development**: Follow bottom-up approach (Model → Entity → Repository → Service → Controller) |
| 233 | +3. **Testing**: Write tests at appropriate layer, run individual tests for fast iteration |
| 234 | +4. **Validation**: Run linter, typecheck, relevant tests before committing |
| 235 | +5. **Commit**: Use semantic commit messages (feat/fix/docs/test/chore) |
| 236 | + |
| 237 | +## Integration as NPM Package |
| 238 | + |
| 239 | +cnpmcore can be integrated into Egg.js/Tegg applications as an NPM package, allowing enterprises to: |
| 240 | +- Customize infrastructure adapters (storage, auth, queue) |
| 241 | +- Override default behavior while receiving updates |
| 242 | +- Integrate with existing enterprise systems |
| 243 | + |
| 244 | +See INTEGRATE.md for detailed integration guide. |
| 245 | + |
| 246 | +## Performance Notes |
| 247 | + |
| 248 | +Typical command execution times: |
| 249 | +- Development server startup: ~20 seconds |
| 250 | +- TypeScript build: ~6 seconds |
| 251 | +- Full test suite: 4-15 minutes |
| 252 | +- Single test file: ~12 seconds |
| 253 | +- Linting: <1 second |
| 254 | +- Database initialization: <2 seconds |
| 255 | + |
| 256 | +## Prerequisites |
| 257 | + |
| 258 | +- Node.js: 20.18.0+ or 22.18.0+ |
| 259 | +- Database: MySQL 5.7+ or PostgreSQL 17+ |
| 260 | +- Cache: Redis 6+ |
| 261 | +- Optional: Elasticsearch 8.x |
| 262 | + |
| 263 | +## Key Services & Controllers |
| 264 | + |
| 265 | +Core components to understand: |
| 266 | +- **PackageController**: Package CRUD operations |
| 267 | +- **PackageManagerService**: Core package management logic |
| 268 | +- **BinarySyncerService**: Binary package synchronization |
| 269 | +- **ChangesStreamService**: NPM registry change stream processing |
| 270 | +- **UserController**: User authentication and profiles |
0 commit comments