UNPKG

scyllinx

Version:

A modern TypeScript ORM for ScyllaDB and SQL databases with Laravel-inspired syntax

441 lines (340 loc) 10.2 kB
# Contributing to ScyllinX Thank you for considering contributing to ScyllinX! This document provides guidelines and information for contributors. ## Development Setup ### Prerequisites - Node.js 18+ - TypeScript 4.9+ - Your database instance for testing - Git ### Setup Instructions 1. **Fork and Clone** ```bash git clone https://github.com/selori/scyllinx.git cd scyllinx ``` 2. **Install Dependencies** ```bash npm install ``` 3. **Setup Test Database** ```bash # Start ScyllaDB with Docker docker run --name scylla-test -p 9042:9042 -d scylladb/scylla # Or use existing instance export SCYLLA_HOST=localhost export SCYLLA_PORT=9042 export SCYLLA_KEYSPACE=test_scyllinx ``` 4. **Run Tests** ```bash # Unit tests npm test # Integration tests (requires ScyllaDB) npm run test:integration # All tests npm run test:all ``` 5. **Build Project** ```bash npm run build ``` ## Project Structure ``` scyllinx/ ├── src/ # Source code │ ├── connection/ # Database connection management │ ├── drivers/ # Database drivers │ ├── model/ # Model and Active Record implementation │ ├── query/ # Query builder │ ├── relationships/ # Relationship implementations │ ├── schema/ # Schema builder │ ├── migration/ # Migration system │ ├── seeder/ # Seeding system │ └── types/ # TypeScript type definitions ├── __tests__/ # Test files ├── examples/ # Example implementations ├── docs/ # Documentation └── scripts/ # Build and utility scripts ``` ## Coding Standards ### TypeScript Guidelines 1. **Use strict TypeScript configuration** - Enable `strict: true` - Use explicit return types for public methods - Prefer interfaces over type aliases for object shapes 2. **Naming Conventions** - Classes: PascalCase (`UserModel`, `QueryBuilder`) - Methods/Variables: camelCase (`findUser`, `queryBuilder`) - Constants: UPPER_SNAKE_CASE (`DEFAULT_LIMIT`) - Files: kebab-case (`query-builder.ts`) 3. **Code Organization** - One class per file - Group related functionality - Use barrel exports (`index.ts`) ### Code Style ```ts // Good class User extends Model<UserAttributes> { protected static table = 'users'; protected static fillable = ['name', 'email']; public async save(): Promise<boolean> { // Implementation return true; } private validateEmail(email: string): boolean { // Implementation return true; } } // Avoid class user extends Model<any> { static table = 'users' save() { // Implementation } } ``` ### Documentation 1. **JSDoc Comments** ```ts /** * Find a model by its primary key * @param id - The primary key value * @returns Promise resolving to model instance or null */ public static async find<T extends Model>(id: any): Promise<T | null> { // Implementation } ``` 2. **README Updates** - Update documentation for new features - Include code examples - Update API reference ## Testing Guidelines ### Test Structure ```ts describe('Feature Name', () => { let mockDependency: any; beforeEach(() => { // Setup }); afterEach(() => { // Cleanup }); describe('Specific Functionality', () => { it('should do something specific', () => { // Test implementation }); it('should handle edge case', () => { // Edge case test }); }); }); ``` ### Test Categories 1. **Unit Tests** (`*.test.ts`) - Test individual functions/methods - Mock external dependencies - Fast execution 2. **Integration Tests** (`*.integration.test.ts`) - Test component interactions - Use real database connections - Slower execution 3. **End-to-End Tests** (`*.e2e.test.ts`) - Test complete workflows - Use real database and full setup ### Writing Good Tests ```ts // Good describe('User Model', () => { it('should create user with valid attributes', async () => { const userData = { name: 'John Doe', email: 'john@example.com' }; const user = await User.create(userData); expect(user).toBeInstanceOf(User); expect(user.name).toBe('John Doe'); expect(user.email).toBe('john@example.com'); expect(user.exists).toBe(true); }); it('should throw error for invalid email', async () => { const userData = { name: 'John Doe', email: 'invalid-email' }; await expect(User.create(userData)).rejects.toThrow('Invalid email format'); }); }); // Avoid it('should work', async () => { const user = new User(); expect(user).toBeTruthy(); }); ``` ## Contribution Process ### 1. Issue First - Check existing issues before creating new ones - Use issue templates when available - Provide detailed reproduction steps for bugs - Include use cases for feature requests ### 2. Branch Naming ```bash # Features git checkout -b feature/add-soft-deletes git checkout -b feature/improve-query-performance # Bug fixes git checkout -b fix/relationship-loading-bug git checkout -b fix/migration-rollback-issue # Documentation git checkout -b docs/update-readme git checkout -b docs/add-examples ``` ### 3. Commit Messages Follow conventional commits format: ```bash # Features git commit -m "feat: add soft delete functionality to models" git commit -m "feat(query): implement batch insert operations" # Bug fixes git commit -m "fix: resolve relationship eager loading issue" git commit -m "fix(migration): handle rollback errors properly" # Documentation git commit -m "docs: update README with new examples" git commit -m "docs(api): add JSDoc comments to QueryBuilder" # Tests git commit -m "test: add comprehensive model factory tests" git commit -m "test(integration): add ScyllaDB connection tests" ``` ### 4. Pull Request Process 1. **Before Submitting** - Run all tests: `npm run test:all` - Run linting: `npm run lint` - Update documentation if needed - Add/update tests for new functionality 2. **PR Description Template** markdown ## Description Brief description of changes ## Type of Change - [ ] Bug fix - [ ] New feature - [ ] Breaking change - [ ] Documentation update ## Testing - [ ] Unit tests pass - [ ] Integration tests pass - [ ] Manual testing completed ## Checklist - [ ] Code follows style guidelines - [ ] Self-review completed - [ ] Documentation updated - [ ] Tests added/updated 3. **Review Process** - Address reviewer feedback promptly - Keep discussions focused and constructive - Update PR based on feedback ## Feature Development Guidelines ### Adding New Features 1. **Design First** - Create issue with detailed design - Discuss API design with maintainers - Consider backward compatibility 2. **Implementation** - Follow existing patterns - Add comprehensive tests - Update documentation 3. **Examples** ```ts // When adding new query methods class QueryBuilder { // Add method with proper typing public whereJsonContains<K extends keyof TAttrs>( column: K, value: any ): this { // Implementation return this; } } // Add tests describe('JSON Query Methods', () => { it('should add JSON contains clause', () => { const query = builder.whereJsonContains('metadata', { key: 'value' }); // Test implementation }); }); // Update documentation // Add to README and API docs ``` ### ScyllaDB Specific Features When adding ScyllaDB-specific functionality: 1. **Research ScyllaDB Documentation** - Understand the feature thoroughly - Check version compatibility - Consider performance implications 2. **Implementation Pattern** ```ts // Add to QueryBuilder public newScyllaFeature(params: any): this { if (!this.driver.supportsFeature('new_feature')) { throw new Error('Feature not supported by current driver'); } // Implementation return this; } // Add to Grammar public compileNewFeature(query: QueryComponent): string { // CQL generation } // Add to Driver public supportsFeature(feature: string): boolean { return ['new_feature', ...otherFeatures].includes(feature); } ``` ## Performance Considerations ### Query Optimization 1. **Efficient Queries** - Minimize ALLOW FILTERING usage - Use appropriate partition keys - Implement proper pagination 2. **Connection Management** - Reuse connections - Implement connection pooling - Handle connection errors gracefully 3. **Memory Usage** - Avoid loading large result sets - Implement streaming for large operations - Clean up resources properly ### Benchmarking ```ts // Add performance tests for new features describe('Performance Tests', () => { it('should handle large result sets efficiently', async () => { const startTime = Date.now(); const results = await User.query().limit(10000).get(); const endTime = Date.now(); expect(endTime - startTime).toBeLessThan(5000); // 5 seconds max expect(results).toHaveLength(10000); }); }); ``` ## Release Process ### Version Management - Follow semantic versioning (SemVer) - Update CHANGELOG.md - Tag releases properly ### Breaking Changes 1. **Deprecation Period** - Mark old APIs as deprecated - Provide migration guide - Maintain backward compatibility when possible 2. **Documentation** - Update migration guide - Provide examples for new APIs - Document breaking changes clearly ## Getting Help - **Discord**: Join our community Discord server - **GitHub Issues**: For bug reports and feature requests - **GitHub Discussions**: For questions and general discussion - **Email**: maintainers@scyllinx.dev ## Recognition Contributors will be recognized in: - CONTRIBUTORS.md file - Release notes - Project documentation Thank you for contributing to ScyllinX! 🚀